Mturk Hourly

Record time spent working on HITs.

От 28.08.2017. Виж последната версия.

// ==UserScript==
// @name        Mturk Hourly
// @author      @Kerek
// @description Record time spent working on HITs.
// @match       https://www.mturk.com/mturk/accept*
// @match       https://www.mturk.com/mturk/preview*
// @match       https://www.mturk.com/mturk/continue*
// @match       https://www.mturk.com/mturk/submit
// @match       https://www.mturk.com/mturk/return*
// @match       https://www.mturk.com/mturk/statusdetail*
// @match https://www.mturk.com/mturk/dashboard
// @match https://worker.mturk.com/mturk_hourly_sync_page
// @match  https://worker.mturk.com/projects/*/tasks/*assignment_id=*
// @require     http://code.jquery.com/jquery-latest.min.js
// @version     1.3
// @grant       GM_getValue
// @grant       GM_setValue
// @namespace https://greasyfork.org/users/11205
// ==/UserScript==

if (window.location.href == "https://www.mturk.com/mturk/dashboard" && $("#total_earnings_amount").length){
    //Sync_Worker_Data();
    Remove_Old_Data()
    Todays_Projected_Earnings();
}
else if (window.location.href == 'https://worker.mturk.com/mturk_hourly_sync_page'){

    var hit_time_items = [];
    for(var i=0, len=localStorage.length; i<len; i++) {
        var key = localStorage.key(i);
        var value = localStorage[key];
        if(key.indexOf('time_data.') > -1){
            hit_time_items.push(key);
            window.opener.postMessage(key + 'SYNCWORKERSPLIT' + value,'https://www.mturk.com/');
        }
        for (i=0;i<hit_time_items.length;i++){
            localStorage.removeItem(hit_time_items[i]);
        }
    }
    GM_setValue('mturk_hourly_new_worker_hits','no');
    window.opener.postMessage('close','https://www.mturk.com/');

}
else if (window.location.href.indexOf('worker.mturk.com') > -1){
    var hit_returned = false;
    if(typeof(Storage)!=="undefined")
    {
        $('button:contains("Return")').click(function(){
            hit_returned = true;
        });
        store_data('openWorker');
        window.addEventListener('beforeunload', function(){
            GM_setValue('mturk_hourly_new_worker_hits','yes');
            store_data('closeWorker');
        });
    }
}
else{
    var hit_returned = false;
    if(typeof(Storage)!=="undefined")
    {
        $('img[src="/images/return_hit.gif"]').parent().click(function(){
            hit_returned = true;
        });
        store_data('open');
        window.addEventListener('beforeunload', function(){store_data('close');});

        var $requesters = $('td[class="statusdetailRequesterColumnValue"]');
        if ($requesters.length > 0)
        {
            $requesters.each(function(){
                var hitId = $(this).find('a[href^="/mturk/contact?"]').attr('href').match(/[A-Z0-9]{30}/);
                var $feedback_value = $(this).parent().find('td[class="statusdetailStatusColumnValue"]').next();
                var feedback_str = create_feedback_str(hitId);
                var status_value = $feedback_value.html() + feedback_str;
                $feedback_value.html(status_value);
            });
        }
    }
}

function Todays_Projected_Earnings(){
    var TPEhitLOG = {}; var TPEdetailsLOG = {}; var TPEhourlyLOG = {}; var pe = 0;

    var today = $("a[href^='/mturk/statusdetail?encodedDate']:contains(Today)").eq(0).prop("href");

    var $peTR = $('<div id="TPE_div" class="even" style="display:table-row">');
    var $peTD1 = $('<td class="metrics-table-first-value">');
    var $peTD2 = $('<td>');
    var $peA = $('<a href="javascript:void(0)">Today\'s Projected Earnings</a>');
    var $TPE_details = $('<span style="color: blue; font-size: 10px; cursor: pointer; float: right;">Details<img style="margin-left: 5px;" src="/media/more.gif" border="0/"></span>');
    var $peSPAN = $('<span class="reward">$0.00</span>');
    $("td.metrics-table-first-value:contains(Total Earnings)").parent().after($peTR);
    $peTR.append($peTD1.append($peA,$TPE_details),$peTD2.append($peSPAN));

    var $TPED_table = $('<table style="display: none;" width="760" align="center" cellspacing="0" cellpadding="0">');
    var $TPED_tboday = $('<tbody>');
    var $TPED_tr_1 = $('<tr id="TPE_tr"  height="25px"><td width="10" bgcolor="#7fb4cf" style="padding-left: 10px;"></td><td width="100%" bgcolor="#7fb4cf" class="white_text_14_bold">Today\'s Projected Earnings Details&nbsp;&nbsp;<a id="fourmEXPORT" href="javascript:void(0)" class="whatis" >(Forum Export)</a></td><td width="10" align="right" bgcolor="#7fb4cf"></td></tr>');
    var $TPED_tr_2 = $('<tr><td class="container-content" colspan="3"><table class="metrics-table" width="100%"><tbody><tr><td width="100%"><table class="metrics-table" width="100%"><tbody id="tbody2"></tbody></table></td></tr></tbody></table></td></tr>');
    var $TPED_tr_h = $('<tr class="metrics-table-header-row"><th class="metrics-table-first-header">Requester</th><th>Submitted</th><th>Projected</th><th>Hourly</th></tr>');

    $("#subtabs_and_searchbar").next().next().after($TPED_table);
    $TPED_table.append($TPED_tboday);
    $TPED_tboday.append($TPED_tr_1,$TPED_tr_2);
    $("#tbody2").append($TPED_tr_h);

    $("#fourmEXPORT").click(function(){
        var exportcode = "";
        var bonus = $("#bonus").text();
        if (bonus !== "$0.00"){
            var total = (Number(pe)+Number(bonus.replace(/[^0-9.]/g, ""))).toFixed(2);
            exportcode += "[b]Today's Projected Earnings: $"+Number(pe).toFixed(2)+" + Bonuses: "+bonus+" = $"+total+"[/b]\n";

        }
        else {
            exportcode += "[b]Today's Projected Earnings: $"+Number(pe).toFixed(2)+"[/b]\n";
        }
        exportcode += "[spoiler=Today's Projected Earnings Full Details][table][tr][th][b]Requester[/b][/th][th][b]Submitted[/b][/th][th][b]Projected[/b][/th][/tr]";

        var x_sorted = Object.keys(TPEdetailsLOG).sort(function(a,b){return TPEdetailsLOG[a].reward - TPEdetailsLOG[b].reward;});
        for (var j = x_sorted.length-1; j > -1; j--){
            var xkey = x_sorted[j];
            var x_req = TPEdetailsLOG[xkey].req;
            var x_reqid = TPEdetailsLOG[xkey].reqid;
            var x_submitted = TPEdetailsLOG[xkey].submit;
            var x_reward = Number(TPEdetailsLOG[xkey].reward).toFixed(2);
            if (x_req === "Bonuses"){
                if (x_reward !== "0.00"){
                    exportcode += "[tr][td]"+x_req+"[/td][td]"+x_submitted+"[/td][td]$"+x_reward+"[/td][/tr]\n";
                }
            }
            else {
                exportcode += "[tr][td][url=https://www.mturk.com/mturk/searchbar?selectedSearchType=hitgroups&requesterId="+x_reqid+"]"+x_req+"[/url][/td][td]"+x_submitted+"[/td][td]$"+x_reward+"[/td][/tr]\n";
            }
        }
        exportcode += "[/table][/spoiler]";

        GM_setClipboard(exportcode);
        alert("Forum Export copied to your clipboard.");
    });

    $peA.click(function(){
        if ($peA.text() === "Today's Projected Earnings"){
            var confirmation = confirm("Are you sure you want to recalculate Today's Projected Earnings?");
            if (confirmation === true){
                TPEhitLOG = {}; TPEdetailsLOG = {}; TPEhourlyLOG = {}; pe = 0;
                $("#tbody2").find("tr.odd, tr.even").remove();
                getDATA(today);
                $peSPAN.text("$0.00");
            }
        }
    });

    $TPE_details.click(function(){
        $TPE_details.find("img").attr("src", ($TPE_details.find("img").attr("src") === "/media/more.gif") ? "/media/less.gif" : "/media/more.gif");
        $TPED_table.toggle();
    });

    if (today){
        var date = today.split("encodedDate=")[1];
        if (date === localStorage.TPE_date){
            if (localStorage.TPEhitLOG){
                TPEhitLOG = JSON.parse(localStorage.TPEhitLOG);
            }
            pe = Number(localStorage.TPE_pe) || 0;
            $peSPAN.text("$"+Number(pe).toFixed(2));
            getDATA(localStorage.TPE_lastpage);
        }
        else {
            localStorage.TPE_date = date;
            localStorage.Goal_progress = 0;
            TPEhitLOG = {}; pe = 0;
            $peSPAN.text("$0.00");
            getDATA(today);
        }
    }

    function getDATA(URL){
        var page = URL.match(/Number=([0-9]*)/g);
        if (page){
            $peA.text("Calculating Page "+page.toString().replace(/[^0-9.]/g, ""));
        }
        else {
            localStorage.removeItem("TPEhitLOG");
            localStorage.Goal_progress = 0;
            $peA.text("Calculating Page 1");
        }

        $.get(URL, function(data){
            var $data = $(data);
            var $hits = $data.find("#dailyActivityTable").find("tr[valign='top']");
            var pagereqerr = $data.find("td.error_title:contains(You have exceeded the maximum allowed page request rate for this website.)").length;
            var noactivity = $data.find("#dailyActivityTable").find("td:contains(You have no HIT activity on this day matching the selected status.)").length;
            if ($hits.length){
                console.log("hit length");
                var url = $data.find("a:contains(Next)[href*=statusdetail]").eq(0).prop("href");
                for (var i = 0; i < $hits.length; i++){
                    var req = $hits.eq(i).find("td.statusdetailRequesterColumnValue").text().trim();
                    var title = $hits.eq(i).find("td.statusdetailTitleColumnValue").text().trim();
                    var reward = $hits.eq(i).find("td.statusdetailAmountColumnValue").text().trim();
                    var status = $hits.eq(i).find("td.statusdetailStatusColumnValue").text().trim();
                    var reqid = $hits.eq(i).find("a").prop("href").split("requesterId=")[1].split("&")[0];
                    var hitid = $hits.eq(i).find("a").prop("href").split("HIT+")[1];

                    if (!TPEhitLOG[hitid]){
                        TPEhitLOG[hitid] = {
                            req    : req,
                            title  : title,
                            reward : reward,
                            status : status,
                            reqid  : reqid,
                            hitid  : hitid
                        };
                    }
                }
                if (url){
                    getDATA(url);
                }
                else {
                    pe = 0;
                    for(var key in TPEhitLOG){
                        if (TPEhitLOG[key].status !== "Rejected"){
                            pe += parseFloat(TPEhitLOG[key].reward.replace(/[^0-9.]/g, ""));
                        }
                        if (!TPEdetailsLOG[TPEhitLOG[key].reqid]){
                            TPEdetailsLOG[TPEhitLOG[key].reqid] = {
                                req    : TPEhitLOG[key].req,
                                submit : 1,
                                reward : parseFloat(TPEhitLOG[key].reward.replace(/[^0-9.]/g, "")),
                                reqid  : TPEhitLOG[key].reqid
                            };
                        }
                        else {
                            TPEdetailsLOG[TPEhitLOG[key].reqid].submit = TPEdetailsLOG[TPEhitLOG[key].reqid].submit + 1;
                            TPEdetailsLOG[TPEhitLOG[key].reqid].reward = TPEdetailsLOG[TPEhitLOG[key].reqid].reward + parseFloat(TPEhitLOG[key].reward.replace(/[^0-9.]/g, ""));
                        }
                        if (!TPEhourlyLOG[TPEhitLOG[key].reqid]){
                            var time_data = localStorage.getItem('time_data.' + TPEhitLOG[key].hitid.split('&')[0]);
                            if (time_data != null){
                                var starts = time_data.split("$#$")[3].split('?'); 
                                var last_start = starts[starts.length-1];

                                var stops = time_data.split("$#$")[4].split('?');
                                var last_stop = stops[stops.length-1];

                                if (last_start.length && last_stop.length){
                                    TPEhourlyLOG[TPEhitLOG[key].reqid] = {
                                        req : TPEhitLOG[key].req,
                                        intervals :[[last_start,last_stop]],
                                        totalReward : parseFloat(time_data.split("$#$")[2]),
                                    };
                                }

                            }
                        }
                        else{
                            var time_data = localStorage.getItem('time_data.' + TPEhitLOG[key].hitid.split('&')[0]);
                            if (time_data != null){

                                var starts = time_data.split("$#$")[3].split('?');
                                var last_start = starts[starts.length-1];
                                var stops = time_data.split("$#$")[4].split('?');
                                var last_stop = stops[stops.length-1];
                                if (last_start.length && last_stop.length){

                                    //console.log(TPEhourlyLOG[TPEhitLOG[key].reqid].intervals);
                                    TPEhourlyLOG[TPEhitLOG[key].reqid].intervals.push([last_start,last_stop]);
                                }
                                // console.log(TPEhourlyLOG[TPEhitLOG[key].reqid].intervals);
                                TPEhourlyLOG[TPEhitLOG[key].reqid].totalReward += parseFloat(time_data.split("$#$")[2]);
                            }
                        }
                    }

                    if (!TPEdetailsLOG.bonuses && $("#bonus").length){
                        TPEdetailsLOG.bonuses  = {
                            req    : "Bonuses",
                            submit : "N/A",
                            reward : parseFloat($("#bonus").text().replace(/[^0-9.]/g, "")),
                            reqid  : "N/A"
                        };
                    }
                    else if ($("#bonus").length){
                        TPEdetailsLOG.bonuses.reward = parseFloat($("#bonus").text().replace(/[^0-9.]/g, ""));
                    }

                    var d_sorted = Object.keys(TPEdetailsLOG).sort(function(a,b){return TPEdetailsLOG[a].reward - TPEdetailsLOG[b].reward;});
                    var oddeven = true;
                    var total_intervals = [];
                    var total_recorded_earnings = 0;
                    for (var j = d_sorted.length-1; j > -1; j--){
                        var dkey = d_sorted[j];
                        var d_req = TPEdetailsLOG[dkey].req;
                        var d_submitted = TPEdetailsLOG[dkey].submit;
                        var d_reward = Number(TPEdetailsLOG[dkey].reward).toFixed(2);
                        var d_hourly = "N/A";
                        if (d_req !== "Bonuses" && TPEhourlyLOG[TPEdetailsLOG[dkey].reqid]){
                            var intervals = TPEhourlyLOG[TPEdetailsLOG[dkey].reqid].intervals;
                            var d_intervals_sum = 0;
                            for (i=0;i<intervals.length;i++){
                                d_intervals_sum += (intervals[i][1]-intervals[i][0]);
                            }
                            var d_intervals_avg = d_intervals_sum / intervals.length;
                            var d_intervals = mergeIntervals(intervals);
                            d_intervals = combineIntervals(intervals,Math.max(2*60*1000,Math.min(10*d_intervals_avg,12*60*1000)));
                            total_intervals = total_intervals.concat(d_intervals);
                            total_recorded_earnings += TPEhourlyLOG[TPEdetailsLOG[dkey].reqid].totalReward;
                            var d_time = 0;
                            for (i=0;i<d_intervals.length; i++){
                                var s = new Date(parseInt(d_intervals[i][0]));
                                var f = new Date(parseInt(d_intervals[i][1]));
                                console.log(d_req, i, s.toLocaleTimeString(),f.toLocaleTimeString());
                                d_time += (d_intervals[i][1] - d_intervals[i][0])/(1000*60*60);
                            }
                            d_hourly = "$" + (TPEhourlyLOG[TPEdetailsLOG[dkey].reqid].totalReward / d_time).toFixed(2);
                        }
                        if (oddeven){
                            oddeven = false;
                            $("#tbody2").append('<tr class="odd"><td class="metrics-table-first-value">'+d_req+'</td><td>'+d_submitted+'</td><td><span class="reward">$'+d_reward+'</span></td><td><span class="reward">'+d_hourly+'</span></tr>');
                        }
                        else {
                            oddeven = true;
                            $("#tbody2").append('<tr class="even"><td class="metrics-table-first-value">'+d_req+'</td><td>'+d_submitted+'</td><td><span class="reward">$'+d_reward+'</span><td><span class="reward">'+d_hourly+'</span></td></tr>');
                        }
                    }
                    total_intervals = mergeIntervals(total_intervals);
                    var total_time = 0;
                    for (i=0;i<total_intervals.length; i++){
                        var s = new Date(parseInt(total_intervals[i][0]));
                        var f = new Date(parseInt(total_intervals[i][1]));
                        console.log(i, s.toLocaleTimeString(),f.toLocaleTimeString());
                        total_time += (total_intervals[i][1] - total_intervals[i][0])/(1000*60*60);
                    }

                    var total_hourly = '$' + (total_recorded_earnings/total_time).toFixed(2);
                    var hour = Math.floor(total_time);
                    var min = Math.floor((total_time - hour)*60);
                    var sec = Math.floor((total_time - hour - min/60)*3600);
                    var time_display = (hour>0?pad(hour,2) + ":":"") + pad(min,2) + ":" + pad(sec,2);
                    if (oddeven){
                        oddeven = false;
                        $("#tbody2").append('<tr class="odd"><td class="metrics-table-first-value">Total Hourly</td><td>'+time_display+'</td><td><span class="reward">$'+total_recorded_earnings.toFixed(2)+'</span></td><td><span class="reward">'+total_hourly+'</span></tr>');
                    }
                    else {
                        oddeven = true;
                        $("#tbody2").append('<tr class="even"><td class="metrics-table-first-value">Total Hourly</td><td>'+time_display+'</td><td><span class="reward">$'+total_recorded_earnings.toFixed(2)+'</span><td><span class="reward">'+total_hourly+'</span></td></tr>');
                    }
                    localStorage.TPEhitLOG = JSON.stringify(TPEhitLOG);
                    localStorage.TPE_lastpage = URL;
                    localStorage.TPE_pe = pe;
                    $peA.text("Today's Projected Earnings");
                    $peSPAN.text("$"+Number(pe).toFixed(2));
                    $('#today_total').text("$"+(Number($('#bonus').text().replace('$','')) +Number($('#TPE_div span.reward').text().replace('$',''))).toFixed(2));
                    if ($('#today_total').length)
                        document.title =  $('#today_total').text();
                    localStorage.Goal_percent = ((Number(localStorage.TPE_pe)/Number(localStorage.Goal_goal))*100);
                    localStorage.Goal_progress = Number(pe)-Number(localStorage.Goal_goal);
                    if ($("#goalDIV").length){
                        $("#progress").width(Number(localStorage.Goal_percent)+"%");
                        $("#progressper").text(Number(localStorage.Goal_progress).toFixed(2));
                    }
                    Unsynced();
                }
            }
            else if (noactivity){
                console.log("no activity");
                localStorage.TPE_lastpage = URL;
                localStorage.TPE_pe = 0;
                localStorage.Goal_progress = 0;
                $peA.text("Today's Projected Earnings");
                $peSPAN.text("$0.00");
                $('#today_total').text("$"+(Number($('#bonus').text().replace('$','')) +Number($('#TPE_div span.reward').text().replace('$',''))).toFixed(2));

            }
            else if (pagereqerr) {
                console.log("set timeout");
                setTimeout(function(){ getDATA(URL); }, 2000);
            }
        });
    }

    function Unsynced(){
        var hitscalced = Object.keys(TPEhitLOG).length;
        var submitted  = Number($("a[href^='/mturk/statusdetail?encodedDate']:contains(Today)").eq(0).parent().next().text());

        if (hitscalced < submitted){
            $peSPAN.css({backgroundColor:"red"});
        }
        else {
            $peSPAN.css({backgroundColor:""});
        }
    }
}


function create_feedback_str(hitId)
{
    var time_str = '';
    var time_data = localStorage.getItem('time_data.' + hitId);
    if (time_data === null)
    {
        return time_str;
    }
    var last_start = time_data.split("$#$")[3].split('?');
    last_start = new Date(parseInt(last_start[last_start.length - 1]));
    var last_finish = time_data.split("$#$")[4].split('?');
    last_finish = new Date(parseInt(last_finish[last_finish.length - 1]));

    console.log(time_data, last_start, last_finish, time_data.split("$#$")[3].split('?'), time_data.split("$#$")[4].split('?'));
    var reward = time_data.split("$#$")[2];
    var time_spent = last_finish - last_start;
    var h = Math.floor(time_spent/(1000*60*60));
    var m = Math.floor((time_spent - h*1000*60*60)/(1000*60));
    var s = Math.floor((time_spent - h*1000*60*60 - m*1000*60)/(1000));
    // return time_data;
    //  return time_data.split("$#$")[0] + " - " + time_data.split("$#$")[1]  + " - " + time_data.split("$#$")[2] + "<br>" +last_start*1000 + "<br>" + last_finish*1000;
    return "Opened: " + last_start.toLocaleTimeString() + "<br>Submitted: " + last_finish.toLocaleTimeString()+ "<br>Time: " + (h.length?pad(h,2) + ":":"") + pad(m,2) + ":" + pad(s,2) + "<br>Hourly: $" + (reward/((last_finish-last_start)/(60*60*1000))).toFixed(2) ;

    //  return time_data.split("$#$")[0] + " - " + time_data.split("$#$")[1]  + " - " + time_data.split("$#$")[2] + "<br>" + (new Date(last_start)).toTimeString() + "<br>" + (new Date(last_finish)).toTimeString();
}

function store_data(action_type)
{
    if (action_type.indexOf('Worker') > -1){
        if (!hit_returned){
            var now_in_milliseconds = new Date().getTime();
            var hitReview_hitId = window.location.href.split('tasks/')[1].split('?')[0];
        //                console.log(hitReview_hitId);

            var $data_holder = $('a:contains("HIT Details"):eq(0)').parent().attr('data-react-props');
            var requester_name = JSON.parse($data_holder).modalOptions.requesterName;
            var hit_title = JSON.parse($data_holder).modalOptions.projectTitle;
            var hit_reward = JSON.parse($data_holder).modalOptions.monetaryReward.amountInDollars;
            var open_list = "";
            var close_list = "";
            var stored_copy = localStorage.getItem('time_data.' + hitReview_hitId);
            if (stored_copy !== null){
                open_list = stored_copy.split('$#$')[3];
                close_list = stored_copy.split('$#$')[4];
            }
            if (action_type == "openWorker"){
                open_list += "?" + now_in_milliseconds;
            }
            else if (action_type == "closeWorker"){
                close_list += "?" + now_in_milliseconds;
            }
            var autoapprove_data = requester_name + "$#$" + hit_title + "$#$"+ hit_reward +"$#$" + open_list + "$#$" + close_list;
     //       console.log(autoapprove_data);
            localStorage.setItem('time_data.' + hitReview_hitId, autoapprove_data);

        }
    }
    else{
    var $isAccepted = $('input[type="hidden"][name="isAccepted"][value="true"]');
    if ($isAccepted.length > 0 && !hit_returned)
    {
        var hitReview_hitId = $('form[name="hitForm"][action="/mturk/hitReview"] input[name="hitId"]').val();
        console.log(hitReview_hitId);
        var hit_reward = $('form[name="hitForm"][action="/mturk/submit"] input[name="prevReward"]').val().replace('USD','');
        console.log(hit_reward);
        var requester_name = $('td.capsule_field_title:contains("Requester:"):eq(0)').next().text().trim();
        var hit_title = $('table:contains("Requester:"):eq(0) table:eq(0)').text().trim();
        var now_in_milliseconds = new Date().getTime();
        var open_list = "";
        var close_list = "";
        var stored_copy = localStorage.getItem('time_data.' + hitReview_hitId);
        if (stored_copy !== null){
            open_list = stored_copy.split('$#$')[3];
            close_list = stored_copy.split('$#$')[4];
        }
        if (action_type == "open"){
            open_list += "?" + now_in_milliseconds;
        }
        else{
            close_list += "?" + now_in_milliseconds;
        }
        var autoapprove_data = requester_name + "$#$" + hit_title + "$#$"+ hit_reward +"$#$" + open_list + "$#$" + close_list;
        console.log(autoapprove_data);
        localStorage.setItem('time_data.' + hitReview_hitId, autoapprove_data);
    }
}
}

function mergeIntervals(intervals) {
    // test if there are at least 2 intervals
    if(intervals.length <= 1)
        return intervals;

    var stack = [];
    var top   = null;

    // sort the intervals based on their start values
    intervals = intervals.sort(function (startValue, endValue) {
        if (startValue[0] > endValue[0]) {
            return 1;
        }
        if (startValue[0] < endValue[0]) {
            return -1;
        }
        return 0;
    });

    // push the 1st interval into the stack
    stack.push(intervals[0]);

    // start from the next interval and merge if needed
    for (var i = 1; i < intervals.length; i++) {
        // get the top element
        top = stack[stack.length - 1];

        // if the current interval doesn't overlap with the 
        // stack top element, push it to the stack
        if (top[1] < intervals[i][0]) {
            stack.push(intervals[i]);
        }
        // otherwise update the end value of the top element
        // if end of current interval is higher
        else if (top[1] < intervals[i][1])
        {
            top[1] = intervals[i][1];
            stack.pop();
            stack.push(top);
        }
    }

    return stack;
}

function combineIntervals(intervals, padding) {
    // test if there are at least 2 intervals
    if(intervals.length <= 1)
        return intervals;

    var stack = [];
    var top = null;

    // push the 1st interval into the stack
    stack.push(intervals[0]);

    // start from the next interval and merge if needed
    for (var i = 1; i < intervals.length; i++) {
        // get the top element
        top = stack[stack.length - 1];

        // if the current interval doesn't overlap with the 
        // stack top element, push it to the stack
        if ((intervals[i][0]-top[1]) > padding) {
            //  console.log("hi",(intervals[i][0]-top[1]) + padding);
            stack.push(intervals[i]);
        }
        // otherwise update the end value of the top element
        // if end of current interval is higher
        else if (top[1] < intervals[i][1])
        {
            top[1] = intervals[i][1];
            stack.pop();
            stack.push(top);
        }
    }
    //console.log("stack",stack);
    return stack;
}

function pad(n, width, z) {
    z = z || '0';
    n = n + '';
    return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}

function Sync_Worker_Data(){
    if (GM_getValue('mturk_hourly_new_worker_hits') == 'yes'){
    var w = window.open('https://worker.mturk.com/mturk_hourly_sync_page');
    window.addEventListener('message',function(evt){
        if (evt.data !== "close"){
            if (localStorage.getItem(evt.data.split('SYNCWORKERSPLIT')[0]) === null)
                localStorage.setItem(evt.data.split('SYNCWORKERSPLIT')[0],evt.data.split('SYNCWORKERSPLIT')[1]);
        }
        else
            w.close();
    });
    }
}

function Remove_Old_Data(){
    var today = new Date();
    var date = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate();
    if (localStorage.getItem('mturkHourlyRemovalDate') != date){
        console.log('mturk hourly data older than 45 days has been cleared');
        
 //thanks to @ChrisTurk for the old key removal code:
 Object.keys(localStorage)
         .forEach(function(key){
              if (/^time_data\./.test(key)) {
                  console.log(localStorage[key]);
                  var stored = localStorage[key].split(/\?/).pop(); // grabs last timestamp from the stack
                    // days * hours * min * sec * 1000 [millisec 'cause of the way its stored]
                    if (($.now()-stored) > 45*24*60*60*1000) {
                        // console.log('older than 45 days');
                         localStorage.removeItem(key); //uncomment this line to delete keys
                    } else { 
                      //  console.log('fresh memes'); //less than 1 day old, don't delete it.
                    }
              }
       });
        localStorage.setItem('mturkHourlyRemovalDate',date);
    }
}