<commit_before>var current_page;
var responses_expected = 0;
var response_counter = 0;
var total_submission_list = [];

self.port.on("show", function onShow(url) {
    reset();
    current_page = url;
    getAllSubmissions(url);
});

self.port.on("hide", function onHide() {
    var e_links = document.getElementById("links");
    while (e_links.firstChild) {
        e_links.removeChild(e_links.firstChild);
    }
});

function reset() {
    current_page = "";
    responses_expected = 0;
    response_counter = 0;
    total_submission_list = [];
    document.getElementById("loading").style.display = "block";
}

function getAllSubmissions(url) {
    var reddit_urls = getAllURLVersions(url);
    responses_expected = reddit_urls.length;
    for (var i = 0; reddit_url = reddit_urls[i]; i++) {
        getJSON(reddit_url, handleResponse);
    }
}

function getAllURLVersions(url) {
    var host = url.split("/")[2];
    var result = [];
    if ((host === 'youtube.com' || host === 'www.youtube.com') && url.split("/")[3].indexOf('watch?') === 0) {
        youtubeID = function () {
            var query = url.substring(url.indexOf('?') + 1);
            var parameters = query.split('&');
            for (var i = 0; i < parameters.length; i++) {
                var pair = parameters[i].split('=');
                if (pair[0] === 'v') {
                    return pair[1];
                }
            }
            return '';
        }();
        result.push('http://api.reddit.com/search.json?q=' + encodeURIComponent('(url:' + youtubeID + ') (site:youtube.com OR site:youtu.be)'))
    } else {
        var without_http = "";
        if (url.slice(-1) === "/") {
            url = url.substring(0, url.length - 1);
        }
        result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent(url));
        result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent(url + "/"));
        if (url.indexOf('https') === 0) {
            without_http = url.substring(8);
            result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent(without_http));
            result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent(without_http + "/"));
        } else {
            if (url.indexOf('http') === 0) {
                without_http = url.substring(7);
                result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent("https://" + without_http));
                result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent("https://" + without_http + "/"));
            } else {
                without_http = url;
                result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent("https://" + without_http));
                result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent("https://" + without_http + "/"));
            }
        }
    }
    return result;
}

function handleResponse(jsonData) {
    var now = new Date();
    var now_timestamp = now.getTime(); // = new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds());
    var submissions = [];
    for (var i = 0; entry = jsonData.data.children[i]; i++) {
        submission_timestamp = entry.data.created_utc * 1000;
        submissions[i] = {
            link: "http://reddit.com" + entry.data.permalink,
            title: entry.data.title,
            score: entry.data.score + "",
            age: (now_timestamp - submission_timestamp),
            comments: entry.data.num_comments + "",
            subreddit: entry.data.subreddit,
        };
    }
    total_submission_list.push.apply(total_submission_list, submissions);

    response_counter++;
    if (response_counter === responses_expected) {
        document.getElementById("loading").style.display = "none";
        putSubmissionsIntoUI(total_submission_list);
    }
}

function putSubmissionsIntoUI(submissions) {
    if (submissions.length === 0) {
        var t_nonefound = document.createTextNode("This page hasn't been submitted to reddit, yet.");
        var t_submit = document.createTextNode("Create submission");

        var e_nonefound = document.createElement("div");
        e_nonefound.setAttribute("align", "center");
        e_nonefound.setAttribute("style", "font-size: 140% !important");
        e_nonefound.appendChild(t_nonefound);

        var e_submit = document.createElement("div");
        e_submit.setAttribute("class", "submitbutton");
        e_submit.appendChild(t_submit);

        e_submit.onclick = function () {
            openLink("http://www.reddit.com/submit?url=" + encodeURIComponent(current_page));
        };

        document.getElementById("links").appendChild(e_nonefound);
        document.getElementById("links").appendChild(e_submit);
    } else {
        for (var i = 0; submission = submissions[i]; i++) {
            var t_score = document.createTextNode(submission.score);
            var t_title = document.createTextNode(submission.title);
            var t_subreddit = document.createTextNode("/r/" + submission.subreddit);
            var t_comments = document.createTextNode(submission.comments + " comments");
            var t_time = document.createTextNode(formatAge(submission.age));

            var e_submission = document.createElement("div");
            e_submission.setAttribute("class", "submission");

            var e_score = document.createElement("div");
            e_score.setAttribute("class", "score");
            e_score.setAttribute("align", "center");
            e_score.appendChild(t_score);

            var e_title = document.createElement("div");
            e_title.setAttribute("class", "title");
            e_title.appendChild(t_title);

            var e_infocontainer = document.createElement("div");

            var e_subreddit = document.createElement("div");
            e_subreddit.setAttribute("class", "subreddit");
            e_subreddit.appendChild(t_subreddit);

            var e_comments = document.createElement("div");
            e_comments.setAttribute("class", "comment");
            e_comments.appendChild(t_comments);

            var e_time = document.createElement("div");
            e_time.setAttribute("class", "time");
            e_time.appendChild(t_time);

            e_infocontainer.appendChild(e_subreddit);
            e_infocontainer.appendChild(e_comments);
            e_infocontainer.appendChild(e_time);

            e_submission.appendChild(e_score);
            e_submission.appendChild(e_title);
            e_submission.appendChild(e_infocontainer);

            e_submission.onclick = function (submission_link) {
                return function () {
                    openLink(submission_link);
                }
            }(submission.link);

            document.getElementById("links").appendChild(e_submission);
        }
    }
    console.timeEnd("all");
}

function formatAge(age) {
    var MINUTE = 60 * 1000;
    var HOUR = MINUTE * 60;
    var DAY = HOUR * 24;
    var WEEK = DAY * 7;
    var YEAR = WEEK * 52;
    var MONTH = YEAR / 12;
    if (age < MINUTE) {
        return age + " seconds ago";
    } else if (age < HOUR) {
        return Math.floor(age / MINUTE) + " minutes ago";
    } else if (age < DAY) {
        return Math.floor(age / HOUR) + " hours ago";
    } else if (age < WEEK) {
        return Math.floor(age / DAY) + " days ago";
    } else if (age < MONTH) {
        return Math.floor(age / WEEK) + " weeks ago";
    } else if (age < YEAR) {
        return Math.floor(age / MONTH) + " months ago";
    } else {
        return Math.floor(age / YEAR) + " years ago";
    }
}

function openLink(url) {
    self.port.emit("open-link", url);
}

function getJSON(path, success) {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                if (success)
                    success(JSON.parse(xhr.responseText));
            }
        }
    };
    xhr.open("GET", path, true);
    xhr.send();
}<commit_msg>add submit-button at the end, even if submissions were found<commit_after>var current_page;
var responses_expected = 0;
var response_counter = 0;
var total_submission_list = [];

self.port.on("show", function onShow(url) {
    reset();
    current_page = url;
    getAllSubmissions(url);
});

self.port.on("hide", function onHide() {
    var e_links = document.getElementById("links");
    while (e_links.firstChild) {
        e_links.removeChild(e_links.firstChild);
    }
});

function reset() {
    current_page = "";
    responses_expected = 0;
    response_counter = 0;
    total_submission_list = [];
    document.getElementById("loading").style.display = "block";
}

function getAllSubmissions(url) {
    var reddit_urls = getAllURLVersions(url);
    responses_expected = reddit_urls.length;
    for (var i = 0; reddit_url = reddit_urls[i]; i++) {
        getJSON(reddit_url, handleResponse);
    }
}

function getAllURLVersions(url) {
    var host = url.split("/")[2];
    var result = [];
    if ((host === 'youtube.com' || host === 'www.youtube.com') && url.split("/")[3].indexOf('watch?') === 0) {
        youtubeID = function () {
            var query = url.substring(url.indexOf('?') + 1);
            var parameters = query.split('&');
            for (var i = 0; i < parameters.length; i++) {
                var pair = parameters[i].split('=');
                if (pair[0] === 'v') {
                    return pair[1];
                }
            }
            return '';
        }();
        result.push('http://api.reddit.com/search.json?q=' + encodeURIComponent('(url:' + youtubeID + ') (site:youtube.com OR site:youtu.be)'))
    } else {
        var without_http = "";
        if (url.slice(-1) === "/") {
            url = url.substring(0, url.length - 1);
        }
        result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent(url));
        result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent(url + "/"));
        if (url.indexOf('https') === 0) {
            without_http = url.substring(8);
            result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent(without_http));
            result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent(without_http + "/"));
        } else {
            if (url.indexOf('http') === 0) {
                without_http = url.substring(7);
                result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent("https://" + without_http));
                result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent("https://" + without_http + "/"));
            } else {
                without_http = url;
                result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent("https://" + without_http));
                result.push('http://www.reddit.com/api/info.json?url=' + encodeURIComponent("https://" + without_http + "/"));
            }
        }
    }
    return result;
}

function handleResponse(jsonData) {
    var now = new Date();
    var now_timestamp = now.getTime(); // = new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds());
    var submissions = [];
    for (var i = 0; entry = jsonData.data.children[i]; i++) {
        submission_timestamp = entry.data.created_utc * 1000;
        submissions[i] = {
            link: "http://reddit.com" + entry.data.permalink,
            title: entry.data.title,
            score: entry.data.score + "",
            age: (now_timestamp - submission_timestamp),
            comments: entry.data.num_comments + "",
            subreddit: entry.data.subreddit,
        };
    }
    total_submission_list.push.apply(total_submission_list, submissions);

    response_counter++;
    if (response_counter === responses_expected) {
        document.getElementById("loading").style.display = "none";
        putSubmissionsIntoUI(total_submission_list);
    }
}

function putSubmissionsIntoUI(submissions) {
    if (submissions.length === 0) {
        var t_nonefound = document.createTextNode("This page hasn't been submitted to reddit, yet.");

        var e_nonefound = document.createElement("div");
        e_nonefound.setAttribute("align", "center");
        e_nonefound.setAttribute("style", "font-size: 140% !important");
        e_nonefound.appendChild(t_nonefound);

        document.getElementById("links").appendChild(e_nonefound);
    } else {
        for (var i = 0; submission = submissions[i]; i++) {
            var t_score = document.createTextNode(submission.score);
            var t_title = document.createTextNode(submission.title);
            var t_subreddit = document.createTextNode("/r/" + submission.subreddit);
            var t_comments = document.createTextNode(submission.comments + " comments");
            var t_time = document.createTextNode(formatAge(submission.age));

            var e_submission = document.createElement("div");
            e_submission.setAttribute("class", "submission");

            var e_score = document.createElement("div");
            e_score.setAttribute("class", "score");
            e_score.setAttribute("align", "center");
            e_score.appendChild(t_score);

            var e_title = document.createElement("div");
            e_title.setAttribute("class", "title");
            e_title.appendChild(t_title);

            var e_infocontainer = document.createElement("div");

            var e_subreddit = document.createElement("div");
            e_subreddit.setAttribute("class", "subreddit");
            e_subreddit.appendChild(t_subreddit);

            var e_comments = document.createElement("div");
            e_comments.setAttribute("class", "comment");
            e_comments.appendChild(t_comments);

            var e_time = document.createElement("div");
            e_time.setAttribute("class", "time");
            e_time.appendChild(t_time);

            e_infocontainer.appendChild(e_subreddit);
            e_infocontainer.appendChild(e_comments);
            e_infocontainer.appendChild(e_time);

            e_submission.appendChild(e_score);
            e_submission.appendChild(e_title);
            e_submission.appendChild(e_infocontainer);

            e_submission.onclick = function (submission_link) {
                return function () {
                    openLink(submission_link);
                }
            }(submission.link);

            document.getElementById("links").appendChild(e_submission);
        }
    }
    var t_submit = document.createTextNode("Create submission");
    var e_submit = document.createElement("div");
    e_submit.setAttribute("class", "submitbutton");
    e_submit.appendChild(t_submit);

    e_submit.onclick = function () {
        openLink("http://www.reddit.com/submit?resubmit=true&url=" + encodeURIComponent(current_page));
    };
    document.getElementById("links").appendChild(e_submit);
}

function formatAge(age) {
    var MINUTE = 60 * 1000;
    var HOUR = MINUTE * 60;
    var DAY = HOUR * 24;
    var WEEK = DAY * 7;
    var YEAR = WEEK * 52;
    var MONTH = YEAR / 12;
    if (age < MINUTE) {
        return age + " seconds ago";
    } else if (age < HOUR) {
        return Math.floor(age / MINUTE) + " minutes ago";
    } else if (age < DAY) {
        return Math.floor(age / HOUR) + " hours ago";
    } else if (age < WEEK) {
        return Math.floor(age / DAY) + " days ago";
    } else if (age < MONTH) {
        return Math.floor(age / WEEK) + " weeks ago";
    } else if (age < YEAR) {
        return Math.floor(age / MONTH) + " months ago";
    } else {
        return Math.floor(age / YEAR) + " years ago";
    }
}

function openLink(url) {
    self.port.emit("open-link", url);
}

function getJSON(path, success) {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                if (success)
                    success(JSON.parse(xhr.responseText));
            }
        }
    };
    xhr.open("GET", path, true);
    xhr.send();
}<|endoftext|>


<commit_before>
// stomp-server/basicbroker.js -- Definition of the BasicBroker class

/*  Copyright 2010, Jason B. Alonso
 *
 *  This file is part of node-stomp-server.
 *
 *  node-stomp-server is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  node-stomp-server is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with node-stomp-server.  If not, see <http://www.gnu.org/licenses/>
 */

// Load modules
var middleware          = require('./middleware'),
    Broker              = require('./broker').Broker,
    Frame               = require('./frame').Frame,
    sys                 = require('sys');

// keep a table of subscriptions by regular expression
// keep a table mapping destinations to subscriptions
function BasicBroker(bufferLimit) {
    var self = this;

    // Extend Broker
    Broker.call(this, bufferLimit);

    // Initialize the subscription table
    this.subscriptions = [];

    // Initialize the destination table
    this.destinations = {};

    // Extend default middleware
    this._cf.recv_middleware.push({cbk: SubscribeRecvCurry(this)});
    this._cf.recv_middleware.push({cbk: SendRecvCurry(this)});

    // Install the automatic RECEIPT command middleware
    this._cf.recv_middleware.push({cbk: middleware.AutoReceiptRecv});

    // Clean up subscriptions on connection close
    var orig_newConnection = this.newConnection;
    this.newConnection = function(stream) {
        // Construct a connection
        var conn = orig_newConnection(stream);

        // Remove all connection subscriptions on close event
        conn.on('close', function(had_error) { self.unsubscribe(conn); });

        // Operation Complete!
        return conn;
    };
}
sys.inherits(BasicBroker, Broker);

// NOTE: This routine aims to escape all regular expression characters *except*
// for *
var escapeChars = /([\[\]\(\)\{\}\/\\\?\+\^\$\|\.\!])/;
function escapePattern(str) {
    return str.replace(escapeChars, '\\$1');
}

function patternFromDestination(destination) {
    // Escape most characters
    destination = escapePattern(destination);

    // Escape stars
    destination = destination.replace('.*', '.[^.]+');
    destination = destination.replace('*', '\\*');

    // Escape subtree references
    destination = destination.replace('>', '(\\.[^.]+)+');

    // Force complete string match
    destination = '^' + destination + '$';

    // Operation Complete!
    return new RegExp(destination);
}

BasicBroker.prototype.subscribe = function(conn, destination, id, ack) {
    // Set defaults
    if( ack == null ) ack = 'auto';

    // Prepare the subscription structure
    var subscription = {
        rex: patternFromDestination(destination),
        destination: destination,
        id: id,
        conn: conn,
        ack: ack,
    };

    // Make sure this is not a duplicate subscription
    for( var i in this.subscriptions )
        if( this.subscriptions[i].id == id &&
            this.subscriptions[i].conn == conn )
            throw new middleware.ProtocolError(conn, null, 'Duplicate SUBSCRIBE');

    // Add the entry to the subscription table
    this.subscriptions.push(subscription);

    // Update the destination table
    for( var i in this.destinations )
        if( subscription.rex.test(i) )
            this.destinations[i].subscriptions.push(subscription);
    // FIXME: transmit messages queued for destination

    // Operation Complete!
};

BasicBroker.prototype.getDestination = function(destination) {
    // Return an existing destination if possible
    if( destination in this.destinations )
        return this.destinations[destination];

    // Prepare a new destination structure
    var dst = {subscriptions: []};

    // Find matching subscriptions
    for( var i in this.subscriptions )
        if( this.subscriptions[i].rex.test(destination) )
            dst.subscriptions.push(this.subscriptions[i]);

    // Save the new destination
    this.destinations[destination] = dst;

    // Operation Complete!
    return dst;
};

BasicBroker.prototype.unsubscribe = function(conn, destination, id) {
    // Make sure this subscription can be found
    var idx = -1;
    for( var i in this.subscriptions ) {
        if( conn != this.subscriptions[i].conn ) continue;
        if( id ) {
            if( id == this.subscriptions[i].id &&
                ( ( destination == null )
                  || (destination == this.subscriptions[i].destination)))
                idx = i;
        } else if( ( this.subscriptions[i].destination == destination )
                   && ( this.subscriptions[i].id == id ) )
            idx = i;
        else if( destination == null && id == null )
            idx = i;
    }

    // Fail if no match found
    if( idx == -1 )
        throw new middleware.ProtocolError(conn, null, 'No SUBSCRIBE matches given UNSUBSCRIBE');

    // Remove record from subscription table
    var sbscr = this.subscriptions.splice(i,1);

    // Remove records from destination table
    for( var i in this.destinations ) {
        var dst = this.destinations[i];
        var idxs = [];
        for( var j in dst.subscriptions ) {
            if( conn != dst.subscriptions[j].conn ) continue;
            if( dst.subscriptions[j].destination == sbscr.destination &&
                dst.subscriptions[j].id == sbscr.id )
                idxs.unshift(j);
        }

        // NOTE: Subscriptions are removed in reverse order to keep the indices
        // valid
        for( var j in idxs )
            dst.subscriptions.splice(idxs[idxs.length-1-j],1);
    }

    // Operation Complete!
};

function SubscribeRecvCurry(broker) {
    return function(conn, frame_obj) {
        // Handle SUBSCRIBE and UNSUBSCRIBE
        switch(frame_obj.cmd) {
            case 'SUBSCRIBE':
                // Extract requisite parameters
                if( !frame_obj.headers.destination )
                    throw new middleware.ProtocolError(conn, frame_obj, 'SUBSCRIBE requires a destination header');
                var destination = frame_obj.headers.destination;
                var id = frame_obj.headers.id;
                var ack = frame_obj.headers.ack || 'auto';

                // Subscribe
                broker.subscribe(conn, destination, id, ack);

                // Request is handled
                frame_obj.handled = true;
                break;

            case 'UNSUBSCRIBE':
                // Extract requisite parameters
                if( !frame_obj.headers.destination && !frame_obj.headers.id )
                    throw new middleware.ProtocolError(conn, frame_obj, 'UNSUBSCRIBE requires at least a destination or an id header');
                var destination = frame_obj.headers.destination;
                var id = frame_obj.headers.id;

                // Unsubscribe
                broker.unsubscribe(conn, destination, id);

                // Request is handled
                frame_obj.handled = true;
                break;
            default:
        };

        // Operation Complete!
        return frame_obj;
    };
}

function SendRecvCurry(broker) {
    return function(conn, frame_obj) {
        // Handle SEND
        if( frame_obj.cmd != 'SEND' ) return frame_obj;

        // Get the destination header
        if( !frame_obj.headers.destination )
            throw new middleware.ProtocolError(conn, frame_obj, 'SEND requires a destination header');
        var destination = frame_obj.headers.destination;

        // Obtain a destination object
        // FIXME: There should be additional intelligence in constructing new
        // destination objects
        var dst = broker.getDestination(destination);

        // FIXME: The following logic should be a method of the destination
        // object

        // Push the message out to subscribers
        for( var i in dst.subscriptions ) {
            var subscr = dst.subscriptions[i];
            var headers = filterHeaders(frame_obj.headers);

            // FIXME: Errors should still be thrown, but attempts should be
            // made on each subscription, regardless of errors on various attempts.

            // Transmit a MESSAGE frame
            var frame_out = new Frame('MESSAGE', headers, frame_obj.body);
            if( subscr.id )
                frame_out.headers.id = subscr.id;
            broker._cf.send_frame(subscr.conn, frame_out);
        }

        // Operation Complete!
        frame_obj.handled = true;
        return frame_obj;
    };
}

var _re_x_header = /^x-/;
var _pass_headers = ['reply-to', 'destination'];
function filterHeaders(headers) {
    var hdr_out = {};
    for( var i in headers )
        if( _pass_headers.indexOf(i) >= 0
            || _re_x_header.test(i) )
            hdr_out[i] = headers[i];

    // Operation Complete!
    return hdr_out;
}

// Export classes
module.exports.BasicBroker = BasicBroker;
<commit_msg>Using the timestamp middleware on incoming frames, and making 'timestamp' a pass-through header<commit_after>
// stomp-server/basicbroker.js -- Definition of the BasicBroker class

/*  Copyright 2010, Jason B. Alonso
 *
 *  This file is part of node-stomp-server.
 *
 *  node-stomp-server is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  node-stomp-server is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with node-stomp-server.  If not, see <http://www.gnu.org/licenses/>
 */

// Load modules
var middleware          = require('./middleware'),
    Broker              = require('./broker').Broker,
    Frame               = require('./frame').Frame,
    sys                 = require('sys');

// keep a table of subscriptions by regular expression
// keep a table mapping destinations to subscriptions
function BasicBroker(bufferLimit) {
    var self = this;

    // Extend Broker
    Broker.call(this, bufferLimit);

    // Initialize the subscription table
    this.subscriptions = [];

    // Initialize the destination table
    this.destinations = {};

    // Extend default middleware
    this._cf.recv_middleware.push({cbk: SubscribeRecvCurry(this)});
    this._cf.recv_middleware.push({cbk: middleware.TimestampFrame});
    this._cf.recv_middleware.push({cbk: SendRecvCurry(this)});

    // Install the automatic RECEIPT command middleware
    this._cf.recv_middleware.push({cbk: middleware.AutoReceiptRecv});

    // Clean up subscriptions on connection close
    var orig_newConnection = this.newConnection;
    this.newConnection = function(stream) {
        // Construct a connection
        var conn = orig_newConnection(stream);

        // Remove all connection subscriptions on close event
        conn.on('close', function(had_error) { self.unsubscribe(conn); });

        // Operation Complete!
        return conn;
    };
}
sys.inherits(BasicBroker, Broker);

// NOTE: This routine aims to escape all regular expression characters *except*
// for *
var escapeChars = /([\[\]\(\)\{\}\/\\\?\+\^\$\|\.\!])/;
function escapePattern(str) {
    return str.replace(escapeChars, '\\$1');
}

function patternFromDestination(destination) {
    // Escape most characters
    destination = escapePattern(destination);

    // Escape stars
    destination = destination.replace('.*', '.[^.]+');
    destination = destination.replace('*', '\\*');

    // Escape subtree references
    destination = destination.replace('>', '(\\.[^.]+)+');

    // Force complete string match
    destination = '^' + destination + '$';

    // Operation Complete!
    return new RegExp(destination);
}

BasicBroker.prototype.subscribe = function(conn, destination, id, ack) {
    // Set defaults
    if( ack == null ) ack = 'auto';

    // Prepare the subscription structure
    var subscription = {
        rex: patternFromDestination(destination),
        destination: destination,
        id: id,
        conn: conn,
        ack: ack,
    };

    // Make sure this is not a duplicate subscription
    for( var i in this.subscriptions )
        if( this.subscriptions[i].id == id &&
            this.subscriptions[i].conn == conn )
            throw new middleware.ProtocolError(conn, null, 'Duplicate SUBSCRIBE');

    // Add the entry to the subscription table
    this.subscriptions.push(subscription);

    // Update the destination table
    for( var i in this.destinations )
        if( subscription.rex.test(i) )
            this.destinations[i].subscriptions.push(subscription);
    // FIXME: transmit messages queued for destination

    // Operation Complete!
};

BasicBroker.prototype.getDestination = function(destination) {
    // Return an existing destination if possible
    if( destination in this.destinations )
        return this.destinations[destination];

    // Prepare a new destination structure
    var dst = {subscriptions: []};

    // Find matching subscriptions
    for( var i in this.subscriptions )
        if( this.subscriptions[i].rex.test(destination) )
            dst.subscriptions.push(this.subscriptions[i]);

    // Save the new destination
    this.destinations[destination] = dst;

    // Operation Complete!
    return dst;
};

BasicBroker.prototype.unsubscribe = function(conn, destination, id) {
    // Make sure this subscription can be found
    var idx = -1;
    for( var i in this.subscriptions ) {
        if( conn != this.subscriptions[i].conn ) continue;
        if( id ) {
            if( id == this.subscriptions[i].id &&
                ( ( destination == null )
                  || (destination == this.subscriptions[i].destination)))
                idx = i;
        } else if( ( this.subscriptions[i].destination == destination )
                   && ( this.subscriptions[i].id == id ) )
            idx = i;
        else if( destination == null && id == null )
            idx = i;
    }

    // Fail if no match found
    if( idx == -1 )
        throw new middleware.ProtocolError(conn, null, 'No SUBSCRIBE matches given UNSUBSCRIBE');

    // Remove record from subscription table
    var sbscr = this.subscriptions.splice(i,1);

    // Remove records from destination table
    for( var i in this.destinations ) {
        var dst = this.destinations[i];
        var idxs = [];
        for( var j in dst.subscriptions ) {
            if( conn != dst.subscriptions[j].conn ) continue;
            if( dst.subscriptions[j].destination == sbscr.destination &&
                dst.subscriptions[j].id == sbscr.id )
                idxs.unshift(j);
        }

        // NOTE: Subscriptions are removed in reverse order to keep the indices
        // valid
        for( var j in idxs )
            dst.subscriptions.splice(idxs[idxs.length-1-j],1);
    }

    // Operation Complete!
};

function SubscribeRecvCurry(broker) {
    return function(conn, frame_obj) {
        // Handle SUBSCRIBE and UNSUBSCRIBE
        switch(frame_obj.cmd) {
            case 'SUBSCRIBE':
                // Extract requisite parameters
                if( !frame_obj.headers.destination )
                    throw new middleware.ProtocolError(conn, frame_obj, 'SUBSCRIBE requires a destination header');
                var destination = frame_obj.headers.destination;
                var id = frame_obj.headers.id;
                var ack = frame_obj.headers.ack || 'auto';

                // Subscribe
                broker.subscribe(conn, destination, id, ack);

                // Request is handled
                frame_obj.handled = true;
                break;

            case 'UNSUBSCRIBE':
                // Extract requisite parameters
                if( !frame_obj.headers.destination && !frame_obj.headers.id )
                    throw new middleware.ProtocolError(conn, frame_obj, 'UNSUBSCRIBE requires at least a destination or an id header');
                var destination = frame_obj.headers.destination;
                var id = frame_obj.headers.id;

                // Unsubscribe
                broker.unsubscribe(conn, destination, id);

                // Request is handled
                frame_obj.handled = true;
                break;
            default:
        };

        // Operation Complete!
        return frame_obj;
    };
}

function SendRecvCurry(broker) {
    return function(conn, frame_obj) {
        // Handle SEND
        if( frame_obj.cmd != 'SEND' ) return frame_obj;

        // Get the destination header
        if( !frame_obj.headers.destination )
            throw new middleware.ProtocolError(conn, frame_obj, 'SEND requires a destination header');
        var destination = frame_obj.headers.destination;

        // Obtain a destination object
        // FIXME: There should be additional intelligence in constructing new
        // destination objects
        var dst = broker.getDestination(destination);

        // FIXME: The following logic should be a method of the destination
        // object

        // Push the message out to subscribers
        for( var i in dst.subscriptions ) {
            var subscr = dst.subscriptions[i];
            var headers = filterHeaders(frame_obj.headers);

            // FIXME: Errors should still be thrown, but attempts should be
            // made on each subscription, regardless of errors on various attempts.

            // Transmit a MESSAGE frame
            var frame_out = new Frame('MESSAGE', headers, frame_obj.body);
            if( subscr.id )
                frame_out.headers.id = subscr.id;
            broker._cf.send_frame(subscr.conn, frame_out);
        }

        // Operation Complete!
        frame_obj.handled = true;
        return frame_obj;
    };
}

var _re_x_header = /^x-/;
var _pass_headers = ['reply-to', 'destination', 'timestamp'];
function filterHeaders(headers) {
    var hdr_out = {};
    for( var i in headers )
        if( _pass_headers.indexOf(i) >= 0
            || _re_x_header.test(i) )
            hdr_out[i] = headers[i];

    // Operation Complete!
    return hdr_out;
}

// Export classes
module.exports.BasicBroker = BasicBroker;
<|endoftext|>


<commit_before>/* global jest, beforeEach, describe, it, expect */
jest.unmock('../../../modules/server/repositories/MetaRepository');

import MetaRepository from '../../../modules/server/repositories/MetaRepository';
const chai = require('chai');
chai.use(require('chai-as-promised'));
const { expect, assert } = chai;

describe('MetaRepository', () => {
  let gitMock = {};
  let metaRepo = new MetaRepository(gitMock);

  describe('getRuleMeta', async () => {
    it('should be able to get rule meta: call git repo with correct rule name and return correct meta after parsing a json file', async () => {
      // Arrange
      const metaMock = {
        displayName: 'some displayName',
        description: 'some description',
        tags: ['tag1', 'tag2'],
      };

      const stringMetaMock = JSON.stringify(metaMock);

      const requestedRuleName = 'some rule name';

      const expectedFileName =
        `${MetaRepository.META_REPOSITORY_DIRECTORY_NAME}/${requestedRuleName}${MetaRepository.META_REPOSITORY_FILE_EXTENSION_NAME}`;

      gitMock.readFile = jest.fn(async () => stringMetaMock);

      // Act
      let result = await metaRepo.getRuleMeta(requestedRuleName);

      // Assert
      expect(result).to.eql(metaMock);
      expect(gitMock.readFile.mock.calls.length).to.eql(1);
      expect(gitMock.readFile.mock.calls[0][0]).to.eql(expectedFileName);
    });

    it('should fail: git repo readFile rejected', async () => {
      // Arrange
      let expectedException = 'pita exception';

      gitMock.readFile = async () => Promise.reject(expectedException);

      // Act & Assert
      await expect(metaRepo.getRuleMeta()).to.eventually.be.rejectedWith(expectedException);
    });
  });

  describe('updateRuleMeta', async () => {
    it('should be able to update rule meta: call git repo with correct parameters', async () => {
      // Arrange
      const metaPayload = 'some meta';
      const requestedRuleName = 'some rule name';

      const expectedFileName =
        `${MetaRepository.META_REPOSITORY_DIRECTORY_NAME}/${requestedRuleName}${MetaRepository.META_REPOSITORY_FILE_EXTENSION_NAME}`;

      const name = 'some user';
      const email = 'email';

      const expectedAuthor = {
        name,
        email,
      };

      gitMock.updateFile = jest.fn(() => Promise.resolve(''));

      // Act
      await metaRepo.updateRuleMeta(requestedRuleName, metaPayload, expectedAuthor);

      // Assert
      expect(gitMock.updateFile.mock.calls.length).to.eql(1);
      expect(gitMock.updateFile.mock.calls[0][0]).to.eql(expectedFileName);
      expect(gitMock.updateFile.mock.calls[0][1]).to.eql(metaPayload);
      expect(gitMock.updateFile.mock.calls[0][2]).to.eql(expectedAuthor);
    });

    it('should fail: git repo updateFile rejected', async () => {
      // Arrange
      let expectedException = 'some exception';

      gitMock.updateFile = async () => {
        return Promise.reject(expectedException);
      };

      // Act & Assert
      await expect(metaRepo.updateRuleMeta()).to.eventually.be.rejectedWith(expectedException);
    });
  });
});
<commit_msg>fix test in meta repository spec<commit_after>/* global jest, beforeEach, describe, it, expect */
jest.unmock('../../../modules/server/repositories/MetaRepository');

import MetaRepository from '../../../modules/server/repositories/MetaRepository';
const chai = require('chai');
chai.use(require('chai-as-promised'));
const { expect, assert } = chai;

describe('MetaRepository', () => {
  let gitMock = {};
  let metaRepo = new MetaRepository(gitMock);

  describe('getRuleMeta', async () => {
    it('should be able to get rule meta: call git repo with correct rule name and return correct meta after parsing a json file', async () => {
      // Arrange
      const metaMock = {
        displayName: 'some displayName',
        description: 'some description',
        tags: ['tag1', 'tag2'],
      };

      const stringMetaMock = JSON.stringify(metaMock);

      const requestedRuleName = 'some rule name';

      const expectedFileName =
        `${MetaRepository.META_REPOSITORY_DIRECTORY_NAME}/${requestedRuleName}${MetaRepository.META_REPOSITORY_FILE_EXTENSION_NAME}`;

      gitMock.readFile = jest.fn(async () => stringMetaMock);

      // Act
      let result = await metaRepo.getRuleMeta(requestedRuleName);

      // Assert
      expect(result).to.eql(metaMock);
      expect(gitMock.readFile.mock.calls.length).to.eql(1);
      expect(gitMock.readFile.mock.calls[0][0]).to.eql(expectedFileName);
    });

    it('should fail: git repo readFile rejected', async () => {
      // Arrange
      let expectedException = 'pita exception';

      gitMock.readFile = async () => Promise.reject(expectedException);

      // Act & Assert
      await expect(metaRepo.getRuleMeta()).to.eventually.be.rejectedWith(expectedException);
    });
  });

  describe('updateRuleMeta', async () => {
    it('should be able to update rule meta: call git repo with correct parameters', async () => {
      // Arrange
      const metaMock = {
        displayName: 'some displayName',
        description: 'some description',
        tags: ['tag1', 'tag2'],
      };

      const metaPayload = JSON.stringify(metaMock);
      const requestedRuleName = 'some rule name';

      const expectedFileName =
        `${MetaRepository.META_REPOSITORY_DIRECTORY_NAME}/${requestedRuleName}${MetaRepository.META_REPOSITORY_FILE_EXTENSION_NAME}`;

      const name = 'some user';
      const email = 'email';

      const expectedAuthor = {
        name,
        email,
      };

      gitMock.updateFile = jest.fn(() => Promise.resolve(''));

      // Act
      await metaRepo.updateRuleMeta(requestedRuleName, metaMock, expectedAuthor);

      // Assert
      expect(gitMock.updateFile.mock.calls.length).to.eql(1);
      expect(gitMock.updateFile.mock.calls[0][0]).to.eql(expectedFileName);
      expect(gitMock.updateFile.mock.calls[0][1]).to.eql(metaPayload);
      expect(gitMock.updateFile.mock.calls[0][2]).to.eql(expectedAuthor);
    });

    it('should fail: git repo updateFile rejected', async () => {
      // Arrange
      let expectedException = 'some exception';

      gitMock.updateFile = async () => {
        return Promise.reject(expectedException);
      };

      // Act & Assert
      await expect(metaRepo.updateRuleMeta()).to.eventually.be.rejectedWith(expectedException);
    });
  });
});
<|endoftext|>


<commit_before>import { createElement as h } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'

// Round upward to the next group of ten
const round = (num) => Math.ceil(num / 10) * 10

const max = (items) => round(Math.max.apply(Math, items))
const mid = (items) => max(items) / 2
const min = () => 0

const percentage = (amount, max) => {
	if (amount === 0 && max === 0) return 0
	return (amount / max) * 100
}

const Row = (props) => {
	return (
		h('div', {
			className: classNames({
				'barChart__row': true,
				'barChart__row--top': props.position === 'top',
				'barChart__row--middle': props.position === 'middle',
				'barChart__row--bottom': props.position === 'bottom',
				'color-light': true,
			}),
		}, props.children)
	)
}

const Column = (props) => {
	return (
		h('div', {
			className: classNames({
				barChart__column: true,
				active: props.active,
			}),
			onMouseEnter: props.onEnter,
			onMouseLeave: props.onLeave,
		},
			h('div', {
				'className': 'barChart__bar color-black',
				'style': { '--size': props.size },
				'data-label': props.label,
			}),
		)
	)
}

const PresentationBarChart = (props) => {
	const hasItems = props.items.length > 0

	return (
		h('div', { className: 'barChart' },
			h('div', { className: 'barChart__axis' },
				h(Row, { position: 'top' }, hasItems === true ? props.formatter(max(props.items)) : ''),
				h(Row, { position: 'middle' }, hasItems === true ? props.formatter(mid(props.items)) : ''),
				h(Row, { position: 'bottom' }, hasItems === true ? props.formatter(min()) : ''),
			),
			h('div', { className: 'barChart__columns' },
				props.items.map((item, index) => (
					h(Column, {
						key: index,
						active: props.active === index,
						size: `${ percentage(item, max(props.items)) }%`,
						onEnter: () => props.onEnter(index),
						onLeave: () => props.onLeave(index),
						label: props.formatter(item),
					})
				)),
			),
		)
	)
}

PresentationBarChart.propTypes = {
	items: PropTypes.arrayOf(PropTypes.number).isRequired,
	formatter: PropTypes.func.isRequired,
	onEnter: PropTypes.func.isRequired,
	onLeave: PropTypes.func.isRequired,
}

export default PresentationBarChart<commit_msg>Remove hasItems logic in charts which wasn't working anyway<commit_after>import { createElement as h } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'

// Round upward to the next group of ten
const round = (num) => Math.ceil(num / 10) * 10

const max = (items) => round(Math.max.apply(Math, items))
const mid = (items) => max(items) / 2
const min = () => 0

const percentage = (amount, max) => {
	if (amount === 0 && max === 0) return 0
	return (amount / max) * 100
}

const Row = (props) => {
	return (
		h('div', {
			className: classNames({
				'barChart__row': true,
				'barChart__row--top': props.position === 'top',
				'barChart__row--middle': props.position === 'middle',
				'barChart__row--bottom': props.position === 'bottom',
				'color-light': true,
			}),
		}, props.children)
	)
}

const Column = (props) => {
	return (
		h('div', {
			className: classNames({
				barChart__column: true,
				active: props.active,
			}),
			onMouseEnter: props.onEnter,
			onMouseLeave: props.onLeave,
		},
			h('div', {
				'className': 'barChart__bar color-black',
				'style': { '--size': props.size },
				'data-label': props.label,
			}),
		)
	)
}

const PresentationBarChart = (props) => {
	return (
		h('div', { className: 'barChart' },
			h('div', { className: 'barChart__axis' },
				h(Row, { position: 'top' }, props.formatter(max(props.items))),
				h(Row, { position: 'middle' }, props.formatter(mid(props.items))),
				h(Row, { position: 'bottom' }, props.formatter(min())),
			),
			h('div', { className: 'barChart__columns' },
				props.items.map((item, index) => (
					h(Column, {
						key: index,
						active: props.active === index,
						size: `${ percentage(item, max(props.items)) }%`,
						onEnter: () => props.onEnter(index),
						onLeave: () => props.onLeave(index),
						label: props.formatter(item),
					})
				)),
			),
		)
	)
}

PresentationBarChart.propTypes = {
	items: PropTypes.arrayOf(PropTypes.number).isRequired,
	formatter: PropTypes.func.isRequired,
	onEnter: PropTypes.func.isRequired,
	onLeave: PropTypes.func.isRequired,
}

export default PresentationBarChart<|endoftext|>


<commit_before>var globalConf = {
    defaultServer : 'http://bigdatasearchvg.pi1g.local:10200',
    confServer : 'http://bigdatasearchvg.pi1g.local:10200',
    confIndex : "curiosity-config",
    defaultConfDocumentType : "conf-doc"
}<commit_msg>Delete INT.conf.js<commit_after><|endoftext|>


<commit_before>if (!process.env.SLACK_TOKEN) {
    console.log('Error: Specify token in environment');
    process.exit(1);
}

var cleverbot = require("cleverbot.io"),
cleverbot = new cleverbot('i8TjAndLnTqKNxFv', 'nfwwaciaKYccoLj5KLuOGhLoddV8O6zo');
cleverbot.setNick("Bob");
cleverbot.create(function (err, session) {
    if (err) {
        console.log('cleverbot create fail.');
    } else {
        console.log('cleverbot create success.');
    }
});

var Botkit = require('./lib/Botkit.js');
var os = require('os');

var controller = Botkit.slackbot({
    debug: true,
});

var bot = controller.spawn({
    token: process.env.SLACK_TOKEN
}).startRTM();

controller.hears(['cuenteme algo', 'cuenteme un chiste', 'cuente un chiste', 'estoy aburrido'], 'direct_message,direct_mention,mention', function(bot, message){
  var stories = ['- Mama que haces en frente de la computadora con los hojos cerrados??? \n- Nada hijo es que Windows me dijo que cierre las pestañas',
                 'Un hombre está haciendo un vuelo en un globo aerostático.' +
                 'Se extravía y decide descender y preguntar a alguien. Baja a ' +
                 'unos 10 metros del suelo y pregunta a una persona que pasaba por allí: \n' +
                 '- Por favor, ¿puede decirme donde estoy?\n' +
                 '- Pues mire, está usted en un globo aerostático, a unos 10 metros del suelo.\n'+
                 '- Usted es informático, ¿verdad?\n'+
                 '- Sí, ¿cómo lo sabe?\n'+
                 '- Porque me ha dado una respuesta técnicamente correcta, pero que no me soluciona nada.\n'+
                 '- Y usted es usuario, ¿verdad?\n'+
                 '- Pues sí, ¿cómo lo sabe?\n'+
                 '- Porque está igual de perdido que antes, pero ahora me echa la culpa a mí.',
                 '¿Qué le dice una IP a otra?\n'+
                 '—¿Qué tramas?', 'http://img.desmotivaciones.es/201508/sexo-silencio-desmotivaciones.jpg',
                 'http://img.desmotivaciones.es/201112/informatica.jpg', 'https://lh3.googleusercontent.com/-RanqDppqME4/VUOZ06phYnI/AAAAAAAAADQ/q0sTl6Ygrcs/w800-h800/por-esos-chistes-malos-carteles-chistes-tonterias-risa-sonrisa-desmotivaciones.jpg',
                 'http://img.desmotivaciones.es/201101/images_4701.jpg', 'http://img.desmotivaciones.es/201011/bandaancha.jpg'];
  var story = stories[Math.floor(Math.random() * stories.length)];
  bot.reply(message, 'Que tal un chiste?')
  bot.reply(message, story);
});

controller.hears(['insulte'], 'direct_message,direct_mention,mention', function(bot,message){
  var insults = ["http://i.imgur.com/9IZACjN.jpg",
                  "http://i.imgur.com/ftc2h0p.jpg",
                  "http://i.imgur.com/QEb2GaS.jpg",
                  "http://i.imgur.com/LHQ5FXf.jpg",
                  "http://i.imgur.com/rNW9J0D.jpg",
                  "http://i.imgur.com/slGGjh9.png",
                  "http://i.imgur.com/ijuqlp7.png",
                  "http://i.imgur.com/riHJxSx.png",
                  "http://i.imgur.com/21vNjxl.png",
                  "http://i.imgur.com/XlMRGDE.png",
                  "http://i.imgur.com/y9iuoKk.png",
                  "http://i.imgur.com/zmU7RBF.png",
                  "http://i.imgur.com/nrLtb7S.png",
                  "http://i.imgur.com/m0U288v.gif",
                  "http://i.imgur.com/W5LGPj7.gif",
                  "http://i.imgur.com/GZfjPDH.gif",
                  "http://i.imgur.com/TM0qYRi.gif",
                  "http://i.imgur.com/dUb0C1V.gif",
                  "http://i.imgur.com/WFdLzo9.gif",
                  "http://i.imgur.com/doE0ChC.gif",
                  "http://i.imgur.com/Jal12NU.gif",
                  "http://i.imgur.com/SOEQQ6m.gif",
                  "http://i.imgur.com/OJkjPnP.gif",
                  "http://i.imgur.com/CjTPuvh.gif",
                  "http://i.imgur.com/5cpGsja.gif",
                  "http://i.imgur.com/23J4RqC.gif",
                  "http://i.imgur.com/m7qUTLq.gif",
                  "http://i.imgur.com/qwk5kKT.gif",
                  "http://i.imgur.com/6b9OqaI.gif",
                  "http://i.imgur.com/mcL83zN.gif",
                  "http://i.imgur.com/bwzg7nr.gif",
                  "http://i.imgur.com/H92PB5P.gif",
                  "http://i.imgur.com/8sqhwOg.gif",
                  "http://i.imgur.com/WPGwXHq.gif",
                  "http://i.imgur.com/cOVQQq6.gif",
                  "http://cdn.emgn.com/wp-content/uploads/2015/01/Rude-Animal-Facts-EMGN1.png",
                  "http://cdn.emgn.com/wp-content/uploads/2015/01/Rude-Animal-Facts-EMGN2.png",
                  "http://cdn.emgn.com/wp-content/uploads/2015/01/Rude-Animal-Facts-EMGN4.png",
                  "http://cdn.emgn.com/wp-content/uploads/2015/01/Rude-Animal-Facts-EMGN5.png",
                  "http://cdn.emgn.com/wp-content/uploads/2015/01/Rude-Animal-Facts-EMGN6.jpg",
                  "http://i.imgur.com/dMDdQOI.gif",
                  "http://i.imgur.com/4PhyZKv.gif",
                  "http://i.imgur.com/6OGiTqc.gif",
                  "http://i.imgur.com/WtDpHiL.gif",
                  "http://i.imgur.com/7PIIlI7.gif",
                  "http://replygif.net/i/186.gif",
                  "http://i.imgur.com/TLDBTeV.gif",
                  "http://i.imgur.com/HegZkkc.gif",
                  "http://i.imgur.com/clcdkfP.gif",
                  "http://i.imgur.com/c5yeWVx.gif",
                  "http://i.imgur.com/IG4Evs9.gif",
                  "http://i.imgur.com/VJCWcnK.gif",
                  "http://i.imgur.com/QFrk1X5.gif",
                  "http://i.imgur.com/kXGUKwD.gif",
                  "http://i.imgur.com/Jnit9mq.gif",
                  "http://i.imgur.com/iQjNakW.gif",
                  "http://i.imgur.com/5Zfltup.gif",
                  "http://i.imgur.com/OdYTqoB.gif",
                  "http://i.imgur.com/LCy4zVv.gif",
                  "http://i.imgur.com/DZ64Fu7.gif",
                  "http://i.imgur.com/DAzfQh6.gif",
                  "http://i.imgur.com/R8Ou2cj.gif"];
    var insult = insults[Math.floor(Math.random() * insults.length)];
    bot.reply(message, insult);
});

controller.hears(['picnic'], 'ambient', function(bot, message){
  var userID = message.user;
  var user = "<@"+userID+">";
  var reply = user + " hagame mention, no hable a mis espaldas :rage:";
  bot.reply(message, reply);
});

controller.hears(['no entiendo', 'no entendi', 'expliqueme', 'me explica'], 'ambient', function(bot, message){
  bot.reply(message, "https://s-media-cache-ak0.pinimg.com/736x/2e/29/87/2e298711c7fec1d77637a982235c9910.jpg");
});

controller.hears(['julio', 'orlando', 'orly'], 'direct_message,direct_mention,mention,ambient', function(bot, message){
  var userID = message.user;
  var user = "<@"+userID+">";
  var reply = user + " eso que dijo merece esto";
  bot.reply(message, reply);
  bot.reply(message, 'http://gph.is/1HlCG6v');
});

controller.hears(['me llamo (.*)', 'mi nombre es (.*)', 'soy (.*)'], 'direct_message,direct_mention,mention', function(bot, message) {
    var name = message.match[1];
    bot.reply(message, 'Quiere un trofeo o fiesta en hooters? ' + "<@"+message.user+">");
});

controller.on("bot_channel_join", function(bot, message){
  bot.reply(message,"Ahora si empieza lo bueno :smirk:");
});

controller.on("bot_group_join", function(bot, message){
  bot.reply(message,"Ahora si empieza lo bueno :smirk:");
});

controller.hears(['jale a comer', 'almuerzo', 'comida', 'comen'], 'ambient', function(bot, message){
  var userID = message.user;
  var user = "<@"+userID+">";
  bot.reply(message, "Yo me encargo " + user);
  bot.say({text: "@channel: hey todos dice " + user + " que tiene hambre a donde vamos",
           channel:"C11429SQ6"});
});

controller.hears('','direct_message,direct_mention,mention',function(bot,message) {
    var msg = message.text;
    cleverbot.ask(msg, function (err, response) {
        if (!err) {
            bot.reply(message, response);
        } else {
            console.log('cleverbot err: ' + err);
        }
    });
})

function formatUptime(uptime) {
    var unit = 'second';
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'minute';
    }
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'hour';
    }
    if (uptime != 1) {
        unit = unit + 's';
    }

    uptime = uptime + ' ' + unit;
    return uptime;
}
<commit_msg>Add dra polo<commit_after>if (!process.env.SLACK_TOKEN) {
    console.log('Error: Specify token in environment');
    process.exit(1);
}

var cleverbot = require("cleverbot.io"),
cleverbot = new cleverbot('i8TjAndLnTqKNxFv', 'nfwwaciaKYccoLj5KLuOGhLoddV8O6zo');
cleverbot.setNick("Bob");
cleverbot.create(function (err, session) {
    if (err) {
        console.log('cleverbot create fail.');
    } else {
        console.log('cleverbot create success.');
    }
});

var Botkit = require('./lib/Botkit.js');
var os = require('os');

var controller = Botkit.slackbot({
    debug: true,
});

var bot = controller.spawn({
    token: process.env.SLACK_TOKEN
}).startRTM();

controller.hears(['cuenteme algo', 'cuenteme un chiste', 'cuente un chiste', 'estoy aburrido'], 'direct_message,direct_mention,mention', function(bot, message){
  var stories = ['- Mama que haces en frente de la computadora con los hojos cerrados??? \n- Nada hijo es que Windows me dijo que cierre las pestañas',
                 'Un hombre está haciendo un vuelo en un globo aerostático.' +
                 'Se extravía y decide descender y preguntar a alguien. Baja a ' +
                 'unos 10 metros del suelo y pregunta a una persona que pasaba por allí: \n' +
                 '- Por favor, ¿puede decirme donde estoy?\n' +
                 '- Pues mire, está usted en un globo aerostático, a unos 10 metros del suelo.\n'+
                 '- Usted es informático, ¿verdad?\n'+
                 '- Sí, ¿cómo lo sabe?\n'+
                 '- Porque me ha dado una respuesta técnicamente correcta, pero que no me soluciona nada.\n'+
                 '- Y usted es usuario, ¿verdad?\n'+
                 '- Pues sí, ¿cómo lo sabe?\n'+
                 '- Porque está igual de perdido que antes, pero ahora me echa la culpa a mí.',
                 '¿Qué le dice una IP a otra?\n'+
                 '—¿Qué tramas?', 'http://img.desmotivaciones.es/201508/sexo-silencio-desmotivaciones.jpg',
                 'http://img.desmotivaciones.es/201112/informatica.jpg', 'https://lh3.googleusercontent.com/-RanqDppqME4/VUOZ06phYnI/AAAAAAAAADQ/q0sTl6Ygrcs/w800-h800/por-esos-chistes-malos-carteles-chistes-tonterias-risa-sonrisa-desmotivaciones.jpg',
                 'http://img.desmotivaciones.es/201101/images_4701.jpg', 'http://img.desmotivaciones.es/201011/bandaancha.jpg'];
  var story = stories[Math.floor(Math.random() * stories.length)];
  bot.reply(message, 'Que tal un chiste?')
  bot.reply(message, story);
});

controller.hears(['este es un trabajo para la doctora'], 'ambient', function(bot,message){
  bot.reply(message, "http://67.media.tumblr.com/6339f52d38760a700ca9adc48eab43ea/tumblr_o17hspcySq1rzlfxxo1_1280.gif");
});

controller.hears(['insulte'], 'direct_message,direct_mention,mention', function(bot,message){
  var insults = ["http://i.imgur.com/9IZACjN.jpg",
                  "http://i.imgur.com/ftc2h0p.jpg",
                  "http://i.imgur.com/QEb2GaS.jpg",
                  "http://i.imgur.com/LHQ5FXf.jpg",
                  "http://i.imgur.com/rNW9J0D.jpg",
                  "http://i.imgur.com/slGGjh9.png",
                  "http://i.imgur.com/ijuqlp7.png",
                  "http://i.imgur.com/riHJxSx.png",
                  "http://i.imgur.com/21vNjxl.png",
                  "http://i.imgur.com/XlMRGDE.png",
                  "http://i.imgur.com/y9iuoKk.png",
                  "http://i.imgur.com/zmU7RBF.png",
                  "http://i.imgur.com/nrLtb7S.png",
                  "http://i.imgur.com/m0U288v.gif",
                  "http://i.imgur.com/W5LGPj7.gif",
                  "http://i.imgur.com/GZfjPDH.gif",
                  "http://i.imgur.com/TM0qYRi.gif",
                  "http://i.imgur.com/dUb0C1V.gif",
                  "http://i.imgur.com/WFdLzo9.gif",
                  "http://i.imgur.com/doE0ChC.gif",
                  "http://i.imgur.com/Jal12NU.gif",
                  "http://i.imgur.com/SOEQQ6m.gif",
                  "http://i.imgur.com/OJkjPnP.gif",
                  "http://i.imgur.com/CjTPuvh.gif",
                  "http://i.imgur.com/5cpGsja.gif",
                  "http://i.imgur.com/23J4RqC.gif",
                  "http://i.imgur.com/m7qUTLq.gif",
                  "http://i.imgur.com/qwk5kKT.gif",
                  "http://i.imgur.com/6b9OqaI.gif",
                  "http://i.imgur.com/mcL83zN.gif",
                  "http://i.imgur.com/bwzg7nr.gif",
                  "http://i.imgur.com/H92PB5P.gif",
                  "http://i.imgur.com/8sqhwOg.gif",
                  "http://i.imgur.com/WPGwXHq.gif",
                  "http://i.imgur.com/cOVQQq6.gif",
                  "http://cdn.emgn.com/wp-content/uploads/2015/01/Rude-Animal-Facts-EMGN1.png",
                  "http://cdn.emgn.com/wp-content/uploads/2015/01/Rude-Animal-Facts-EMGN2.png",
                  "http://cdn.emgn.com/wp-content/uploads/2015/01/Rude-Animal-Facts-EMGN4.png",
                  "http://cdn.emgn.com/wp-content/uploads/2015/01/Rude-Animal-Facts-EMGN5.png",
                  "http://cdn.emgn.com/wp-content/uploads/2015/01/Rude-Animal-Facts-EMGN6.jpg",
                  "http://i.imgur.com/dMDdQOI.gif",
                  "http://i.imgur.com/4PhyZKv.gif",
                  "http://i.imgur.com/6OGiTqc.gif",
                  "http://i.imgur.com/WtDpHiL.gif",
                  "http://i.imgur.com/7PIIlI7.gif",
                  "http://replygif.net/i/186.gif",
                  "http://i.imgur.com/TLDBTeV.gif",
                  "http://i.imgur.com/HegZkkc.gif",
                  "http://i.imgur.com/clcdkfP.gif",
                  "http://i.imgur.com/c5yeWVx.gif",
                  "http://i.imgur.com/IG4Evs9.gif",
                  "http://i.imgur.com/VJCWcnK.gif",
                  "http://i.imgur.com/QFrk1X5.gif",
                  "http://i.imgur.com/kXGUKwD.gif",
                  "http://i.imgur.com/Jnit9mq.gif",
                  "http://i.imgur.com/iQjNakW.gif",
                  "http://i.imgur.com/5Zfltup.gif",
                  "http://i.imgur.com/OdYTqoB.gif",
                  "http://i.imgur.com/LCy4zVv.gif",
                  "http://i.imgur.com/DZ64Fu7.gif",
                  "http://i.imgur.com/DAzfQh6.gif",
                  "http://i.imgur.com/R8Ou2cj.gif"];
    var insult = insults[Math.floor(Math.random() * insults.length)];
    bot.reply(message, insult);
});

controller.hears(['picnic'], 'ambient', function(bot, message){
  var userID = message.user;
  var user = "<@"+userID+">";
  var reply = user + " hagame mention, no hable a mis espaldas :rage:";
  bot.reply(message, reply);
});

controller.hears(['no entiendo', 'no entendi', 'expliqueme', 'me explica'], 'ambient', function(bot, message){
  bot.reply(message, "https://s-media-cache-ak0.pinimg.com/736x/2e/29/87/2e298711c7fec1d77637a982235c9910.jpg");
});

controller.hears(['julio', 'orlando', 'orly'], 'direct_message,direct_mention,mention,ambient', function(bot, message){
  var userID = message.user;
  var user = "<@"+userID+">";
  var reply = user + " eso que dijo merece esto";
  bot.reply(message, reply);
  bot.reply(message, 'http://gph.is/1HlCG6v');
});

controller.hears(['me llamo (.*)', 'mi nombre es (.*)', 'soy (.*)'], 'direct_message,direct_mention,mention', function(bot, message) {
    var name = message.match[1];
    bot.reply(message, 'Quiere un trofeo o fiesta en hooters? ' + "<@"+message.user+">");
});

controller.on("bot_channel_join", function(bot, message){
  bot.reply(message,"Ahora si empieza lo bueno :smirk:");
});

controller.on("bot_group_join", function(bot, message){
  bot.reply(message,"Ahora si empieza lo bueno :smirk:");
});

controller.hears(['jale a comer', 'almuerzo', 'comida', 'comen'], 'ambient', function(bot, message){
  var userID = message.user;
  var user = "<@"+userID+">";
  bot.reply(message, "Yo me encargo " + user);
  bot.say({text: "@channel: hey todos dice " + user + " que tiene hambre a donde vamos",
           channel:"C11429SQ6"});
});

controller.hears('','direct_message,direct_mention,mention',function(bot,message) {
    var msg = message.text;
    cleverbot.ask(msg, function (err, response) {
        if (!err) {
            bot.reply(message, response);
        } else {
            console.log('cleverbot err: ' + err);
        }
    });
})

function formatUptime(uptime) {
    var unit = 'second';
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'minute';
    }
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'hour';
    }
    if (uptime != 1) {
        unit = unit + 's';
    }

    uptime = uptime + ' ' + unit;
    return uptime;
}
<|endoftext|>


<commit_before>/**
 * CSSS javascript code
 * @author Lea Verou (http://leaverou.me)
 * @version 1.0
 */

(function(head, body){

// Check for classList support and include the polyfill if it's not supported
if(!('classList' in body)) {
	var thisScript = document.querySelector('script[src$="slideshow.js"]'),
	    script = document.createElement('script');
	    script.src = thisScript.src.replace(/\bslideshow\.js/, 'classList.js');
	thisScript.parentNode.insertBefore(script, thisScript);
}

// Cache <title> element, we may need it for slides that don't have titles
var documentTitle = document.title + '';

window.SlideShow = function(container, slide) {
	var me = this;

	container = container || body;

	// Current slide
	this.slide = slide || 0;

	// Current .delayed item in the slide
	this.item = 0;

	// Do we need to add a timer?
	this.duration = container.getAttribute('data-duration');

	if(this.duration > 0) {
		var timer = document.createElement('div'),
		    declaration = 'transition: ' + this.duration * 60 + 's; ';

		timer.id = 'timer';
		timer.setAttribute('style', '-moz-' + declaration + '-webkit-' + declaration + '-o-' + declaration + '-ms-' + declaration + declaration);
		container.appendChild(timer);

		setTimeout(function() {
			timer.className = 'end';
		}, 1);
	}

	// Get the slide elements into an array
	this.slides = Array.prototype.slice.apply(container.querySelectorAll('.slide'));

	for(var i=0; i<this.slides.length; i++) {
		var slide = this.slides[i]; // to speed up references

		// Asign ids to slides that don't have one
		if(!slide.id) {
			slide.id = 'slide' + (i+1);
		}

		// Set data-title attribute to the title of the slide
		if(!slide.title) {
			// no title attribute, fetch title from heading(s)
			var heading = slide.querySelector('hgroup') || slide.querySelector('h1,h2,h3,h4,h5,h6');

			if(heading && heading.textContent.trim()) {
				slide.setAttribute('data-title', heading.textContent);
			}
		}
		else {
			// The title attribute is set, use that
			slide.setAttribute('data-title', slide.title);
			slide.removeAttribute('title');
		}
	}

	// If there's already a hash, update current slide number...
	this.goto(location.hash.substr(1) || 0);

	// ...and keep doing so every time the hash changes
	this.onhashchange = function() {
		me.goto(location.hash.substr(1) || 0);
	};
	window.addEventListener('hashchange', this.onhashchange, false);

	// Adjust the font-size when the window is resized
	window.addEventListener('resize', function() {
		me.adjustFontSize();
	}, false);

	// In some browsers DOMContentLoaded is too early, so try again onload
	window.addEventListener('load', function() {
		me.adjustFontSize();
	}, false);

	/**
		Keyboard navigation
		Home : First slide
		End : Last slide
		Up/Right arrow : Next item/slide
		Ctrl + Up/Right arrow : Next slide
		Down/Left arrow : Previous item/slide
		Ctrl + Down/Left arrow : Previous slide
		Ctrl+G : Go to slide...
		Ctrl+H : Show thumbnails and go to slide
		(Shift instead of Ctrl works too)
	*/
	document.addEventListener('keyup', function(evt) {
		if(evt.ctrlKey || evt.shiftKey) {
			switch(evt.keyCode) {
				case 71: // G
					var slide = prompt('Which slide?');
					me.goto(+slide? slide - 1 : slide);
					break;
				case 72: // H
					if(body.classList.contains('show-thumbnails')) {
						body.classList.remove('show-thumbnails');
						body.classList.remove('headers-only');
					}
					else {
						body.classList.add('show-thumbnails');

						if(!evt.shiftKey || !evt.ctrlKey) {
							body.classList.add('headers-only');
						}

						body.addEventListener('click', function(evt) {
							var slide = evt.target;

							while(slide && !slide.classList.contains('slide')) {
								slide = slide.parentNode;
							}

							if(slide) {
								me.goto(slide.id);
								setTimeout(function() { me.adjustFontSize(); }, 1000); // for Opera
							}

							body.classList.remove('show-thumbnails');
							body.classList.remove('headers-only');
						}, false);
					}
					break;
				case 74: // J
					if(body.classList.contains('hide-elements')) {
						body.classList.remove('hide-elements');
					}
					else {
						body.classList.add('hide-elements');
					}
			}
		}

		if(evt.target === body || evt.target === body.parentNode) {
			if(evt.keyCode >= 35 && evt.keyCode <= 40) {
				evt.preventDefault();
			}

			switch(evt.keyCode) {
				case 35: // end
					me.end();
					break;
				case 36: // home
					me.start();
					break;
				case 37: // <-
				case 38: // up arrow
					me.previous(evt.ctrlKey || evt.shiftKey);
					break;
				case 39: // ->
				case 40: // down arrow
					me.next(evt.ctrlKey || evt.shiftKey);
					break;
			}
		}
	}, false);
}

SlideShow.prototype = {
	start: function() {
		this.goto(0);
	},

	end: function() {
		this.goto(this.slides.length - 1);
	},

	/**
		@param hard {Boolean} Whether to advance to the next slide (true) or
			just the next step (which could very well be showing a list item)
	 */
	next: function(hard) {
		if(!hard && this.items.length) {
			// If there's no current, then just mark the first one as such
			if(!this.item) {
				this.items[this.item++].classList.add('current');
			}
			// Add .current to current item if it exists, otherwise advance to next slide
			else if(this.item < this.items.length) {
				classes = this.items[this.item - 1].classList; // to speed up lookups

				classes.remove('current');
				classes.add('displayed');

				this.items[this.item++].classList.add('current');
			}
			else {
				this.item = 0;
				this.next(true);
			}
		}
		else {
			this.goto(this.slide + 1);

			this.item = 0;

			// Mark all items as not displayed, if there are any
			if(this.items.length) {
				for (var i=0; i<this.items.length; i++) {
					if(this.items[i].classList) {
						this.items[i].classList.remove('displayed');
						this.items[i].classList.remove('current');
					}
				}
			}
		}
	},

	previous: function(hard) {
		if(!hard && this.item > 0) {
			var classes = this.items[this.item - 1].classList; // to speed up lookups

			classes.remove('current');

			if(this.item > 1) {
				classes = this.items[--this.item - 1].classList;

				classes.remove('displayed');
				classes.add('current');
			}
			else {
				this.item = 0;
			}
		}
		else {

			this.goto(this.slide - 1);

			this.item = this.items.length;

			// Mark all items as displayed, if there are any
			if(this.items.length) {
				for (var i=0; i<this.items.length; i++) {
					if(this.items[i].classList) {
						this.items[i].classList.add('displayed');
					}
				}

				// Mark the last one as current
				var lastItem = this.items[this.items.length - 1];

				lastItem.classList.remove('displayed');
				lastItem.classList.add('current');
			}
		}
	},

	/**
		Go to an aribtary slide
		@param which {String|Integer} Which slide (identifier or slide number)
	*/
	goto: function(which) {
		var slide;

		// We have to remove it to prevent multiple calls to goto messing up
		// our current item (and there's no point either, so we save on performance)
		window.removeEventListener('hashchange', this.onhashchange, false);

		if(which + 0 === which && which in this.slides) { // Argument is a valid slide number
			this.slide = which;

			slide = this.slides[this.slide];
			location.hash = '#' + slide.id;
		}
		else if(which + '' === which) { // Argument is a slide id
			slide = document.getElementById(which);

			if(slide) {
				this.slide = this.slides.indexOf(slide);
				location.hash = '#' + which;
			}
		}

		if(slide) { // Slide actually changed, perform any other tasks needed
			document.title = slide.getAttribute('data-title') || documentTitle;

			this.adjustFontSize();

			// Update items collection
			this.items = this.slides[this.slide].querySelectorAll('.delayed');
			this.item = 0;
		}

		// If you attach the listener immediately again then it will catch the event
		// We have to do it asynchronously
		var me = this;
		setTimeout(function() {
			window.addEventListener('hashchange', me.onhashchange, false);
		}, 1000);
	},

	adjustFontSize: function() {
		// Cache long lookup chains, for performance
		var bodyStyle = body.style,
			scrollRoot = document[document.documentElement.scrollHeight? 'documentElement' : 'body'],
			innerHeight = window.innerHeight,
			innerWidth = window.innerWidth,
			slide = this.slides[this.slide];

		// Clear previous styles
		bodyStyle.fontSize = '';

		if(body.classList.contains('show-thumbnails')
			|| slide.classList.contains('dont-resize')) {
			return;
		}

		for(
			var percent = 100;
			(scrollRoot.scrollHeight > innerHeight || scrollRoot.scrollWidth > innerWidth) && percent >= 35;
			percent-=5
		) {
			bodyStyle.fontSize = percent + '%';
		}

		// Individual slide

		if(slide.clientHeight && slide.clientWidth) {
			// Strange FF bug: scrollHeight doesn't work properly with overflow:hidden
			var previousStyle = slide.getAttribute('style');
			slide.style.overflow = 'auto';

			for(
				;
				(slide.scrollHeight > slide.clientHeight || slide.scrollWidth > slide.clientWidth) && percent >= 35;
				percent--
			) {
				bodyStyle.fontSize = percent + '%';
			}

			slide.setAttribute('style', previousStyle);
		}
	}
};

})(document.head = document.getElementsByTagName('head')[0], document.body);<commit_msg>Minor improvement<commit_after>/**
 * CSSS javascript code
 * @author Lea Verou (http://leaverou.me)
 * @version 1.0
 */

(function(head, body){

// Check for classList support and include the polyfill if it's not supported
if(!('classList' in body)) {
	var thisScript = document.querySelector('script[src$="slideshow.js"]'),
	    script = document.createElement('script');
	    script.src = thisScript.src.replace(/\bslideshow\.js/, 'classList.js');
	thisScript.parentNode.insertBefore(script, thisScript);
}

// Cache <title> element, we may need it for slides that don't have titles
var documentTitle = document.title + '';

window.SlideShow = function(container, slide) {
	var me = this;

	container = container || body;

	// Current slide
	this.slide = slide || 0;

	// Current .delayed item in the slide
	this.item = 0;

	// Do we need to add a timer?
	this.duration = container.getAttribute('data-duration');

	if(this.duration > 0) {
		var timer = document.createElement('div'),
		    declaration = 'transition: ' + this.duration * 60 + 's; ';

		timer.id = 'timer';
		timer.setAttribute('style', '-moz-' + declaration + '-webkit-' + declaration + '-o-' + declaration + '-ms-' + declaration + declaration);
		container.appendChild(timer);

		setTimeout(function() {
			timer.className = 'end';
		}, 1);
	}

	// Get the slide elements into an array
	this.slides = Array.prototype.slice.apply(container.querySelectorAll('.slide'));

	for(var i=0; i<this.slides.length; i++) {
		var slide = this.slides[i]; // to speed up references

		// Asign ids to slides that don't have one
		if(!slide.id) {
			slide.id = 'slide' + (i+1);
		}

		// Set data-title attribute to the title of the slide
		if(!slide.title) {
			// no title attribute, fetch title from heading(s)
			var heading = slide.querySelector('hgroup') || slide.querySelector('h1,h2,h3,h4,h5,h6');

			if(heading && heading.textContent.trim()) {
				slide.setAttribute('data-title', heading.textContent);
			}
		}
		else {
			// The title attribute is set, use that
			slide.setAttribute('data-title', slide.title);
			slide.removeAttribute('title');
		}
	}

	// If there's already a hash, update current slide number...
	this.goto(location.hash.substr(1) || 0);

	// ...and keep doing so every time the hash changes
	this.onhashchange = function() {
		me.goto(location.hash.substr(1) || 0);
	};
	window.addEventListener('hashchange', this.onhashchange, false);

	// Adjust the font-size when the window is resized
	window.addEventListener('resize', function() {
		me.adjustFontSize();
	}, false);

	// In some browsers DOMContentLoaded is too early, so try again onload
	window.addEventListener('load', function() {
		me.adjustFontSize();
	}, false);

	/**
		Keyboard navigation
		Home : First slide
		End : Last slide
		Up/Right arrow : Next item/slide
		Ctrl + Up/Right arrow : Next slide
		Down/Left arrow : Previous item/slide
		Ctrl + Down/Left arrow : Previous slide
		Ctrl+G : Go to slide...
		Ctrl+H : Show thumbnails and go to slide
		(Shift instead of Ctrl works too)
	*/
	document.addEventListener('keyup', function(evt) {
		if(evt.ctrlKey || evt.shiftKey) {
			switch(evt.keyCode) {
				case 71: // G
					var slide = prompt('Which slide?');
					me.goto(+slide? slide - 1 : slide);
					break;
				case 72: // H
					if(body.classList.contains('show-thumbnails')) {
						body.classList.remove('show-thumbnails');
						body.classList.remove('headers-only');
					}
					else {
						body.classList.add('show-thumbnails');

						if(!evt.shiftKey || !evt.ctrlKey) {
							body.classList.add('headers-only');
						}

						body.addEventListener('click', function(evt) {
							var slide = evt.target;

							while(slide && !slide.classList.contains('slide')) {
								slide = slide.parentNode;
							}

							if(slide) {
								me.goto(slide.id);
								setTimeout(function() { me.adjustFontSize(); }, 1000); // for Opera
							}

							body.classList.remove('show-thumbnails');
							body.classList.remove('headers-only');
						}, false);
					}
					break;
				case 74: // J
					if(body.classList.contains('hide-elements')) {
						body.classList.remove('hide-elements');
					}
					else {
						body.classList.add('hide-elements');
					}
			}
		}

		if(evt.target === body || evt.target === body.parentNode) {
			if(evt.keyCode >= 35 && evt.keyCode <= 40) {
				evt.preventDefault();
			}

			switch(evt.keyCode) {
				case 35: // end
					me.end();
					break;
				case 36: // home
					me.start();
					break;
				case 37: // <-
				case 38: // up arrow
					me.previous(evt.ctrlKey || evt.shiftKey);
					break;
				case 39: // ->
				case 40: // down arrow
					me.next(evt.ctrlKey || evt.shiftKey);
					break;
			}
		}
	}, false);
}

SlideShow.prototype = {
	start: function() {
		this.goto(0);
	},

	end: function() {
		this.goto(this.slides.length - 1);
	},

	/**
		@param hard {Boolean} Whether to advance to the next slide (true) or
			just the next step (which could very well be showing a list item)
	 */
	next: function(hard) {
		if(!hard && this.items.length) {
			// If there's no current, then just mark the first one as such
			if(!this.item) {
				this.items[this.item++].classList.add('current');
			}
			// Add .current to current item if it exists, otherwise advance to next slide
			else if(this.item < this.items.length) {
				classes = this.items[this.item - 1].classList; // to speed up lookups

				classes.remove('current');
				classes.add('displayed');

				this.items[this.item++].classList.add('current');
			}
			else {
				this.item = 0;
				this.next(true);
			}
		}
		else {
			this.goto(this.slide + 1);

			this.item = 0;

			// Mark all items as not displayed, if there are any
			if(this.items.length) {
				for (var i=0; i<this.items.length; i++) {
					if(this.items[i].classList) {
						this.items[i].classList.remove('displayed');
						this.items[i].classList.remove('current');
					}
				}
			}
		}
	},

	previous: function(hard) {
		if(!hard && this.item > 0) {
			var classes = this.items[this.item - 1].classList; // to speed up lookups

			classes.remove('current');

			if(this.item > 1) {
				classes = this.items[--this.item - 1].classList;

				classes.remove('displayed');
				classes.add('current');
			}
			else {
				this.item = 0;
			}
		}
		else {

			this.goto(this.slide - 1);

			this.item = this.items.length;

			// Mark all items as displayed, if there are any
			if(this.items.length) {
				for (var i=0; i<this.items.length; i++) {
					if(this.items[i].classList) {
						this.items[i].classList.add('displayed');
					}
				}

				// Mark the last one as current
				var lastItem = this.items[this.items.length - 1];

				lastItem.classList.remove('displayed');
				lastItem.classList.add('current');
			}
		}
	},

	/**
		Go to an aribtary slide
		@param which {String|Integer} Which slide (identifier or slide number)
	*/
	goto: function(which) {
		var slide;

		// We have to remove it to prevent multiple calls to goto messing up
		// our current item (and there's no point either, so we save on performance)
		window.removeEventListener('hashchange', this.onhashchange, false);

		if(which + 0 === which && which in this.slides) { // Argument is a valid slide number
			this.slide = which;

			slide = this.slides[this.slide];
			location.hash = '#' + slide.id;
		}
		else if(which + '' === which) { // Argument is a slide id
			slide = document.getElementById(which);

			if(slide) {
				this.slide = this.slides.indexOf(slide);
				location.hash = '#' + which;
			}
		}

		if(slide) { // Slide actually changed, perform any other tasks needed
			document.title = slide.getAttribute('data-title') || documentTitle;

			this.adjustFontSize();

			// Update items collection
			this.items = this.slides[this.slide].querySelectorAll('.delayed');
			this.item = 0;
		}

		// If you attach the listener immediately again then it will catch the event
		// We have to do it asynchronously
		var me = this;
		setTimeout(function() {
			window.addEventListener('hashchange', me.onhashchange, false);
		}, 1000);
	},

	adjustFontSize: function() {
		// Cache long lookup chains, for performance
		var bodyStyle = body.style,
			scrollRoot = document[document.documentElement.scrollHeight? 'documentElement' : 'body'],
			innerHeight = window.innerHeight,
			innerWidth = window.innerWidth,
			slide = this.slides[this.slide];

		// Clear previous styles
		bodyStyle.fontSize = '';

		if(body.classList.contains('show-thumbnails')
			|| slide.classList.contains('dont-resize')) {
			return;
		}

		for(
			var percent = 100;
			(scrollRoot.scrollHeight > innerHeight || scrollRoot.scrollWidth > innerWidth) && percent >= 35;
			percent-=5
		) {
			bodyStyle.fontSize = percent + '%';
		}

		// Individual slide

		if(slide.clientHeight && slide.clientWidth) {
			// Strange FF bug: scrollHeight doesn't work properly with overflow:hidden
			var previousStyle = slide.getAttribute('style');
			slide.style.overflow = 'auto';

			for(
				;
				(slide.scrollHeight > slide.clientHeight || slide.scrollWidth > slide.clientWidth) && percent >= 35;
				percent--
			) {
				bodyStyle.fontSize = percent + '%';
			}

			slide.setAttribute('style', previousStyle);
		}
	}
};

})(document.head || document.getElementsByTagName('head')[0], document.body);<|endoftext|>


<commit_before>//
// Usage:
//
// var config = {
//     canvas: document.getElementById('myCanvas')
// };
//
// var data = {
//     labels: ["Jan", "Feb", "Mar"],
//     datasets: [
//         {
//             data: [3.5, 12.2, 6.8]
//         },
//         {
//             data: [5.4, 9.9, 4.6]
//         }
//     ]
// };
//
// new SlimChart(config).draw(data);
//
function SlimChart(config) {
    var self = this;

    //
    // Override these default configuration parameters yourself
    //
    self.initialize = function (config) {
        self.config = {
            canvas: config.canvas, // required - a canvas element
            axisLineWidth: config.axisLineWidth || 1,
            axisLineColor: config.axisLineColor || "#000000",
            axisTextColor: config.axisTextColor || "#000000",
            xAxisFormatter: config.xAxisFormatter || self.defaultXAxisFormatter,
            yAxisFormatter: config.yAxisFormatter || self.defaultYAxisFormatter,
            yAxisMin: config.yAxisMin,
            yAxisMax: config.yAxisMax,
            yAxisSteps: config.yAxisSteps || 5,
            datasetLineWidth: config.datasetLineWidth || 2,
            datasetColorPicker: config.datasetColorPicker || self.defaultColorPicker
        };

        if (self.config.canvas instanceof HTMLCanvasElement) {
            self.config.context = canvas.getContext('2d');
        } else {
            throw new Error("canvas is not an element");
        }
    };

    //
    // Draws (or redraws) the chart; this can safely be called again with new data
    //
    self.draw = function (data) {
        self.clear();
        self.validateData(data);

        var start = new Date().getTime();

        self.scaleForRetina();
        self.analyzeData(data);
        self.drawDatasets(data);
        self.drawAxes(data);

        var end = new Date().getTime();

        console.log("drawn in " + (end - start));
    };

    //
    // Analyzes chart data to compute maximum and minimum
    //
    self.analyzeData = function (data) {
        var config = self.config;
        var ctx = config.context;
        var w = ctx.canvas.clientWidth;
        var h = ctx.canvas.clientHeight;

        config.graphArea = {
            left: 50,
            top: 10,
            bottom: h - 50,
            right: w
        };

        if (typeof(config.yAxisMax) === 'undefined' || typeof(config.yAxisMin) === 'undefined') {
            var yAxisMax = Number.MIN_VALUE;
            var yAxisMin = Number.MAX_VALUE;

            data.datasets.forEach(function(dataset) {
                dataset.data.forEach(function(value) {
                    yAxisMax = Math.max(yAxisMax, value);
                    yAxisMin = Math.min(yAxisMin, value);
                });
            });

            if (typeof(config.yAxisMax) === 'undefined') {
                config.yAxisMax = yAxisMax;
            }

            if (typeof(config.yAxisMin) === 'undefined') {
                config.yAxisMin = yAxisMin;
            }

            config.yAxisMax = self.calculateYAxisCeiling(config.yAxisMax);
        }

        config.xStep = (config.graphArea.right - config.graphArea.left) / data.labels.length;
    };

    //
    // Draws the x-axis, y-axis, and labels
    //
    self.drawAxes = function (data) {
        var config = self.config;
        var ctx = config.context;
        var g = config.graphArea;

        ctx.translate(0.5, 0.5);
        ctx.lineWidth = config.axisLineWidth;
        ctx.strokeStyle = config.axisLineColor;
        ctx.beginPath();
        ctx.moveTo(g.left, g.top);
        ctx.lineTo(g.left, g.bottom);
        ctx.lineTo(g.right, g.bottom);
        ctx.stroke();
        ctx.fillStyle = config.axisTextColor;
        ctx.textBaseline = 'top';
        ctx.textAlign = 'center';

        for (var i = 0; i < data.labels.length; i++) {
            var x = Math.round(g.left + (config.xStep * i));
            var y = g.bottom;
            var t = config.xAxisFormatter(data.labels[i]);

            if (t) {
                ctx.beginPath();
                ctx.moveTo(x, y);
                ctx.lineTo(x, y + 5);
                ctx.stroke();

                ctx.fillText(t, x, y + 5);
            }
        }

        ctx.textBaseline = 'middle';
        ctx.textAlign = 'end';

        var yStep = (g.bottom - g.top) / config.yAxisSteps;

        for (var i = 0; i <= config.yAxisSteps; i++) {
            var x = g.left;
            var y = g.top + (i * yStep);
            var steps = config.yAxisSteps;
            var range = config.yAxisMax - config.yAxisMin;
            var v = config.yAxisMax - (i / config.yAxisSteps) * range;
            var t = config.yAxisFormatter(v, config.yAxisMax);

            ctx.beginPath();
            ctx.moveTo(x, y);
            ctx.lineTo(x - 5, y);
            ctx.stroke();

            ctx.fillText(t, x - 8, y);
        }

        ctx.translate(-0.5, -0.5);
    };

    //
    // Draws a line showing values for each dataset
    //
    self.drawDatasets = function (data) {
        var config = self.config;
        var ctx = config.context;
        var g = config.graphArea;
        var width = g.right - g.left;
        var height = g.bottom - g.top;
        var range = config.yAxisMax - config.yAxisMin;

        data.datasets.forEach(function (dataset) {
            ctx.lineWidth = config.datasetLineWidth;
            ctx.strokeStyle = config.datasetColorPicker(dataset);
            ctx.beginPath();

            dataset.data.forEach(function (val, i) {
                var ratio = (val - config.yAxisMin) / (config.yAxisMax - config.yAxisMin);
                var x = g.left + (i * config.xStep);
                var y = g.bottom - (height * ratio);

                if (i == 0) {
                    ctx.moveTo(x, y);
                } else {
                    ctx.lineTo(x, y);
                }
            });

            ctx.stroke();
        });
    };

    //
    // Scales the canvas for retina devices
    //
    self.scaleForRetina = function () {
        var ctx = self.config.context;
        var width = ctx.canvas.width;
        var height = ctx.canvas.height;

        if (window.devicePixelRatio) {
            ctx.canvas.style.width = width + "px";
            ctx.canvas.style.height = height + "px";
            ctx.canvas.height = height * window.devicePixelRatio;
            ctx.canvas.width = width * window.devicePixelRatio;
            ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
        }
    };

    //
    // Clears the canvas
    //
    self.clear = function () {
        self.config.context.clearRect(0, 0, self.config.canvas.width, self.config.canvas.height);
    };

    //
    // Calculates the graph ceiling for the y-axis (allowing for a nice round
    // number as the maximum)
    //
    self.calculateYAxisCeiling = function (value) {
        var steps = self.config.yAxisSteps;
        var magnitude = Math.floor(Math.log(value) / Math.LN10);
        var multiplier = Math.pow(10, magnitude);
        var stepSize = multiplier * Math.ceil(value / steps / multiplier);

        return stepSize * steps;
    };

    //
    // Validates the chart data has the bare minimum
    //
    self.validateData = function (data) {
        if (!data) {
            throw new Error("data is required");
        } else if (!Array.isArray(data.labels)) {
            throw new Error("data.labels must be an array");
        } else if (!Array.isArray(data.datasets)) {
            throw new Error("data.datasets must be an array");
        }
    };

    //
    // Default x-axis formatter that returns the literal value
    //
    self.defaultXAxisFormatter = function (val) {
        return val;
    };

    //
    // Default y-axis formatter that shows a reasonable amount of precision
    //
    self.defaultYAxisFormatter = function (value, max) {
        if (value == 0) {
            return "";
        } else if (max <= 0.1) {
            return value.toFixed(2);
        } else if (max <= 1) {
            return value.toFixed(1);
        } else if (max <= 10) {
            return value.toFixed(1);
        } else {
            return value.toFixed(0);
        }
    };

    //
    // Default color picker that picks a random color per dataset
    //
    self.defaultColorPicker = function (dataset) {
        return '#' + Math.floor(Math.random() * 16777215).toString(16);
    };

    self.initialize(config);
}
<commit_msg>Only scale for retina if we haven't already.<commit_after>//
// Usage:
//
// var config = {
//     canvas: document.getElementById('myCanvas')
// };
//
// var data = {
//     labels: ["Jan", "Feb", "Mar"],
//     datasets: [
//         {
//             data: [3.5, 12.2, 6.8]
//         },
//         {
//             data: [5.4, 9.9, 4.6]
//         }
//     ]
// };
//
// new SlimChart(config).draw(data);
//
function SlimChart(config) {
    var self = this;

    //
    // Override these default configuration parameters yourself
    //
    self.initialize = function (config) {
        self.config = {
            canvas: config.canvas, // required - a canvas element
            axisLineWidth: config.axisLineWidth || 1,
            axisLineColor: config.axisLineColor || "#000000",
            axisTextColor: config.axisTextColor || "#000000",
            xAxisFormatter: config.xAxisFormatter || self.defaultXAxisFormatter,
            yAxisFormatter: config.yAxisFormatter || self.defaultYAxisFormatter,
            yAxisMin: config.yAxisMin,
            yAxisMax: config.yAxisMax,
            yAxisSteps: config.yAxisSteps || 5,
            datasetLineWidth: config.datasetLineWidth || 2,
            datasetColorPicker: config.datasetColorPicker || self.defaultColorPicker
        };

        if (self.config.canvas instanceof HTMLCanvasElement) {
            self.config.context = canvas.getContext('2d');
        } else {
            throw new Error("canvas is not an element");
        }
    };

    //
    // Draws (or redraws) the chart; this can safely be called again with new data
    //
    self.draw = function (data) {
        self.clear();
        self.validateData(data);

        var start = new Date().getTime();

        self.scaleForRetina();
        self.analyzeData(data);
        self.drawDatasets(data);
        self.drawAxes(data);

        var end = new Date().getTime();

        console.log("drawn in " + (end - start));
    };

    //
    // Analyzes chart data to compute maximum and minimum
    //
    self.analyzeData = function (data) {
        var config = self.config;
        var ctx = config.context;
        var w = ctx.canvas.clientWidth;
        var h = ctx.canvas.clientHeight;

        config.graphArea = {
            left: 50,
            top: 10,
            bottom: h - 50,
            right: w
        };

        if (typeof(config.yAxisMax) === 'undefined' || typeof(config.yAxisMin) === 'undefined') {
            var yAxisMax = Number.MIN_VALUE;
            var yAxisMin = Number.MAX_VALUE;

            data.datasets.forEach(function(dataset) {
                dataset.data.forEach(function(value) {
                    yAxisMax = Math.max(yAxisMax, value);
                    yAxisMin = Math.min(yAxisMin, value);
                });
            });

            if (typeof(config.yAxisMax) === 'undefined') {
                config.yAxisMax = yAxisMax;
            }

            if (typeof(config.yAxisMin) === 'undefined') {
                config.yAxisMin = yAxisMin;
            }

            config.yAxisMax = self.calculateYAxisCeiling(config.yAxisMax);
        }

        config.xStep = (config.graphArea.right - config.graphArea.left) / data.labels.length;
    };

    //
    // Draws the x-axis, y-axis, and labels
    //
    self.drawAxes = function (data) {
        var config = self.config;
        var ctx = config.context;
        var g = config.graphArea;

        ctx.translate(0.5, 0.5);
        ctx.lineWidth = config.axisLineWidth;
        ctx.strokeStyle = config.axisLineColor;
        ctx.beginPath();
        ctx.moveTo(g.left, g.top);
        ctx.lineTo(g.left, g.bottom);
        ctx.lineTo(g.right, g.bottom);
        ctx.stroke();
        ctx.fillStyle = config.axisTextColor;
        ctx.textBaseline = 'top';
        ctx.textAlign = 'center';

        for (var i = 0; i < data.labels.length; i++) {
            var x = Math.round(g.left + (config.xStep * i));
            var y = g.bottom;
            var t = config.xAxisFormatter(data.labels[i]);

            if (t) {
                ctx.beginPath();
                ctx.moveTo(x, y);
                ctx.lineTo(x, y + 5);
                ctx.stroke();

                ctx.fillText(t, x, y + 5);
            }
        }

        ctx.textBaseline = 'middle';
        ctx.textAlign = 'end';

        var yStep = (g.bottom - g.top) / config.yAxisSteps;

        for (var i = 0; i <= config.yAxisSteps; i++) {
            var x = g.left;
            var y = g.top + (i * yStep);
            var steps = config.yAxisSteps;
            var range = config.yAxisMax - config.yAxisMin;
            var v = config.yAxisMax - (i / config.yAxisSteps) * range;
            var t = config.yAxisFormatter(v, config.yAxisMax);

            ctx.beginPath();
            ctx.moveTo(x, y);
            ctx.lineTo(x - 5, y);
            ctx.stroke();

            ctx.fillText(t, x - 8, y);
        }

        ctx.translate(-0.5, -0.5);
    };

    //
    // Draws a line showing values for each dataset
    //
    self.drawDatasets = function (data) {
        var config = self.config;
        var ctx = config.context;
        var g = config.graphArea;
        var width = g.right - g.left;
        var height = g.bottom - g.top;
        var range = config.yAxisMax - config.yAxisMin;

        data.datasets.forEach(function (dataset) {
            ctx.lineWidth = config.datasetLineWidth;
            ctx.strokeStyle = config.datasetColorPicker(dataset);
            ctx.beginPath();

            dataset.data.forEach(function (val, i) {
                var ratio = (val - config.yAxisMin) / (config.yAxisMax - config.yAxisMin);
                var x = g.left + (i * config.xStep);
                var y = g.bottom - (height * ratio);

                if (i == 0) {
                    ctx.moveTo(x, y);
                } else {
                    ctx.lineTo(x, y);
                }
            });

            ctx.stroke();
        });
    };

    //
    // Scales the canvas for retina devices
    //
    self.scaleForRetina = function () {
        var ctx = self.config.context;
        var width = ctx.canvas.width;
        var height = ctx.canvas.height;

        if (window.devicePixelRatio && !ctx.canvas.scaled) {
            ctx.canvas.height = height * window.devicePixelRatio;
            ctx.canvas.width = width * window.devicePixelRatio;
            ctx.canvas.scaled = "true";
            ctx.canvas.style.width = width + "px";
            ctx.canvas.style.height = height + "px";
            ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
        }
    };

    //
    // Clears the canvas
    //
    self.clear = function () {
        self.config.context.clearRect(0, 0, self.config.canvas.width, self.config.canvas.height);
    };

    //
    // Calculates the graph ceiling for the y-axis (allowing for a nice round
    // number as the maximum)
    //
    self.calculateYAxisCeiling = function (value) {
        var steps = self.config.yAxisSteps;
        var magnitude = Math.floor(Math.log(value) / Math.LN10);
        var multiplier = Math.pow(10, magnitude);
        var stepSize = multiplier * Math.ceil(value / steps / multiplier);

        return stepSize * steps;
    };

    //
    // Validates the chart data has the bare minimum
    //
    self.validateData = function (data) {
        if (!data) {
            throw new Error("data is required");
        } else if (!Array.isArray(data.labels)) {
            throw new Error("data.labels must be an array");
        } else if (!Array.isArray(data.datasets)) {
            throw new Error("data.datasets must be an array");
        }
    };

    //
    // Default x-axis formatter that returns the literal value
    //
    self.defaultXAxisFormatter = function (val) {
        return val;
    };

    //
    // Default y-axis formatter that shows a reasonable amount of precision
    //
    self.defaultYAxisFormatter = function (value, max) {
        if (value == 0) {
            return "";
        } else if (max <= 0.1) {
            return value.toFixed(2);
        } else if (max <= 1) {
            return value.toFixed(1);
        } else if (max <= 10) {
            return value.toFixed(1);
        } else {
            return value.toFixed(0);
        }
    };

    //
    // Default color picker that picks a random color per dataset
    //
    self.defaultColorPicker = function (dataset) {
        return '#' + Math.floor(Math.random() * 16777215).toString(16);
    };

    self.initialize(config);
}
<|endoftext|>


<commit_before>var gulp = require('gulp');

gulp.task('default', function(done){
    gulp.src(__dirname + '/template/**')
        .pipe(gulp.dest('./'))
        .on('end', function () {
            done();
        });
});<commit_msg>Fix for .bowerrc not being copied<commit_after>var gulp = require('gulp');

gulp.task('default', function(done){
    gulp.src(__dirname + '/template/**', {dot: true})
        .pipe(gulp.dest('./'))
        .on('end', function () {
            done();
        });
});<|endoftext|>