<commit_before>/**
 * Stack Exchange Chat Status
 * Copyright 2015 - Nathan Osman
 */

(function() {

    // TODO: get rid of this
    if(!location.pathname.startsWith('/rooms/201/')) {
        return;
    }

    /**
     * Global settings.
     */
    var DEBUG = true,
        SERVER = 'sechat.quickmediasolutions.com';

    /**
     * Log the specified message to the console for debug purposes.
     */
    function log(msg) {
        console.log('[ChatStatus] ' + msg);
    }

    /**
     * Return the current time as a unix timestamp.
     */
    function now() {
        return parseInt(new Date().getTime() / 1000);
    }

    /**
     * Given a message, obtain its ID.
     */
    function messageId(e) {
        return $(e).attr('id').match(/message-(\d+)/)[1];
    }

    /**
     * Given a message, obtain the user's ID. This is made simpler by the fact
     * that SE stores the ID directly in one of the parent elements.
     */
    function messageUserId(e) {
        return $(e).closest('.user-container').data('user');
    }

    /**
     * Load the specified script into the page.
     * @param name filename of the script
     * @param callback function to execute after loading
     */
    function loadScript(name, callback) {
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = 'https://' + SERVER + '/static/' + name;
        script.onload = callback;
        document.body.appendChild(script);
    }

    var pendingContainers = {};

    /**
     * Obtain the container for a specific message, creating it if necessary.
     * @param msgId the ID of the message
     * @return the container for indicators
     *
     * If the message does not exist, the container will be added to a list of
     * "pending" containers to be created when the message does exist.
     */
    function getContainer(msgId) {
        if(msgId in pendingContainers) {
            return pendingContainers[msgId];
        }
        var $msg = $('#message-' + msgId),
            $container = $msg.next('.secs-container');
        if(!$container.length) {
            $container = $('<div>')
                .addClass('secs-container')
                .css('marginLeft', '18px');
            if($msg.length) {
                $container.insertAfter($msg);
            } else {
                pendingContainers[msgId] = $container;
            }
        }
        return $container;
    }

    var users = {};

    /**
     * Obtain the indicator element for a user, creating it if necessary.
     * @param userId the ID of the user
     * @return the indicator for the user
     */
    function getUser(userId) {
        var $user;
        if(userId in users) {
            $user = users[userId];
        } else {
            var $typing = $('<img>')
                    .attr('src', '//cdn-chat.sstatic.net/chat/img/progress-dots.gif')
                    .css('marginRight', '4px')
                    .hide();
            $user = $('<span>')
                .attr('title', "User has read this far")
                .css({
                    backgroundColor: 'rgba(0, 0, 0, 0.1)',
                    borderRadius: '12px',
                    display: 'inline-block',
                    margin: '4px 4px 4px 0',
                    padding: '4px'
                })
                .append($(CHAT.RoomUsers.createAvatarImage(userId, 16))
                    .css({
                        borderRadius: '9px',
                        padding: '1px'
                    }))
                .append(' ')
                .append($typing)
                .data('typing', $typing);
            users[userId] = $user;
        }
        return $user;
    }

    /**
     * Update the message that the user has last read.
     * @param userId the ID of the user to update
     * @param msgId the ID of the message or null for the indicator tray
     */
    function updateUserMessage(userId, msgId) {
        getContainer(msgId).append(getUser(userId).detach());
    }

    /**
     * Indicate that a user has stopped typing.
     * @param userId the ID of the user
     */
    function userStoppedTyping(userId) {
        var $user = getUser(userId),
            timeout = $user.data('timeout');
        if(timeout) {
            window.clearTimeout(timeout);
            $user.data('typing').hide();
            $user.removeData('timeout');
        }
    }

    /**
     * Indicate that a user has started typing.
     * @param userId the ID of the user to update
     *
     * The typing indicator will be displayed for four seconds or until it is
     * reset by another call to this function for the same user.
     */
    function userStartedTyping(userId) {
        var $user = getUser(userId),
            timeout = $user.data('timeout');
        if(timeout) {
            window.clearTimeout(timeout);
        } else {
            $user.data('typing').css('display', 'inline');
        }
        $user.data('timeout', window.setTimeout(userStoppedTyping, 4000, userId));
    }

    var socket,
        lastTypingMsg = 0,
        lastMessageRead = 0,
        windowActive = true;

    /**
     * Determine if the socket is active.
     */
    function isActive() {
        return socket && socket.readyState == WebSocket.OPEN;
    }

    /**
     * Send a ping every 30 seconds to avoid having the socket timeout.
     */
    function ping() {
        if(isActive()) {
            DEBUG && log("ping");
            socket.send('');
        }
    }
    window.setInterval(ping, 30000);

    /**
     * Hook the input box in order to notify other users of typing. Only send
     * the messages once per two seconds to avoid excessive bandwidth.
     */
    $('#input').on('keypress', function() {
        if(isActive()) {
            var t = now();
            if(lastTypingMsg < (t - 2)) {
                DEBUG && log("last_char_entered: " + t);
                socket.send(JSON.stringify({
                    id: CHAT.CURRENT_USER_ID,
                    data: {
                        last_char_entered: t.toString()
                    }
                }));
                lastTypingMsg = t;
            }
        }
    });

    /**
     * Broadcast the last message read to the other users.
     */
    function broadcastLastMessageRead() {
        if(isActive()) {
            var msgId = messageId($('.message').last());
            if(parseInt(msgId) > parseInt(lastMessageRead)) {
                lastMessageRead = msgId;
                DEBUG && log("last_message_read: " + lastMessageRead);
                socket.send(JSON.stringify({
                    id: CHAT.CURRENT_USER_ID,
                    data: {
                        last_message_read: lastMessageRead
                    }
                }));
            }
        }
    }

    /**
     * Load Live Query and watch for new messages coming in. When one comes in,
     * that user has stopped typing. Also, check for pending containers.
     */
    loadScript('jquery.livequery.min.js', function() {
        log("Live Query loaded")
        var firstLoad = true;
        $('#chat').livequery('.message', function(e) {
            if(!firstLoad) {
                var msgId = messageId(e),
                    userId = messageUserId(e);
                if(msgId in pendingContainers) {
                    $(e).append(pendingContainers[msgId]);
                    delete pendingContainers[msgId];
                }
                userStoppedTyping(userId);
                if(windowActive) {
                    broadcastLastMessageRead();
                }
            }
        });
        firstLoad = false;
    });

    /**
     * Keep track of whether the window (or tab) is active.
     */
    $(window).on({
        'blur': function() {
            DEBUG && log("Window has lost focus");
            windowActive = false;
        },
        'focus': function() {
            DEBUG && log("Window has gained focus");
            windowActive = true;
            broadcastLastMessageRead();
        }
    });

    /**
     * Connect to the WebSocket endpoint and set the appropriate callbacks;
     */
    function connect() {
        socket = new WebSocket('wss://' + SERVER + '/api/connect');
        socket.onopen = onopen;
        socket.onmessage = onmessage;
        socket.onerror = onerror;
        socket.onclose = onclose;
    }

    /**
     * Log the successful connection.
     */
    function onopen() {
        log("socket opened");
        broadcastLastMessageRead();
    }

    /**
     * Process a message received from another user.
     */
    function onmessage(e) {
        if(e.data.length) {
            DEBUG && log("msg: " + e.data);
            var s = JSON.parse(e.data);
            if('last_char_entered' in s.data &&
                    parseInt(s.data.last_char_entered) > (now() - 4)) {
                userStartedTyping(s.id);
            }
            if('last_message_read' in s.data) {
                updateUserMessage(s.id, s.data.last_message_read);
            }
        }
    }

    /**
     * Log an error that occurs.
     */
    function onerror(e) {
        log("socket error: " + e);
    }

    /**
     * Attempt to reconnect after one minute.
     */
    function onclose() {
        log("socket closed");
        socket = null;
        window.setTimeout(connect, 60 * 1000);
    }

    /**
     * Connect for the first time.
     */
    connect();

})();
<commit_msg>Added preferences dialog (fixes #1).<commit_after>/**
 * Stack Exchange Chat Status
 * Copyright 2015 - Nathan Osman
 */

(function() {

    // TODO: get rid of this
    if(!location.pathname.startsWith('/rooms/201/')) {
        return;

    /**
     * Default values for global settings.
     */
    var DEFAULTS = {
        debug: false,
        self: true,
        server: 'sechat.quickmediasolutions.com'
    };

    /**
     * Store a value in localStorage.
     * @param key item to set
     * @param val new value
     */
    function set(key, val) {
        localStorage.setItem(key, JSON.stringify(val));
    }

    /**
     * Retrieve a setting from localStorage.
     * @param key item to retrieve
     *
     * The DEFAULTS variable will be used for setting the default value if the
     * key is not set.
     */
    function get(key) {
        var val = localStorage.getItem(key);
        if(val === null) {
            set(key, DEFAULTS[key]);
            return DEFAULTS[key];
        } else {
            return JSON.parse(val);
        }
    }

    /**
     * Log the specified message to the console for debug purposes.
     */
    function log(msg) {
        get('debug') && console.log('[ChatStatus] ' + msg);
    }

    /**
     * Return the current time as a unix timestamp.
     */
    function now() {
        return parseInt(new Date().getTime() / 1000);
    }

    /**
     * Given a message, obtain its ID.
     */
    function messageId(e) {
        return $(e).attr('id').match(/message-(\d+)/)[1];
    }

    /**
     * Given a message, obtain the user's ID. This is made simpler by the fact
     * that SE stores the ID directly in one of the parent elements.
     */
    function messageUserId(e) {
        return $(e).closest('.user-container').data('user');
    }

    /**
     * Load the specified script into the page.
     * @param name filename of the script
     * @param callback function to execute after loading
     */
    function loadScript(name, callback) {
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = 'https://' + get('server') + '/static/' + name;
        script.onload = callback;
        document.body.appendChild(script);
    }

    var pendingContainers = {};

    /**
     * Obtain the container for a specific message, creating it if necessary.
     * @param msgId the ID of the message
     * @return the container for indicators
     *
     * If the message does not exist, the container will be added to a list of
     * "pending" containers to be created when the message does exist.
     */
    function getContainer(msgId) {
        if(msgId in pendingContainers) {
            return pendingContainers[msgId];
        }
        var $msg = $('#message-' + msgId),
            $container = $msg.next('.secs-container');
        if(!$container.length) {
            $container = $('<div>')
                .addClass('secs-container')
                .css('marginLeft', '18px');
            if($msg.length) {
                $container.insertAfter($msg);
            } else {
                pendingContainers[msgId] = $container;
            }
        }
        return $container;
    }

    var users = {};

    /**
     * Obtain the indicator element for a user, creating it if necessary.
     * @param userId the ID of the user
     * @return the indicator for the user
     */
    function getUser(userId) {
        var $user;
        if(userId in users) {
            $user = users[userId];
        } else {
            var $typing = $('<img>')
                    .attr('src', '//cdn-chat.sstatic.net/chat/img/progress-dots.gif')
                    .css('marginRight', '4px')
                    .hide();
            $user = $('<span>')
                .attr('title', "User has read this far")
                .css({
                    backgroundColor: 'rgba(0, 0, 0, 0.1)',
                    borderRadius: '12px',
                    display: 'inline-block',
                    margin: '4px 4px 4px 0',
                    padding: '4px'
                })
                .append($(CHAT.RoomUsers.createAvatarImage(userId, 16))
                    .css({
                        borderRadius: '9px',
                        padding: '1px'
                    }))
                .append(' ')
                .append($typing)
                .data('typing', $typing);
            users[userId] = $user;
        }
        return $user;
    }

    /**
     * Update the message that the user has last read.
     * @param userId the ID of the user to update
     * @param msgId the ID of the message or null for the indicator tray
     */
    function updateUserMessage(userId, msgId) {
        getContainer(msgId).append(getUser(userId).detach());
    }

    /**
     * Indicate that a user has stopped typing.
     * @param userId the ID of the user
     */
    function userStoppedTyping(userId) {
        var $user = getUser(userId),
            timeout = $user.data('timeout');
        if(timeout) {
            window.clearTimeout(timeout);
            $user.data('typing').hide();
            $user.removeData('timeout');
        }
    }

    /**
     * Indicate that a user has started typing.
     * @param userId the ID of the user to update
     *
     * The typing indicator will be displayed for four seconds or until it is
     * reset by another call to this function for the same user.
     */
    function userStartedTyping(userId) {
        var $user = getUser(userId),
            timeout = $user.data('timeout');
        if(timeout) {
            window.clearTimeout(timeout);
        } else {
            $user.data('typing').css('display', 'inline');
        }
        $user.data('timeout', window.setTimeout(userStoppedTyping, 4000, userId));
    }

    var socket,
        lastTypingMsg = 0,
        lastMessageRead = 0,
        windowActive = true;

    /**
     * Determine if the socket is active.
     */
    function isActive() {
        return socket && socket.readyState == WebSocket.OPEN;
    }

    /**
     * Send a ping every 30 seconds to avoid having the socket timeout.
     */
    function ping() {
        if(isActive()) {
            log("ping");
            socket.send('');
        }
    }
    window.setInterval(ping, 30000);

    /**
     * Hook the input box in order to notify other users of typing. Only send
     * the messages once per two seconds to avoid excessive bandwidth.
     */
    $('#input').on('keypress', function() {
        if(isActive()) {
            var t = now();
            if(lastTypingMsg < (t - 2)) {
                log("last_char_entered: " + t);
                socket.send(JSON.stringify({
                    id: CHAT.CURRENT_USER_ID,
                    data: {
                        last_char_entered: t.toString()
                    }
                }));
                lastTypingMsg = t;
            }
        }
    });

    /**
     * Broadcast the last message read to the other users.
     */
    function broadcastLastMessageRead() {
        if(isActive()) {
            var msgId = messageId($('.message').last());
            if(parseInt(msgId) > parseInt(lastMessageRead)) {
                lastMessageRead = msgId;
                log("last_message_read: " + lastMessageRead);
                socket.send(JSON.stringify({
                    id: CHAT.CURRENT_USER_ID,
                    data: {
                        last_message_read: lastMessageRead
                    }
                }));
            }
        }
    }

    /**
     * Load Live Query and watch for new messages coming in. When one comes in,
     * that user has stopped typing. Also, check for pending containers.
     */
    loadScript('jquery.livequery.min.js', function() {
        log("Live Query loaded")
        var firstLoad = true;
        $('#chat').livequery('.message', function(e) {
            if(!firstLoad) {
                var msgId = messageId(e),
                    userId = messageUserId(e);
                if(msgId in pendingContainers) {
                    $(e).append(pendingContainers[msgId]);
                    delete pendingContainers[msgId];
                }
                userStoppedTyping(userId);
                if(windowActive) {
                    broadcastLastMessageRead();
                }
            }
        });
        firstLoad = false;
    });

    /**
     * Keep track of whether the window (or tab) is active.
     */
    $(window).on({
        'blur': function() {
            log("Window has lost focus");
            windowActive = false;
        },
        'focus': function() {
            log("Window has gained focus");
            windowActive = true;
            broadcastLastMessageRead();
        }
    });

    /**
     * Connect to the WebSocket endpoint and set the appropriate callbacks;
     */
    function connect() {
        socket = new WebSocket('wss://' + get('server') + '/api/connect');
        socket.onopen = onopen;
        socket.onmessage = onmessage;
        socket.onerror = onerror;
        socket.onclose = onclose;
    }

    /**
     * Log the successful connection.
     */
    function onopen() {
        log("socket opened");
        broadcastLastMessageRead();
    }

    /**
     * Process a message received from another user.
     */
    function onmessage(e) {
        if(e.data.length) {
            log("msg: " + e.data);
            var s = JSON.parse(e.data);
            if('last_char_entered' in s.data &&
                    parseInt(s.data.last_char_entered) > (now() - 4)) {
                userStartedTyping(s.id);
            }
            if('last_message_read' in s.data) {
                updateUserMessage(s.id, s.data.last_message_read);
            }
        }
    }

    /**
     * Log an error that occurs.
     */
    function onerror(e) {
        log("socket error: " + e);
    }

    /**
     * Attempt to reconnect after one minute.
     */
    function onclose() {
        log("socket closed");
        socket = null;
        window.setTimeout(connect, 60 * 1000);
    }

    connect();

    /**
     * Update the visibility of the current user.
     */
    function updateSelf(val) {
        getUser(CHAT.CURRENT_USER_ID).toggle(val);
    }
    updateSelf();

    /**
     * Preference dialog
     */
    var $prefCover = $('<div>')
            .css({
                backgroundColor: 'rgba(0, 0, 0, 0.4)',
                height: '100%',
                left: '0',
                position: 'fixed',
                top: '0',
                width: '100%',
                zIndex: '6'
            })
            .hide()
            .appendTo(document.body),
        $prefDialog = $('<div>')
            .css({
                backgroundColor: '#fff',
                boxShadow: '0 1px 15px #555555',
                boxSizing: 'border-box',
                height: '200px',
                left: '50%',
                marginLeft: '-150px',
                marginTop: '-100px',
                padding: '8px',
                position: 'fixed',
                top: '50%',
                width: '300px'
            })
            .append($('<a>')
                .attr('href', '#')
                .css('float', 'right')
                .html('×')
                .click(function() {
                    $prefCover.fadeOut();
                    return false;
                }))
            .append($('<h4>').text("Preferences"))
            .append('<br>')
            .appendTo($prefCover),
        $debugOption = $('<div>')
            .append($('<input>')
                .attr('type', 'checkbox')
                .prop('checked', get('debug'))
                .change(function() {
                    set('debug', this.checked);
                }))
            .append(" debug messages")
            .appendTo($prefDialog),
        $selfOption = $('<div>')
            .append($('<input>')
                .attr('type', 'checkbox')
                .prop('checked', get('self'))
                .change(function() {
                    set('self', this.checked);
                    updateSelf(this.checked);
                }))
            .append(" show me")
            .appendTo($prefDialog);
    $('input[type=checkbox]').css('verticalAlign', 'middle');
    updateSelf();

    /**
     * Add a link to the bottom of the page for changing preferences
     */
    $('#footer-legal')
        .prepend(' | ')
        .prepend($('<a>')
            .attr('href', '#')
            .text('prefs')
            .click(function() {
                $prefCover.fadeIn();
                return false;
            }));

})();
<|endoftext|>


<commit_before>/*jslint forin:true sub:true anon:true, sloppy:true, stupid:true nomen:true, node:true continue:true*/
/*
 * Copyright (c) 2014, Yahoo! Inc. All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 *
 * Library which supports opening , closing  and listing open sessions
 */

var fs = require("fs");
var util = require("util");
var log4js = require("log4js");
var WdSession = require("../lib/session/wdsession");
var wd = require("../lib/util/wd-wrapper");
var CapabilityManager = require("../lib/util/capabilitymanager");
var logger = log4js.getLogger("selLib");

/**
 *
 * @param config
 * @param capabilities
 * @constructor
 */
function SelLib(config, capabilities) {
    this.config = config;
    this.hub = new WdSession(config);
    this.capabilities = capabilities;
}

/**
 *  Build webdriver object based on properties set in webdriverConfObj
 * @param webdriverConfObj
 * @returns webdriver object
 */
SelLib.prototype.buildWebDriver = function(webdriverConfObj) {

    var webdriver;

    if (webdriverConfObj) {

        webdriver = new wd.Builder().
            usingServer(webdriverConfObj.seleniumHost).
            usingSession(webdriverConfObj.sessionId).
            withCapabilities(webdriverConfObj.capabilities).
            build();
    }

    return webdriver;
};

/**
 *
 * @param arrSessions
 * @param cb
 */
SelLib.prototype.listSessions = function (arrSessions, cb) {

    if (!arrSessions) {
        logger.info('No sessions found');
        cb();
    }

    if (arrSessions.length > 0) {

        var sessionId = arrSessions[0],
            self = this,
            webdriver,
            webdriverConfObj = {};

        arrSessions.shift();

        // Build webdriver object
        webdriverConfObj.seleniumHost = self.config["seleniumHost"];
        webdriverConfObj.sessionId = sessionId;
        webdriver = self.buildWebDriver(webdriverConfObj);

        webdriver.getCapabilities().then(function(sessionCaps) {

            if (sessionCaps) {
                console.log('\n');
                console.log(sessionCaps.toJSON());
            }
            self.listSessions(arrSessions, cb);

        }, function(err) {
            logger.error('Error while listing sessions:' + err);
            self.listSessions(arrSessions, cb);
        });

    } else {
        cb();
    }

};

/**
 *
 */
SelLib.prototype.listHelp = function () {
    console.log('***In listHelp');
    console.info("\nCommandline Options :" + "\n" +
        "--list : Lists all selenium browser sessions" + "\n" +
        "--open=<browser1[, browser2]> : Comma seperated list of browsers to launch" + "\n" +
        "--open=<browser> : browser to choose from capabilities.json" + " --capabilities= path to capabilities.json" + "\n" +
        "--close : Close all selenium controller browser sessions" + "\n\n" +
        "Examples:\n" +
        "Open Firefox and Chrome browser instances:\n" +
        "arrow_selenium --open=firefox,chrome\n"  +
        "Open Firefox with given capabilities:\n" +
        "arrow_selenium --open=firefox --capabilities=./cap.json\n"
        );
};

/**
 *
 * @param capabilities
 * @param browserName
 * @returns capability object based on pass capabilities and browser
 */
SelLib.prototype.getCapabilityObject = function(capabilities, browserName) {

    var caps,
        cm;

    if (capabilities) {
        caps = {
            "platform": "ANY",
            "javascriptEnabled": true,
            "seleniumProtocol": "WebDriver"
        };
        caps.browserName = browserName;

        if (!caps.browserName) {
            logger.error("No Browser is specified");
            process.exit(1);
        }

        cm = new CapabilityManager();
        capabilities = cm.getCapability(capabilities, caps.browserName);
        if (capabilities === null) {
            logger.error("No related capability for " + caps.browserName + " in " + capabilities);
            process.exit(1);
        }
    } else {

        capabilities = {
            "browserName": browserName,
            "version": "",
            "platform": "ANY",
            "javascriptEnabled": true
        };
    }

    return capabilities;

};

/**
 *
 * @param browserList - Browsers to open
 * @param openBrowserList - Already open browsers
 * @param cb
 */
SelLib.prototype.openBrowsers = function(browserList, openBrowserList, cb) {

    var capabilities,
        webdriver,
        browserToOpen,
        self = this,
        webdriverConfObj = {};

    if (browserList && browserList.length > 0) {

        browserToOpen = browserList[0];

        browserList.shift();

        if (openBrowserList.indexOf(browserToOpen) !== -1) {
            logger.info('Browser ' + browserToOpen + ' is already open');
            self.openBrowsers(browserList, openBrowserList, cb);
        } else {
            logger.info('Opening browser..' + browserToOpen);

            capabilities = self.getCapabilityObject(self.capabilities, browserToOpen);

            // Build webdriver object
            webdriverConfObj.seleniumHost = self.config["seleniumHost"];
            webdriverConfObj.capabilities = capabilities;
            webdriver = self.buildWebDriver(webdriverConfObj);

            webdriver.getCapabilities().then(function(sessionCaps) {

                console.log(sessionCaps);

                openBrowserList.push(browserToOpen);
                logger.info('Session created for the browser ' + browserToOpen);
                // Open another browser, if asked for
                self.openBrowsers(browserList, openBrowserList, cb);

            }, function(err) {
                logger.error('Error encountered while opening browser ' + browserToOpen +  ' - Error :'  + err);
                // Open another browser, if asked for
                self.openBrowsers(browserList, openBrowserList, cb);
            });

        }

    } else {
        cb();
    }

};

/**
 * Get browser name for the webdriver object
 * @param webdriver
 * @param cb
 */
SelLib.prototype.getBrowserName = function(webdriver, cb) {

    if (webdriver) {

        webdriver.getCapabilities().then(function(val) {

            if (val) {
                var browserName = val.get("browserName");
                cb(browserName);
            }

        }, function(err) {
            logger.error('Error in getBrowserName getting browser name :' + err);
            cb();
        });
    } else {
        cb();
    }

};

/**
 *
 * @param arrSessions - Already open sessions
 * @param openBrowserList - List of open browsers
 * @param cb
 */
SelLib.prototype.getListOfOpenBrowsers = function(arrSessions, openBrowserList, cb) {

    var
        webdriver,
        self = this,
        sessionId;

//    console.log('**In getListOfOpenBrowsers..' + JSON.stringify(arrSessions));

    if (arrSessions.length === 0) {
//        console.log('**open browsers::' + openBrowserList);
        cb(openBrowserList);
    } else {

        sessionId = arrSessions[0];
//        console.log('**sessionId::' + sessionId);
        arrSessions.shift();

        webdriver = new wd.Builder().
            usingServer(self.config["seleniumHost"]).
            usingSession(sessionId).
            build();

        self.getBrowserName(webdriver, function(browserName) {
            if (browserName) {
                openBrowserList.push(browserName);
            }
            self.getListOfOpenBrowsers(arrSessions, openBrowserList, cb);
        });
    }
};

/**
 *
 * @param arrSessions
 * @param cb
 */
SelLib.prototype.closeBrowsers = function (arrSessions, cb) {

    var
        sessionId,
        self = this,
        webdriver;

    if (arrSessions) {

        if (arrSessions.length === 0) {
            cb();
        } else {

            sessionId = arrSessions[0];
            arrSessions.shift();

            webdriver  = new wd.Builder().
                usingServer(self.config["seleniumHost"]).
                usingSession(sessionId).
                build();
            logger.info("Killing Session ID :" + sessionId);

            webdriver.quit().then(function() {
                self.closeBrowsers(arrSessions, cb);
            }, function(err) {
                logger.info("Error in Killing Session ID :" + sessionId + " , error :" + err);
                self.closeBrowsers(arrSessions, cb);
            });

        }

    }

};

/**
 *
 * @param browsers - comma separated list of arguments to --open e.g firefox,chrome
 */
SelLib.prototype.open = function (browsers, capabilities) {

    var self = this;

    self.capabilities = capabilities;

    self.hub.getSessions(function (error, arrSessions) {

        var browserList = browsers.split(","),
            openBrowserList = [];

        if (arrSessions) {
            logger.info("Found " + arrSessions.length + " Browsers.");
        }

        self.getListOfOpenBrowsers(arrSessions, openBrowserList, function(openBrowserList) {

            self.openBrowsers(browserList, openBrowserList, function() {
                logger.info('Done opening all browsers...' + browsers.split(","));
            });

        });

    }, true);

};

/**
 * Close all open browsers
 */
SelLib.prototype.close = function () {

    var self = this;
    self.hub.getSessions(function (error, arrSessions) {

        if (error) {
            logger.info(error);
        } else if (arrSessions && arrSessions.length > 0) {

            logger.info("Found " + arrSessions.length + " Browsers.");
            self.closeBrowsers(arrSessions, function() {
                logger.info('Closed all open browsers');
            });
        }
    });

};

/**
 * List all open sessions
 */
SelLib.prototype.list = function () {

    var self = this;
    self.hub.getSessions(function (error, arrSessions) {
        if (error) {
            logger.info(error);
        } else {
            self.listSessions(arrSessions, function() {
                logger.info('Listed all sessions');
            });
        }
    }, false);

};

module.exports = SelLib;<commit_msg>Cleanup<commit_after>/*jslint forin:true sub:true anon:true, sloppy:true, stupid:true nomen:true, node:true continue:true*/
/*
 * Copyright (c) 2014, Yahoo! Inc. All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 *
 * Library which supports opening , closing  and listing open sessions
 */

var fs = require("fs");
var util = require("util");
var log4js = require("log4js");
var WdSession = require("../lib/session/wdsession");
var wd = require("../lib/util/wd-wrapper");
var CapabilityManager = require("../lib/util/capabilitymanager");
var logger = log4js.getLogger("selLib");

/**
 *
 * @param config
 * @param capabilities
 * @constructor
 */
function SelLib(config, capabilities) {
    this.config = config;
    this.hub = new WdSession(config);
    this.capabilities = capabilities;
}

/**
 *  Build webdriver object based on properties set in webdriverConfObj
 * @param webdriverConfObj
 * @returns webdriver object
 */
SelLib.prototype.buildWebDriver = function(webdriverConfObj) {

    var webdriver;

    if (webdriverConfObj) {

        webdriver = new wd.Builder().
            usingServer(webdriverConfObj.seleniumHost).
            usingSession(webdriverConfObj.sessionId).
            withCapabilities(webdriverConfObj.capabilities).
            build();
    }

    return webdriver;
};

/**
 *
 * @param arrSessions
 * @param cb
 */
SelLib.prototype.listSessions = function (arrSessions, cb) {

    if (!arrSessions) {
        logger.info('No sessions found');
        cb();
    }

    if (arrSessions.length > 0) {

        var sessionId = arrSessions[0],
            self = this,
            webdriver,
            webdriverConfObj = {};

        arrSessions.shift();

        // Build webdriver object
        webdriverConfObj.seleniumHost = self.config["seleniumHost"];
        webdriverConfObj.sessionId = sessionId;
        webdriver = self.buildWebDriver(webdriverConfObj);

        webdriver.getCapabilities().then(function(sessionCaps) {

            if (sessionCaps) {
                console.log('\n');
                console.log(sessionCaps.toJSON());
            }
            self.listSessions(arrSessions, cb);

        }, function(err) {
            logger.error('Error while listing sessions:' + err);
            self.listSessions(arrSessions, cb);
        });

    } else {
        cb();
    }

};

/**
 *
 */
SelLib.prototype.listHelp = function () {
    console.info("\nCommandline Options :" + "\n" +
        "--list : Lists all selenium browser sessions" + "\n" +
        "--open=<browser1[, browser2]> : Comma seperated list of browsers to launch" + "\n" +
        "--open=<browser> : browser to choose from capabilities.json" + " --capabilities= path to capabilities.json" + "\n" +
        "--close : Close all selenium controller browser sessions" + "\n\n" +
        "Examples:\n" +
        "Open Firefox and Chrome browser instances:\n" +
        "arrow_selenium --open=firefox,chrome\n"  +
        "Open Firefox with given capabilities:\n" +
        "arrow_selenium --open=firefox --capabilities=./cap.json\n"
        );
};

/**
 *
 * @param capabilities
 * @param browserName
 * @returns capability object based on pass capabilities and browser
 */
SelLib.prototype.getCapabilityObject = function(capabilities, browserName) {

    var caps,
        cm;

    if (capabilities) {
        caps = {
            "platform": "ANY",
            "javascriptEnabled": true,
            "seleniumProtocol": "WebDriver"
        };
        caps.browserName = browserName;

        if (!caps.browserName) {
            logger.error("No Browser is specified");
            process.exit(1);
        }

        cm = new CapabilityManager();
        capabilities = cm.getCapability(capabilities, caps.browserName);
        if (capabilities === null) {
            logger.error("No related capability for " + caps.browserName + " in " + capabilities);
            process.exit(1);
        }
    } else {

        capabilities = {
            "browserName": browserName,
            "version": "",
            "platform": "ANY",
            "javascriptEnabled": true
        };
    }

    return capabilities;

};

/**
 *
 * @param browserList - Browsers to open
 * @param openBrowserList - Already open browsers
 * @param cb
 */
SelLib.prototype.openBrowsers = function(browserList, openBrowserList, cb) {

    var capabilities,
        webdriver,
        browserToOpen,
        self = this,
        webdriverConfObj = {};

    if (browserList && browserList.length > 0) {

        browserToOpen = browserList[0];

        browserList.shift();

        if (openBrowserList.indexOf(browserToOpen) !== -1) {
            logger.info('Browser ' + browserToOpen + ' is already open');
            self.openBrowsers(browserList, openBrowserList, cb);
        } else {
            logger.info('Opening browser..' + browserToOpen);

            capabilities = self.getCapabilityObject(self.capabilities, browserToOpen);

            // Build webdriver object
            webdriverConfObj.seleniumHost = self.config["seleniumHost"];
            webdriverConfObj.capabilities = capabilities;
            webdriver = self.buildWebDriver(webdriverConfObj);

            webdriver.getCapabilities().then(function(sessionCaps) {

                console.log(sessionCaps);

                openBrowserList.push(browserToOpen);
                logger.info('Session created for the browser ' + browserToOpen);
                // Open another browser, if asked for
                self.openBrowsers(browserList, openBrowserList, cb);

            }, function(err) {
                logger.error('Error encountered while opening browser ' + browserToOpen +  ' - Error :'  + err);
                // Open another browser, if asked for
                self.openBrowsers(browserList, openBrowserList, cb);
            });

        }

    } else {
        cb();
    }

};

/**
 * Get browser name for the webdriver object
 * @param webdriver
 * @param cb
 */
SelLib.prototype.getBrowserName = function(webdriver, cb) {

    if (webdriver) {

        webdriver.getCapabilities().then(function(val) {

            if (val) {
                var browserName = val.get("browserName");
                cb(browserName);
            }

        }, function(err) {
            logger.error('Error in getBrowserName getting browser name :' + err);
            cb();
        });
    } else {
        cb();
    }

};

/**
 *
 * @param arrSessions - Already open sessions
 * @param openBrowserList - List of open browsers
 * @param cb
 */
SelLib.prototype.getListOfOpenBrowsers = function(arrSessions, openBrowserList, cb) {

    var
        webdriver,
        self = this,
        sessionId;

    if (arrSessions.length === 0) {
        cb(openBrowserList);
    } else {

        sessionId = arrSessions[0];
        arrSessions.shift();

        webdriver = new wd.Builder().
            usingServer(self.config["seleniumHost"]).
            usingSession(sessionId).
            build();

        self.getBrowserName(webdriver, function(browserName) {
            if (browserName) {
                openBrowserList.push(browserName);
            }
            self.getListOfOpenBrowsers(arrSessions, openBrowserList, cb);
        });
    }
};

/**
 *
 * @param arrSessions
 * @param cb
 */
SelLib.prototype.closeBrowsers = function (arrSessions, cb) {

    var
        sessionId,
        self = this,
        webdriver;

    if (arrSessions) {

        if (arrSessions.length === 0) {
            cb();
        } else {

            sessionId = arrSessions[0];
            arrSessions.shift();

            webdriver  = new wd.Builder().
                usingServer(self.config["seleniumHost"]).
                usingSession(sessionId).
                build();
            logger.info("Killing Session ID :" + sessionId);

            webdriver.quit().then(function() {
                self.closeBrowsers(arrSessions, cb);
            }, function(err) {
                logger.info("Error in Killing Session ID :" + sessionId + " , error :" + err);
                self.closeBrowsers(arrSessions, cb);
            });

        }

    }

};

/**
 *
 * @param browsers - comma separated list of arguments to --open e.g firefox,chrome
 */
SelLib.prototype.open = function (browsers, capabilities) {

    var self = this;

    self.capabilities = capabilities;

    self.hub.getSessions(function (error, arrSessions) {

        var browserList = browsers.split(","),
            openBrowserList = [];

        if (arrSessions) {
            logger.info("Found " + arrSessions.length + " Browsers.");
        }

        self.getListOfOpenBrowsers(arrSessions, openBrowserList, function(openBrowserList) {

            self.openBrowsers(browserList, openBrowserList, function() {
                logger.info('Done opening all browsers...' + browsers.split(","));
            });

        });

    }, true);

};

/**
 * Close all open browsers
 */
SelLib.prototype.close = function () {

    var self = this;
    self.hub.getSessions(function (error, arrSessions) {

        if (error) {
            logger.info(error);
        } else if (arrSessions && arrSessions.length > 0) {

            logger.info("Found " + arrSessions.length + " Browsers.");
            self.closeBrowsers(arrSessions, function() {
                logger.info('Closed all open browsers');
            });
        }
    });

};

/**
 * List all open sessions
 */
SelLib.prototype.list = function () {

    var self = this;
    self.hub.getSessions(function (error, arrSessions) {
        if (error) {
            logger.info(error);
        } else {
            self.listSessions(arrSessions, function() {
                logger.info('Listed all sessions');
            });
        }
    }, false);

};

module.exports = SelLib;<|endoftext|>


<commit_before>jsPlumb.ready(function () {

	var past = new Array();
	var future = new Array();
	var baseTime = $.now();

    function stateName(s) {
    	return $("#" + s + "-name").html();
    }
	function log(s, c, d1, d2, d3) {
		var t = $.now() - baseTime;
		var data = [s, t, stateName(c.sourceId), c.sourceId, stateName(c.targetId), c.targetId];
		if (d1 !== null && d1 !== undefined) data.push(d1);
		if (d2 !== null && d2 !== undefined) data.push(d2);
		if (d3 !== null && d3 !== undefined) data.push(d3);
		past.push(data);
	}

	function checkTime() {
		var curTime = $.now();
		var diff = Math.floor((curTime - baseTime) / 1000);
		var min = Math.floor(diff / 60);
		var sek = Math.floor(diff % 60);

		if (min == 10 && sek == 0) alert("Bitte kommen Sie zum Ende.");

		if (sek < 10) sek = '0' + sek;
		$('#zeit').html(min + ':' + sek);
	}

    // setup some defaults for jsPlumb.
    var instance = jsPlumb.getInstance({
        Endpoint: ["Dot", {radius: 2}],
        /*HoverPaintStyle: {strokeStyle: "#1e8151", lineWidth: 2 },*/
        ConnectionOverlays: [
            [ "Arrow", { location: 1, id: "arrow", length: 14, foldback: 0.8 } ],
			[ "Label", { label: "", id: "label", cssClass: "aLabel edit", location: 0.6 }],
            [ "Label", { label: "", id: "delete", cssClass: "aLabel", location: 0.25,
            	events:{click:function(overlay,event){
            		var c = overlay.component;
            		log("detach", c, c.getOverlay("label").getLabel());
            		jsPlumb.detach(c);
				}}
            }]
        ],
        Container: "conceptmap"
    });

    window.jsp = instance;
    var windows = jsPlumb.getSelector(".conceptmap .w");

    function layout() {
		var instance = window.jsp;
		var options = {name: "springy", repulsion: 10000, animate: false, padding: 100, infinite: false, maxSimulationTime: 1000, random: true, fit: true, boundingBox: {x1: 0,y1: 0,w: $("#conceptmap").width()-100,h: $("#conceptmap").height()-100}};
//		var options = {name: "cola", maxSimulationTime: 1000, randomize: true, fit: false, boundingBox: {x1: 0,y1: 0,w: $("#conceptmap").width() - 150,h: $("#conceptmap").height() - 150}};
		var cy = cytoscape({headless: true, layout: options});
		$('.w').each(function(i,obj){ cy.add({ group: "nodes", data: {id: obj.id}, position: {x: i*20, y: i*20} }); });
		var cons = instance.getAllConnections();
		$.each(cons, function(i,obj){ cy.add({ group: "edges", data: {source: obj.sourceId, target: obj.targetId} }); });
		var layout = cy.makeLayout(options);
		layout.run();
		setTimeout(function(){
			layout.stop();
			$.each(cy.json().elements.nodes, function(i,obj){ var o = $("#" + obj.data.id); o.css("left",obj.position.x); o.css("top", obj.position.y); o.simulate("drag", {moves: 1, dx: 1, dy: 0}); });
		}, 1000);
	}
	$("#layout").click(layout);

	function backward() {
		if (past.length == 0) return;
		var a = past.pop();
		future.push(a);
		if (a[0] == "connect") {
			var c = instance.getConnections({source: a[3], target: a[5]})[0];
			jsPlumb.detach(c);
		} else if (a[0] == "detach") {
			var c = instance.connect({source: a[3], target: a[5]});
			past.pop();
		} else if (a[0] == "rename") {
			var c = instance.getConnections({source: a[3], target: a[5]})[0];
			c.getOverlay("label").setLabel(a[6]);
		}
//		alert(past + " -- " + future);
	}
	$("#backward").click(backward);
	function forward() {
		if (future.length == 0) return;
		var c = future.pop();
		past.push(c);
		if (c[0] == "connect") {
			instance.connect({source: c[3], target: c[5]});
			past.pop();
		} else if (c[0] == "detach") {
			var conn = instance.getConnections({source: c[3], target: c[5]})[0];
			jsPlumb.detach(conn);
		} else if (c[0] == "rename") {
			var conn = instance.getConnections({source: c[3], target: c[5]})[0];
			conn.getOverlay("label").setLabel(c[7]);
		}
//		alert(past + " -- " + future);
	}
	$("#forward").click(forward);

	function findEmptyLabels() {
		var res = [];
		var conns = instance.getConnections();
		for (var i = 0; i < conns.length; i++) {
			var ol = conns[i].getOverlay("label");
			var str = ol.canvas.innerHTML;
			if (str == "Click to edit") res.push(ol.canvas);
		}
		return res;
	}

	function finish() {
		if (session != "") {
			var empty = findEmptyLabels();
			if (empty.length > 0) {
				if (!confirm("Du hast noch unbeschriftete Pfeile. Bist du dir sicher, dass du abgeben möchtest?")) {
					$.each(empty, function(i,e){$(e).css("background-color", "red");});
					setTimeout(function(){
						$.each(empty, function(i,e){$(e).css("background-color", "");});
					}, 1000);
					return;
				}
			}
			if (confirm("Möchtest du die ConceptMap wirklich abschließen?") === true) {
				$.ajax({
    				type: "POST",
    				url: "ajax.php",
    				data: { "session": experiment + "-" + session, "finish": "1", "data": past },
	    			success: function(data,status,xhr) {
    					alert("Vielen Dank!");
    					$(location).attr("href", "index.php");
    				}
	    		});
	    	}
		}
	}
	$("#finish").click(finish);

    // initialise draggable elements.
    instance.draggable(windows, {containment: "parent", handle: ".w-drag"});

    // On create connection
    instance.bind("connection", function (info) {
    	log("connect", info);
		instance.recalculateOffsets("conceptmap");
		instance.repaintEverything();
        info.connection.getOverlay("label").setLabel("");
        $(".edit").editable(function(value,settings,arg){
        	log("rename", info, arg, value);
        	return (value);
		},{
			submitdata: function(val,settings) { return {original: this.revert}; },
			onblur: "submit"
		});
		var label = info.connection.getOverlay("label").canvas;
		label.click();
    });

    // Send data to server every few seconds
    function sendData() {
    	if (session != "") {
	    	$.ajax({
    			type: "POST",
    			url: "ajax.php",
    			data: { "session": experiment + "-" + session, "data": past },
	    		success: function(data,status,xhr) {}
    		});
		}
    }
    setInterval(sendData, 3000);

    // suspend drawing and initialise.
    instance.batch(function () {
        instance.makeSource(windows, {
            filter: ".ep",
            anchor: "Continuous",
            connector: [ "StateMachine", { curviness: 20 } ],
            connectorStyle: { strokeStyle: "#5c96bc", lineWidth: 2, outlineColor: "transparent", outlineWidth: 4 },
            maxConnections: 5,
            onMaxConnections: function (info, e) {
                alert("Maximum connections (" + info.maxConnections + ") reached");
            }
        });

        // initialise all '.w' elements as connection targets.
        instance.makeTarget(windows, {
            dropOptions: { hoverClass: "dragHover" },
            anchor: "Continuous",
            allowLoopback: false
        });

    });

    while (restore_data.length > 0) {
    	future.push(restore_data.pop());
    }
    if ($("#zeit").length) {
		setInterval(checkTime, 500);
    }
});

<commit_msg>Fixing click() for crappy safari.<commit_after>jsPlumb.ready(function () {

	var past = new Array();
	var future = new Array();
	var baseTime = $.now();

    function stateName(s) {
    	return $("#" + s + "-name").html();
    }
	function log(s, c, d1, d2, d3) {
		var t = $.now() - baseTime;
		var data = [s, t, stateName(c.sourceId), c.sourceId, stateName(c.targetId), c.targetId];
		if (d1 !== null && d1 !== undefined) data.push(d1);
		if (d2 !== null && d2 !== undefined) data.push(d2);
		if (d3 !== null && d3 !== undefined) data.push(d3);
		past.push(data);
	}

	function checkTime() {
		var curTime = $.now();
		var diff = Math.floor((curTime - baseTime) / 1000);
		var min = Math.floor(diff / 60);
		var sek = Math.floor(diff % 60);

		if (min == 10 && sek == 0) alert("Bitte kommen Sie zum Ende.");

		if (sek < 10) sek = '0' + sek;
		$('#zeit').html(min + ':' + sek);
	}

    // setup some defaults for jsPlumb.
    var instance = jsPlumb.getInstance({
        Endpoint: ["Dot", {radius: 2}],
        /*HoverPaintStyle: {strokeStyle: "#1e8151", lineWidth: 2 },*/
        ConnectionOverlays: [
            [ "Arrow", { location: 1, id: "arrow", length: 14, foldback: 0.8 } ],
			[ "Label", { label: "", id: "label", cssClass: "aLabel edit", location: 0.6 }],
            [ "Label", { label: "", id: "delete", cssClass: "aLabel", location: 0.25,
            	events:{click:function(overlay,event){
            		var c = overlay.component;
            		log("detach", c, c.getOverlay("label").getLabel());
            		jsPlumb.detach(c);
				}}
            }]
        ],
        Container: "conceptmap"
    });

    window.jsp = instance;
    var windows = jsPlumb.getSelector(".conceptmap .w");

    function layout() {
		var instance = window.jsp;
		var options = {name: "springy", repulsion: 10000, animate: false, padding: 100, infinite: false, maxSimulationTime: 1000, random: true, fit: true, boundingBox: {x1: 0,y1: 0,w: $("#conceptmap").width()-100,h: $("#conceptmap").height()-100}};
//		var options = {name: "cola", maxSimulationTime: 1000, randomize: true, fit: false, boundingBox: {x1: 0,y1: 0,w: $("#conceptmap").width() - 150,h: $("#conceptmap").height() - 150}};
		var cy = cytoscape({headless: true, layout: options});
		$('.w').each(function(i,obj){ cy.add({ group: "nodes", data: {id: obj.id}, position: {x: i*20, y: i*20} }); });
		var cons = instance.getAllConnections();
		$.each(cons, function(i,obj){ cy.add({ group: "edges", data: {source: obj.sourceId, target: obj.targetId} }); });
		var layout = cy.makeLayout(options);
		layout.run();
		setTimeout(function(){
			layout.stop();
			$.each(cy.json().elements.nodes, function(i,obj){ var o = $("#" + obj.data.id); o.css("left",obj.position.x); o.css("top", obj.position.y); o.simulate("drag", {moves: 1, dx: 1, dy: 0}); });
		}, 1000);
	}
	$("#layout").click(layout);

	function backward() {
		if (past.length == 0) return;
		var a = past.pop();
		future.push(a);
		if (a[0] == "connect") {
			var c = instance.getConnections({source: a[3], target: a[5]})[0];
			jsPlumb.detach(c);
		} else if (a[0] == "detach") {
			var c = instance.connect({source: a[3], target: a[5]});
			past.pop();
		} else if (a[0] == "rename") {
			var c = instance.getConnections({source: a[3], target: a[5]})[0];
			c.getOverlay("label").setLabel(a[6]);
		}
//		alert(past + " -- " + future);
	}
	$("#backward").click(backward);
	function forward() {
		if (future.length == 0) return;
		var c = future.pop();
		past.push(c);
		if (c[0] == "connect") {
			instance.connect({source: c[3], target: c[5]});
			past.pop();
		} else if (c[0] == "detach") {
			var conn = instance.getConnections({source: c[3], target: c[5]})[0];
			jsPlumb.detach(conn);
		} else if (c[0] == "rename") {
			var conn = instance.getConnections({source: c[3], target: c[5]})[0];
			conn.getOverlay("label").setLabel(c[7]);
		}
//		alert(past + " -- " + future);
	}
	$("#forward").click(forward);

	function findEmptyLabels() {
		var res = [];
		var conns = instance.getConnections();
		for (var i = 0; i < conns.length; i++) {
			var ol = conns[i].getOverlay("label");
			var str = ol.canvas.innerHTML;
			if (str == "Click to edit") res.push(ol.canvas);
		}
		return res;
	}

	function finish() {
		if (session != "") {
			var empty = findEmptyLabels();
			if (empty.length > 0) {
				if (!confirm("Du hast noch unbeschriftete Pfeile. Bist du dir sicher, dass du abgeben möchtest?")) {
					$.each(empty, function(i,e){$(e).css("background-color", "red");});
					setTimeout(function(){
						$.each(empty, function(i,e){$(e).css("background-color", "");});
					}, 1000);
					return;
				}
			}
			if (confirm("Möchtest du die ConceptMap wirklich abschließen?") === true) {
				$.ajax({
    				type: "POST",
    				url: "ajax.php",
    				data: { "session": experiment + "-" + session, "finish": "1", "data": past },
	    			success: function(data,status,xhr) {
    					alert("Vielen Dank!");
    					$(location).attr("href", "index.php");
    				}
	    		});
	    	}
		}
	}
	$("#finish").click(finish);

    // initialise draggable elements.
    instance.draggable(windows, {containment: "parent", handle: ".w-drag"});

    function triggerClick(elem) {
    	if (elem.click) elem.click();
    	else {
			var ev = document.createEvent("MouseEvents");
			ev.initEvent("click", true /* bubble */, true /* cancelable */);
			elem.dispatchEvent(ev);
    	}
    }

    // On create connection
    instance.bind("connection", function (info) {
    	log("connect", info);
		instance.recalculateOffsets("conceptmap");
		instance.repaintEverything();
        info.connection.getOverlay("label").setLabel("");
        $(".edit").editable(function(value,settings,arg){
        	if (arg != value) {
	        	log("rename", info, arg, value);
			}
        	return (value);
		},{
			submitdata: function(val,settings) { return {original: this.revert}; },
			onblur: "submit"
		});
		var label = info.connection.getOverlay("label").canvas;
    	triggerClick(label);
    });

    // Send data to server every few seconds
    function sendData() {
    	if (session != "") {
	    	$.ajax({
    			type: "POST",
    			url: "ajax.php",
    			data: { "session": experiment + "-" + session, "data": past },
	    		success: function(data,status,xhr) {}
    		});
		}
    }
    setInterval(sendData, 3000);

    // suspend drawing and initialise.
    instance.batch(function () {
        instance.makeSource(windows, {
            filter: ".ep",
            anchor: "Continuous",
            connector: [ "StateMachine", { curviness: 20 } ],
            connectorStyle: { strokeStyle: "#5c96bc", lineWidth: 2, outlineColor: "transparent", outlineWidth: 4 },
            maxConnections: 5,
            onMaxConnections: function (info, e) {
                alert("Maximum connections (" + info.maxConnections + ") reached");
            }
        });

        // initialise all '.w' elements as connection targets.
        instance.makeTarget(windows, {
            dropOptions: { hoverClass: "dragHover" },
            anchor: "Continuous",
            allowLoopback: false
        });

    });

    while (restore_data.length > 0) {
    	future.push(restore_data.pop());
    }
    if ($("#zeit").length) {
		setInterval(checkTime, 500);
    }
});

<|endoftext|>


<commit_before>//--------------------------------------------------------------------------------------------------

var spawn = require('child_process').spawn;
var colors = require('colors/safe');
var async = require('async');

//--------------------------------------------------------------------------------------------------

var streem = function(message, color) {
    process.stdout.write(colors[color](message.toString()));
};

//--------------------------------------------------------------------------------------------------

var exec = function(args, callback) {
    var command = args.shift();
    var ch = spawn(command, args);

    console.log(123);

    ch.stdout.on('data', function(data) {
        streem(data, 'green');
    });
    ch.stderr.on('data', function(data) {
        streem(data, 'red');
    });
    ch.on('close', function(code) {
        callback(null, command);
        console.log('close = '+ command);
    });
};

//--------------------------------------------------------------------------------------------------
/*
async.waterfall([
    function(callback) {
        callback(null, 'один', 'два');
    },
    function(arg1, arg2, callback){
        callback(null, 'три');
    },
    function(arg1, callback){
        callback(null, 'Готово');
    }
], function (err, result) {
});
*/
//--------------------------------------------------------------------------------------------------

var commands = [
    ['git', 'add', '.'],
    ['git', 'commit', '-m', '"update"'],
    ['git', 'push']
];
async.filter(commands, exec, function(err, r) {
    console.log('r:');
    console.log(r);
});
/*
async.map([['clear']], exec, function(err, r) {

});
*/
/*
async.series([
    function(callback) {
        exec('git', ['status'], callback);
    },
    function(callback) {
        exec('git', ['status'], callback);
    },
    function(callback) {
        exec('git', ['status'], callback);
    }
]);
*/
//--------------------------------------------------------------------------------------------------
/*
var colors = require('colors/safe');
const exec = require('child_process').exec;
*/
/*--------------------------------------------------------------------------------------------------
|
| -> Загрузка на GitHub
|
|-------------------------------------------------------------------------------------------------*/
/*
//exec('clear && git add . && git commit -m "update" && git push', function(error, stdout, stderr) {
exec('git', function(error, stdout, stderr) {
    if (error) {
        process.stdout.write(colors.red(error));
        return;
    }
    process.stdout.write(colors.green(stdout.toString()));
    process.stdout.write(colors.red(stderr.toString()));
});
*/
//--------------------------------------------------------------------------------------------------<commit_msg>"update"<commit_after>//--------------------------------------------------------------------------------------------------

var spawn = require('child_process').spawn;
var colors = require('colors/safe');
var async = require('async');

//--------------------------------------------------------------------------------------------------

var streem = function(message, color) {
    process.stdout.write(colors[color](message.toString()));
};

//--------------------------------------------------------------------------------------------------

var exec = function(args, callback) {
    var command = args.shift();
    var ch = spawn(command, args);

    console.log(123);

    ch.stdout.on('data', function(data) {
        streem(data, 'green');
    });
    ch.stderr.on('data', function(data) {
        streem(data, 'red');
    });
    ch.on('close', function(code) {
        callback(null, command);
        //console.log('close = '+ command);
    });
};

//--------------------------------------------------------------------------------------------------
/*
async.waterfall([
    function(callback) {
        callback(null, 'один', 'два');
    },
    function(arg1, arg2, callback){
        callback(null, 'три');
    },
    function(arg1, callback){
        callback(null, 'Готово');
    }
], function (err, result) {
});
*/
//--------------------------------------------------------------------------------------------------

var commands = [
    ['git', 'add', '.'],
    ['git', 'commit', '-m', '"update"'],
    ['git', 'push']
];
async.filter(commands, exec, function(err, r) {
    console.log('r:');
    console.log(r);
});
/*
async.map([['clear']], exec, function(err, r) {

});
*/
/*
async.series([
    function(callback) {
        exec('git', ['status'], callback);
    },
    function(callback) {
        exec('git', ['status'], callback);
    },
    function(callback) {
        exec('git', ['status'], callback);
    }
]);
*/
//--------------------------------------------------------------------------------------------------
/*
var colors = require('colors/safe');
const exec = require('child_process').exec;
*/
/*--------------------------------------------------------------------------------------------------
|
| -> Загрузка на GitHub
|
|-------------------------------------------------------------------------------------------------*/
/*
//exec('clear && git add . && git commit -m "update" && git push', function(error, stdout, stderr) {
exec('git', function(error, stdout, stderr) {
    if (error) {
        process.stdout.write(colors.red(error));
        return;
    }
    process.stdout.write(colors.green(stdout.toString()));
    process.stdout.write(colors.red(stderr.toString()));
});
*/
//--------------------------------------------------------------------------------------------------<|endoftext|>


<commit_before>(function primus() {
  'use strict';

  /* {primus::library} */
  /* {primus::parser} */

  /**
   * Primus in a real-time library agnostic framework for establishing real-time
   * connections with servers.
   *
   * @param {String} url The url of your server.
   */
  function Primus(url) {
    if (!(this instanceof Primus)) return new Primus(url);

    this.events = {};             // Stores the events.
    this.backoff = {};            // Stores the backoff configuration.
    this.url = this.parse(url);

    this.initialise().connect();
  }

  /**
   * Initialise the Primus and setup all parsers and internal listeners.
   *
   * @api private
   */
  Primus.prototype.initialise = function initalise() {
    var primus = this;

    this.on('primus::data', function message(data) {
      primus.decoder(data, function decoding(err, packet) {
        //
        // Do a "save" emit('error') when we fail to parse a message. We don't
        // want to throw here as listening to errors should be optional.
        //
        if (err) return primus.listeners('error').length && socket.emit('error', err);
        primus.emit('data', packet);
      });
    });

    this.on('primus::end', function end() {
      this.reconnect(function (fail, backoff) {
        primus.backoff = backoff; // Save the opts again of this backoff.
        if (fail) return self.emit('end', fail);

        // Try to re-open the connection again.
        primus.emit('primus::reconnect');
      }, primus.backoff);
    });

    return this;
  };

  /**
   * Establish a connection with the server.
   *
   * @api private
   */
  Primus.prototype.connect = function connect() {
    this.emit('primus::connect', this.uri());
  };

  /**
   * Close the connection.
   *
   * @api public
   */
  Primus.prototype.end = function end() {

  };

  /**
   * Exponential backoff algorithm for retry aperations. It uses an randomized
   * retry so we don't DDOS our server when it goes down under presure.
   *
   * @param {Function} callback Callback to be called after the timeout.
   * @param {Object} opts Options for configuring the timeout.
   * @api private
   */
  Primus.prototype.backoff = function backoff(callback, opts) {
    opts = opts || {};

    opts.maxDelay = opts.maxDelay || Infinity;  // Maximum delay
    opts.minDelay = opts.minDelay || 500;       // Minimum delay
    opts.retries = opts.retries || 25;          // Amount of allowed retries
    opts.attempt = (+opts.attempt || 0) + 1;    // Current attempt
    opts.factor = opts.factor || 2;             // Backoff factor

    // Bailout if we are about to make to much attempts. Please note that we use
    // `>` because we already incremented the value above.
    if (opts.attempt > opts.retries || opts.backoff) {
      return callback(new Error('Unable to retry'), opts);
    }

    // Prevent duplicate backoff attempts.
    opts.backoff = true;

    // Calculate the timeout, but make it randomly so we don't retry connections
    // at the same interval and defeat the purpose. This exponential backoff is
    // based on the work of:
    //
    // http://dthain.blogspot.nl/2009/02/exponential-backoff-in-distributed.html
    opts.timeout = opts.attempt !== 1
      ? Math.min(Math.round(
          (Math.random() * 1) * opts.minDelay * Math.pow(opts.factor, opts.attempt)
        ), opts.maxDelay)
      : opts.minDelay;

    setTimeout(function delay() {
      opts.backoff = false;
      callback(undefined, opts);
    }, opts.timeout);

    return this;
  };

  /**
   * Parse the connection string.
   *
   * @param {String} url Connection url
   * @returns {Object} Parsed connection.
   * @api public
   */
  Primus.prototype.parse = function parse(url) {
    var a = document.createElement('a');
    a.href = url;

    return a;
  };

  /**
   * Generates a connection url.
   *
   * @returns {String} The url.
   * @api private
   */
  Primus.prototype.uri = function uri() {
    var server = [];

    server.push(this.url.protocol === 'https:' ? 'wss:' : 'ws:', '');
    server.push(this.url.host, this.pathname.slice(1));

    //
    // Optionally add a search query
    //
    if (this.url.search) server.push(this.url.search);
    return server.join('/');
  };

  /**
   * Return a list of assigned event listeners.
   *
   * @param {String} event The events that should be listed.
   * @returns {Array}
   * @api public
   */
  Primus.prototype.listeners = function listeners(event) {
    return (this.events[event] || []).slice(0);
  };

  /**
   * Emit an event to all registered event listeners.
   *
   * @param {String} event The name of the event.
   * @returns {Boolean} Indication if we've emitted an event.
   * @api public
   */
  Primus.prototype.emit = function emit(event) {
    if (!(event in this.events)) return false;

    var args = Array.prototype.slice.call(arguments, 1)
      , length = this.events[event].length
      , i = 0;

    for (; i < length; i++) {
      this.events[event][i].apply(this, args);
    }

    return true;
  };

  /**
   * Register a new EventListener for the given event.
   *
   * @param {String} event Name of the event.
   * @param {Functon} fn Callback function.
   * @api public
   */
  Primus.prototype.on = function on(event, fn) {
    if (!(event in this.events)) this.events[event] = [];
    this.events[event].push(fn);

    return this;
  };

  /**
   * Simple emit wrapper that returns a function that emits an event once it's
   * called. This makes it easier for transports to emit specific events. The
   * scope of this function is limited as it will only emit one single argument.
   *
   * @param {String} event Name of the event that we should emit.
   * @param {Function} parser Argument parser.
   * @api public
   */
  Primus.prototype.emits = function emits(event, parser) {
    var primus = this;

    return function emit(arg) {
      var data = parser ? parser.apply(primus, arguments) : arg;

      //
      // Timeout is required to prevent crashes on WebSockets connections on
      // mobile devices. We need to handle these edge cases in our own library
      // as we cannot be certain that all frameworks fix these issues.
      //
      setTimeout(function timeout() {
        primus.emit('primus::'+ event, data);
      }, 0);
    };
  };

  //
  // These libraries are automatically are automatically inserted at the
  // serverside using the Primus#library method.
  //
  Primus.prototype.client = null; // @import {primus::transport};
  Primus.prototype.pathname = null; // @import {primus::pathname};
  Primus.prototype.encoder = null; // @import {primus::encoder};
  Primus.prototype.decoder = null; // @import {primus::decoder};
  Primus.prototype.version = null; // @import {primus::version};
})(this);
<commit_msg>[minor] moving the client to incoming/outgoing pattern.<commit_after>(function primus() {
  'use strict';

  /* {primus::library} */
  /* {primus::parser} */

  /**
   * Primus in a real-time library agnostic framework for establishing real-time
   * connections with servers.
   *
   * @param {String} url The url of your server.
   */
  function Primus(url) {
    if (!(this instanceof Primus)) return new Primus(url);

    this.events = {};             // Stores the events.
    this.backoff = {};            // Stores the backoff configuration.
    this.url = this.parse(url);

    this.initialise().connect();
  }

  /**
   * Initialise the Primus and setup all parsers and internal listeners.
   *
   * @api private
   */
  Primus.prototype.initialise = function initalise() {
    var primus = this;

    this.on('incoming::data', function message(data) {
      primus.decoder(data, function decoding(err, packet) {
        //
        // Do a "save" emit('error') when we fail to parse a message. We don't
        // want to throw here as listening to errors should be optional.
        //
        if (err) return primus.listeners('error').length && primus.emit('error', err);
        primus.emit('data', packet);
      });
    });

    this.on('incoming::end', function end() {
      this.reconnect(function (fail, backoff) {
        primus.backoff = backoff; // Save the opts again of this backoff.
        if (fail) return primus.emit('end', fail);

        // Try to re-open the connection again.
        primus.emit('outgoing::reconnect');
      }, primus.backoff);
    });

    return this;
  };

  /**
   * Establish a connection with the server.
   *
   * @api private
   */
  Primus.prototype.connect = function connect() {
    this.emit('outgoing::connect', this.uri());

    return this;
  };

  /**
   * Close the connection.
   *
   * @api public
   */
  Primus.prototype.end = function end() {
    this.emit('outgoing::end');
    this.emit('end');

    return this;
  };

  /**
   * Exponential backoff algorithm for retry aperations. It uses an randomized
   * retry so we don't DDOS our server when it goes down under presure.
   *
   * @param {Function} callback Callback to be called after the timeout.
   * @param {Object} opts Options for configuring the timeout.
   * @api private
   */
  Primus.prototype.backoff = function backoff(callback, opts) {
    opts = opts || {};

    opts.maxDelay = opts.maxDelay || Infinity;  // Maximum delay.
    opts.minDelay = opts.minDelay || 500;       // Minimum delay.
    opts.retries = opts.retries || 25;          // Amount of allowed retries.
    opts.attempt = (+opts.attempt || 0) + 1;    // Current attempt.
    opts.factor = opts.factor || 2;             // Backoff factor.

    // Bailout if we are about to make to much attempts. Please note that we use
    // `>` because we already incremented the value above.
    if (opts.attempt > opts.retries || opts.backoff) {
      return callback(new Error('Unable to retry'), opts);
    }

    // Prevent duplicate backoff attempts.
    opts.backoff = true;

    // Calculate the timeout, but make it randomly so we don't retry connections
    // at the same interval and defeat the purpose. This exponential backoff is
    // based on the work of:
    //
    // http://dthain.blogspot.nl/2009/02/exponential-backoff-in-distributed.html
    opts.timeout = opts.attempt !== 1
      ? Math.min(Math.round(
          (Math.random() * 1) * opts.minDelay * Math.pow(opts.factor, opts.attempt)
        ), opts.maxDelay)
      : opts.minDelay;

    setTimeout(function delay() {
      opts.backoff = false;
      callback(undefined, opts);
    }, opts.timeout);

    return this;
  };

  /**
   * Parse the connection string.
   *
   * @param {String} url Connection url
   * @returns {Object} Parsed connection.
   * @api public
   */
  Primus.prototype.parse = function parse(url) {
    var a = document.createElement('a');
    a.href = url;

    return a;
  };

  /**
   * Generates a connection url.
   *
   * @returns {String} The url.
   * @api private
   */
  Primus.prototype.uri = function uri() {
    var server = [];

    server.push(this.url.protocol === 'https:' ? 'wss:' : 'ws:', '');
    server.push(this.url.host, this.pathname.slice(1));

    //
    // Optionally add a search query.
    //
    if (this.url.search) server.push(this.url.search);
    return server.join('/');
  };

  /**
   * Return a list of assigned event listeners.
   *
   * @param {String} event The events that should be listed.
   * @returns {Array}
   * @api public
   */
  Primus.prototype.listeners = function listeners(event) {
    return (this.events[event] || []).slice(0);
  };

  /**
   * Emit an event to all registered event listeners.
   *
   * @param {String} event The name of the event.
   * @returns {Boolean} Indication if we've emitted an event.
   * @api public
   */
  Primus.prototype.emit = function emit(event) {
    if (!(event in this.events)) return false;

    var args = Array.prototype.slice.call(arguments, 1)
      , length = this.events[event].length
      , i = 0;

    for (; i < length; i++) {
      this.events[event][i].apply(this, args);
    }

    return true;
  };

  /**
   * Register a new EventListener for the given event.
   *
   * @param {String} event Name of the event.
   * @param {Functon} fn Callback function.
   * @api public
   */
  Primus.prototype.on = function on(event, fn) {
    if (!(event in this.events)) this.events[event] = [];
    this.events[event].push(fn);

    return this;
  };

  /**
   * Simple emit wrapper that returns a function that emits an event once it's
   * called. This makes it easier for transports to emit specific events. The
   * scope of this function is limited as it will only emit one single argument.
   *
   * @param {String} event Name of the event that we should emit.
   * @param {Function} parser Argument parser.
   * @api public
   */
  Primus.prototype.emits = function emits(event, parser) {
    var primus = this;

    return function emit(arg) {
      var data = parser ? parser.apply(primus, arguments) : arg;

      //
      // Timeout is required to prevent crashes on WebSockets connections on
      // mobile devices. We need to handle these edge cases in our own library
      // as we cannot be certain that all frameworks fix these issues.
      //
      setTimeout(function timeout() {
        primus.emit('incoming::'+ event, data);
      }, 0);
    };
  };

  //
  // These libraries are automatically are automatically inserted at the
  // serverside using the Primus#library method.
  //
  Primus.prototype.pathname = null; // @import {primus::pathname};
  Primus.prototype.client = null; // @import {primus::transport};
  Primus.prototype.encoder = null; // @import {primus::encoder};
  Primus.prototype.decoder = null; // @import {primus::decoder};
  Primus.prototype.version = null; // @import {primus::version};
})(this);
<|endoftext|>


<commit_before>/*******************************************************************************
Open Source Initiative OSI - The MIT License (MIT):Licensing
[OSI Approved License]
The MIT License (MIT)

Copyright (c) Sean McGregor

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

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

var privly = {

  //Matches:
  //              http://
  //              https://
  //                        priv.ly/textAndNumbers/any/number/of/times
  //
  //also matches localhost:3000
  privlyReferencesRegex: /\b(https?:\/\/){0,1}(priv\.ly|localhost:3000)(\/posts)(\/\w*){1,}\b/gi,

  // Takes a domain with an optional http(s) in front and returns a fully formed domain name
  makeHref: function(domain)
  {
    var hasHTTPRegex = /^((https?)\:\/\/)/i
    if(!hasHTTPRegex.test(domain))
        domain = "http://" + domain;
    return domain;
  },

  //Make plain text links into anchor elements
  createLinks: function()
  {
      /*************************************************************************
      Inspired by Linkify script:
        http://downloads.mozdev.org/greasemonkey/linkify.user.js

      Originally written by Anthony Lieuallen of http://arantius.com/
      Licensed for unlimited modification and redistribution as long as
      this notice is kept intact.
      **************************************************************************/

      var excludeParents = ["a", "applet", "button", "code", "form",
                             "input", "option", "script", "select", "meta",
                             "style", "textarea", "title", "div","span"];
      var excludedParentsString = excludeParents.join(" or parent::");
      var xpathExpression = ".//text()[not(parent:: " + excludedParentsString +")]";

      textNodes = document.evaluate(xpathExpression, document.body, null,
                                    XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

      for(var i=0; i < textNodes.snapshotLength; i++){
          item = textNodes.snapshotItem(i);

          var itemText = item.nodeValue;

          privly.privlyReferencesRegex.lastIndex = 0;
          if(privly.privlyReferencesRegex.test(itemText)){
              var span = document.createElement("span");
              var lastLastIndex = 0;
              privly.privlyReferencesRegex.lastIndex = 0;
              for(var results = null; results = privly.privlyReferencesRegex.exec(itemText); ){
                  var href = results[0];
                  span.appendChild(document.createTextNode(itemText.substring(lastLastIndex, results.index)));

                  var text = (href.indexOf(" ")==0)?href.substring(1):href;

                  var href = privly.makeHref(text);

                  var a = document.createElement("a");
                  a.setAttribute("href", href);
                  a.appendChild(document.createTextNode(text.substring(0,4).toLowerCase()+text.substring(4)));
                  if(href.indexOf(" ")==0)
                      span.appendChild(document.createTextNode(" "));
                  span.appendChild(a);
                  lastLastIndex = privly.privlyReferencesRegex.lastIndex;
              }
              span.appendChild(document.createTextNode(itemText.substring(lastLastIndex)));
              item.parentNode.replaceChild(span, item);
          }
      }
  },

  //Kill default link behaviour on Privly Links
  makePassive: function(anchor)
  {
    //Preventing the default link behavior
    anchor.addEventListener("mousedown", function(e){
        e.cancelBubble = true;
        e.stopPropagation();
        e.preventDefault();
        privly.replaceLink(anchor);
        console.log("a")
      },
      true);
  },

  //Checks link attributes and text for privly links without the proper href attribute.
  //Twitter and other hosts change links so they can collect click events.
  correctIndirection: function()
  {
    var anchors = document.links;
    var i = anchors.length;
    while (i--){
      var a = anchors[i];

      if(a.href && (a.href.indexOf("priv.ly/posts/") == -1 || a.href.indexOf("priv.ly/posts/") > 9))
      {
        if (privly.privlyReferencesRegex.test(a.innerHTML)) {
          // If the href is not present or is on a different domain
          privly.privlyReferencesRegex.lastIndex = 0;
          var results = privly.privlyReferencesRegex.exec(a.innerHTML);
          var newHref = privly.makeHref(results[0]);
          a.setAttribute("href", newHref);
        }
      }
    }
  },

  nextAvailableFrameID: 0,

  // Replace an anchor element with its referenced content.
  replaceLink: function(object)
  {
    var iFrame = document.createElement('iframe');
    iFrame.setAttribute("frameborder","0");
    iFrame.setAttribute("vspace","0");
    iFrame.setAttribute("hspace","0");
    iFrame.setAttribute("name","privlyiframe");
    iFrame.setAttribute("width","100%");
    iFrame.setAttribute("marginwidth","0");
    iFrame.setAttribute("marginheight","0");
    iFrame.setAttribute("height","1px");
    iFrame.setAttribute("src",object.href + ".iframe?frame_id=" + privly.nextAvailableFrameID);
    iFrame.setAttribute("id","ifrm"+privly.nextAvailableFrameID);
    iFrame.setAttribute("frameborder","0");
    privly.nextAvailableFrameID++;
    iFrame.setAttribute("style","width: 100%; height: 32px; overflow: hidden;");
    iFrame.setAttribute("scrolling","no");
    iFrame.setAttribute("overflow","hidden");

    object.parentNode.replaceChild(iFrame, object);
  },

  //Replace all Privly links with their iframe
  replaceLinks: function(){
    var anchors = document.links;
    var i = anchors.length;
    while (i--){
      var a = anchors[i];
      privly.privlyReferencesRegex.lastIndex = 0;
      if(a.href && privly.privlyReferencesRegex.test(a.href))
      {
        var exclude = a.getAttribute("privly");
        if(exclude != "exclude")
        {
          if(privly.active)
          {
            privly.replaceLink(a);
          }
          else
          {
            privly.makePassive(a);
          }
        }
      }
    }
  },

  //do nothing. Actual implementation is in extension-host-interface.js
  resizeIframe: function(evt){},

  //prevents DOMNodeInserted from sending hundreds of extension runs
  runPending: false,

  //prep the page and replace the links if it is in active mode
  run: function(){

    //create and correct the links pointing
    //to Privly content
    privly.createLinks();
    privly.correctIndirection();

    //replace all available links on load, if in active mode,
    //otherwise replace all links default behavior
    privly.replaceLinks();
  },

  //runs privly once then registers the update listener
  //for dynamic pages
  listeners: function(){
    //don't recursively replace links
    if(document.URL.indexOf('priv.ly') != -1 || document.URL.indexOf('localhost:3000') != -1)
      return;

    privly.runPending=true;
      setTimeout(
        function(){
          privly.runPending=false;
          privly.run();
        },
        100);

    //Everytime the page is updated via javascript, we have to check
    //for new Privly content. This might not be supported on other platforms
    document.addEventListener("DOMNodeInserted", function(event) {

      //we check the page a maximum of two times a second
      if(privly.runPending )
        return;
      privly.runPending=true;

      setTimeout(
        function(){
          privly.runPending=false;
          privly.run();
        },
        500);
    });

    //The content's iframe will fire a resize event when it has loaded, resizeIframe
    //sets the height of the iframe to the height of the content contained within.
    window.addEventListener("IframeResizeEvent", function(e) { privly.resizeIframe(e); }, false, true);
  },

  //indicates whether the extension shoud immediatly replace all Privly
  //links it encounters
  active: false
};

privly.listeners();<commit_msg>working iframe resizing using posted message<commit_after>/*******************************************************************************
Open Source Initiative OSI - The MIT License (MIT):Licensing
[OSI Approved License]
The MIT License (MIT)

Copyright (c) Sean McGregor

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

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

var privly = {

  //Matches:
  //              http://
  //              https://
  //                        priv.ly/textAndNumbers/any/number/of/times
  //
  //also matches localhost:3000
  privlyReferencesRegex: /\b(https?:\/\/){0,1}(priv\.ly|localhost:3000)(\/posts)(\/\w*){1,}\b/gi,

  // Takes a domain with an optional http(s) in front and returns a fully formed domain name
  makeHref: function(domain)
  {
    var hasHTTPRegex = /^((https?)\:\/\/)/i
    if(!hasHTTPRegex.test(domain))
        domain = "http://" + domain;
    return domain;
  },

  //Make plain text links into anchor elements
  createLinks: function()
  {
      /*************************************************************************
      Inspired by Linkify script:
        http://downloads.mozdev.org/greasemonkey/linkify.user.js

      Originally written by Anthony Lieuallen of http://arantius.com/
      Licensed for unlimited modification and redistribution as long as
      this notice is kept intact.
      **************************************************************************/

      var excludeParents = ["a", "applet", "button", "code", "form",
                             "input", "option", "script", "select", "meta",
                             "style", "textarea", "title", "div","span"];
      var excludedParentsString = excludeParents.join(" or parent::");
      var xpathExpression = ".//text()[not(parent:: " + excludedParentsString +")]";

      textNodes = document.evaluate(xpathExpression, document.body, null,
                                    XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

      for(var i=0; i < textNodes.snapshotLength; i++){
          item = textNodes.snapshotItem(i);

          var itemText = item.nodeValue;

          privly.privlyReferencesRegex.lastIndex = 0;
          if(privly.privlyReferencesRegex.test(itemText)){
              var span = document.createElement("span");
              var lastLastIndex = 0;
              privly.privlyReferencesRegex.lastIndex = 0;
              for(var results = null; results = privly.privlyReferencesRegex.exec(itemText); ){
                  var href = results[0];
                  span.appendChild(document.createTextNode(itemText.substring(lastLastIndex, results.index)));

                  var text = (href.indexOf(" ")==0)?href.substring(1):href;

                  var href = privly.makeHref(text);

                  var a = document.createElement("a");
                  a.setAttribute("href", href);
                  a.appendChild(document.createTextNode(text.substring(0,4).toLowerCase()+text.substring(4)));
                  if(href.indexOf(" ")==0)
                      span.appendChild(document.createTextNode(" "));
                  span.appendChild(a);
                  lastLastIndex = privly.privlyReferencesRegex.lastIndex;
              }
              span.appendChild(document.createTextNode(itemText.substring(lastLastIndex)));
              item.parentNode.replaceChild(span, item);
          }
      }
  },

  //Kill default link behaviour on Privly Links
  makePassive: function(anchor)
  {
    //Preventing the default link behavior
    anchor.addEventListener("mousedown", function(e){
        e.cancelBubble = true;
        e.stopPropagation();
        e.preventDefault();
        privly.replaceLink(anchor);
      },
      true);
  },

  //Checks link attributes and text for privly links without the proper href attribute.
  //Twitter and other hosts change links so they can collect click events.
  correctIndirection: function()
  {
    var anchors = document.links;
    var i = anchors.length;
    while (i--){
      var a = anchors[i];

      if(a.href && (a.href.indexOf("priv.ly/posts/") == -1 || a.href.indexOf("priv.ly/posts/") > 9))
      {
        if (privly.privlyReferencesRegex.test(a.innerHTML)) {
          // If the href is not present or is on a different domain
          privly.privlyReferencesRegex.lastIndex = 0;
          var results = privly.privlyReferencesRegex.exec(a.innerHTML);
          var newHref = privly.makeHref(results[0]);
          a.setAttribute("href", newHref);
        }
      }
    }
  },

  nextAvailableFrameID: 0,

  // Replace an anchor element with its referenced content.
  replaceLink: function(object)
  {
    var iFrame = document.createElement('iframe');
    iFrame.setAttribute("frameborder","0");
    iFrame.setAttribute("vspace","0");
    iFrame.setAttribute("hspace","0");
    iFrame.setAttribute("name","privlyiframe");
    iFrame.setAttribute("width","100%");
    iFrame.setAttribute("marginwidth","0");
    iFrame.setAttribute("marginheight","0");
    iFrame.setAttribute("height","1px");
    iFrame.setAttribute("src",object.href + ".iframe?frame_id=" + privly.nextAvailableFrameID);
    iFrame.setAttribute("id","ifrm"+privly.nextAvailableFrameID);
    iFrame.setAttribute("frameborder","0");
    privly.nextAvailableFrameID++;
    iFrame.setAttribute("style","width: 100%; height: 32px; overflow: hidden;");
    iFrame.setAttribute("scrolling","no");
    iFrame.setAttribute("overflow","hidden");

    object.parentNode.replaceChild(iFrame, object);
  },

  //Replace all Privly links with their iframe
  replaceLinks: function(){
    var anchors = document.links;
    var i = anchors.length;
    while (i--){
      var a = anchors[i];
      privly.privlyReferencesRegex.lastIndex = 0;
      if(a.href && privly.privlyReferencesRegex.test(a.href))
      {
        var exclude = a.getAttribute("privly");
        if(exclude != "exclude")
        {
          if(privly.active)
          {
            privly.replaceLink(a);
          }
          else
          {
            privly.makePassive(a);
          }
        }
      }
    }
  },

  //resize the iframe using a posted message
  resizeIframe: function(message){

    if(message.origin !== "https://priv.ly" && message.origin !== "http://localhost:3000")
      return;

    var data = message.data.split(",");
    var iframe = document.getElementById("ifrm"+data[0]);
    iframe.style.height = data[1]+'px';
  },

  //prevents DOMNodeInserted from sending hundreds of extension runs
  runPending: false,

  //prep the page and replace the links if it is in active mode
  run: function(){

    //create and correct the links pointing
    //to Privly content
    privly.createLinks();
    privly.correctIndirection();

    //replace all available links on load, if in active mode,
    //otherwise replace all links default behavior
    privly.replaceLinks();
  },

  //runs privly once then registers the update listener
  //for dynamic pages
  listeners: function(){
    //don't recursively replace links
    if(document.URL.indexOf('priv.ly') != -1 || document.URL.indexOf('localhost:3000') != -1)
      return;

    //The content's iframe will post a message to the hosting document. This listener sets the height
    //of the iframe according to the messaged height
    window.addEventListener("message", privly.resizeIframe, false, true);

    privly.runPending=true;
      setTimeout(
        function(){
          privly.runPending=false;
          privly.run();
        },
        100);

    //Everytime the page is updated via javascript, we have to check
    //for new Privly content. This might not be supported on other platforms
    document.addEventListener("DOMNodeInserted", function(event) {

      //we check the page a maximum of two times a second
      if(privly.runPending )
        return;
      privly.runPending=true;

      setTimeout(
        function(){
          privly.runPending=false;
          privly.run();
        },
        500);
    });
  },

  //indicates whether the extension shoud immediatly replace all Privly
  //links it encounters
  active: true
};

privly.listeners();
<|endoftext|>