CH Plaintext HIT Export

Export HIT information in multi-line plain text format.

目前为 2015-06-03 提交的版本。查看 最新版本

// ==UserScript==
// @name          CH Plaintext HIT Export
// @description   Export HIT information in multi-line plain text format.
// @version       1.6c
// @include       https://www.mturk.com/mturk/findhits*
// @include       https://www.mturk.com/mturk/viewhits*
// @include       https://www.mturk.com/mturk/sorthits*
// @include       https://www.mturk.com/mturk/searchbar*selectedSearchType=hitgroups*
// @include       https://www.mturk.com/mturk/viewsearchbar*selectedSearchType=hitgroups*
// @include       https://www.mturk.com/mturk/sortsearchbar*HITGroup*
// @include       https://www.mturk.com/mturk/preview*
// @grant         GM_setClipboard
// @author        Cristo + clickhappier
// @namespace     mturkgrind
// ==/UserScript==


// based on 'IRC Export (reformatted output mod)': https://greasyfork.org/en/scripts/6254-irc-export-reformatted-output-mod


var caps = document.getElementsByClassName('capsulelink');
for (var c = 0; c < caps.length/2; c++){
    button = document.createElement('button');
    button.setAttribute("place",c);
    button.textContent = 'TXT';
    button.style.height = '14px';
    button.style.width = '30px';
    button.style.fontSize = '8px';
    button.style.border = '1px solid';
    button.style.padding = '0px';
    button.style.backgroundColor = 'transparent';
    button.title = 'Click to save Hit information to your clipboard';
    button.addEventListener("click", display, false);
    document.getElementById('capsule'+c+'-0').parentNode.appendChild(button);
}

function getTO(f){
    var toComp = [];
    var toUrl = 'https://mturk-api.istrack.in/multi-attrs.php?ids='+f;
    var toUrl2 = 'https://turkopticon.ucsd.edu/api/multi-attrs.php?ids='+f;
    requestTO = new XMLHttpRequest();
    try{   // first try Miku's TO mirror server (istrack.in)
        requestTO.onreadystatechange = function () {
            if ((requestTO.readyState ===4) && (requestTO.status ===200)) {
                if (requestTO.responseText.split(':').length > 2) {
                    var toInfo = requestTO.responseText.split('{')[3].split('}')[0].split(',');
                    for (var t = 0; t < 4; t++) {
                        var arrTo = toInfo[t].split(':');
                        toComp.push(arrTo[1].substring(1,4));
                    }
                } 
                else { toComp = ['-','-','-','-']; }
            }
        };
        requestTO.open('GET', toUrl, false);
        requestTO.send(null);
        return toComp;
    }
    catch(err){   // if mirror unavailable, try main TO server
        try{
            requestTO.onreadystatechange = function () {
                if ((requestTO.readyState ===4) && (requestTO.status ===200)) {
                    if (requestTO.responseText.split(':').length > 2) {
                        var toInfo = requestTO.responseText.split('{')[3].split('}')[0].split(',');
                        for (var t = 0; t < 4; t++) {
                            var arrTo = toInfo[t].split(':');
                            toComp.push(arrTo[1].substring(1,4));
                        }
                    } 
                    else { toComp = ['-','-','-','-']; }
                }
            };
            requestTO.open('GET', toUrl2, false);
            requestTO.send(null);
            return toComp;
        }
        catch(err){   // if both unavailable, return 'na's
            toComp = ['na','na','na','na'];
            return toComp;
        }
    }
}

// output display box
var txtexportdiv = document.createElement('div');
var txtexporttextarea = document.createElement('textarea');
txtexportdiv.style.position = 'fixed';
txtexportdiv.style.width = '500px';
txtexportdiv.style.height = '255px';
txtexportdiv.style.left = '50%';
txtexportdiv.style.right = '50%';
txtexportdiv.style.margin = '-250px 0px 0px -250px';
txtexportdiv.style.top = '300px';
txtexportdiv.style.padding = '5px';
txtexportdiv.style.border = '2px';
txtexportdiv.style.backgroundColor = 'black';
txtexportdiv.style.color = 'white';
txtexportdiv.style.zIndex = '100';
txtexportdiv.setAttribute('id','txtexport_div');
txtexportdiv.style.display = 'none';
txtexporttextarea.style.padding = '2px';
txtexporttextarea.style.width = '500px';
txtexporttextarea.style.height = '230px';
txtexporttextarea.title = 'Plaintext Export Output';
txtexporttextarea.setAttribute('id','txtexport_text');
txtexportdiv.textContent = 'Plaintext Export: Press Ctrl+C to (re-)copy to clipboard. Click textarea to close.';
txtexportdiv.style.fontSize = '12px';
txtexportdiv.appendChild(txtexporttextarea);
document.body.insertBefore(txtexportdiv, document.body.firstChild);
txtexporttextarea.addEventListener("click", function(){ txtexportdiv.style.display = 'none'; }, false);


function display(e){
    var theButton = e.target;
    theButton.style.backgroundColor = '#CC0000';
    
    var capHand = document.getElementById('capsule'+theButton.getAttribute("place")+'-0');
    var tBodies = capHand.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;
    
    var capReq = tBodies.getElementsByClassName('requesterIdentity')[0].textContent;
    var capReqId = tBodies.getElementsByClassName('requesterIdentity')[0].parentNode.href.split('requesterId=')[1];
    // if on certain types of searches when logged out, more infernal Amazon-meddling may add a useless &state= value to the end of the requester link
    if ( capReqId.indexOf('&state=') > -1 )
    {
        capReqId = capReqId.split('&state')[1];
    }
    
    var capTitle = capHand.textContent.trim();
    capTitle = capTitle.replace(/<(\w+)[^>]*>.*<\/\1>/gi, "").trim();  // addition to strip html tags and their contents, appearing inside the title link (re 10-20-2014 appearance of "<span class="tags"></span>")

    var capGId = 'unavailable';  // handle logged-out export requests for HITs with no preview/notqualified links
    // if hit has a preview or notqualified link
    if ( capHand.parentNode.parentNode.getElementsByClassName('capsulelink')[1].firstChild.nextSibling.href !== '' )  
    {
        // if this is a preview link
        if ( capHand.parentNode.parentNode.getElementsByClassName('capsulelink')[1].firstChild.nextSibling.href.indexOf('preview') > -1 )
        {
            capGId = capHand.parentNode.parentNode.getElementsByClassName('capsulelink')[1].firstChild.nextSibling.href.split('=')[1];
        }
        // if this is a notqualified link
        else if ( capHand.parentNode.parentNode.getElementsByClassName('capsulelink')[1].firstChild.nextSibling.href.indexOf('notqualified') > -1 )
        {
            capGId = capHand.parentNode.parentNode.getElementsByClassName('capsulelink')[1].firstChild.nextSibling.href.split('=')[1];
            // Amazon messed up the notqualified links, now looking like https://www.mturk.com/mturk/notqualified?hitGroupId=3ID43DSF4IQ1X8LO308D15ZSD5J5GX&hitId=3ID43DSF4IQ1X8LO308D15ZSD5J5GX ; this and the above split happening on = instead of a specific value address that
            capGId = capGId.replace("&hitId", "").replace("&hitGroupId", "");  // added a removal of hitGroupId too since Amazon flipped the order of these on 6/2/15
        }
        // if this is a requestqualification link we shouldn't be on, but are anyway because of stuff Amazon screwed with on 6/2/15
        else if ( capHand.parentNode.parentNode.getElementsByClassName('capsulelink')[1].firstChild.nextSibling.href.indexOf('requestqualification') > -1 )
        {
            // go to the next link, the "(why?)" notqualified link instead
            capGId = capHand.parentNode.parentNode.getElementsByClassName('capsulelink')[1].firstChild.nextSibling.nextElementSibling.href.split('=')[1];
            // Amazon messed up the notqualified links, now looking like https://www.mturk.com/mturk/notqualified?hitGroupId=3ID43DSF4IQ1X8LO308D15ZSD5J5GX&hitId=3ID43DSF4IQ1X8LO308D15ZSD5J5GX ; this and the above split happening on = instead of a specific value address that
            capGId = capGId.replace("&hitId", "").replace("&hitGroupId", "");  // added a removal of hitGroupId too since Amazon flipped the order of these on 6/2/15
        }
    }
    
    var capRew = tBodies.getElementsByClassName('reward')[0].textContent;

    var capTime = tBodies.getElementsByClassName('capsule_field_text')[2].textContent;

    var capAvailable = tBodies.getElementsByClassName('capsule_field_text')[4].textContent;

    var qualList = document.getElementById('capsule'+theButton.getAttribute("place")+'target').getElementsByTagName('tbody')[2];
    if ( document.location.href.indexOf('?last_hits_previewed') > -1 ) { qualList = document.getElementById('capsule'+theButton.getAttribute("place")+'target').getElementsByTagName('tbody')[1]; }
    var qualColl = qualList.getElementsByTagName('td');
    var qualStart = 3;
    if ( document.getElementById('lnkWorkerSignin') ) { qualStart = 1; }  // handle logged-out export requests - difference in qual table coding
    if ( document.location.href.indexOf('?last_hits_previewed') > -1 ) { qualStart = 2; }
    var masterStat = '';
    for ( var m = qualStart; m < qualColl.length; m++ ) {
        if ( qualColl[m].textContent.indexOf('Masters') > -1 ) {
            masterStat = 'MASTERS ';
        }
    }
    
    var capUrl = 'https://www.mturk.com/mturk/preview?groupId='+capGId;
    var capReqUrl = 'https://www.mturk.com/mturk/searchbar?selectedSearchType=hitgroups&requesterId='+capReqId;
    var hitLinkUnav = '';
    if ( capGId == 'unavailable' ) { capUrl = capReqUrl; hitLinkUnav = " (preview link unavailable)"; }  // handle logged-out export requests for HITs with no preview/notqualified links
    var toLink = 'http://turkopticon.ucsd.edu/'+capReqId;
    var capToStats = getTO(capReqId);


    // additions for plaintext export:
    
    function DST() {    // check if daylight savings time should be adjusted for, from http://www.mresoftware.com/simpleDST.htm
        var today = new Date();
        var yr = today.getFullYear();
        var dst_start = new Date("March 14, "+yr+" 02:00:00"); // 2nd Sunday in March can't occur after the 14th 
        var dst_end = new Date("November 07, "+yr+" 02:00:00"); // 1st Sunday in November can't occur after the 7th
        var day = dst_start.getDay(); // day of week of 14th
        dst_start.setDate(14-day); // Calculate 2nd Sunday in March of this year
        day = dst_end.getDay(); // day of the week of 7th
        dst_end.setDate(7-day); // Calculate first Sunday in November of this year
        if (today >= dst_start && today < dst_end) { //does today fall inside of DST period?
            return true; //if so then return true
        }
        return false; //if not then return false
    }

    var currentDate = new Date();
    var utc = currentDate.getTime() + (currentDate.getTimezoneOffset() * 60000);   // http://www.techrepublic.com/article/convert-the-local-time-to-another-time-zone-with-this-javascript/
    var offset = '';
    if ( DST() == true ) { offset = "-7"; } else { offset = "-8"; }   // adjust Pacific Time's UTC offset for daylight savings time - http://stackoverflow.com/questions/8207655/how-to-get-time-of-specific-timezone-using-javascript/8207708#8207708
    var amazonDate = new Date(utc + (3600000*offset));
    var month = amazonDate.getMonth() + 1;
    var day = amazonDate.getDate();
    var year = amazonDate.getFullYear();
    var hours = amazonDate.getHours();
    if (hours < 10) { hours = '0' + hours; }   // http://stackoverflow.com/questions/6838197/get-local-date-string-and-time-string/6838658#6838658
    var minutes = amazonDate.getMinutes();
    if (minutes < 10) { minutes = '0' + minutes; }
    var dateStr = month + "/" + day + "/" + year + " " + hours + ":" + minutes + " PT";
    
    var capDesc = '"' + tBodies.getElementsByClassName('capsule_field_text')[5].textContent.trim().replace(/(\t)+/g,' ').replace(/(\n)+/g,' ').replace(/(\r)+/g,' ').replace(/(  )+/g,' ').replace(/(\s)+/g,' ') + '"';
    if (capDesc == '""') { capDesc = "none"; }

    var capKeywords = "";
    if ( document.location.href.indexOf('?last_hits_previewed') > -1 ) { capKeywords = "unavailable (exported from Last HITs Previewed)"; }
    else { capKeywords = '"' + tBodies.getElementsByClassName('capsule_field_text')[6].textContent.trim().replace(/(\t)+/g,' ').replace(/(\n)+/g,' ').replace(/(\r)+/g,' ').replace(/(  )+/g,' ').replace(/(\s)+/g,' ') + '"'; }
    if (capKeywords == '""' || capKeywords == '') { capKeywords = "none"; }

    var qualStr = "";
    for ( var q = qualStart; q < qualColl.length; q++ ) {
        if ( ( (qualColl[q].textContent.indexOf('is') > -1) || (qualColl[q].textContent.indexOf('has') > -1) ) && (qualColl[q].textContent.indexOf('You meet this') < 0) && (qualColl[q].textContent.indexOf('Contact the Requester') < 0) ) {
            if (qualStr != "") { qualStr += '   '; }
            qualStr += qualColl[q].textContent.trim().replace(/(\t)+/g,' ').replace(/(\n)+/g,' ').replace(/(\r)+/g,' ').replace(/(  )+/g,' ').replace(/(\s)+/g,' ') + '  \r\n';
        }
    }
    if (qualStr == "") { qualStr = "none  \r\n"; }


    var exString = dateStr + '  \r\n'
                 + masterStat + 'HIT: ' + capTitle + '   - ' + capUrl + hitLinkUnav + '  \r\n'
                 + 'Requester: ' + capReq + '   - ' + capReqUrl + '  \r\n'
                 + 'TO Ratings: ' + 'Pay='+capToStats[1] + ' Fair='+capToStats[2] + ' Comm='+capToStats[0] + ' Speed='+capToStats[3] + '   - ' + toLink + '  \r\n'
                 + 'Time Allotted: ' + capTime + '  \r\n'
                 + 'Reward: ' + capRew + '  \r\n'
                 + 'HITs Available: ' + capAvailable + '  \r\n'
                 + 'Description: ' + capDesc + '  \r\n'
                 + 'Keywords: ' + capKeywords + '  \r\n'
                 + 'Qualifications: ' + qualStr + '  \r\n' ;
    if (GM_setClipboard) { GM_setClipboard(exString); }
    window.setTimeout(function(){ theButton.style.backgroundColor = 'transparent'; }, 500);
    txtexporttextarea.textContent = exString;
    txtexportdiv.style.display = 'block';
    txtexporttextarea.select();
}