<commit_before>#!/usr/bin/env node
// This is a load simulator, designed to hammer an Unhangout server with a huge
// number of sockets and connection traffic, to help us identify places where
// optimization is needed.
//
// To use:
//
// 1. On both the client and the server, copy the ``simulatorConf.js.example``
//    files to ``simulatorConf.js``, and edit the values inside.  Be sure to use
//    the same EVENT_ID for both CLIENT and SERVER configuration on both hosts.
//    Particular things to pay attention to:
//
//     - on the client: the UNHANGOUT_SESSION_SECRET parameter must be set to
//       the value of UNHANGOUT_SESSION_SECRET used by the server's
//       ``conf.json`` (that's its regular conf.json, not simulatorConf.js).
//
//     - both client and server must use the same EVENT_ID. Set it properly
//       in both places.  Set it to a sacrificial event that we can fill up
//       with test sessions.
//
// 2. On the server, run ``bin/prepare-load-simulator-data.js``.  This will
//    create a large number of sessions in the specified EVENT_ID event,
//    as well as a large number of test users.
//
// 3. Finally, on the client, run ``bin/load-simulator.js``, which will
//    commence the barrage.  You can use multiple different clients
//    simultaneously -- you may want to use different USER_RANGE values
//    for each client so that you get unique users from the different machines.
//
// 4. When finished, on the server, run:
//
//      bin/prepare-load-simulator-data.js --delete
//
//    which will delete all of the test users, sessions, and event indicated by
//    simulatorConf.js's SERVER section.
//
// If you get the error EMFILE (too many open files), try fixing it with:
//
//      ulimit -n 2048
//

var logger = require("../lib/logging.js").getLogger(),
    models = require("../lib/server-models.js"),
    simConf = require("../simulatorConf.js").CLIENT,
    _ = require("underscore"),
    events = require("events"),
    sock_client = require("sockjs-client-ws");

process.env.NODE_TLS_REJECT_UNAUTHORIZED = simConf.NODE_TLS_REJECT_UNAUTHORIZED;

var EVENT_ROOM_ID = "event/" + simConf.EVENT_ID;
models.USER_KEY_SALT = simConf.UNHANGOUT_SESSION_SECRET;

function RunningVariance() {
    // Numerically stable calculation of a running variance.
    // from http://math.stackexchange.com/a/116344/6677 and
    // http://www.johndcook.com/standard_deviation.html.
    // Calculate a running variance.
    var m, s, old_m, k;
    k = 0;
    this.nextSample = function(x) {
        k++;
        if (k == 1) {
            m = x;
            s = 0;
        } else {
            old_m = m;
            m = old_m + (x - old_m) / k;
            s = s + (x - old_m) * (x - m);
        };
    };
    this.getMean = function() {
        return m;
    };
    this.getVariance = function() {
        return (k <= 2) ? 0 : s / (k - 1);
    };
    this.getSD = function() {
        return Math.pow(this.getVariance(), 0.5);
    };
}

// Total variance for ack delays of various types for all users:
var ackDelays = {};
var queueLengths = {};
_.each(["chat", "blur", "focus", "join", "leave", "auth"], function(key) {
    ackDelays[key] = new RunningVariance();
    queueLengths[key] = new RunningVariance();
});

/*
 * Object holding a socket and its authorization/join state.
 */
function AuthSock(options) {
    this.state = "disconnected";
    this.userId = options.userId;
    this.sockKey = models.generateSockKey(this.userId);
    this.sock = sock_client.create(simConf.SERVER_URL + "/sock");
    this.init();
}

AuthSock.prototype = _.extend({
    init: function() {
        _.bindAll(this, "onerror", "write");

        this.sock.on("connection", _.bind(function() {
            this.write("auth", {key: this.sockKey, id: this.userId});
        }, this));
        this.messagesReceived = 0;
        this.messagesSent = 0;
        this.ackTable = {
            'auth': [],
            'join': [],
            'leave': [],
            'chat': [],
            'focus': [],
            'blur': [],
        }
        // Assume that any ack/err we get is responding to the oldest request
        // -- that is, we never get responses out of order.  May not be a
        // correct assumption, but probably good enough to get a sense of how
        // the server's performing.
        this.acknowledged = _.bind(function(type) {
            if (type in this.ackTable) {
                var last = this.ackTable[type].shift();
                if (last) {
                    ackDelays[type].nextSample(new Date().getTime() - last);
                } else {
                    logger.error("Received " + type +
                                 " -ack or -err without corresponding request");
                }
            }
        }, this);
        this.updateQueueLength = _.bind(function() {
            for (var type in this.ackTable) {
                queueLengths[type].nextSample(this.ackTable[type].length);
            }
        }, this);

        this.sock.on("data", _.bind(function(message) {
            this.messagesReceived += 1;
            var msg = JSON.parse(message);
            var isAck = msg.type.indexOf("-ack") != -1;
            var isErr = msg.type.indexOf("-err") != -1;
            if (isAck || isErr) {
                this.acknowledged(msg.type.substring(0, msg.type.length - 4));
            }
            switch (msg.type) {
                case 'join-err':
                    if (this.state == "joining") {
                        this.room = null;
                        this.state = "authorized";
                    } else {
                        this.onerror(msg);
                    }
                    break;
                case 'auth-ack':
                    this.state = "authorized";
                    break;
                case "join-ack":
                    this.state = "joined";
                    break;
                case "blur":
                    if (msg.args.id == this.userId) {
                        this.acknowledged("blur");
                    }
                    break;
                case "focus":
                    if (msg.args.id == this.userId) {
                        this.acknowledged("focus");
                    }
                    break;
                default:
                    if (msg.type.indexOf("-err") != -1) {
                        this.onerror(msg);
                    }
                    break;
            }
            this.emit("data", msg);
        }, this));
        this.sock.on("error", this.onerror);
    },
    onerror: function(err) {
        logger.error(this.userId + " error", err);

    },
    write: function(type, args) {
        this.messagesSent += 1;
        if (type in this.ackTable) {
            this.ackTable[type].push(new Date().getTime());
        }
        this.sock.write(JSON.stringify({type: type, args: args}));
    }
}, events.EventEmitter.prototype);

/*
 * Class representing a random user.  It periodically sends chat messages,
 * joins sessions, and connects and disconnects, and focuses/unfocuses.
 */
function Rando(options) {
    this.event = options.event;
    this.userId = options.userId;
    // Initialize with a random timeout, to mix up the clock ticks.
    setTimeout(_.bind(function() {
        this.init();
    }, this), Math.random() * 1000);
};
Rando.prototype = {
    init: function() {
        _.bindAll(this, "ontick");
        this.eventSock = new AuthSock({userId: this.userId});
        this.sessionSock = new AuthSock({userId: this.userId});
        setInterval(this.ontick, 1000);
    },
    ontick: function() {
        this.eventSock.updateQueueLength();
        this.sessionSock.updateQueueLength();
        // Event actions
        switch (this.eventSock.state) {
            case "disconnected":
            case "joining":
                break;
            case "authorized":
                // If we're authorized, but haven't started joining, join the
                // event with a 70% probability.
                if (Math.random() > 0.7) {
                    this.eventSock.state = "joining";
                    this.eventSock.write("join", {id: EVENT_ROOM_ID});
                }
                break;
            case "joined":
                if (Math.random() > 0.99) {
                    if (!simConf.DISABLE_CHAT) {
                        this.eventSock.write("chat", {
                            roomId: EVENT_ROOM_ID,
                            text: this.chatMessages[
                                Math.floor(Math.random() * this.chatMessages.length)
                            ]
                        });
                    }
                } else if (Math.random() > 0.99) {
                    if (!simConf.DISABLE_EVENT_LEAVING) {
                        this.eventSock.write("leave", { id: EVENT_ROOM_ID, });
                        this.eventSock.state = "authorized";
                    }
                } else if (Math.random() > 0.9) {
                    if (!simConf.DISABLE_BLUR) {
                        if (this.eventSock.focused) {
                            this.eventSock.write("blur", {roomId: EVENT_ROOM_ID});
                            this.eventSock.focused = false;
                        } else {
                            this.eventSock.write("focus", {roomId: EVENT_ROOM_ID});
                            this.eventSock.focused = true;
                        }
                    }
                }
                break;
        };
        // Session actions
        switch (this.sessionSock.state) {
            case "disconnected":
            case "joining":
                break;
            case "authorized":
                if (!simConf.DISABLE_SESSION_JOINING && Math.random() > 0.9) {
                    var sessionId = simConf.SESSION_RANGE[0] + Math.floor(
                        (simConf.SESSION_RANGE[1] - simConf.SESSION_RANGE[0]) * Math.random()
                    );
                    var roomId = "session/loadSession" + sessionId;
                    this.sessionSock.write("join", {id: roomId});
                    this.sessionSock.state = "joining";
                    this.sessionSock.room = roomId;
                }
                break;
            case "joined":
                if (!simConf.DISABLE_SESSION_LEAVING && Math.random() > 0.9) {
                    this.sessionSock.write("leave", {id: this.sessionSock.room});
                    this.sessionSock.state = "authorized";
                    this.sessionSock.room = null;
                }
                break;
        };
    },
    write: function(type, args) {
        this.sock.write(JSON.stringify({type: type, args: args}));
    },
    chatMessages: [
        "From fairest creatures we desire increase,",
        "That thereby beauty's rose might never die,",
        "But as the riper should by time decease,",
        "His tender heir might bear his memory;",
        "But thou, contracted to thine own bright eyes,",
        "Feed'st thy light's flame with self-substantial fuel,",
        "Making a famine where abundance lies,",
        "Thyself thy foe, to thy sweet self too cruel.",
        "Thou, that art now the world's fresh ornament",
        "And only herald to the gaudy spring,",
        "Within thine own bud buriest thy content",
        "And, tender churl, mak'st waste in niggarding.",
        "    Pity the world, or else this glutton be,",
        "    To eat the world's due, by the grave and thee."
    ]
};

function main() {
    randos = [];
    for (var i = simConf.USER_RANGE[0]; i < simConf.USER_RANGE[1]; i++) {
        randos.push(new Rando({userId: "loadUser" + i}));
    }
    logger.warn(
        "Random users loading page: " + simConf.SERVER_URL + "/" + EVENT_ROOM_ID
    );
    function printAggregations(agg, key) {
        var mean = agg.getMean();
        mean = mean ? mean.toFixed(2) : mean;
        var sd = agg.getSD();
        sd = sd ? sd.toFixed(2) : sd;
        while (key.length < 8) {
            key = key + " ";
        }
        logger.info("  " + key + ": mean " + mean + "; std dev " + sd);
    }
    // Reporting to the console
    var messagesSentPerSecond = new RunningVariance();
    var messagesReceivedPerSecond = new RunningVariance();
    var then = new Date().getTime();
    var now;
    setInterval(function() {
        logger.info("------------------");
        logger.info("Average ack delays:");
        _.each(ackDelays, printAggregations);
        logger.info("Average queue lengths:");
        _.each(queueLengths, printAggregations);
        var eventStates = {}
        var sessionStates = {}
        var totalConnected = 0;
        var totalMessagesSent = 0;
        var totalMessagesReceived = 0;
        var now = new Date().getTime();
        for (var i = 0; i < randos.length; i++) {
            // Count socket states
            eventStates[randos[i].eventSock.state] = (eventStates[randos[i].eventSock.state] || 0) + 1;
            sessionStates[randos[i].sessionSock.state] = (sessionStates[randos[i].sessionSock.state] || 0) + 1;
            _.each([randos[i].eventSock, randos[i].sessionSock], function(sock) {
                if (sock.state != "disconnected") {
                    totalConnected += 1;
                }
                totalMessagesSent += sock.messagesSent;
                totalMessagesReceived += sock.messagesReceived;
                sock.messagesSent = 0;
                sock.messagesReceived = 0;
            });
        }
        messagesSentPerSecond.nextSample(totalMessagesSent / ((now - then) / 1000));
        messagesReceivedPerSecond.nextSample(totalMessagesReceived / ((now - then) / 1000));
        then = now;
        logger.info("Event socket states:");
        for (var key in eventStates)   { logger.info("  " + key + ": " + eventStates[key]); }
        logger.info("Session socket states:");
        for (var key in sessionStates) { logger.info("  " + key + ": " + sessionStates[key]); }
        logger.info("Total messages/sec:")
        printAggregations(messagesSentPerSecond, "sent");
        printAggregations(messagesReceivedPerSecond, "received");
        logger.info("Total connected sockets: " + totalConnected + " / " + (randos.length*2));
    }, 5000);
}

if (require.main === module) {
    main();
}
<commit_msg>Decrease probability of event leaving in simulator<commit_after>#!/usr/bin/env node
// This is a load simulator, designed to hammer an Unhangout server with a huge
// number of sockets and connection traffic, to help us identify places where
// optimization is needed.
//
// To use:
//
// 1. On both the client and the server, copy the ``simulatorConf.js.example``
//    files to ``simulatorConf.js``, and edit the values inside.  Be sure to use
//    the same EVENT_ID for both CLIENT and SERVER configuration on both hosts.
//    Particular things to pay attention to:
//
//     - on the client: the UNHANGOUT_SESSION_SECRET parameter must be set to
//       the value of UNHANGOUT_SESSION_SECRET used by the server's
//       ``conf.json`` (that's its regular conf.json, not simulatorConf.js).
//
//     - both client and server must use the same EVENT_ID. Set it properly
//       in both places.  Set it to a sacrificial event that we can fill up
//       with test sessions.
//
// 2. On the server, run ``bin/prepare-load-simulator-data.js``.  This will
//    create a large number of sessions in the specified EVENT_ID event,
//    as well as a large number of test users.
//
// 3. Finally, on the client, run ``bin/load-simulator.js``, which will
//    commence the barrage.  You can use multiple different clients
//    simultaneously -- you may want to use different USER_RANGE values
//    for each client so that you get unique users from the different machines.
//
// 4. When finished, on the server, run:
//
//      bin/prepare-load-simulator-data.js --delete
//
//    which will delete all of the test users, sessions, and event indicated by
//    simulatorConf.js's SERVER section.
//
// If you get the error EMFILE (too many open files), try fixing it with:
//
//      ulimit -n 2048
//

var logger = require("../lib/logging.js").getLogger(),
    models = require("../lib/server-models.js"),
    simConf = require("../simulatorConf.js").CLIENT,
    _ = require("underscore"),
    events = require("events"),
    sock_client = require("sockjs-client-ws");

process.env.NODE_TLS_REJECT_UNAUTHORIZED = simConf.NODE_TLS_REJECT_UNAUTHORIZED;

var EVENT_ROOM_ID = "event/" + simConf.EVENT_ID;
models.USER_KEY_SALT = simConf.UNHANGOUT_SESSION_SECRET;

function RunningVariance() {
    // Numerically stable calculation of a running variance.
    // from http://math.stackexchange.com/a/116344/6677 and
    // http://www.johndcook.com/standard_deviation.html.
    // Calculate a running variance.
    var m, s, old_m, k;
    k = 0;
    this.nextSample = function(x) {
        k++;
        if (k == 1) {
            m = x;
            s = 0;
        } else {
            old_m = m;
            m = old_m + (x - old_m) / k;
            s = s + (x - old_m) * (x - m);
        };
    };
    this.getMean = function() {
        return m;
    };
    this.getVariance = function() {
        return (k <= 2) ? 0 : s / (k - 1);
    };
    this.getSD = function() {
        return Math.pow(this.getVariance(), 0.5);
    };
}

// Total variance for ack delays of various types for all users:
var ackDelays = {};
var queueLengths = {};
_.each(["chat", "blur", "focus", "join", "leave", "auth"], function(key) {
    ackDelays[key] = new RunningVariance();
    queueLengths[key] = new RunningVariance();
});

/*
 * Object holding a socket and its authorization/join state.
 */
function AuthSock(options) {
    this.state = "disconnected";
    this.userId = options.userId;
    this.sockKey = models.generateSockKey(this.userId);
    this.sock = sock_client.create(simConf.SERVER_URL + "/sock");
    this.init();
}

AuthSock.prototype = _.extend({
    init: function() {
        _.bindAll(this, "onerror", "write");

        this.sock.on("connection", _.bind(function() {
            this.write("auth", {key: this.sockKey, id: this.userId});
        }, this));
        this.messagesReceived = 0;
        this.messagesSent = 0;
        this.ackTable = {
            'auth': [],
            'join': [],
            'leave': [],
            'chat': [],
            'focus': [],
            'blur': [],
        }
        // Assume that any ack/err we get is responding to the oldest request
        // -- that is, we never get responses out of order.  May not be a
        // correct assumption, but probably good enough to get a sense of how
        // the server's performing.
        this.acknowledged = _.bind(function(type) {
            if (type in this.ackTable) {
                var last = this.ackTable[type].shift();
                if (last) {
                    ackDelays[type].nextSample(new Date().getTime() - last);
                } else {
                    logger.error("Received " + type +
                                 " -ack or -err without corresponding request");
                }
            }
        }, this);
        this.updateQueueLength = _.bind(function() {
            for (var type in this.ackTable) {
                queueLengths[type].nextSample(this.ackTable[type].length);
            }
        }, this);

        this.sock.on("data", _.bind(function(message) {
            this.messagesReceived += 1;
            var msg = JSON.parse(message);
            var isAck = msg.type.indexOf("-ack") != -1;
            var isErr = msg.type.indexOf("-err") != -1;
            if (isAck || isErr) {
                this.acknowledged(msg.type.substring(0, msg.type.length - 4));
            }
            switch (msg.type) {
                case 'join-err':
                    if (this.state == "joining") {
                        this.room = null;
                        this.state = "authorized";
                    } else {
                        this.onerror(msg);
                    }
                    break;
                case 'auth-ack':
                    this.state = "authorized";
                    break;
                case "join-ack":
                    this.state = "joined";
                    break;
                case "blur":
                    if (msg.args.id == this.userId) {
                        this.acknowledged("blur");
                    }
                    break;
                case "focus":
                    if (msg.args.id == this.userId) {
                        this.acknowledged("focus");
                    }
                    break;
                default:
                    if (msg.type.indexOf("-err") != -1) {
                        this.onerror(msg);
                    }
                    break;
            }
            this.emit("data", msg);
        }, this));
        this.sock.on("error", this.onerror);
    },
    onerror: function(err) {
        logger.error(this.userId + " error", err);

    },
    write: function(type, args) {
        this.messagesSent += 1;
        if (type in this.ackTable) {
            this.ackTable[type].push(new Date().getTime());
        }
        this.sock.write(JSON.stringify({type: type, args: args}));
    }
}, events.EventEmitter.prototype);

/*
 * Class representing a random user.  It periodically sends chat messages,
 * joins sessions, and connects and disconnects, and focuses/unfocuses.
 */
function Rando(options) {
    this.event = options.event;
    this.userId = options.userId;
    // Initialize with a random timeout, to mix up the clock ticks.
    setTimeout(_.bind(function() {
        this.init();
    }, this), Math.random() * 1000);
};
Rando.prototype = {
    init: function() {
        _.bindAll(this, "ontick");
        this.eventSock = new AuthSock({userId: this.userId});
        this.sessionSock = new AuthSock({userId: this.userId});
        setInterval(this.ontick, 1000);
    },
    ontick: function() {
        this.eventSock.updateQueueLength();
        this.sessionSock.updateQueueLength();
        // Event actions
        switch (this.eventSock.state) {
            case "disconnected":
            case "joining":
                break;
            case "authorized":
                // If we're authorized, but haven't started joining, join the
                // event with a 70% probability.
                if (Math.random() > 0.7) {
                    this.eventSock.state = "joining";
                    this.eventSock.write("join", {id: EVENT_ROOM_ID});
                }
                break;
            case "joined":
                if (Math.random() > 0.99) {
                    if (!simConf.DISABLE_CHAT) {
                        this.eventSock.write("chat", {
                            roomId: EVENT_ROOM_ID,
                            text: this.chatMessages[
                                Math.floor(Math.random() * this.chatMessages.length)
                            ]
                        });
                    }
                } else if (Math.random() > 0.999) {
                    if (!simConf.DISABLE_EVENT_LEAVING) {
                        this.eventSock.write("leave", { id: EVENT_ROOM_ID, });
                        this.eventSock.state = "authorized";
                    }
                } else if (Math.random() > 0.9) {
                    if (!simConf.DISABLE_BLUR) {
                        if (this.eventSock.focused) {
                            this.eventSock.write("blur", {roomId: EVENT_ROOM_ID});
                            this.eventSock.focused = false;
                        } else {
                            this.eventSock.write("focus", {roomId: EVENT_ROOM_ID});
                            this.eventSock.focused = true;
                        }
                    }
                }
                break;
        };
        // Session actions
        switch (this.sessionSock.state) {
            case "disconnected":
            case "joining":
                break;
            case "authorized":
                if (!simConf.DISABLE_SESSION_JOINING && Math.random() > 0.9) {
                    var sessionId = simConf.SESSION_RANGE[0] + Math.floor(
                        (simConf.SESSION_RANGE[1] - simConf.SESSION_RANGE[0]) * Math.random()
                    );
                    var roomId = "session/loadSession" + sessionId;
                    this.sessionSock.write("join", {id: roomId});
                    this.sessionSock.state = "joining";
                    this.sessionSock.room = roomId;
                }
                break;
            case "joined":
                if (!simConf.DISABLE_SESSION_LEAVING && Math.random() > 0.9) {
                    this.sessionSock.write("leave", {id: this.sessionSock.room});
                    this.sessionSock.state = "authorized";
                    this.sessionSock.room = null;
                }
                break;
        };
    },
    write: function(type, args) {
        this.sock.write(JSON.stringify({type: type, args: args}));
    },
    chatMessages: [
        "From fairest creatures we desire increase,",
        "That thereby beauty's rose might never die,",
        "But as the riper should by time decease,",
        "His tender heir might bear his memory;",
        "But thou, contracted to thine own bright eyes,",
        "Feed'st thy light's flame with self-substantial fuel,",
        "Making a famine where abundance lies,",
        "Thyself thy foe, to thy sweet self too cruel.",
        "Thou, that art now the world's fresh ornament",
        "And only herald to the gaudy spring,",
        "Within thine own bud buriest thy content",
        "And, tender churl, mak'st waste in niggarding.",
        "    Pity the world, or else this glutton be,",
        "    To eat the world's due, by the grave and thee."
    ]
};

function main() {
    randos = [];
    for (var i = simConf.USER_RANGE[0]; i < simConf.USER_RANGE[1]; i++) {
        randos.push(new Rando({userId: "loadUser" + i}));
    }
    logger.warn(
        "Random users loading page: " + simConf.SERVER_URL + "/" + EVENT_ROOM_ID
    );
    function printAggregations(agg, key) {
        var mean = agg.getMean();
        mean = mean ? mean.toFixed(2) : mean;
        var sd = agg.getSD();
        sd = sd ? sd.toFixed(2) : sd;
        while (key.length < 8) {
            key = key + " ";
        }
        logger.info("  " + key + ": mean " + mean + "; std dev " + sd);
    }
    // Reporting to the console
    var messagesSentPerSecond = new RunningVariance();
    var messagesReceivedPerSecond = new RunningVariance();
    var then = new Date().getTime();
    var now;
    setInterval(function() {
        logger.info("------------------");
        logger.info("Average ack delays:");
        _.each(ackDelays, printAggregations);
        logger.info("Average queue lengths:");
        _.each(queueLengths, printAggregations);
        var eventStates = {}
        var sessionStates = {}
        var totalConnected = 0;
        var totalMessagesSent = 0;
        var totalMessagesReceived = 0;
        var now = new Date().getTime();
        for (var i = 0; i < randos.length; i++) {
            // Count socket states
            eventStates[randos[i].eventSock.state] = (eventStates[randos[i].eventSock.state] || 0) + 1;
            sessionStates[randos[i].sessionSock.state] = (sessionStates[randos[i].sessionSock.state] || 0) + 1;
            _.each([randos[i].eventSock, randos[i].sessionSock], function(sock) {
                if (sock.state != "disconnected") {
                    totalConnected += 1;
                }
                totalMessagesSent += sock.messagesSent;
                totalMessagesReceived += sock.messagesReceived;
                sock.messagesSent = 0;
                sock.messagesReceived = 0;
            });
        }
        messagesSentPerSecond.nextSample(totalMessagesSent / ((now - then) / 1000));
        messagesReceivedPerSecond.nextSample(totalMessagesReceived / ((now - then) / 1000));
        then = now;
        logger.info("Event socket states:");
        for (var key in eventStates)   { logger.info("  " + key + ": " + eventStates[key]); }
        logger.info("Session socket states:");
        for (var key in sessionStates) { logger.info("  " + key + ": " + sessionStates[key]); }
        logger.info("Total messages/sec:")
        printAggregations(messagesSentPerSecond, "sent");
        printAggregations(messagesReceivedPerSecond, "received");
        logger.info("Total connected sockets: " + totalConnected + " / " + (randos.length*2));
    }, 5000);
}

if (require.main === module) {
    main();
}
<|endoftext|>


<commit_before>/*global dump:false*/
/**
 * @file Template for outputting doc in json format.
 * Example of output can be seen in output.EXAMPLE.json) file.
 */
'use strict';

var handle = require('jsdoc/lib/jsdoc/util/error').handle;

exports.publish = function(data, options) {
    data({undocumented: true}).remove();
    var blocks = data({kind: 'block'}).distinct('name');

    if (blocks.length === 0) {
        //Is there a mod JS?
        blocks = data({kind: 'mod'}).distinct('block');
    }

    if (blocks.length > 1) {
        var errMsg = [
            'Error - only 1 block can be documented in one set',
            'Blocks currently in set:',
        ].concat(blocks).join('\n');

        handle(new Error(errMsg));
    }

    var docs = genBlockDocs(data, blocks[0]);

    dump(docs);
};

function genBlockDocs(data, blockName) {
    var members = data({memberof: blockName, access: {'!is': 'private'}}),
        base = genBlockBase(data, blockName),
        o = {
            blockName: blockName,
            description: genEntityDescription(data, 'block', blockName),
            jsParams: genBlockParams(data, blockName),
            methods: genMethods(members),
            properties: genProperties(members),
            events: genBlockEvents(members),
            mods: genBlockMods(data, blockName)
        };

    if (base.length > 0) {
        o.baseBlock = base;
    }

    return o;
}
/**
 * Generates description for a bem entity by merging
 * doclets descriptions from all levels
 */
function genEntityDescription(data, kind, entityName) {
    return data({kind: kind, name: entityName}).select('description').join('\n\n');
}

function genBlockBase(data, name) {
    return data({kind: 'block', name: name, baseBlock: {isUndefined: false}}).select('baseBlock');
}

function genBlockParams(data, blockName) {
    var params = data({kind: 'block', name: blockName}).first().params || [];
    return params.map(genParam);
}

function genMethods(members) {
    return members.filter({
            kind: 'function',
        })
        .distinct('name')
        .map(genBlockMethod.bind(null, members));
}

/**
 * Generates docs for a method.
 *
 * Descriptions got merged from all levels. Other params
 * will not be merged and first non-empty value will be used
 */
function genBlockMethod(members, name) {
    var res = {
        name: name,
        description: '',
        params: [],
        returns: [],
        deprecated: false,
        final: false
    };
    members.filter({
        kind: 'function',
        name: name
    }).each(function(doclet) {
        if (doclet.description) {
            res.description += doclet.description + '\n';
        }

        if (!res.access) {
            res.access = doclet.access;
        }

        if (!res.scope) {
            res.scope = doclet.scope;
        }

        if (res.returns.length === 0) {
            res.returns = genMethodReturns(doclet.returns);
        }

        if (res.params.length === 0) {
            res.params  = genMethodParams(doclet.params);
        }

        if (doclet.deprecated) {
            res.deprecated = doclet.deprecated;
        }

        if (doclet.final) {
            res.final = doclet.final;
        }
    });

    res.scope = res.scope || 'instance';
    res.access = res.access || 'public';

    return res;
}

function genMethodParams(params) {
    if (!params) {
        return [];
    }
    return params.map(genParam);
}

/**
 * Generates doc for method or event parameter
 */
function genParam(param) {
    var res = {
        name: param.name || '',
        description: param.description || '',
        optional: !!param.optional
    };
    if (param.type) {
        res.types = param.type.names.slice(0);
    }

    if (param.defaultvalue) {
        res['default'] = param.defaultvalue;
    }
    return res;
}

function genMethodReturns(returns) {
    if (!returns) {
        return [];
    }
    return returns.map(genMethodReturn);
}

function genMethodReturn(methodReturn) {
    var res = {
        description: methodReturn.description || ''
    };

    if (methodReturn.type) {
        res.types = methodReturn.type.names.slice(0);
    }
    return res;
}

function genProperties(members) {
    return members
        .filter({kind: 'member'})
        .distinct('name')
        .map(genProperty.bind(null, members));
}

/**
 * Generates docs for a property.
 *
 * Descriptions got merged from all levels. Other params
 * will not be merged and first non-empty value will be used
 */
function genProperty(members, name) {
    var res = {
        name: name,
        deprecated: false,
        description: '',
        types: []
    };

    members.filter({
        kind: 'member',
        name: name
    }).each(function(doclet) {
        if (doclet.description) {
            res.description += doclet.description + '\n';
        }

        if (!res.access) {
            res.access = doclet.access;
        }

        if (!res.scope) {
            res.scope = doclet.scope;
        }

        if (doclet.deprected) {
            res.deprecated = true;
        }
        if (res.types.length === 0 && doclet.type) {
            res.types = doclet.type.names.slice(0);
        }
    });

    res.scope = res.scope || 'instance';
    res.access = res.acccess || 'public';

    return res;
}

function genBlockMods(data, blockName) {
    var modDoclets = data({kind: 'mod', block: blockName});
    return modDoclets.map(genBlockMod.bind(null, data));
}

function genBlockMod(data, doclet) {

    var name = doclet.block + '_' + doclet.mod.name + '_' + doclet.mod.value,
        members = data({memberof: name, access: {'!is': 'private'}});

    return {
        name: doclet.mod.name,
        value: doclet.mod.value,
        methods: genMethods(members),
        properties: genProperties(members),
        events: genBlockEvents(members),
        description: genEntityDescription(data, 'mod', name),
    };
}

function genBlockEvents(members) {
    return members.filter({
        kind: 'event',
    }).map(genBlockEvent);
}

function genBlockEvent(doclet) {
    var params = doclet.params || [],
        res =  {
            name: doclet.name,
            description: doclet.description || '',
            params: params.map(genParam)
        };

    if (doclet.type) {
        res.data.types = doclet.type.names.slice(0);
    }
    return res;
}
<commit_msg>Support for new versions of jsdoc<commit_after>/**
 * @file Template for outputting doc in json format.
 * Example of output can be seen in output.EXAMPLE.json) file.
 */
'use strict';

var dump = require('jsdoc/util/dumper').dump,
    handle = require('jsdoc/lib/jsdoc/util/error').handle;

exports.publish = function(data, options) {
    data({undocumented: true}).remove();
    var blocks = data({kind: 'block'}).distinct('name');

    if (blocks.length === 0) {
        //Is there a mod JS?
        blocks = data({kind: 'mod'}).distinct('block');
    }

    if (blocks.length > 1) {
        var errMsg = [
            'Error - only 1 block can be documented in one set',
            'Blocks currently in set:',
        ].concat(blocks).join('\n');

        handle(new Error(errMsg));
    }

    var docs = genBlockDocs(data, blocks[0]);

    console.log(dump(docs));
};

function genBlockDocs(data, blockName) {
    var members = data({memberof: blockName, access: {'!is': 'private'}}),
        base = genBlockBase(data, blockName),
        o = {
            blockName: blockName,
            description: genEntityDescription(data, 'block', blockName),
            jsParams: genBlockParams(data, blockName),
            methods: genMethods(members),
            properties: genProperties(members),
            events: genBlockEvents(members),
            mods: genBlockMods(data, blockName)
        };

    if (base.length > 0) {
        o.baseBlock = base;
    }

    return o;
}
/**
 * Generates description for a bem entity by merging
 * doclets descriptions from all levels
 */
function genEntityDescription(data, kind, entityName) {
    return data({kind: kind, name: entityName}).select('description').join('\n\n');
}

function genBlockBase(data, name) {
    return data({kind: 'block', name: name, baseBlock: {isUndefined: false}}).select('baseBlock');
}

function genBlockParams(data, blockName) {
    var params = data({kind: 'block', name: blockName}).first().params || [];
    return params.map(genParam);
}

function genMethods(members) {
    return members.filter({
            kind: 'function',
        })
        .distinct('name')
        .map(genBlockMethod.bind(null, members));
}

/**
 * Generates docs for a method.
 *
 * Descriptions got merged from all levels. Other params
 * will not be merged and first non-empty value will be used
 */
function genBlockMethod(members, name) {
    var res = {
        name: name,
        description: '',
        params: [],
        returns: [],
        deprecated: false,
        final: false
    };
    members.filter({
        kind: 'function',
        name: name
    }).each(function(doclet) {
        if (doclet.description) {
            res.description += doclet.description + '\n';
        }

        if (!res.access) {
            res.access = doclet.access;
        }

        if (!res.scope) {
            res.scope = doclet.scope;
        }

        if (res.returns.length === 0) {
            res.returns = genMethodReturns(doclet.returns);
        }

        if (res.params.length === 0) {
            res.params  = genMethodParams(doclet.params);
        }

        if (doclet.deprecated) {
            res.deprecated = doclet.deprecated;
        }

        if (doclet.final) {
            res.final = doclet.final;
        }
    });

    res.scope = res.scope || 'instance';
    res.access = res.access || 'public';

    return res;
}

function genMethodParams(params) {
    if (!params) {
        return [];
    }
    return params.map(genParam);
}

/**
 * Generates doc for method or event parameter
 */
function genParam(param) {
    var res = {
        name: param.name || '',
        description: param.description || '',
        optional: !!param.optional
    };
    if (param.type) {
        res.types = param.type.names.slice(0);
    }

    if (param.defaultvalue) {
        res['default'] = param.defaultvalue;
    }
    return res;
}

function genMethodReturns(returns) {
    if (!returns) {
        return [];
    }
    return returns.map(genMethodReturn);
}

function genMethodReturn(methodReturn) {
    var res = {
        description: methodReturn.description || ''
    };

    if (methodReturn.type) {
        res.types = methodReturn.type.names.slice(0);
    }
    return res;
}

function genProperties(members) {
    return members
        .filter({kind: 'member'})
        .distinct('name')
        .map(genProperty.bind(null, members));
}

/**
 * Generates docs for a property.
 *
 * Descriptions got merged from all levels. Other params
 * will not be merged and first non-empty value will be used
 */
function genProperty(members, name) {
    var res = {
        name: name,
        deprecated: false,
        description: '',
        types: []
    };

    members.filter({
        kind: 'member',
        name: name
    }).each(function(doclet) {
        if (doclet.description) {
            res.description += doclet.description + '\n';
        }

        if (!res.access) {
            res.access = doclet.access;
        }

        if (!res.scope) {
            res.scope = doclet.scope;
        }

        if (doclet.deprected) {
            res.deprecated = true;
        }
        if (res.types.length === 0 && doclet.type) {
            res.types = doclet.type.names.slice(0);
        }
    });

    res.scope = res.scope || 'instance';
    res.access = res.acccess || 'public';

    return res;
}

function genBlockMods(data, blockName) {
    var modDoclets = data({kind: 'mod', block: blockName});
    return modDoclets.map(genBlockMod.bind(null, data));
}

function genBlockMod(data, doclet) {

    var name = doclet.block + '_' + doclet.mod.name + '_' + doclet.mod.value,
        members = data({memberof: name, access: {'!is': 'private'}});

    return {
        name: doclet.mod.name,
        value: doclet.mod.value,
        methods: genMethods(members),
        properties: genProperties(members),
        events: genBlockEvents(members),
        description: genEntityDescription(data, 'mod', name),
    };
}

function genBlockEvents(members) {
    return members.filter({
        kind: 'event',
    }).map(genBlockEvent);
}

function genBlockEvent(doclet) {
    var params = doclet.params || [],
        res =  {
            name: doclet.name,
            description: doclet.description || '',
            params: params.map(genParam)
        };

    if (doclet.type) {
        res.data.types = doclet.type.names.slice(0);
    }
    return res;
}
<|endoftext|>


<commit_before>/*
 * monitor.js: Core functionality for the Monitor object.
 *
 * (C) 2010 Nodejitsu Inc.
 * MIT LICENCE
 *
 */

var util = require('util'),
    fs = require('fs'),
    path = require('path'),
    events = require('events'),
    spawn = require('child_process').spawn,
    winston = require('winston'),
    watch = require('watch'),
    minimatch = require('minimatch'),
    psTree = require('ps-tree'),
    utile = require('utile'),
    forever = require('../forever');

//
// ### function Monitor (script, options)
// #### @script {string} Location of the target script to run.
// #### @options {Object} Configuration for this instance.
// Creates a new instance of forever with specified params.
//
var Monitor = exports.Monitor = function (script, options) {
  var self = this;
  events.EventEmitter.call(this);

  //
  // Setup basic configuration options
  //
  options          = options || {};
  this.silent      = options.silent || false;
  this.forever     = options.forever || false;
  this.killTree    = options.killTree !== false;
  this.uid         = options.uid || utile.randomString(4);
  this.pidFile     = options.pidFile || path.join(forever.config.get('pidPath'), this.uid + '.pid');
  this.max         = options.max;
  this.killTTL     = options.killTTL;
  this.childExists = false;
  this.times       = 0;

  //
  // Setup restart timing. These options control how quickly forever restarts
  // a child process as well as when to kill a "spinning" process
  //
  this.minUptime     = typeof options.minUptime !== 'number' ? 0 : options.minUptime;
  this.spinSleepTime = options.spinSleepTime || null;

  //
  // Setup the command to spawn and the options to pass
  // to that command.
  //
  this.command   = options.command || 'node';
  this.options   = options.options || [];
  this.spawnWith = options.spawnWith || {};
  this.sourceDir = options.sourceDir;
  this.cwd       = options.cwd || null;
  this.env       = options.env || {};
  this.hideEnv   = options.hideEnv || [];
  this._hideEnv  = {};

  //
  // Create a simple mapping of `this.hideEnv` to an easily indexable
  // object
  //
  this.hideEnv.forEach(function (key) {
    self._hideEnv[key] = true;
  });

  //
  // Setup watch configuration options
  //
  this.watchIgnoreDotFiles = options.watchIgnoreDotFiles || true;
  this.watchIgnorePatterns = options.watchIgnorePatterns || [];
  this.watchDirectory      = options.watchDirectory || this.sourceDir;

  if (options.watch) {
    this.watch();
  }

  //
  // Setup log files and logger for this instance.
  //
  this.logFile = options.logFile || path.join(forever.config.get('root'), this.uid + '.log');
  this.outFile = options.outFile;
  this.errFile = options.errFile;
  this.logger  = options.logger || new (winston.Logger)({
    transports: [new winston.transports.Console({ silent: this.silent })]
  });

  //
  // Extend from the winston logger.
  //
  this.logger.extend(this);

  if (Array.isArray(script)) {
    this.command = script[0];
    this.options = script.slice(1);
  }
  else {
    this.options.unshift(script);
  }

  if (this.sourceDir) {
    this.options[0] = path.join(this.sourceDir, this.options[0]);
  }

  // If we should log stdout, open a file buffer
  if (this.outFile) {
    this.stdout = fs.createWriteStream(this.outFile, { flags: 'a+', encoding: 'utf8', mode: '0666' });
  }

  // If we should log stderr, open a file buffer
  if (this.errFile) {
    this.stderr = fs.createWriteStream(this.errFile, { flags: 'a+', encoding: 'utf8', mode: '0666' });
  }

  //
  // Last if any hooks have been passed in attach
  // this instance to them
  //
  if (options.hooks && options.hooks.length > 0) {
    options.hooks.forEach(function (hook) {
      if (typeof hook === 'function') {
        return hook(self);
      }

      hook.attach(self);
    });
  }
};

// Inherit from events.EventEmitter
util.inherits(Monitor, events.EventEmitter);

//
// ### function start ([restart])
// #### @restart {boolean} Value indicating whether this is a restart.
// Start the process that this instance is configured for
//
Monitor.prototype.start = function (restart) {
  var self = this,
      child;

  if (this.running && !restart) {
    process.nextTick(function () {
      self.emit('error', new Error('Cannot start process that is already running.'));
    });
    return this;
  }

  child = this.trySpawn();
  if (!child) {
    process.nextTick(function () {
      self.emit('error', new Error('Target script does not exist: ' + self.options[0]));
    });
    return this;
  }

  this.ctime = Date.now();
  this.child = child;
  this.running = true;

  process.nextTick(function () {
    self.emit(restart ? 'restart' : 'start', self, self.data);
  });

  // Hook all stream data and process it
  function listenTo(stream) {
    function ldata(data) {
      if (!self.silent && !self[stream]) {
        //
        // If we haven't been silenced, and we don't have a file stream
        // to output to write to the process stdout stream
        //
        process.stdout.write(data);
      }
      else if (self[stream]) {
        //
        // If we have been given an output file for the stream, write to it
        //
        self[stream].write(data);
      }

      self.emit(stream, data);
    }

    child[stream].on('data', ldata);

    child.on('exit', function () {
      child[stream].removeListener('data', ldata);
    });
  }

  // Listen to stdout and stderr
  listenTo('stdout');
  listenTo('stderr');

  child.on('exit', function (code) {
    var spinning = Date.now() - self.ctime < self.minUptime;
    self.warn('Forever detected script exited with code: ' + code);

    function letChildDie() {
      self.running = false;
      self.forceStop = false;

      //
      // If had to write to an stdout file, close it
      //
      if (self.stdout) {
        self.stdout.end();
      }

      //
      // If had to write to an stderr file, close it
      //
      if (self.stderr) {
        self.stderr.end();
      }

      self.emit('exit', self, spinning);
    }

    function restartChild() {
      self.forceRestart = false;
      process.nextTick(function () {
        self.warn('Forever restarting script for ' + self.times + ' time');
        self.start(true);
      });
    }

    self.times++;

    if (self.forceStop || (!self.forever && self.times >= self.max)
      || (spinning && typeof self.spinSleepTime !== 'number') && !self.forceRestart) {
      letChildDie();
    }
    else if (spinning) {
      setTimeout(restartChild, self.spinSleepTime);
    }
    else {
      restartChild();
    }
  });

  return this;
};

//
// ### function trySpawn()
// Tries to spawn the target Forever child process. Depending on
// configuration, it checks the first argument of the options
// to see if the file exists. This is useful is you are
// trying to execute a script with an env: e.g. node myfile.js
//
Monitor.prototype.trySpawn = function () {
  if (this.command === 'node' || (this.checkFile && !this.childExists)) {
    try {
      var stats = fs.statSync(this.options[0]);
      this.childExists = true;
    }
    catch (ex) {
      return false;
    }
  }

  this.spawnWith.cwd = this.cwd || this.spawnWith.cwd;
  this.spawnWith.env = this._getEnv();

  return spawn(this.command, this.options, this.spawnWith);
};

//
// ### @data {Object}
// Responds with the appropriate information about
// this `Monitor` instance and it's associated child process.
//
Monitor.prototype.__defineGetter__('data', function () {
  var self = this,
      childData;

  if (!this.running) {
    //
    // TODO: Return settings from this forever instance
    // with a state indicator that it is currently stopped.
    //
    return {};
  }

  childData = {
    ctime: this.ctime,
    command: this.command,
    file: this.options[0],
    foreverPid: process.pid,
    logFile: this.logFile,
    options: this.options.slice(1),
    pid: this.child.pid,
    silent: this.silent,
    uid: this.uid,
    spawnWith: this.spawnWith
  };

  ['pidFile', 'outFile', 'errFile', 'env', 'cwd'].forEach(function (key) {
    if (self[key]) {
      childData[key] = self[key];
    }
  });

  if (this.sourceDir) {
    childData.sourceDir = this.sourceDir;
    childData.file = childData.file.replace(this.sourceDir + '/', '');
  }

  this.childData = childData;
  return this.childData;

  //
  // Setup the forever process to listen to
  // SIGINT and SIGTERM events so that we can
  // clean up the *.pid file
  //
  // Remark: This should work, but the fd gets screwed up
  //         with the daemon process.
  //
  // process.on('SIGINT', function () {
  //   process.exit(0);
  // });
  //
  // process.on('SIGTERM', function () {
  //   process.exit(0);
  // });
  // process.on('exit', function () {
  //   fs.unlinkSync(childPath);
  // });
});

//
// ### function restart ()
// Restarts the target script associated with this instance.
//
Monitor.prototype.restart = function () {
  this.forceRestart = true;
  return this.kill(false);
};

//
// ### function stop ()
// Stops the target script associated with this instance. Prevents it from auto-respawning
//
Monitor.prototype.stop = function () {
  return this.kill(true);
};

//
// ### function kill (forceStop)
// #### @forceStop {boolean} Value indicating whether short circuit forever auto-restart.
// Kills the ChildProcess object associated with this instance.
//
Monitor.prototype.kill = function (forceStop) {
  var self = this,
    child = this.child;

  if (!child || !this.running) {
    process.nextTick(function () {
      self.emit('error', new Error('Cannot stop process that is not running.'));
    });
  }
  else {
    //
    // Set an instance variable here to indicate this
    // stoppage is forced so that when `child.on('exit', ..)`
    // fires in `Monitor.prototype.start` we can short circuit
    // and prevent auto-restart
    //
    var toKill = [this.child.pid];
    if (forceStop) {
      this.forceStop = true;
      //
      // If we have a time before we truly kill forcefully, set up a timer
      //
      if (this.killTTL) {
        var timer = setTimeout(function () {
          toKill.forEach(function(pid) {
            try {
              process.kill(pid, 'SIGKILL');
            }
            catch (e) {
              //conditions for races may exist, this is most likely an ESRCH
              //these should be ignored, and then we should emit that it is dead
            }
          });
          self.emit('stop', this.childData);
        }, this.killTTL);
        child.on('exit', function () {
          clearTimeout(timer);
        });
      }
    }

    forever.kill(this.child.pid, this.killTree, function() {
      self.emit('stop', self.childData);
    });
  }

  return this;
};

//
// ### function watch ()
// Starts watching directory tree for changes.
//
Monitor.prototype.watch = function () {
  var self = this;

  fs.readFile(path.join(this.watchDirectory, '.foreverignore'), 'utf8', function (err, data) {
    if (err) {
      forever.log.warn('Could not read .foreverignore file.');
      return forever.log.silly(err.message);
    }

    Array.prototype.push.apply(self.watchIgnorePatterns, data.split('\n'));
  });

  watch.watchTree(this.watchDirectory, function (f, curr, prev) {
    if (!(curr === null && prev === null && typeof f === 'object')) {
      //
      // `curr` == null && `prev` == null && typeof f == "object" when watch
      // finishes walking the tree to add listeners. We don't need to know
      // about it, so we simply ignore it (anything different means that
      // some file changed/was removed/created - that's what we want to know).
      //
      if (self._watchFilter(f)) {
        self.info('restaring script because ' + f + ' changed');
        self.restart();
      }
    }
  });
};

//
// ### @private function _watchFilter
// #### @file {string} File name
// Determines whether we should restart if `file` change (@mikeal's filtering
// is pretty messed up).
//
Monitor.prototype._watchFilter = function (fileName) {
  if (this.watchIgnoreDotFiles && path.basename(fileName)[0] === '.') {
    return false;
  }

  for (var key in this.watchIgnorePatterns) {
    if (minimatch(fileName, this.watchIgnorePatterns[key], { matchBase: this.watchDirectory })) {
      return false;
    }
  }

  return true;
};

//
// ### @private function _getEnv ()
// Returns the environment variables that should be passed along
// to the target process spawned by this instance.
//
Monitor.prototype._getEnv = function () {
  var self = this,
      merged = {};

  function addKey(key, source) {
    merged[key] = source[key];
  }

  //
  // Mixin the key:value pairs from `process.env` and the custom
  // environment variables in `this.env`.
  //
  Object.keys(process.env).forEach(function (key) {
    if (!self._hideEnv[key]) {
      addKey(key, process.env);
    }
  });

  Object.keys(this.env).forEach(function (key) {
    addKey(key, self.env);
  });

  return merged;
};
<commit_msg>[api] Remove redudant `forever` options<commit_after>/*
 * monitor.js: Core functionality for the Monitor object.
 *
 * (C) 2010 Nodejitsu Inc.
 * MIT LICENCE
 *
 */

var util = require('util'),
    fs = require('fs'),
    path = require('path'),
    events = require('events'),
    spawn = require('child_process').spawn,
    winston = require('winston'),
    watch = require('watch'),
    minimatch = require('minimatch'),
    psTree = require('ps-tree'),
    utile = require('utile'),
    forever = require('../forever');

//
// ### function Monitor (script, options)
// #### @script {string} Location of the target script to run.
// #### @options {Object} Configuration for this instance.
// Creates a new instance of forever with specified params.
//
var Monitor = exports.Monitor = function (script, options) {
  var self = this;
  events.EventEmitter.call(this);

  //
  // Setup basic configuration options
  //
  options          = options || {};
  this.silent      = options.silent || false;
  this.killTree    = options.killTree !== false;
  this.uid         = options.uid || utile.randomString(4);
  this.pidFile     = options.pidFile || path.join(forever.config.get('pidPath'), this.uid + '.pid');
  this.max         = options.max;
  this.killTTL     = options.killTTL;
  this.childExists = false;
  this.times       = 0;

  //
  // Setup restart timing. These options control how quickly forever restarts
  // a child process as well as when to kill a "spinning" process
  //
  this.minUptime     = typeof options.minUptime !== 'number' ? 0 : options.minUptime;
  this.spinSleepTime = options.spinSleepTime || null;

  //
  // Setup the command to spawn and the options to pass
  // to that command.
  //
  this.command   = options.command || 'node';
  this.options   = options.options || [];
  this.spawnWith = options.spawnWith || {};
  this.sourceDir = options.sourceDir;
  this.cwd       = options.cwd || null;
  this.env       = options.env || {};
  this.hideEnv   = options.hideEnv || [];
  this._hideEnv  = {};

  //
  // Create a simple mapping of `this.hideEnv` to an easily indexable
  // object
  //
  this.hideEnv.forEach(function (key) {
    self._hideEnv[key] = true;
  });

  //
  // Setup watch configuration options
  //
  this.watchIgnoreDotFiles = options.watchIgnoreDotFiles || true;
  this.watchIgnorePatterns = options.watchIgnorePatterns || [];
  this.watchDirectory      = options.watchDirectory || this.sourceDir;

  if (options.watch) {
    this.watch();
  }

  //
  // Setup log files and logger for this instance.
  //
  this.logFile = options.logFile || path.join(forever.config.get('root'), this.uid + '.log');
  this.outFile = options.outFile;
  this.errFile = options.errFile;
  this.logger  = options.logger || new (winston.Logger)({
    transports: [new winston.transports.Console({ silent: this.silent })]
  });

  //
  // Extend from the winston logger.
  //
  this.logger.extend(this);

  if (Array.isArray(script)) {
    this.command = script[0];
    this.options = script.slice(1);
  }
  else {
    this.options.unshift(script);
  }

  if (this.sourceDir) {
    this.options[0] = path.join(this.sourceDir, this.options[0]);
  }

  // If we should log stdout, open a file buffer
  if (this.outFile) {
    this.stdout = fs.createWriteStream(this.outFile, { flags: 'a+', encoding: 'utf8', mode: '0666' });
  }

  // If we should log stderr, open a file buffer
  if (this.errFile) {
    this.stderr = fs.createWriteStream(this.errFile, { flags: 'a+', encoding: 'utf8', mode: '0666' });
  }

  //
  // Last if any hooks have been passed in attach
  // this instance to them
  //
  if (options.hooks && options.hooks.length > 0) {
    options.hooks.forEach(function (hook) {
      if (typeof hook === 'function') {
        return hook(self);
      }

      hook.attach(self);
    });
  }
};

// Inherit from events.EventEmitter
util.inherits(Monitor, events.EventEmitter);

//
// ### function start ([restart])
// #### @restart {boolean} Value indicating whether this is a restart.
// Start the process that this instance is configured for
//
Monitor.prototype.start = function (restart) {
  var self = this,
      child;

  if (this.running && !restart) {
    process.nextTick(function () {
      self.emit('error', new Error('Cannot start process that is already running.'));
    });
    return this;
  }

  child = this.trySpawn();
  if (!child) {
    process.nextTick(function () {
      self.emit('error', new Error('Target script does not exist: ' + self.options[0]));
    });
    return this;
  }

  this.ctime = Date.now();
  this.child = child;
  this.running = true;

  process.nextTick(function () {
    self.emit(restart ? 'restart' : 'start', self, self.data);
  });

  // Hook all stream data and process it
  function listenTo(stream) {
    function ldata(data) {
      if (!self.silent && !self[stream]) {
        //
        // If we haven't been silenced, and we don't have a file stream
        // to output to write to the process stdout stream
        //
        process.stdout.write(data);
      }
      else if (self[stream]) {
        //
        // If we have been given an output file for the stream, write to it
        //
        self[stream].write(data);
      }

      self.emit(stream, data);
    }

    child[stream].on('data', ldata);

    child.on('exit', function () {
      child[stream].removeListener('data', ldata);
    });
  }

  // Listen to stdout and stderr
  listenTo('stdout');
  listenTo('stderr');

  child.on('exit', function (code) {
    var spinning = Date.now() - self.ctime < self.minUptime;
    self.warn('Forever detected script exited with code: ' + code);

    function letChildDie() {
      self.running = false;
      self.forceStop = false;

      //
      // If had to write to an stdout file, close it
      //
      if (self.stdout) {
        self.stdout.end();
      }

      //
      // If had to write to an stderr file, close it
      //
      if (self.stderr) {
        self.stderr.end();
      }

      self.emit('exit', self, spinning);
    }

    function restartChild() {
      self.forceRestart = false;
      process.nextTick(function () {
        self.warn('Forever restarting script for ' + self.times + ' time');
        self.start(true);
      });
    }

    self.times++;

    if (self.forceStop || (self.times >= self.max)
      || (spinning && typeof self.spinSleepTime !== 'number') && !self.forceRestart) {
      letChildDie();
    }
    else if (spinning) {
      setTimeout(restartChild, self.spinSleepTime);
    }
    else {
      restartChild();
    }
  });

  return this;
};

//
// ### function trySpawn()
// Tries to spawn the target Forever child process. Depending on
// configuration, it checks the first argument of the options
// to see if the file exists. This is useful is you are
// trying to execute a script with an env: e.g. node myfile.js
//
Monitor.prototype.trySpawn = function () {
  if (this.command === 'node' || (this.checkFile && !this.childExists)) {
    try {
      var stats = fs.statSync(this.options[0]);
      this.childExists = true;
    }
    catch (ex) {
      return false;
    }
  }

  this.spawnWith.cwd = this.cwd || this.spawnWith.cwd;
  this.spawnWith.env = this._getEnv();

  return spawn(this.command, this.options, this.spawnWith);
};

//
// ### @data {Object}
// Responds with the appropriate information about
// this `Monitor` instance and it's associated child process.
//
Monitor.prototype.__defineGetter__('data', function () {
  var self = this,
      childData;

  if (!this.running) {
    //
    // TODO: Return settings from this forever instance
    // with a state indicator that it is currently stopped.
    //
    return {};
  }

  childData = {
    ctime: this.ctime,
    command: this.command,
    file: this.options[0],
    foreverPid: process.pid,
    logFile: this.logFile,
    options: this.options.slice(1),
    pid: this.child.pid,
    silent: this.silent,
    uid: this.uid,
    spawnWith: this.spawnWith
  };

  ['pidFile', 'outFile', 'errFile', 'env', 'cwd'].forEach(function (key) {
    if (self[key]) {
      childData[key] = self[key];
    }
  });

  if (this.sourceDir) {
    childData.sourceDir = this.sourceDir;
    childData.file = childData.file.replace(this.sourceDir + '/', '');
  }

  this.childData = childData;
  return this.childData;

  //
  // Setup the forever process to listen to
  // SIGINT and SIGTERM events so that we can
  // clean up the *.pid file
  //
  // Remark: This should work, but the fd gets screwed up
  //         with the daemon process.
  //
  // process.on('SIGINT', function () {
  //   process.exit(0);
  // });
  //
  // process.on('SIGTERM', function () {
  //   process.exit(0);
  // });
  // process.on('exit', function () {
  //   fs.unlinkSync(childPath);
  // });
});

//
// ### function restart ()
// Restarts the target script associated with this instance.
//
Monitor.prototype.restart = function () {
  this.forceRestart = true;
  return this.kill(false);
};

//
// ### function stop ()
// Stops the target script associated with this instance. Prevents it from auto-respawning
//
Monitor.prototype.stop = function () {
  return this.kill(true);
};

//
// ### function kill (forceStop)
// #### @forceStop {boolean} Value indicating whether short circuit forever auto-restart.
// Kills the ChildProcess object associated with this instance.
//
Monitor.prototype.kill = function (forceStop) {
  var self = this,
    child = this.child;

  if (!child || !this.running) {
    process.nextTick(function () {
      self.emit('error', new Error('Cannot stop process that is not running.'));
    });
  }
  else {
    //
    // Set an instance variable here to indicate this
    // stoppage is forced so that when `child.on('exit', ..)`
    // fires in `Monitor.prototype.start` we can short circuit
    // and prevent auto-restart
    //
    var toKill = [this.child.pid];
    if (forceStop) {
      this.forceStop = true;
      //
      // If we have a time before we truly kill forcefully, set up a timer
      //
      if (this.killTTL) {
        var timer = setTimeout(function () {
          toKill.forEach(function(pid) {
            try {
              process.kill(pid, 'SIGKILL');
            }
            catch (e) {
              //conditions for races may exist, this is most likely an ESRCH
              //these should be ignored, and then we should emit that it is dead
            }
          });
          self.emit('stop', this.childData);
        }, this.killTTL);
        child.on('exit', function () {
          clearTimeout(timer);
        });
      }
    }

    forever.kill(this.child.pid, this.killTree, function() {
      self.emit('stop', self.childData);
    });
  }

  return this;
};

//
// ### function watch ()
// Starts watching directory tree for changes.
//
Monitor.prototype.watch = function () {
  var self = this;

  fs.readFile(path.join(this.watchDirectory, '.foreverignore'), 'utf8', function (err, data) {
    if (err) {
      forever.log.warn('Could not read .foreverignore file.');
      return forever.log.silly(err.message);
    }

    Array.prototype.push.apply(self.watchIgnorePatterns, data.split('\n'));
  });

  watch.watchTree(this.watchDirectory, function (f, curr, prev) {
    if (!(curr === null && prev === null && typeof f === 'object')) {
      //
      // `curr` == null && `prev` == null && typeof f == "object" when watch
      // finishes walking the tree to add listeners. We don't need to know
      // about it, so we simply ignore it (anything different means that
      // some file changed/was removed/created - that's what we want to know).
      //
      if (self._watchFilter(f)) {
        self.info('restaring script because ' + f + ' changed');
        self.restart();
      }
    }
  });
};

//
// ### @private function _watchFilter
// #### @file {string} File name
// Determines whether we should restart if `file` change (@mikeal's filtering
// is pretty messed up).
//
Monitor.prototype._watchFilter = function (fileName) {
  if (this.watchIgnoreDotFiles && path.basename(fileName)[0] === '.') {
    return false;
  }

  for (var key in this.watchIgnorePatterns) {
    if (minimatch(fileName, this.watchIgnorePatterns[key], { matchBase: this.watchDirectory })) {
      return false;
    }
  }

  return true;
};

//
// ### @private function _getEnv ()
// Returns the environment variables that should be passed along
// to the target process spawned by this instance.
//
Monitor.prototype._getEnv = function () {
  var self = this,
      merged = {};

  function addKey(key, source) {
    merged[key] = source[key];
  }

  //
  // Mixin the key:value pairs from `process.env` and the custom
  // environment variables in `this.env`.
  //
  Object.keys(process.env).forEach(function (key) {
    if (!self._hideEnv[key]) {
      addKey(key, process.env);
    }
  });

  Object.keys(this.env).forEach(function (key) {
    addKey(key, self.env);
  });

  return merged;
};
<|endoftext|>


<commit_before>const babelConfig = require('../.babelrc.js');
require('@babel/register').default(babelConfig);

require("core-js/stable");
require("regenerator-runtime/runtime");

window.Rx = require('rxjs');
window.$ = require('jquery');
window.__ = (x) => x;
window.n__ = (x) => x;
window._ = require('lodash');
window.sprintf = require('sprintf-js').sprintf;

require('whatwg-fetch');

require('../app/assets/javascripts/miq_global');

import { rxSubject, sendDataWithRx, listenToRx } from '../app/javascript/miq_observable';
ManageIQ.angular.rxSubject = rxSubject;
window.sendDataWithRx = sendDataWithRx;
window.listenToRx = listenToRx;

// configure enzyme adapter
import Enzyme from 'enzyme';
import EnzymeAdapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new EnzymeAdapter() });

// mock document.body.createTextRange for code mirror
document.body.createTextRange = () => ({
  setEnd: () => {},
  setStart: () => {},
  getBoundingClientRect: () => {
    return {right: 0};
  },
  getClientRects: () => {
    return {
      length: 0,
      left: 0,
      right: 0,
    };
  }});

// configure Redux store

import initializeStore from '../app/javascript/miq-redux/store';

ManageIQ.redux.store = initializeStore();
ManageIQ.redux.store.injectReducers();

Object.defineProperty(Array.prototype, 'flat', {
    value: function(depth = 1) {
      return this.reduce(function (flat, toFlatten) {
        return flat.concat((Array.isArray(toFlatten) && (depth>1)) ? toFlatten.flat(depth-1) : toFlatten);
      }, []);
    }
});

/**
 * mock redirect-back to avoid console errors about: error: not implemented: navigation (except hash changes)
 * unfortunately this cannot be mocked in some helper file it will only work in global setup
 */
jest.mock('../app/javascript/helpers/miq-redirect-back', () => jest.fn());
<commit_msg>jest: mock add_flash<commit_after>const babelConfig = require('../.babelrc.js');
require('@babel/register').default(babelConfig);

require("core-js/stable");
require("regenerator-runtime/runtime");

window.Rx = require('rxjs');
window.$ = require('jquery');
window.__ = (x) => x;
window.n__ = (x) => x;
window._ = require('lodash');
window.sprintf = require('sprintf-js').sprintf;

require('whatwg-fetch');

require('../app/assets/javascripts/miq_global');

import { rxSubject, sendDataWithRx, listenToRx } from '../app/javascript/miq_observable';
ManageIQ.angular.rxSubject = rxSubject;
window.sendDataWithRx = sendDataWithRx;
window.listenToRx = listenToRx;

// mock miq_application helpers
window.add_flash = (x) => true;

// configure enzyme adapter
import Enzyme from 'enzyme';
import EnzymeAdapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new EnzymeAdapter() });

// mock document.body.createTextRange for code mirror
document.body.createTextRange = () => ({
  setEnd: () => {},
  setStart: () => {},
  getBoundingClientRect: () => {
    return {right: 0};
  },
  getClientRects: () => {
    return {
      length: 0,
      left: 0,
      right: 0,
    };
  }});

// configure Redux store

import initializeStore from '../app/javascript/miq-redux/store';

ManageIQ.redux.store = initializeStore();
ManageIQ.redux.store.injectReducers();

Object.defineProperty(Array.prototype, 'flat', {
    value: function(depth = 1) {
      return this.reduce(function (flat, toFlatten) {
        return flat.concat((Array.isArray(toFlatten) && (depth>1)) ? toFlatten.flat(depth-1) : toFlatten);
      }, []);
    }
});

/**
 * mock redirect-back to avoid console errors about: error: not implemented: navigation (except hash changes)
 * unfortunately this cannot be mocked in some helper file it will only work in global setup
 */
jest.mock('../app/javascript/helpers/miq-redirect-back', () => jest.fn());
<|endoftext|>


<commit_before>const path = require('path');

module.exports = config => {
    config.set({
        basePath: path.join(__dirname, './'),
        singleRun: false,
        autoWatch: true,
        client: {
            clearContext: false
        },
        logLevel: config.LOG_INFO,
        junitReporter: {
            outputDir: './test-reports'
        },
        browsers: [
            'Chrome'
        ],
        frameworks: [
            'jasmine'
        ],
        files: [
            './spec-bundle.js'
        ],
        preprocessors: {
            ['./spec-bundle.js']: [
                'webpack'
            ]
        },
        reporters: ['progress', 'coverage', 'kjhtml'],
        coverageReporter: {
            type: 'html',
            dir: '../coverage/'
        },
        webpack: require('./webpack.test')(),
        webpackMiddleware: {
            noInfo: true
        },
        plugins: [
            require('karma-jasmine'),
            require('karma-junit-reporter'),
            require('karma-coverage'),
            require('karma-chrome-launcher'),
            require('karma-webpack'),
            require('karma-jasmine-html-reporter')
        ]
    })
}
<commit_msg>revert code<commit_after>const path = require('path');

module.exports = config => {
    config.set({
        basePath: path.join(__dirname, '../'),
        singleRun: false,
        autoWatch: true,
        client: {
            clearContext: false
        },
        logLevel: config.LOG_INFO,
        browsers: [
            'Chrome'
        ],
        frameworks: [
            'jasmine'
        ],
        files: [
            './config/spec-bundle.js'
        ],
        preprocessors: {
            ['./config/spec-bundle.js']: [
                'webpack'
            ]
        },
        reporters: ['progress', 'coverage', 'kjhtml'],
        coverageReporter: {
            type: 'html',
            dir: 'coverage/'
        },
        webpack: require('./webpack.test')(),
        webpackMiddleware: {
            noInfo: true
        },
        plugins: [
            require('karma-jasmine'),
            require('karma-coverage'),
            require('karma-chrome-launcher'),
            require('karma-webpack'),
            require('karma-jasmine-html-reporter')
        ]
    })
}
<|endoftext|>


<commit_before>// base path, that will be used to resolve files and exclude
basePath = '../';

// list of files / patterns to load in the browser
files = [
  JASMINE,
  JASMINE_ADAPTER,
  'example/components/angular/angular.js',
  'example/components/angular-http-auth/src/angular-http-auth.js',
  'example/components/angular-mocks/angular-mocks.js',
  'js/angular-authentication.js',
  'test/unit/*.spec.js'
];

// list of files to exclude
exclude = [];

// test results reporter to use
// possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
// reporters = ['progress'];

// web server port
// port = 9876;

// cli runner port
// runnerPort = 9100;

// enable / disable colors in the output (reporters and logs)
colors = true;

// level of logging
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
// logLevel = LOG_INFO;

// enable / disable watching file and executing tests whenever any file changes
autoWatch = false;

// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
browsers = ['PhantomJS'];

// If browser does not capture in given timeout [ms], kill it
// captureTimeout = 60000;

// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun = true;<commit_msg>Fix unit testing config file<commit_after>// base path, that will be used to resolve files and exclude
basePath = '../';

// list of files / patterns to load in the browser
files = [
  JASMINE,
  JASMINE_ADAPTER,
  'example/components/jquery/jquery.js',
  'example/components/angular/angular.js',
  'example/components/angular-http-auth/src/angular-http-auth.js',
  'example/components/angular-mocks/angular-mocks.js',
  'js/angular-authentication.js',
  'test/unit/*.spec.js'
];

// list of files to exclude
exclude = [];

// test results reporter to use
// possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
// reporters = ['progress'];

// web server port
// port = 9876;

// cli runner port
// runnerPort = 9100;

// enable / disable colors in the output (reporters and logs)
colors = true;

// level of logging
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
// logLevel = LOG_INFO;

// enable / disable watching file and executing tests whenever any file changes
autoWatch = false;

// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
browsers = ['PhantomJS'];

// If browser does not capture in given timeout [ms], kill it
// captureTimeout = 60000;

// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun = true;<|endoftext|>


<commit_before>window.addEventListener("load", function() {
	function getPath() {
		return new Promise(function(resolve, reject) {
			var template = document.getElementById("file-browser-template").innerHTML;
			var browser = document.getElementById("file-browser");
			var headers = new Headers();
			headers.append('Accept', 'application/json');
			var params = {
				credentials: "include",
				headers: headers
			}

			function directoryListing(route) {
				fetch(route, params).then(function(response) {
					return response.json();
				}).then(function(json) {
					browser.innerHTML = Mustache.render(template, json);

					bindUpload(browser.querySelector("form"));
					bindLinks(browser.querySelectorAll("a"));
				});
			}

			function bindUpload(form) {
				form.addEventListener("submit", function(event) {
					event.preventDefault();

					var fileInput = form.querySelector("input[type='file']");
					var data = new FormData();
					data.append("file", fileInput.files[0]);

					fetch(form.action, merge(params, {
						method: "POST",
						body: data
					})).then(function(response) {
						directoryListing(form.action);
					});
				});
			}

			function bindLinks(links) {
				Array.prototype.forEach.call(browser.querySelectorAll("a"), function(link) {
					link.addEventListener("click", function(event) {
						event.preventDefault();

						if(link.dataset.type === "directory") {
							directoryListing(link.href);
						} else {
							resolve(link.dataset.path);
							browser.innerHTML = "";
						}
					});
				});
			}

			function merge(one, two) {
				merged = JSON.parse(JSON.stringify(one));
				for(key in two) { merged[key] = two[key]; }
				return merged;
			}

			// TODO: remove hardcoded route
			directoryListing("/3/files");
		});
	}

	Array.prototype.forEach.call(document.querySelectorAll("input[type='file']"), function(fileInput) {
		fileInput.addEventListener("click", function(event) {
			event.preventDefault();
			getPath().then(function(path) {
				fileInput.previousElementSibling.value = path;
			});
		});
	});
});
<commit_msg>This has been moved to the assets folder<commit_after><|endoftext|>


<commit_before>'use strict';

angular.module('angularRestfulAuth')
    .controller('HomeCtrl', ['$rootScope', '$scope', '$location', '$localStorage', 'Main', function($rootScope, $scope, $location, $localStorage, Main) {
        $scope.signin = function() {
            var formData = {
                username: $scope.username,
                password: $scope.password
            }

            Main.signin(formData, function(res) {
                if (res.type == false) {
                    alert(res.data);
                } else {
                    Main.changeUser(Main.getUserFromToken($localStorage.token));
                    window.location = '/#/preview';
                }
            }, function() {
                $rootScope.error = 'Failed to signin';
            });
        };

        $scope.register = function() {
            var formData = {
                name: $scope.name,
                username: $scope.username,
                password: $scope.password,
                email: $scope.email
            };

            if ($scope.password === $scope.confirmPassword) {
                Main.register(formData, function(res) {
                    if (res.type == false) {
                        alert(res.data);
                    } else {
                        $localStorage.token = res.data.token;
                        window.location = '/';
                    }
                }, function() {
                    $rootScope.error = 'Failed to register';
                });
            } else {
                $scope.passwordWarning = "Passwords do not match";
            }

        };

        $scope.me = function() {
            Main.me(function(res){
                $scope.myDetails = res;
            }, function() {
                $rootScope.error = 'Failed to fetch details';
            });
        };

        $scope.logout = function() {
            Main.logout(function() {
                window.location = '/';
            }, function() {
                alert('Failed to logout');
            });
        };

        $scope.token = $localStorage.token;
    }])
.controller('AgoraPreviewCtrl', ['$rootScope', '$scope', '$location', '$localStorage', 'Main',
    function($rootScope, $scope, $location, $localStorage, Main) {
        function setSelected(arr, selected) {
            for (var i = 0; i < arr.length; i++) {
                arr[i] = selected == i;
            }
        }

        // if ($rootScope.)
        var currentUser = Main.getUserFromToken($localStorage.token);
        $scope.currentUser = currentUser;
        if (typeof currentUser !== 'undefined') {
            var pageContent = '/partials/agora';
            var focus = [true, false, false, false, false];

            if (window.location.href.indexOf('about') > -1) {
                pageContent += '/about.html';
                setSelected(focus, 4);
                $scope.canEdit = true;
            } else if (window.location.href.indexOf('contact') > -1) {
                pageContent += '/contact.html';
                setSelected(focus, 3);
                $scope.canEdit = true;
            } else if (window.location.href.indexOf('resume') > -1) {
                pageContent += '/resume.html';
                setSelected(focus, 2);
                $scope.canEdit = true;
            } else if (window.location.href.indexOf('portfolio') > -1) {
                pageContent += '/portfolio.html';
                setSelected(focus, 1);
                $scope.canEdit = false;
            } else {
                pageContent += '/home.html';
                setSelected(focus, 0);
                $scope.canEdit = true;
            }

            $scope.pageContent = pageContent;
            $scope.focus = focus;
        } else {
            window.location = '/#/signin';
        }

    }])
.controller('MeCtrl', ['$rootScope', '$scope', '$location', '$localStorage', 'Main',
    function($rootScope, $scope, $location, $localStorage, Main) {
        $scope.currentUser = Main.getUserFromToken($localStorage.token);
    }]);
<commit_msg>Fixed token not updating after sign in<commit_after>'use strict';

angular.module('angularRestfulAuth')
    .controller('HomeCtrl', ['$rootScope', '$scope', '$location', '$localStorage', 'Main', function($rootScope, $scope, $location, $localStorage, Main) {
        $scope.signin = function() {
            var formData = {
                username: $scope.username,
                password: $scope.password
            }

            Main.signin(formData, function(res) {
                if (res.type == false) {
                    alert(res.data);
                } else {
                    $localStorage.token = res.data.token;
                    Main.changeUser(Main.getUserFromToken($localStorage.token));
                    window.location = '/#/preview';
                }
            }, function() {
                $rootScope.error = 'Failed to signin';
            });
        };

        $scope.register = function() {
            var formData = {
                name: $scope.name,
                username: $scope.username,
                password: $scope.password,
                email: $scope.email
            };

            if ($scope.password === $scope.confirmPassword) {
                Main.register(formData, function(res) {
                    if (res.type == false) {
                        alert(res.data);
                    } else {
                        $localStorage.token = res.data.token;
                        window.location = '/';
                    }
                }, function() {
                    $rootScope.error = 'Failed to register';
                });
            } else {
                $scope.passwordWarning = "Passwords do not match";
            }

        };

        $scope.me = function() {
            Main.me(function(res){
                $scope.myDetails = res;
            }, function() {
                $rootScope.error = 'Failed to fetch details';
            });
        };

        $scope.logout = function() {
            Main.logout(function() {
                window.location = '/';
            }, function() {
                alert('Failed to logout');
            });
        };

        $scope.token = $localStorage.token;
    }])
.controller('AgoraPreviewCtrl', ['$rootScope', '$scope', '$location', '$localStorage', 'Main',
    function($rootScope, $scope, $location, $localStorage, Main) {
        function setSelected(arr, selected) {
            for (var i = 0; i < arr.length; i++) {
                arr[i] = selected == i;
            }
        }

        // if ($rootScope.)
        var currentUser = Main.getUserFromToken($localStorage.token);
        $scope.currentUser = currentUser;
        if (typeof currentUser !== 'undefined') {
            var pageContent = '/partials/agora';
            var focus = [true, false, false, false, false];

            if (window.location.href.indexOf('about') > -1) {
                pageContent += '/about.html';
                setSelected(focus, 4);
                $scope.canEdit = true;
            } else if (window.location.href.indexOf('contact') > -1) {
                pageContent += '/contact.html';
                setSelected(focus, 3);
                $scope.canEdit = true;
            } else if (window.location.href.indexOf('resume') > -1) {
                pageContent += '/resume.html';
                setSelected(focus, 2);
                $scope.canEdit = true;
            } else if (window.location.href.indexOf('portfolio') > -1) {
                pageContent += '/portfolio.html';
                setSelected(focus, 1);
                $scope.canEdit = false;
            } else {
                pageContent += '/home.html';
                setSelected(focus, 0);
                $scope.canEdit = true;
            }

            $scope.pageContent = pageContent;
            $scope.focus = focus;
        } else {
            window.location = '/#/signin';
        }

    }])
.controller('MeCtrl', ['$rootScope', '$scope', '$location', '$localStorage', 'Main',
    function($rootScope, $scope, $location, $localStorage, Main) {
        $scope.currentUser = Main.getUserFromToken($localStorage.token);
    }]);
<|endoftext|>


<commit_before>var k = 0
var map = exports.map =
function (read, map) {
  var _k = k++
  map = map || function (e) {return e}
  return function (end, cb) {
    read(end, function (end, data) {
      cb(end, !end ? map(data) : null)
    })
  }
}

var filter = exports.filter =
function (read, test) {
  //regexp
  if('object' === typeof test
    && 'function' === typeof test.test)
    test = test.test.bind(test)

  return function next (end, cb) {
    read(end, function (end, data) {
      if(!end && !test(data))
        return next(end, cb)
      cb(end, data)
    })
  }
}

var through = exports.through =
function (read, op) {
  return function (end, cb) {
    return read(end, function (end, data) {
      op && op(data)
      cb(end, data)
    })
  }
}

var take = exports.take =
function (read, test) {
  var ended = false
  if('number' === typeof test) {
    var n = test; test = function () {
      return n-- > 0
    }
  }
  return function (end, cb) {
    if(end) {
      if(!ended) return ended = end, readable(end, cb)
      cb(ended)
    }
    return read(null, function (end, data) {
      if(end || !test(data)) return read(end || true, cb)
      return cb(null, data)
    })
  }
}

var nextTick = process.nextTick

var highWaterMark = exports.highWaterMark =
function (read, highWaterMark) {
  var buffer = [], waiting = [], ended, reading = false
  highWaterMark = highWaterMark || 10

  function readAhead () {
    while(waiting.length && (buffer.length || ended))
      waiting.shift()(ended, ended ? null : buffer.shift())
  }

  function next () {
    if(ended || reading || buffer.length >= highWaterMark)
      return
    reading = true
    return read(ended, function (end, data) {
      reading = false
      ended = ended || end
      if(data != null) buffer.push(data)

      next(); readAhead()
    })
  }

  nextTick(next)

  return function (end, cb) {
    ended = ended || end
    waiting.push(cb)

    next(); readAhead()
  }
}

<commit_msg>only do op on through when not end<commit_after>var k = 0
var map = exports.map =
function (read, map) {
  var _k = k++
  map = map || function (e) {return e}
  return function (end, cb) {
    read(end, function (end, data) {
      cb(end, !end ? map(data) : null)
    })
  }
}

var filter = exports.filter =
function (read, test) {
  //regexp
  if('object' === typeof test
    && 'function' === typeof test.test)
    test = test.test.bind(test)

  return function next (end, cb) {
    read(end, function (end, data) {
      if(!end && !test(data))
        return next(end, cb)
      cb(end, data)
    })
  }
}

var through = exports.through =
function (read, op) {
  return function (end, cb) {
    return read(end, function (end, data) {
      if(!end) op && op(data)
      cb(end, data)
    })
  }
}

var take = exports.take =
function (read, test) {
  var ended = false
  if('number' === typeof test) {
    var n = test; test = function () {
      return n-- > 0
    }
  }
  return function (end, cb) {
    if(end) {
      if(!ended) return ended = end, readable(end, cb)
      cb(ended)
    }
    return read(null, function (end, data) {
      if(end || !test(data)) return read(end || true, cb)
      return cb(null, data)
    })
  }
}

var nextTick = process.nextTick

var highWaterMark = exports.highWaterMark =
function (read, highWaterMark) {
  var buffer = [], waiting = [], ended, reading = false
  highWaterMark = highWaterMark || 10

  function readAhead () {
    while(waiting.length && (buffer.length || ended))
      waiting.shift()(ended, ended ? null : buffer.shift())
  }

  function next () {
    if(ended || reading || buffer.length >= highWaterMark)
      return
    reading = true
    return read(ended, function (end, data) {
      reading = false
      ended = ended || end
      if(data != null) buffer.push(data)

      next(); readAhead()
    })
  }

  nextTick(next)

  return function (end, cb) {
    ended = ended || end
    waiting.push(cb)

    next(); readAhead()
  }
}

<|endoftext|>


<commit_before>let mpd = require('./mopidyws');
let comm = require('./comm');
var env = require('../env');

let mopidy = mpd.mopidy;
let SendMessage = comm.SendMessage;

function SearchMessage(searchResult){
  console.log("... looking for tracks")
  let msg = "";

  if (searchResult[1].tracks) {
    let tracks = searchResult[1].tracks.slice(0,5);
    let options = Array();

    for (let track of tracks){
      options.push({"text": `${track.name} – ${track.album.artists[0].name} – ${track.album.name} (${track.date})`,
      "value": track.uri});
    }

    msg = {
      "text": "I found these songs:",
      "response_type": "ephemeral",
      "attachments": [
        {
          "text": "Choose one to add to the playlist...",
          "fallback": "Found some songs...",
          "color": "#3AA3E3",
          "attachment_type": "default",
          "callback_id": "addSong",
          "actions": [
            {
              "name": "searchresults",
              "text": "Pick a song...",
              "type": "select",
              "options": options
            }
          ]
        }
      ]
    }
  }
  else {
    msg = {
      "text": "I found no songs!",
      "response_type": "ephemeral"
    }
  }
  return msg
}

function QueueMessage(tracks){
  let tracklist = Array();
  let msg = "";
  if (tracks.length > 0){
    for (let tltrack of tracks){
      let track = tltrack.track;
      tracklist.push(`${ tltrack.tlid }. ${track.name} – ${track.album.artists[0].name}`);
    }
    msg = {
      "text": "This is the current tracklist",
      "attachments": [{
        "title": "Tracklist",
        "text": tracklist.join("\n"),
      }],
      "response_type": "ephemeral"
    }
  }
  else {
    msg = {
      "text": "The playlist is empty! Go add some tracks with `/mopidy search`",
      "response_type": "ephemeral"
    }
  }

  return msg
}

function SkipMessage(track, user){
  let msg = "";
  console.log("Skipping track")
  if (track){
    msg = {
      "text": `"${track.name}" was skipped by @${user}`,
      "fallback": "Track skipped",
      "response_type": "in_channel"
    }
  }
  else {
    msg = {
      "text":"There's no track playing. Go add one with `/mopidy search`",
      "response_type": "ephimeral"
    }
  }
  return msg
}

function ClearMessage(user){
  console.log("Clearing playlist")
  let msg = {
    "text": `Playlist was cleared by @${user}`,
    "fallback": "Playlist cleared",
    "response_type": "in_channel"
  }
  return msg
}

function NowPlayingMessage(track){
  let msg = {
    "fallback": `Now playing ${track.name}`,
    "response_type": "in_channel",
    "attachments": [
      {
        "pretext": "_Now Playing:_",
        "title": track.name,
        "title_link": env.listenurl,
        "text": `${track.album.artists[0].name} - ${track.album.name}`,
        "mrkdwn_in": [
          "text",
          "pretext",
          "footer"
        ],
        "thumb_url": track.album.images[0] || "",
        "footer": `Listen here: ${env.listenurl}\nTry typing \`/mopidy\` to learn more!`,
        "footer_icon": "https://emoji.slack-edge.com/T2A1W6938/cassette/daff7a64b5c7863d.png",
      }
    ]
  }
  return msg
}

/***********************/

exports.postSong = function (){
  mopidy.playback.getCurrentTrack()
  .then(NowPlayingMessage)
  .done((msg) => { SendMessage(msg, env.slackhook) });
}

exports.search = function (query, url){
  mopidy.library.search({'any': [query]})
  .then(SearchMessage)
  .done((msg) => { SendMessage(msg, url) });
}

exports.list = function (url){
  mopidy.tracklist.getTlTracks()
  .then(QueueMessage)
  .done((msg)=>{ SendMessage(msg, url) });
}

exports.skip = function (user, url){
  mopidy.playback.getCurrentTrack()
  .then((track)=>{ return SkipMessage(track, user) })
  .then((msg) => { SendMessage(msg, url) })
  .done(()=>{ mopidy.playback.next(); });
}

exports.clear = function (user, url){
  mopidy.tracklist.clear()
  .then(()=>{ return ClearMessage(user) })
  .done((msg)=> { SendMessage(msg, url) });
}
<commit_msg>Se crea funcion add en backend<commit_after>let mpd = require('./mopidyws');
let comm = require('./comm');
var env = require('../env');

let mopidy = mpd.mopidy;
let SendMessage = comm.SendMessage;

function SearchMessage(searchResult){
  console.log("... looking for tracks")
  let msg = "";

  if (searchResult[1].tracks) {
    let tracks = searchResult[1].tracks.slice(0,5);
    let options = Array();

    for (let track of tracks){
      options.push({"text": `${track.name} – ${track.album.artists[0].name} – ${track.album.name} (${track.date})`,
      "value": track.uri});
    }

    msg = {
      "text": "I found these songs:",
      "response_type": "ephemeral",
      "attachments": [
        {
          "text": "Choose one to add to the playlist...",
          "fallback": "Found some songs...",
          "color": "#3AA3E3",
          "attachment_type": "default",
          "callback_id": "addSong",
          "actions": [
            {
              "name": "searchresults",
              "text": "Pick a song...",
              "type": "select",
              "options": options
            }
          ]
        }
      ]
    }
  }
  else {
    msg = {
      "text": "I found no songs!",
      "response_type": "ephemeral"
    }
  }
  return msg
}

function QueueMessage(tracks){
  let tracklist = Array();
  let msg = "";
  if (tracks.length > 0){
    for (let tltrack of tracks){
      let track = tltrack.track;
      tracklist.push(`${ tltrack.tlid }. ${track.name} – ${track.album.artists[0].name}`);
    }
    msg = {
      "text": "This is the current tracklist",
      "attachments": [{
        "title": "Tracklist",
        "text": tracklist.join("\n"),
      }],
      "response_type": "ephemeral"
    }
  }
  else {
    msg = {
      "text": "The playlist is empty! Go add some tracks with `/mopidy search`",
      "response_type": "ephemeral"
    }
  }

  return msg
}

function SkipMessage(track, user){
  let msg = "";
  console.log("Skipping track")
  if (track){
    msg = {
      "text": `"${track.name}" was skipped by @${user}`,
      "fallback": "Track skipped",
      "response_type": "in_channel"
    }
  }
  else {
    msg = {
      "text":"There's no track playing. Go add one with `/mopidy search`",
      "response_type": "ephimeral"
    }
  }
  return msg
}

function ClearMessage(user){
  console.log("Clearing playlist")
  let msg = {
    "text": `Playlist was cleared by @${user}`,
    "fallback": "Playlist cleared",
    "response_type": "in_channel"
  }
  return msg
}

function NowPlayingMessage(track){
  let msg = {
    "fallback": `Now playing ${track.name}`,
    "response_type": "in_channel",
    "attachments": [
      {
        "pretext": "_Now Playing:_",
        "title": track.name,
        "title_link": env.listenurl,
        "text": `${track.album.artists[0].name} - ${track.album.name}`,
        "mrkdwn_in": [
          "text",
          "pretext",
          "footer"
        ],
        "thumb_url": track.album.images[0] || "",
        "footer": `Listen here: ${env.listenurl}\nTry typing \`/mopidy\` to learn more!`,
        "footer_icon": "https://emoji.slack-edge.com/T2A1W6938/cassette/daff7a64b5c7863d.png",
      }
    ]
  }
  return msg
}

function isStopped(state){
  return state == "stopped"
}

function addSongUri(songuri){
  return mopidy.tracklist.add(null, null, songuri, null)
}

function addedMessage(tltracks){
  let msg = {
    "text": `${tltracks[0].track.name} – added to tracklist`,
    "response_type": "ephemeral"
  }
  return msg
}

function playStopped(){
  mopidy.playback.getState()
  .then((state)=>{
    if (state != "playing"){
      mopidy.playback.play()
    }
    return true;
  })
}

/***********************/

exports.postSong = function (){
  mopidy.playback.getCurrentTrack()
  .then(NowPlayingMessage)
  .done((msg) => { SendMessage(msg, env.slackhook) });
}

exports.search = function (query, url){
  mopidy.library.search({'any': [query]})
  .then(SearchMessage)
  .done((msg) => { SendMessage(msg, url) });
}

exports.list = function (url){
  mopidy.tracklist.getTlTracks()
  .then(QueueMessage)
  .done((msg)=>{ SendMessage(msg, url) });
}

exports.skip = function (user, url){
  mopidy.playback.getCurrentTrack()
  .then((track)=>{ return SkipMessage(track, user) })
  .then((msg) => { SendMessage(msg, url) })
  .done(()=>{ mopidy.playback.next(); });
}

exports.clear = function (user, url){
  mopidy.tracklist.clear()
  .then(()=>{ return ClearMessage(user) })
  .done((msg)=> { SendMessage(msg, url) });
}

exports.add = function (songuri, url){
  mopidy.playback.getState()
  .then(isStopped)
  .then((clear)=>{
    if (clear) {
      mopidy.tracklist.clear()
    }
    return songuri
  })
  .then(addSongUri)
  .then(addedMessage)
  .then((msg)=> { SendMessage(msg, url) })
  .then(playStopped)
}
<|endoftext|>