<commit_before>// ==UserScript==
// @name         facebook.tracking.exposed
// @namespace    https://facebook.tracking.exposed
// @version      0.9.7
// @description  Collection meta-data from Facebook's timeline, in order to analyze and look for potential informative manipulation (if you've never heard about Filter Bubble, and you're still young⌁inside™, start here https://en.wikipedia.org/wiki/Filter_bubble )
// @author       Claudio Agosti @_vecna
// @match        https://www.facebook.com/*
// @connect      facebook.tracking.exposed
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/lodash-compat/3.10.2/lodash.min.js
// ==/UserScript==

GM_addStyle(`
.escvi--main-button {
  position: fixed;
  bottom: -1px;
  left: 25px;
  display: block;
  width: 50px;
  height: 50px;
  background-color: palegreen;
  color: black;
  font-size: 2em;
  font-weight: bold;
  line-height: 50px;
  text-align: center;
  border: 1px solid #888;
  opacity: 0.6;
  transition: opacity 0.3s ease-in-out;
}

.escvi--main-button:hover {
  text-decoration: none;
  opacity: 1;
}
`);

var d = false, /* debug */
    ee = false, /* explicit in the page */
    simple = true; /* small debug entry on the bottom of every post */

var uniqueLocation = { counter: -1, unique: -1 },
    lastLocation = null,
    toBeFlush = {'debug': [], 'timeline': [] },
    user = null,
    init = false,
    lastAnalyzedPost = null,
    STARTING_FRACTION = 300,
    url = 'https://facebook.tracking.exposed',
    version = '0.9.7',
    FLUSH_INTERVAL = 20000;

var renderMainButton = function() {
    var mainButton = $("", {
        html: 'ઉ',
        "class": "escvi--main-button",
        href: url + "/realitycheck/" + user.id,
        target: "_blank"
    });
    $('body').append(mainButton);
};

var extractInfoPromoted = function(nodehtml, postType) {

    var startHref = nodehtml.search(/href="https:\/\/www.facebook.com\//),
        hrefP = nodehtml.substring(startHref + 6).replace(/" .*/, '').replace(/"> .*/, '');

    return {
        href: hrefP,
        additionalInfo: null,
        publicationTime: null,
        type: postType,
        child: _.size($(nodehtml)[0].childNodes)
    };
};

var extractInfoFromFeed = function(nodehtml, postType) {

    if (_.size(nodehtml) < 300) {
        if (d) console.log("node.innerHTML (expected a) " + postType + " skipped: the size is less than 300 (" + _.size(nodehtml) + ")");
        reportError({error: "debugging", reason: "small", content: nodehtml });
        return null;
    }

    for (var fraction = STARTING_FRACTION; fraction <= 550; fraction += 50) {

        var profileS = nodehtml.search(/profileLink/),
            profileDU = nodehtml.substring(profileS + 19, profileS + 100),
            profileHref = profileDU.replace(/".*/, '');

        var t = nodehtml.search(/timestampContent/),
            block = nodehtml.substring(t - fraction, t),
            hrefP = block.match(/href=".* /),
            utimeP = block.match(/data-utime=".*/);

        if (_.isNull(hrefP) || _.isNull(utimeP) )
            continue;

        var href = hrefP[0].replace(/" .*/, '').substr(6),
            utime = utimeP[0].replace(/"\ .*/, '').substr(12);

        if( href === '' || utime === '' )
            continue;

        return {
            /* href_stats: [ block.search(/href="/), fraction ],
               child: _.size($(nodehtml)[0].childNodes),
               parsedUtime: utime */
            href: href,
            additionalInfo: (profileS !== -1) ? profileHref : null,
            publicationTime: moment(_.parseInt(utime) * 1000).format(),
            type: postType
        };
    }
    reportError({error: "debugging", reason: "failure", content: nodehtml });
    return null;
};

var extractPostType = function(nodehtml) {
    var childNodes = _.size($(nodehtml)[0].childNodes);
    switch(childNodes) {
        case 5:
            return('feed');
        case 3:
            return('related');
    }
    reportError({error: "debugging", reason: "child", number: childNodes, content: nodehtml });
    if(d) console.log("return promoted, spotted: " + childNodes + " childNodes");
    return('promoted');
};

var verboseEntry  = function(node, post, whichPost, fromWhere) {
    if (simple) {
       var smallhtml = ['', 'ઉ ' + uniqueLocation.counter, '' ];
       $(node).append(smallhtml.join(" "));
    }
    if (!ee) return;
    var html = [ '', '
', fromWhere, 'Now at[', whichPost, ']#', JSON.stringify(uniqueLocation, undefined, 2),
             post, 'is', JSON.stringify(post, undefined, 2), '
', '
'];
    $(node).append(html.join(" "));
};

var basicSetup = function() {

    var x = $("div > a");

    _.each(x, function(aElem, cnt) {
        var img = _.get($(aElem)[0], 'firstChild');
        if(!_.isUndefined($(img)[0])) {
            var profile_id = _.get($(img)[0], 'id');
            var pattern = 'profile_pic_header_';
            if (_.startsWith(profile_id, pattern)) {
                user = {
                    id: profile_id.substring(_.size(pattern), _.size(profile_id)),
                    href: aElem.href
                };
            }
        }
    });

    if(_.isNull(user) || _.isUndefined(_.get(user, 'id'))) {
        if(d) console.log("Impossible parse the user from " + _.size(x) + " html elements!?");
    } else {
        init = true;
        /* paintLogo */
        console.log("facebook.tracking.exposed initialization: rendering main button, detected user: " + JSON.stringify(user) );
        /* find someone able to implement properly the issue #1 */
        renderMainButton();
    }
};

var newUserContent = function(jNode) {
    var node = jNode[0],
        pathname = document.location.pathname;

    if(!init)
        basicSetup();

    if (_.get(node, 'attributes[2].nodeName') !== 'aria-label')
        return;

    /* clean pathname if has an ?something */
    pathname = pathname.replace(/\?.*/, '');

    /* this fit new location or locantion changes */
    if (pathname !== lastLocation && pathname === '/') {
        refreshIsHappen();
    }
    lastLocation = pathname;

    if(pathname !== "/") {
        uniqueLocation.unique = -1;
        uniqueLocation.when = undefined;
        uniqueLocation.counter = 0;
        return;
    }

    var feedEntry = {
        'when' : moment().format(),
        'refreshId': uniqueLocation.unique
    };

    var postType = extractPostType(node.innerHTML);

    if ( postType === 'feed' ) {
        feedPost = extractInfoFromFeed(node.innerHTML, postType);
        verboseEntry(jNode, lastAnalyzedPost, "previous", "Feed", pathname);
        appendLog(lastAnalyzedPost);
        feedEntry.order = uniqueLocation.counter = (uniqueLocation.counter + 1);
        feedEntry.content = [ feedPost ];
        lastAnalyzedPost = feedEntry;
        verboseEntry(jNode, lastAnalyzedPost, "recorded Feed", "Feed", pathname);
    } else if ( postType === 'promoted' ) {
        promotedPost = extractInfoPromoted(node.innerHTML, postType);
        verboseEntry(jNode, lastAnalyzedPost, "previous", "Promoted", pathname);
        appendLog(lastAnalyzedPost);
        if(!_.isNull(promotedPost)) {
            feedEntry.order = uniqueLocation.counter = (uniqueLocation.counter + 1);
            feedEntry.content = [ promotedPost ];
            lastAnalyzedPost = feedEntry;
            verboseEntry(jNode, lastAnalyzedPost, "recorder Promoted", "Promoted");
        } else {
            verboseEntry(jNode, null, "_.isNull this!", "Promoted");
            lastAnalyzedPost = null;
        }
    } else if ( postType === 'related') {
        postInfo = extractInfoFromFeed(node.innerHTML, postType);
        if (!_.isNull(lastAnalyzedPost) && !_.isNull(lastAnalyzedPost.content[0]) ) {
            verboseEntry(jNode, lastAnalyzedPost, "previous", "Related");
            lastAnalyzedPost.content[0].type = 'friendlink';
            lastAnalyzedPost.content[1] = postInfo;
        } else {
            verboseEntry(jNode, null, "previous isNull?", "Related/Broken");
            feedEntry.order = uniqueLocation.counter = (uniqueLocation.counter + 1);
            feedEntry.content = [ postInfo ];
            feedEntry.content.type = 'broken';
            lastAnalyzedPost = feedEntry;
        }
        verboseEntry(jNode, lastAnalyzedPost, "committing this", "Related (friend)");
        appendLog(lastAnalyzedPost);
        lastAnalyzedPost = null;
    } else {
        if (d) console.log("Parsing of this node didn't success, counter is: " + uniqueLocation.counter);
        reportError({error: "a node impossible to be parsed correctly", node: jNode});
        verboseEntry(jNode, lastAnayzedPost, "Previous", postType);
        appendLog(lastAnalyzedPost);
        lastAnalyzedPost = null;
    }
};

var appendLog = function(entryDict) {
    if(!_.isNull(entryDict)) {
        toBeFlush.timeline.push(entryDict);
    }
};

var reportError = function(errorDict) {
    toBeFlush.debug.push(_.extend(errorDict, {
        when: moment().format()
    }));
};

var checkToFlush = function() {

    if( _.size(toBeFlush.timeline) || _.size(toBeFlush.debug) ) {

        var envelope = _.extend(toBeFlush, { from: user, version: version });
        if (d) console.log(envelope);
        if (d) console.log("After " + FLUSH_INTERVAL + "ms, at: " + moment() + ", " +
                           toBeFlush.timeline.length + " info to be sent, with " +
                           toBeFlush.debug.length + " errors");
        GM_xmlhttpRequest({
            method: "POST",
            url: url + '/F/2',
            headers: { "Content-Type": "application/json" },
            data: JSON.stringify(envelope),
            onload: function(response) {
                if (d) console.log("Received response of XHR: " + response.response);
            }
        });
        toBeFlush = { 'debug': [], 'timeline': [] };
    } else {
        if(d) console.log("There are no data to be send");
    }
    setTimeout (checkToFlush, FLUSH_INTERVAL);
};

var scrollTimeout = 0, // 4000,
    whereScroll = 0,
    refreshTimes = 1,
    height = window.innerHeight !== undefined ? window.innerHeight : document.documentElement.offsetHeight,
    maxScrollTimes = 80;

var scrollDown = function() {
    /* initialized with the stable recurring behavior */
    scrollTimeout = 4000;
    whereScroll += _.random(400, height) + (height * refreshTimes);

    if(refreshTimes >=  maxScrollTimes ) {
        reportError({info: "auto_scroll " + refreshTimes });
        location.reload();
        // implicit, reinit the variables with the .reload()
    } else {
        refreshTimes += 1;
        if (d) console.log("scroll to " + whereScroll + " next scroll at: " + scrollTimeout);
        scrollTo(0, whereScroll);
        setTimeout(scrollDown, scrollTimeout);
    }
};

var refreshIsHappen = function() {
    /* refresh is happened MAYBE -- this function can be called more then once
     * due to the different hooks used inside of the facebook page. if is called
     * in less than 1 second window, is considered a duplication */
    if(_.isUndefined(uniqueLocation.when) || moment(moment() - uniqueLocation.when).isAfter(2, 's')) {
        uniqueLocation.unique = _.random(0x10000000, 0xffffffff);
        uniqueLocation.when = moment().format();
        uniqueLocation.counter = 0;
        var refreshInfo = { 'what': 'refresh', 'when': uniqueLocation.when, 'unique': uniqueLocation.unique };
        appendLog(refreshInfo);
        if(d) console.log(refreshInfo);
    } else {
        if(d)console.log("refresh is NOT after 2 seconds of " + uniqueLocation.when + " compared to now " + moment().format("mm:ss") );
    }
};

(function() {
    'use strict';
    waitForKeyElements ("div .userContentWrapper", newUserContent);
    waitForKeyElements ("div .composerAudienceWrapper", refreshIsHappen);
    setTimeout (checkToFlush, FLUSH_INTERVAL);
    if(scrollTimeout)
        setTimeout (scrollDown, scrollTimeout);
})();

/* this code is copied from https://gist.githubusercontent.com/raw/2625891/waitForKeyElements.js found via stackoverflow */

/*--- waitForKeyElements():  A utility function, for Greasemonkey scripts,
    that detects and handles AJAXed content.

    Usage example:

        waitForKeyElements (
            "div.comments"
            , commentCallbackFunction
        );

        //--- Page-specific function to do what we want when the node is found.
        function commentCallbackFunction (jNode) {
            jNode.text ("This comment changed by waitForKeyElements().");
        }

    IMPORTANT: This function requires your script to have loaded jQuery.
*/
function waitForKeyElements (
    selectorTxt,    /* Required: The jQuery selector string that
                        specifies the desired element(s).
                    */
    actionFunction, /* Required: The code to run when elements are
                        found. It is passed a jNode to the matched
                        element.
                    */
    bWaitOnce,      /* Optional: If false, will continue to scan for
                        new elements even after the first match is
                        found.
                    */
    iframeSelector  /* Optional: If set, identifies the iframe to
                        search.
                    */
) {
    var targetNodes, btargetsFound;

    if (typeof iframeSelector == "undefined")
        targetNodes     = $(selectorTxt);
    else
        targetNodes     = $(iframeSelector).contents ()
                                           .find (selectorTxt);

    if (targetNodes  &&  targetNodes.length > 0) {
        btargetsFound   = true;
        /*--- Found target node(s).  Go through each and act if they
            are new.
        */
        targetNodes.each ( function () {
            var jThis        = $(this);
            var alreadyFound = jThis.data ('alreadyFound')  ||  false;

            if (!alreadyFound) {
                //--- Call the payload function.
                var cancelFound     = actionFunction (jThis);
                if (cancelFound)
                    btargetsFound   = false;
                else
                    jThis.data ('alreadyFound', true);
            }
        } );
    }
    else {
        btargetsFound   = false;
    }

    //--- Get the timer-control variable for this selector.
    var controlObj      = waitForKeyElements.controlObj  ||  {};
    var controlKey      = selectorTxt.replace (/[^\w]/g, "_");
    var timeControl     = controlObj [controlKey];

    //--- Now set or clear the timer as appropriate.
    if (btargetsFound  &&  bWaitOnce  &&  timeControl) {
        //--- The only condition where we need to clear the timer.
        clearInterval (timeControl);
        delete controlObj [controlKey];
    }
    else {
        //--- Set a timer, if needed.
        if ( ! timeControl) {
            timeControl = setInterval ( function () {
                    waitForKeyElements (    selectorTxt,
                                            actionFunction,
                                            bWaitOnce,
                                            iframeSelector
                                        );
                },
                300
            );
            controlObj [controlKey] = timeControl;
        }
    }
    waitForKeyElements.controlObj   = controlObj;
}
<commit_msg>a .format() caused a letal bug in the refreshes. Warning developer, Warning. you're walking in a dangerous path of parsing and untested assumptions<commit_after>// ==UserScript==
// @name         facebook.tracking.exposed
// @namespace    https://facebook.tracking.exposed
// @version      0.9.7
// @description  Collection meta-data from Facebook's timeline, in order to analyze and look for potential informative manipulation (if you've never heard about Filter Bubble, and you're still young⌁inside™, start here https://en.wikipedia.org/wiki/Filter_bubble )
// @author       Claudio Agosti @_vecna
// @match        https://www.facebook.com/*
// @connect      facebook.tracking.exposed
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/lodash-compat/3.10.2/lodash.min.js
// ==/UserScript==

GM_addStyle(`
.escvi--main-button {
  position: fixed;
  bottom: -1px;
  left: 25px;
  display: block;
  width: 50px;
  height: 50px;
  background-color: palegreen;
  color: black;
  font-size: 2em;
  font-weight: bold;
  line-height: 50px;
  text-align: center;
  border: 1px solid #888;
  opacity: 0.6;
  transition: opacity 0.3s ease-in-out;
}

.escvi--main-button:hover {
  text-decoration: none;
  opacity: 1;
}
`);

var d = false, /* debug */
    ee = false, /* explicit in the page */
    simple = true; /* small debug entry on the bottom of every post */

var uniqueLocation = { counter: -1, unique: -1 },
    lastLocation = null,
    toBeFlush = {'debug': [], 'timeline': [] },
    user = null,
    init = false,
    lastAnalyzedPost = null,
    STARTING_FRACTION = 300,
    url = 'https://facebook.tracking.exposed',
    version = '0.9.7',
    FLUSH_INTERVAL = 20000;

var renderMainButton = function() {
    var mainButton = $("", {
        html: 'ઉ',
        "class": "escvi--main-button",
        href: url + "/realitycheck/" + user.id,
        target: "_blank"
    });
    $('body').append(mainButton);
};

var extractInfoPromoted = function(nodehtml, postType) {

    var startHref = nodehtml.search(/href="https:\/\/www.facebook.com\//),
        hrefP = nodehtml.substring(startHref + 6).replace(/" .*/, '').replace(/"> .*/, '');

    return {
        href: hrefP,
        additionalInfo: null,
        publicationTime: null,
        type: postType,
        child: _.size($(nodehtml)[0].childNodes)
    };
};

var extractInfoFromFeed = function(nodehtml, postType) {

    if (_.size(nodehtml) < 300) {
        if (d) console.log("node.innerHTML (expected a) " + postType + " skipped: the size is less than 300 (" + _.size(nodehtml) + ")");
        reportError({error: "debugging", reason: "small", content: nodehtml });
        return null;
    }

    for (var fraction = STARTING_FRACTION; fraction <= 550; fraction += 50) {

        var profileS = nodehtml.search(/profileLink/),
            profileDU = nodehtml.substring(profileS + 19, profileS + 100),
            profileHref = profileDU.replace(/".*/, '');

        var t = nodehtml.search(/timestampContent/),
            block = nodehtml.substring(t - fraction, t),
            hrefP = block.match(/href=".* /),
            utimeP = block.match(/data-utime=".*/);

        if (_.isNull(hrefP) || _.isNull(utimeP) )
            continue;

        var href = hrefP[0].replace(/" .*/, '').substr(6),
            utime = utimeP[0].replace(/"\ .*/, '').substr(12);

        if( href === '' || utime === '' )
            continue;

        return {
            /* href_stats: [ block.search(/href="/), fraction ],
               child: _.size($(nodehtml)[0].childNodes),
               parsedUtime: utime */
            href: href,
            additionalInfo: (profileS !== -1) ? profileHref : null,
            publicationTime: moment(_.parseInt(utime) * 1000).format(),
            type: postType
        };
    }
    reportError({error: "debugging", reason: "failure", content: nodehtml });
    return null;
};

var extractPostType = function(nodehtml) {
    var childNodes = _.size($(nodehtml)[0].childNodes);
    switch(childNodes) {
        case 5:
            return('feed');
        case 3:
            return('related');
    }
    reportError({error: "debugging", reason: "child", number: childNodes, content: nodehtml });
    if(d) console.log("return promoted, spotted: " + childNodes + " childNodes");
    return('promoted');
};

var verboseEntry  = function(node, post, whichPost, fromWhere) {
    if (simple) {
       var smallhtml = ['', 'ઉ ' + uniqueLocation.counter, '' ];
       $(node).append(smallhtml.join(" "));
    }
    if (!ee) return;
    var html = [ '', '
', fromWhere, 'Now at[', whichPost, ']#', JSON.stringify(uniqueLocation, undefined, 2),
             post, 'is', JSON.stringify(post, undefined, 2), '
', '
'];
    $(node).append(html.join(" "));
};

var basicSetup = function() {

    var x = $("div > a");

    _.each(x, function(aElem, cnt) {
        var img = _.get($(aElem)[0], 'firstChild');
        if(!_.isUndefined($(img)[0])) {
            var profile_id = _.get($(img)[0], 'id');
            var pattern = 'profile_pic_header_';
            if (_.startsWith(profile_id, pattern)) {
                user = {
                    id: profile_id.substring(_.size(pattern), _.size(profile_id)),
                    href: aElem.href
                };
            }
        }
    });

    if(_.isNull(user) || _.isUndefined(_.get(user, 'id'))) {
        if(d) console.log("Impossible parse the user from " + _.size(x) + " html elements!?");
    } else {
        init = true;
        /* paintLogo */
        console.log("facebook.tracking.exposed initialization: rendering main button, detected user: " + JSON.stringify(user) );
        /* find someone able to implement properly the issue #1 */
        renderMainButton();
    }
};

var newUserContent = function(jNode) {
    var node = jNode[0],
        pathname = document.location.pathname;

    if(!init)
        basicSetup();

    if (_.get(node, 'attributes[2].nodeName') !== 'aria-label')
        return;

    /* clean pathname if has an ?something */
    pathname = pathname.replace(/\?.*/, '');

    /* this fit new location or locantion changes */
    if (pathname !== lastLocation && pathname === '/') {
        refreshIsHappen();
    }
    lastLocation = pathname;

    if(pathname !== "/") {
        uniqueLocation.unique = -1;
        uniqueLocation.when = undefined;
        uniqueLocation.counter = 0;
        return;
    }

    var feedEntry = {
        'when' : moment().format(),
        'refreshId': uniqueLocation.unique
    };

    var postType = extractPostType(node.innerHTML);

    if ( postType === 'feed' ) {
        feedPost = extractInfoFromFeed(node.innerHTML, postType);
        verboseEntry(jNode, lastAnalyzedPost, "previous", "Feed", pathname);
        appendLog(lastAnalyzedPost);
        feedEntry.order = uniqueLocation.counter = (uniqueLocation.counter + 1);
        feedEntry.content = [ feedPost ];
        lastAnalyzedPost = feedEntry;
        verboseEntry(jNode, lastAnalyzedPost, "recorded Feed", "Feed", pathname);
    } else if ( postType === 'promoted' ) {
        promotedPost = extractInfoPromoted(node.innerHTML, postType);
        verboseEntry(jNode, lastAnalyzedPost, "previous", "Promoted", pathname);
        appendLog(lastAnalyzedPost);
        if(!_.isNull(promotedPost)) {
            feedEntry.order = uniqueLocation.counter = (uniqueLocation.counter + 1);
            feedEntry.content = [ promotedPost ];
            lastAnalyzedPost = feedEntry;
            verboseEntry(jNode, lastAnalyzedPost, "recorder Promoted", "Promoted");
        } else {
            verboseEntry(jNode, null, "_.isNull this!", "Promoted");
            lastAnalyzedPost = null;
        }
    } else if ( postType === 'related') {
        postInfo = extractInfoFromFeed(node.innerHTML, postType);
        if (!_.isNull(lastAnalyzedPost) && !_.isNull(lastAnalyzedPost.content[0]) ) {
            verboseEntry(jNode, lastAnalyzedPost, "previous", "Related");
            lastAnalyzedPost.content[0].type = 'friendlink';
            lastAnalyzedPost.content[1] = postInfo;
        } else {
            verboseEntry(jNode, null, "previous isNull?", "Related/Broken");
            feedEntry.order = uniqueLocation.counter = (uniqueLocation.counter + 1);
            feedEntry.content = [ postInfo ];
            feedEntry.content.type = 'broken';
            lastAnalyzedPost = feedEntry;
        }
        verboseEntry(jNode, lastAnalyzedPost, "committing this", "Related (friend)");
        appendLog(lastAnalyzedPost);
        lastAnalyzedPost = null;
    } else {
        if (d) console.log("Parsing of this node didn't success, counter is: " + uniqueLocation.counter);
        reportError({error: "a node impossible to be parsed correctly", node: jNode});
        verboseEntry(jNode, lastAnayzedPost, "Previous", postType);
        appendLog(lastAnalyzedPost);
        lastAnalyzedPost = null;
    }
};

var appendLog = function(entryDict) {
    if(!_.isNull(entryDict)) {
        toBeFlush.timeline.push(entryDict);
    }
};

var reportError = function(errorDict) {
    toBeFlush.debug.push(_.extend(errorDict, {
        when: moment().format()
    }));
};

var checkToFlush = function() {

    if( _.size(toBeFlush.timeline) || _.size(toBeFlush.debug) ) {

        var envelope = _.extend(toBeFlush, { from: user, version: version });
        if (d) console.log(envelope);
        if (d) console.log("After " + FLUSH_INTERVAL + "ms, at: " + moment() + ", " +
                           toBeFlush.timeline.length + " info to be sent, with " +
                           toBeFlush.debug.length + " errors");
        GM_xmlhttpRequest({
            method: "POST",
            url: url + '/F/2',
            headers: { "Content-Type": "application/json" },
            data: JSON.stringify(envelope),
            onload: function(response) {
                if (d) console.log("Received response of XHR: " + response.response);
            }
        });
        toBeFlush = { 'debug': [], 'timeline': [] };
    } else {
        if(d) console.log("There are no data to be send");
    }
    setTimeout (checkToFlush, FLUSH_INTERVAL);
};

var scrollTimeout = 0, // 4000,
    whereScroll = 0,
    refreshTimes = 1,
    height = window.innerHeight !== undefined ? window.innerHeight : document.documentElement.offsetHeight,
    maxScrollTimes = 80;

var scrollDown = function() {
    /* initialized with the stable recurring behavior */
    scrollTimeout = 4000;
    whereScroll += _.random(400, height) + (height * refreshTimes);

    if(refreshTimes >=  maxScrollTimes ) {
        reportError({info: "auto_scroll " + refreshTimes });
        location.reload();
        // implicit, reinit the variables with the .reload()
    } else {
        refreshTimes += 1;
        if (d) console.log("scroll to " + whereScroll + " next scroll at: " + scrollTimeout);
        scrollTo(0, whereScroll);
        setTimeout(scrollDown, scrollTimeout);
    }
};

var resetLocation = function() {
    uniqueLocation.unique = _.random(0x10000000, 0xffffffff);
    uniqueLocation.when = moment();
    uniqueLocation.counter = 0;
    var refreshInfo = { 'what': 'refresh', 'when': uniqueLocation.when.format(), 'unique': uniqueLocation.unique };
    appendLog(refreshInfo);
    if(d) console.log(refreshInfo);
};

var refreshIsHappen = function() {
    /* refresh is happened MAYBE -- this function can be called more then
     * once due to the different hooks used inside of the facebook page.
     * if is called in less than 2 seconds, is considered a duplication */
    if(_.isUndefined(uniqueLocation.when)) {
        if(d)console.log("Initialized now");
        resetLocation();
    } else {
        if ( moment(moment() - uniqueLocation.when).isAfter(2, 's') ) {
            if(d)console.log("two seconds passed, so, refresh. before: " +
                uniqueLocation.when.format() +
                " now " + moment().format() );
            resetLocation();
        }
        else {
            if(d)console.log("refresh is NOT after 2 seconds of " +
                uniqueLocation.when.format() +
                " compared to now " + moment().format() );
        }
    }
};

(function() {
    'use strict';
    waitForKeyElements ("div .composerAudienceWrapper", refreshIsHappen);
    waitForKeyElements ("div .userContentWrapper", newUserContent);
    setTimeout (checkToFlush, FLUSH_INTERVAL);
    if(scrollTimeout)
        setTimeout (scrollDown, scrollTimeout);
})();

/* this code is copied from https://gist.githubusercontent.com/raw/2625891/waitForKeyElements.js found via stackoverflow */

/*--- waitForKeyElements():  A utility function, for Greasemonkey scripts,
    that detects and handles AJAXed content.

    Usage example:

        waitForKeyElements (
            "div.comments"
            , commentCallbackFunction
        );

        //--- Page-specific function to do what we want when the node is found.
        function commentCallbackFunction (jNode) {
            jNode.text ("This comment changed by waitForKeyElements().");
        }

    IMPORTANT: This function requires your script to have loaded jQuery.
*/
function waitForKeyElements (
    selectorTxt,    /* Required: The jQuery selector string that
                        specifies the desired element(s).
                    */
    actionFunction, /* Required: The code to run when elements are
                        found. It is passed a jNode to the matched
                        element.
                    */
    bWaitOnce,      /* Optional: If false, will continue to scan for
                        new elements even after the first match is
                        found.
                    */
    iframeSelector  /* Optional: If set, identifies the iframe to
                        search.
                    */
) {
    var targetNodes, btargetsFound;

    if (typeof iframeSelector == "undefined")
        targetNodes     = $(selectorTxt);
    else
        targetNodes     = $(iframeSelector).contents ()
                                           .find (selectorTxt);

    if (targetNodes  &&  targetNodes.length > 0) {
        btargetsFound   = true;
        /*--- Found target node(s).  Go through each and act if they
            are new.
        */
        targetNodes.each ( function () {
            var jThis        = $(this);
            var alreadyFound = jThis.data ('alreadyFound')  ||  false;

            if (!alreadyFound) {
                //--- Call the payload function.
                var cancelFound     = actionFunction (jThis);
                if (cancelFound)
                    btargetsFound   = false;
                else
                    jThis.data ('alreadyFound', true);
            }
        } );
    }
    else {
        btargetsFound   = false;
    }

    //--- Get the timer-control variable for this selector.
    var controlObj      = waitForKeyElements.controlObj  ||  {};
    var controlKey      = selectorTxt.replace (/[^\w]/g, "_");
    var timeControl     = controlObj [controlKey];

    //--- Now set or clear the timer as appropriate.
    if (btargetsFound  &&  bWaitOnce  &&  timeControl) {
        //--- The only condition where we need to clear the timer.
        clearInterval (timeControl);
        delete controlObj [controlKey];
    }
    else {
        //--- Set a timer, if needed.
        if ( ! timeControl) {
            timeControl = setInterval ( function () {
                    waitForKeyElements (    selectorTxt,
                                            actionFunction,
                                            bWaitOnce,
                                            iframeSelector
                                        );
                },
                300
            );
            controlObj [controlKey] = timeControl;
        }
    }
    waitForKeyElements.controlObj   = controlObj;
}
<|endoftext|>


<commit_before>// @flow
import * as tt from './types'
import { Node, OperatorNode } from './node'
import type { Tree } from './types'
import { get } from '../utils'

type ExprssionItem = Node|Expression

type Options = {
	parent?: Expression,
	params?: Set<string>,
}

export default class Expression {
	// #private
	operators: OperatorNode[] = []
	lastWrap: number|null = null

	// #public
	items: ExprssionItem[] = []
	hasError: bool = false
	params: Set<string>
	parent: Expression|null

	constructor(
		{
			parent,
			params = parent ? parent.params : new Set(),
		}: Options = {},

		...args: ExprssionItem[]
	) {
		this.params = params
		this.parent = parent || null

		this.add(...args)
	}

	// #private
	/* checks if operators need to be wrapped by brackets because order of operators */
	checkOperatorsOrder() {
		/* if there less than two operators this check is useless
		 * or if last item is the last operator,
		 * than needs the next item to see what will be need to move into a smaller scope */
		if (this.operators.length < 2 || this.get(-1) instanceof OperatorNode) return

		const lastOpOrder = this.getOperator(-1).getOrder()

		// if needs to be wrapped with brackets
		if (lastOpOrder > this.getOperator(-2).getOrder()) {
			/* if there was a wrapping in the previous time,
			 * than it will check operation against the inside wrapper brackets
			 * this happend in the following scenarios: num + num * num ^ num */
			if (this.lastWrap === this.items.length - 3) {
				const insideTree = this.get(-3)

				// actualy, it must be true because it was generated in this method earlier
				if (
					insideTree instanceof Expression
					&& insideTree.getOperator(-1).getOrder() < lastOpOrder
				) {
					insideTree.add(...this.remove(2)[1])
					return
				}
			}

			const [items, remove] = this.remove(3)

			// setting lastwrap to current location
			this.lastWrap = items.length
			this.items = [
				...items,
				new Expression({
					parent: this,
				}, ...remove),
			]
		}
	}

	add(...vals: ExprssionItem[]) {
		vals.forEach((val) => {
			if (val instanceof Node) {
				if (val instanceof OperatorNode) {
					this.operators.push(val)
				} else if (val.is(tt.PARAM)) {
					this.params.add(val.value)
				} else if (!this.hasError && val.is(tt.ERROR)) {
					this.hasError = true
					if (this.parent) this.parent.hasError = true
				}
			}

			this.items.push(val)

			this.checkOperatorsOrder()
		})
	}

	remove(i: number = 1): [ExprssionItem[], ExprssionItem[]] {
		const removePart = this.items.slice(-i)

		this.items = this.items.slice(0, -i)

		// $FlowFixMe - flow can't tell that the filter method will return only OperatorNode[]
		this.operators = (this.items.filter(item => item instanceof OperatorNode): OperatorNode[])

		return [this.items, removePart]
	}

	toArray(): Tree {
		return this.items.map(item => (
			item instanceof Expression ? item.toArray() : item
		))
	}

	get(i: number): ExprssionItem {
		return get(this.items, i)
	}

	getOperator(i: number): OperatorNode {
		return get(this.operators, i)
	}
}
<commit_msg>changed the rest parameter `args` to be a option as `children`<commit_after>// @flow
import * as tt from './types'
import { Node, OperatorNode } from './node'
import type { Tree } from './types'
import { get } from '../utils'

type ExprssionItem = Node|Expression

type Options = {|
	parent?: Expression,
	params?: Set<string>,
	children?: ExprssionItem[],
|}

export default class Expression {
	// #private
	operators: OperatorNode[] = []
	lastWrap: number|null = null

	// #public
	items: ExprssionItem[] = []
	hasError: bool = false
	params: Set<string>
	parent: Expression|null

	constructor({
		parent,
		params = parent ? parent.params : new Set(),
		children,
	}: Options = {}) {
		this.params = params
		this.parent = parent || null

		if (children) {
			this.add(...children)
		}
	}

	// #private
	/* checks if operators need to be wrapped by brackets because order of operators */
	checkOperatorsOrder() {
		/* if there less than two operators this check is useless
		 * or if last item is the last operator,
		 * than needs the next item to see what will be need to move into a smaller scope */
		if (this.operators.length < 2 || this.get(-1) instanceof OperatorNode) return

		const lastOpOrder = this.getOperator(-1).getOrder()

		// if needs to be wrapped with brackets
		if (lastOpOrder > this.getOperator(-2).getOrder()) {
			/* if there was a wrapping in the previous time,
			 * than it will check operation against the inside wrapper brackets
			 * this happend in the following scenarios: num + num * num ^ num */
			if (this.lastWrap === this.items.length - 3) {
				const insideTree = this.get(-3)

				// actualy, it must be true because it was generated in this method earlier
				if (
					insideTree instanceof Expression
					&& insideTree.getOperator(-1).getOrder() < lastOpOrder
				) {
					insideTree.add(...this.remove(2)[1])
					return
				}
			}

			const [items, remove] = this.remove(3)

			// setting lastwrap to current location
			this.lastWrap = items.length
			this.items = [
				...items,
				new Expression({
					parent: this,
					children: remove,
				}),
			]
		}
	}

	add(...vals: ExprssionItem[]) {
		vals.forEach((val) => {
			if (val instanceof Node) {
				if (val instanceof OperatorNode) {
					this.operators.push(val)
				} else if (val.is(tt.PARAM)) {
					this.params.add(val.value)
				} else if (!this.hasError && val.is(tt.ERROR)) {
					this.hasError = true
					if (this.parent) this.parent.hasError = true
				}
			}

			this.items.push(val)

			this.checkOperatorsOrder()
		})
	}

	remove(i: number = 1): [ExprssionItem[], ExprssionItem[]] {
		const removePart = this.items.slice(-i)

		this.items = this.items.slice(0, -i)

		// $FlowFixMe - flow can't tell that the filter method will return only OperatorNode[]
		this.operators = (this.items.filter(item => item instanceof OperatorNode): OperatorNode[])

		return [this.items, removePart]
	}

	toArray(): Tree {
		return this.items.map(item => (
			item instanceof Expression ? item.toArray() : item
		))
	}

	get(i: number): ExprssionItem {
		return get(this.items, i)
	}

	getOperator(i: number): OperatorNode {
		return get(this.operators, i)
	}
}
<|endoftext|>


<commit_before>// testing-routes.js
import cache from './cache';

var express = require('express'),
    middlewares = require('./middlewares'),
    cryptoUtils = require('./cryptoUtils');

var router = express.Router();

// creates a unique app in the cache, with a collection prefix
function createApp(req, res) {
  var appId = cryptoUtils.randomHexString(32);
  // TODO: (nlutsenko) This doesn't work and should die, since there are no controllers on this configuration.
  cache.apps.set(appId, {
    'collectionPrefix': appId + '_',
    'masterKey': 'master'
  });
  var keys = {
    'application_id': appId,
    'client_key': 'unused',
    'windows_key': 'unused',
    'javascript_key': 'unused',
    'webhook_key': 'unused',
    'rest_api_key': 'unused',
    'master_key': 'master'
  };
  res.status(200).send(keys);
}

// deletes all collections with the collectionPrefix of the app
function clearApp(req, res) {
  if (!req.auth.isMaster) {
    return res.status(401).send({"error": "unauthorized"});
  }
  return req.config.database.deleteEverything().then(() => {
    res.status(200).send({});
  });
}

// deletes all collections and drops the app from cache
function dropApp(req, res) {
  if (!req.auth.isMaster) {
    return res.status(401).send({"error": "unauthorized"});
  }
  return req.config.database.deleteEverything().then(() => {
    cache.apps.remove(req.config.applicationId);
    res.status(200).send({});
  });
}

// Lets just return a success response and see what happens.
function notImplementedYet(req, res) {
  res.status(200).send({});
}

router.post('/rest_clear_app',
             middlewares.handleParseHeaders, clearApp);
router.post('/rest_block',
             middlewares.handleParseHeaders, notImplementedYet);
router.post('/rest_mock_v8_client',
             middlewares.handleParseHeaders, notImplementedYet);
router.post('/rest_unmock_v8_client',
             middlewares.handleParseHeaders, notImplementedYet);
router.post('/rest_verify_analytics',
             middlewares.handleParseHeaders, notImplementedYet);
router.post('/rest_create_app', createApp);
router.post('/rest_drop_app',
             middlewares.handleParseHeaders, dropApp);
router.post('/rest_configure_app',
             middlewares.handleParseHeaders, notImplementedYet);

module.exports = {
  router: router
};
<commit_msg>Unbreak and make specific testing routes work.<commit_after>// testing-routes.js
import cache from './cache';
import * as middlewares from './middlewares';
import { ParseServer } from './index';
import { Parse } from 'parse/node';
var express = require('express'),
  cryptoUtils = require('./cryptoUtils');

var router = express.Router();

// creates a unique app in the cache, with a collection prefix
function createApp(req, res) {
  var appId = cryptoUtils.randomHexString(32);

  ParseServer({
    appId: appId,
    masterKey: 'master',
    serverURL: Parse.serverURL,
    collectionPrefix: appId
  });
  var keys = {
    'application_id': appId,
    'client_key'    : 'unused',
    'windows_key'   : 'unused',
    'javascript_key': 'unused',
    'webhook_key'   : 'unused',
    'rest_api_key'  : 'unused',
    'master_key'    : 'master'
  };
  res.status(200).send(keys);
}

// deletes all collections with the collectionPrefix of the app
function clearApp(req, res) {
  if (!req.auth.isMaster) {
    return res.status(401).send({ "error": "unauthorized" });
  }
  return req.config.database.deleteEverything().then(() => {
    res.status(200).send({});
  });
}

// deletes all collections and drops the app from cache
function dropApp(req, res) {
  if (!req.auth.isMaster) {
    return res.status(401).send({ "error": "unauthorized" });
  }
  return req.config.database.deleteEverything().then(() => {
    cache.apps.remove(req.config.applicationId);
    res.status(200).send({});
  });
}

// Lets just return a success response and see what happens.
function notImplementedYet(req, res) {
  res.status(200).send({});
}

router.post('/rest_clear_app', middlewares.handleParseHeaders, clearApp);
router.post('/rest_block', middlewares.handleParseHeaders, notImplementedYet);
router.post('/rest_mock_v8_client', middlewares.handleParseHeaders, notImplementedYet);
router.post('/rest_unmock_v8_client', middlewares.handleParseHeaders, notImplementedYet);
router.post('/rest_verify_analytics', middlewares.handleParseHeaders, notImplementedYet);
router.post('/rest_create_app', createApp);
router.post('/rest_drop_app', middlewares.handleParseHeaders, dropApp);
router.post('/rest_configure_app', middlewares.handleParseHeaders, notImplementedYet);

module.exports = {
  router: router
};
<|endoftext|>


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

module.exports = {
  demo: {
    options: {
      source: 'demo/static/css/',
      destination: 'demo/',
      template: 'node_modules/cf-component-demo/' + ( grunt.option('tpl') || 'raw' ) + '/',
      templateData: {
        ltIE9AltSource: '<%= opt.ltIE9AltSource %>',
        ltIE8Source: '<%= opt.ltIE8Source %>',
        html5Shiv: opt.html5Shiv,
        family: '<%= pkg.name %>',
        title: '<%= pkg.name %> demo',
        repo: '<%= pkg.homepage %>',
        custom: '<%= grunt.file.read("demo/custom.html") %>'
      }
    }
  },
  docs: {
    options: {
      source: 'docs/static/css/',
      destination: 'docs/',
      template: 'node_modules/cf-component-demo/' + ( grunt.option('tpl') || 'code_examples' ) + '/',
      templateData: {
        family: '<%= pkg.name %>',
        description: '<%= pkg.description %>',
        title: '<%= pkg.name %> docs',
        repo: '<%= pkg.homepage %>'
      }
    }
  }
};
<commit_msg>Try typical Grunt var output<commit_after>var grunt = require('grunt');

module.exports = {
  demo: {
    options: {
      source: 'demo/static/css/',
      destination: 'demo/',
      template: 'node_modules/cf-component-demo/' + ( grunt.option('tpl') || 'raw' ) + '/',
      templateData: {
        ltIE9AltSource: '<%= opt.ltIE9AltSource %>',
        ltIE8Source: '<%= opt.ltIE8Source %>',
        html5Shiv: '<%= opt.html5Shiv >%',
        family: '<%= pkg.name %>',
        title: '<%= pkg.name %> demo',
        repo: '<%= pkg.homepage %>',
        custom: '<%= grunt.file.read("demo/custom.html") %>'
      }
    }
  },
  docs: {
    options: {
      source: 'docs/static/css/',
      destination: 'docs/',
      template: 'node_modules/cf-component-demo/' + ( grunt.option('tpl') || 'code_examples' ) + '/',
      templateData: {
        family: '<%= pkg.name %>',
        description: '<%= pkg.description %>',
        title: '<%= pkg.name %> docs',
        repo: '<%= pkg.homepage %>'
      }
    }
  }
};
<|endoftext|>


<commit_before>const cluster = require("cluster");

class Channel {
    /** Is the process a master? */
    static get isMaster() {
        return cluster.isMaster;
    }

    /** Is the process a worker? */
    static get isWorker() {
        return cluster.isWorker;
    }

    /** A reference to cluster. */
    static get cluster() {
        return cluster;
    }

    /**
     * Adds a listener function to an event.
     * @param {String} event The event name.
     *  @param {Function} handler An event handler function, it accepts at
     *  least one parameter, which is the data sent by another worker or the
     *  master process.
     * @return {Channel}
     */
    static on(event, handler) {
        if (cluster.isMaster) {
            cluster.on("message", (worker, msg) => {
                if (msg && msg.event === event) {
                    handler.call(this, ...msg.data);
                }
            });
        } else {
            process.on("message", (msg) => {
                if (msg && msg.event === event) {
                    handler.call(this, ...msg.data);
                }
            });
        }
        return this;
    }

    /**
     * Adds a listener function to an event that will only run once.
     * @param {String} event The event name.
     * @param {Function} handler An event handler function, it accepts at
     *  least one parameter, which is the data sent by another worker or the
     *  master process.
     * @return {Channel}
     */
    static once(event, handler) {
        if (cluster.isMaster) {
            cluster.once("message", (worker, msg) => {
                if (msg && msg.event === event) {
                    handler.call(this, ...msg.data);
                }
            });
        } else {
            process.once("message", (msg) => {
                if (msg && msg.event === event) {
                    handler.call(this, ...msg.data);
                }
            });
        }
        return this;
    }

    /**
     * Emit a message to the master process or to worker processes.
     * @param {String} event The event name.
     * @param {Any} data A list of data, they will be received by the event
     *  listener.
     * @return {Channel}
     */
    static emit(event, ...data) {
        if (cluster.isMaster) {
            return this.emit(event, ...data);
        } else {
            process.send({ event, data });
        }
    }

    /**
     * Emit a message to a specified worker process.
     * @param {Number} id Could be either a worker id or a pid.
     * @param {String} event The event name.
     * @param {Any} data A list of data, they will be received by the event
     *  listener.
     * @return {Channel}
     */
    static emitTo(id, event, ...data) {
        if (cluster.isMaster) {
            if (cluster.workers[id]) {
                cluster.workers[id].send({ event, data });
            } else {
                for (let _id in cluster.workers) {
                    if (cluster.workers[_id].process.pid === id) {
                        cluster.workers[_id].send({ event, data });
                    }
                }
            }
        } else {
            process.send({
                event: "----transmit----",
                data: { receiver: id, event, data }
            });
        }
    }

    /**
     * Broadcast a massage to all worker processes.
     * @param {String} event The event name.
     * @param {Any} data A list of data, they will be received by the event
     *  listener.
     * @return {Channel}
     */
    static broadcast(event, ...data) {
        if (cluster.isMaster) {
            for (let id in cluster.workers) {
                this.emitTo(id, event, ...data);
            }
        } else {
            process.send({
                event: "----broadcast----",
                data: { event, data }
            });
        }
    }
}

if (cluster.isMaster) {
    // Handle transmit and broadcast.
    cluster.on("message", (worker, msg) => {
        if (msg && msg.event == "----transmit----") {
            msg = msg.data;
            Channel.emitTo(msg.receiver, msg.event, ...msg.data);
        } else if (msg && msg.event == "----broadcast----") {
            msg = msg.data;
            Channel.emit(msg.event, ...msg.data);
        }
    });
}

module.exports = Channel;<commit_msg>Fix a bug.<commit_after>const cluster = require("cluster");

class Channel {
    /** Is the process a master? */
    static get isMaster() {
        return cluster.isMaster;
    }

    /** Is the process a worker? */
    static get isWorker() {
        return cluster.isWorker;
    }

    /** A reference to cluster. */
    static get cluster() {
        return cluster;
    }

    /**
     * Adds a listener function to an event.
     * @param {String} event The event name.
     *  @param {Function} handler An event handler function, it accepts at
     *  least one parameter, which is the data sent by another worker or the
     *  master process.
     * @return {Channel}
     */
    static on(event, handler) {
        if (cluster.isMaster) {
            cluster.on("message", (worker, msg) => {
                if (msg && msg.event === event) {
                    handler.call(this, ...msg.data);
                }
            });
        } else {
            process.on("message", (msg) => {
                if (msg && msg.event === event) {
                    handler.call(this, ...msg.data);
                }
            });
        }
        return this;
    }

    /**
     * Adds a listener function to an event that will only run once.
     * @param {String} event The event name.
     * @param {Function} handler An event handler function, it accepts at
     *  least one parameter, which is the data sent by another worker or the
     *  master process.
     * @return {Channel}
     */
    static once(event, handler) {
        if (cluster.isMaster) {
            cluster.once("message", (worker, msg) => {
                if (msg && msg.event === event) {
                    handler.call(this, ...msg.data);
                }
            });
        } else {
            process.once("message", (msg) => {
                if (msg && msg.event === event) {
                    handler.call(this, ...msg.data);
                }
            });
        }
        return this;
    }

    /**
     * Emit a message to the master process or to worker processes.
     * @param {String} event The event name.
     * @param {Any} data A list of data, they will be received by the event
     *  listener.
     * @return {Channel}
     */
    static emit(event, ...data) {
        if (cluster.isMaster) {
            return this.broadcast(event, ...data);
        } else {
            process.send({ event, data });
        }
    }

    /**
     * Emit a message to a specified worker process.
     * @param {Number} id Could be either a worker id or a pid.
     * @param {String} event The event name.
     * @param {Any} data A list of data, they will be received by the event
     *  listener.
     * @return {Channel}
     */
    static emitTo(id, event, ...data) {
        if (cluster.isMaster) {
            if (cluster.workers[id]) {
                cluster.workers[id].send({ event, data });
            } else {
                for (let _id in cluster.workers) {
                    if (cluster.workers[_id].process.pid === id) {
                        cluster.workers[_id].send({ event, data });
                    }
                }
            }
        } else {
            process.send({
                event: "----transmit----",
                data: { receiver: id, event, data }
            });
        }
    }

    /**
     * Broadcast a massage to all worker processes.
     * @param {String} event The event name.
     * @param {Any} data A list of data, they will be received by the event
     *  listener.
     * @return {Channel}
     */
    static broadcast(event, ...data) {
        if (cluster.isMaster) {
            for (let id in cluster.workers) {
                this.emitTo(id, event, ...data);
            }
        } else {
            process.send({
                event: "----broadcast----",
                data: { event, data }
            });
        }
    }
}

if (cluster.isMaster) {
    // Handle transmit and broadcast.
    cluster.on("message", (worker, msg) => {
        if (msg && msg.event == "----transmit----") {
            msg = msg.data;
            Channel.emitTo(msg.receiver, msg.event, ...msg.data);
        } else if (msg && msg.event == "----broadcast----") {
            msg = msg.data;
            Channel.emit(msg.event, ...msg.data);
        }
    });
}

module.exports = Channel;<|endoftext|>


<commit_before>export const OPTIONS = {
  runOnly: {
    type: "tag",
    values: ["wcag2a","wcag2aa"]
  }
};
<commit_msg>Updated to cover wcag 2.1<commit_after>export const OPTIONS = {
  runOnly: {
    type: "tag",
    values: ["wcag21a","wcag21aa","wcag2a","wcag2aa"]
  }
};
<|endoftext|>


<commit_before>
var current_filename = false;
var tests_running = false;

function saveCurrentFile()
{
  if (tests_running)
    return;

  if (current_filename)
  {
    var editor = $j('#editor');
    fileContent(current_filename).setAttribute('value', editor.val());
    fileCaretPos(current_filename).setAttribute('value', editor.caretPos());
    fileScrollLeft(current_filename).setAttribute('value', editor.scrollLeft());
    fileScrollTop(current_filename).setAttribute('value', editor.scrollTop());
  }
}

function loadFile(filename)
{
  if (tests_running)
    return;

  var caret_pos = fileCaretPos(filename).getAttribute('value');
  var scroll_top  = fileScrollTop(filename).getAttribute('value');
  var scroll_left = fileScrollLeft(filename).getAttribute('value');
  var code = fileContent(filename).getAttribute('value');

  var editor = $j('#editor');
  editor.val(code);
  editor.caretPos(caret_pos);
  editor.scrollTop(scroll_top);
  editor.scrollLeft(scroll_left);

  selectFileInFileList(filename);
}

function selectFileInFileList(filename)
{
  // jQuery is much stricter than prototype in what it allows
  // as characters in an id. If a filename has a . in it for
  // example, then prototype works and jQuery does not.
  // One solution to this is to use sha1(filename) as the id
  // of a filename...
  $('radio_' + filename).checked = true;
  $('current_filename').setAttribute('value', filename);

  current_filename = filename;
  var editor = $j('#editor');

  if (filename === 'output') {
    editor.attr('readonly', 'true');
  }
  else {
    editor.removeAttr('readonly');
  }

  if (filename === 'output' || filename === 'cyberdojo.sh')
  {
    $j('#file_op_rename').attr('disabled', true);
    $j('#file_op_delete').attr('disabled', true);
  }
  else
  {
    $j('#file_op_rename').removeAttr('disabled');
    $j('#file_op_delete').removeAttr('disabled');
  }
}

function fileContent(filename)
{
  return $('file_content_for_' + filename);
}

function fileCaretPos(filename)
{
  return $('file_caret_pos_for_' + filename);
}

function fileScrollLeft(filename)
{
  return $('file_scroll_left_for_' + filename);
}

function fileScrollTop(filename)
{
  return $('file_scroll_top_for_' + filename);
}

function sortedFilenames()
{
  filenames = allFilenames();
  sortFilenames(filenames);
  return filenames;
}

function indexOfCurrentFile(filenames)
{
  for(var at = 0; at < filenames.length; at++)
    if (filenames[at] === current_filename)
      var index = at;
  return index;
}

function loadPreviousFile()
{
  var filenames = sortedFilenames();
  var index = indexOfCurrentFile(filenames);
  var previousIndex = (index === 0) ? filenames.length - 1 : index - 1;
  saveCurrentFile();
  loadFile(filenames[previousIndex]);
}

function loadNextFile()
{
  var filenames = sortedFilenames();
  var index = indexOfCurrentFile(filenames);
  var nextIndex = (index === filenames.length - 1) ? 0 : index + 1;
  saveCurrentFile();
  loadFile(filenames[nextIndex]);
}

<commit_msg>converted bit more prototype code to jQuery<commit_after>
var current_filename = false;
var tests_running = false;

function saveCurrentFile()
{
  if (tests_running)
    return;

  if (current_filename)
  {
    var editor = $j('#editor');
    fileContent(current_filename).setAttribute('value', editor.val());
    fileCaretPos(current_filename).setAttribute('value', editor.caretPos());
    fileScrollLeft(current_filename).setAttribute('value', editor.scrollLeft());
    fileScrollTop(current_filename).setAttribute('value', editor.scrollTop());
  }
}

function loadFile(filename)
{
  if (tests_running)
    return;

  var caret_pos = fileCaretPos(filename).getAttribute('value');
  var scroll_top  = fileScrollTop(filename).getAttribute('value');
  var scroll_left = fileScrollLeft(filename).getAttribute('value');
  var code = fileContent(filename).getAttribute('value');

  var editor = $j('#editor');
  editor.val(code);
  editor.caretPos(caret_pos);
  editor.scrollTop(scroll_top);
  editor.scrollLeft(scroll_left);

  selectFileInFileList(filename);
}

function selectFileInFileList(filename)
{
  // can't do $j('radio_' + filename) because filename
  // could contain characters that aren't strictly legal
  // characters in a dom node id
  // NB: This fails if the filename contains a single quote
  $j("[id='radio_" + filename + "']").attr('checked', 'checked');
  $j('#current_filename').attr('value', filename);

  current_filename = filename;
  var editor = $j('#editor');

  if (filename === 'output') {
    editor.attr('readonly', 'true');
  }
  else {
    editor.removeAttr('readonly');
  }

  if (filename === 'output' || filename === 'cyberdojo.sh')
  {
    $j('#file_op_rename').attr('disabled', true);
    $j('#file_op_delete').attr('disabled', true);
  }
  else
  {
    $j('#file_op_rename').removeAttr('disabled');
    $j('#file_op_delete').removeAttr('disabled');
  }
}

function fileContent(filename)
{
  return $('file_content_for_' + filename);
}

function fileCaretPos(filename)
{
  return $('file_caret_pos_for_' + filename);
}

function fileScrollLeft(filename)
{
  return $('file_scroll_left_for_' + filename);
}

function fileScrollTop(filename)
{
  return $('file_scroll_top_for_' + filename);
}

function sortedFilenames()
{
  filenames = allFilenames();
  sortFilenames(filenames);
  return filenames;
}

function indexOfCurrentFile(filenames)
{
  for(var at = 0; at < filenames.length; at++)
    if (filenames[at] === current_filename)
      var index = at;
  return index;
}

function loadPreviousFile()
{
  var filenames = sortedFilenames();
  var index = indexOfCurrentFile(filenames);
  var previousIndex = (index === 0) ? filenames.length - 1 : index - 1;
  saveCurrentFile();
  loadFile(filenames[previousIndex]);
}

function loadNextFile()
{
  var filenames = sortedFilenames();
  var index = indexOfCurrentFile(filenames);
  var nextIndex = (index === filenames.length - 1) ? 0 : index + 1;
  saveCurrentFile();
  loadFile(filenames[nextIndex]);
}

<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const NullDependency = require("./NullDependency");

/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */

class RequireResolveHeaderDependency extends NullDependency {
	constructor(range) {
		super();
		if (!Array.isArray(range)) throw new Error("range must be valid");
		this.range = range;
	}
}

RequireResolveHeaderDependency.Template = class RequireResolveHeaderDependencyTemplate extends NullDependency.Template {
	/**
	 * @param {Dependency} dependency the dependency for which the template should be applied
	 * @param {ReplaceSource} source the current replace source which can be modified
	 * @param {DependencyTemplateContext} templateContext the context object
	 * @returns {void}
	 */
	apply(dependency, source, templateContext) {
		const dep = /** @type {RequireResolveHeaderDependency} */ (dependency);
		source.replace(dep.range[0], dep.range[1] - 1, "/*require.resolve*/");
	}

	applyAsTemplateArgument(name, dep, source) {
		source.replace(dep.range[0], dep.range[1] - 1, "/*require.resolve*/");
	}
};

module.exports = RequireResolveHeaderDependency;
<commit_msg>feat(RequireResolveHeaderDependency): make serializable<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const makeSerializable = require("../util/makeSerializable");
const NullDependency = require("./NullDependency");

/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */

class RequireResolveHeaderDependency extends NullDependency {
	constructor(range) {
		super();

		if (!Array.isArray(range)) throw new Error("range must be valid");

		this.range = range;
	}

	serialize(context) {
		const { write } = context;

		write(this.range);

		super.serialize(context);
	}

	deserialize(context) {
		const { read } = context;

		this.range = read();

		super.deserialize(context);
	}
}

makeSerializable(
	RequireResolveHeaderDependency,
	"webpack/lib/dependencies/RequireResolveHeaderDependency"
);

RequireResolveHeaderDependency.Template = class RequireResolveHeaderDependencyTemplate extends NullDependency.Template {
	/**
	 * @param {Dependency} dependency the dependency for which the template should be applied
	 * @param {ReplaceSource} source the current replace source which can be modified
	 * @param {DependencyTemplateContext} templateContext the context object
	 * @returns {void}
	 */
	apply(dependency, source, templateContext) {
		const dep = /** @type {RequireResolveHeaderDependency} */ (dependency);
		source.replace(dep.range[0], dep.range[1] - 1, "/*require.resolve*/");
	}

	applyAsTemplateArgument(name, dep, source) {
		source.replace(dep.range[0], dep.range[1] - 1, "/*require.resolve*/");
	}
};

module.exports = RequireResolveHeaderDependency;
<|endoftext|>