JR Mturk Panda Crazy Queue Helper

A script add on for Panda Crazy for displaying queue and sorting queue after submitting hits.

目前为 2016-09-23 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name JR Mturk Panda Crazy Queue Helper
  3. // @version 0.2.5
  4. // @namespace https://greasyfork.org/users/6406
  5. // @description A script add on for Panda Crazy for displaying queue and sorting queue after submitting hits.
  6. // @include http*://*mturk.com/mturk/myhits*
  7. // @include http*://*mturk.com/mturk/findhits*
  8. // @include http*://*mturk.com/mturk/sorthits*
  9. // @include http*://*mturk.com/mturk/viewhits*
  10. // @include http*://*mturk.com/mturk/continue*
  11. // @include http*://*mturk.com/mturk/accept*
  12. // @include http*://*mturk.com/mturk/previewandaccept*
  13. // @include http*://*mturk.com/mturk/submit
  14. // @include http*://*mturk.com/mturk/return*
  15. // @include http*://*mturk.com/mturk/*searchbar*
  16. // @include http*://*mturk.com/mturk/return?requesterId=*
  17. // @include http*://*mturk.com/mturk/preview?prevRequester=*
  18. // @exclude http*://*mturk.com/mturk/findhits?*hit_scraper*
  19. // @require http://code.jquery.com/jquery-2.1.4.min.js
  20. // @grant GM_getValue
  21. // @grant GM_setValue
  22. // ==/UserScript==
  23.  
  24. var gScriptVersion="0.2.5";
  25. var gScriptName="pandacrazy";
  26. var gLocation=window.location.href;
  27. var gPandaCrazyLives=false, gHitExternalNextLink="", gHitExternalNextAcceptLink="";
  28. var gPandaCrazyVersion=-1, gHitReturnLink="", gThisJob=null, gNoHits=false, gButtonSet=false;
  29. var gQueueData=null, gPE=0, gTabTextMode=0, gTabTextTimer=0, gOriginalTitle=document.title, gCurrentPostion = 1;
  30. var gSubmitHitID="", gReturnedHitID=""; // previous hitID submitted ore returned just to be sure it doesn't get redone.
  31. var gCorrectVersion=false, gQueueSessOptions=null, gSubmitButton=null, gHitId=null, gIdNum=-1, gTabHitIds={};
  32. var gThisTarget=(new Date().getTime()) + "_JRID"; // target ID from the time to distinguish different scripts and tabs.
  33. var gThisId=-1; // ID number to represent the id in the Panda Crazy external data number ID just for separating messages.
  34. var gQueueSessOptionsDef={"nextPosition":"--","nextIsLast":false,"nextIsSame":false,"tabNum":-1};
  35. var gQueueLocalOptionsDef={"displayTabTitleQ":true,"displayTabTitleTime":true,"displayTabTitleName":true,"displayTabTitleToggle":false,"toggleTime":4000};
  36. var jobDataDefault={"requesterName":"","requesterId":"","groupId":"","pay":"","title":"","duration":"0","hitsAvailable":0,"timeLeft":"","totalSeconds":0,"hitId":"",
  37. "continueURL":"","returnURL":"","durationParsed":null,"jobNumber":"-1","friendlyRName":"","friendlyTitle":"","assignedOn":"","description":"","qual":"","keywords":""};
  38.  
  39. function createDiv(theHtml) { var inner = (theHtml) ? theHtml : ""; return $('<div>').html(inner); }
  40. function createSpan(theHtml) { var inner = (theHtml) ? theHtml : ""; return $('<span>').html(inner); }
  41. function createButton(theText) { var inner = (theText) ? theText : ""; return $('<button>').html(inner); }
  42. function createSelect(theId,theOptions) {
  43. var theSelect = $('<select>').attr({"id":theId});
  44. for (var i=0,len=theOptions.length; i<len; i++) { theSelect.append("<option value='" + theOptions[i] + "'>" + theOptions[i] + "</option>"); }
  45. return theSelect;
  46. }
  47. function createSelectNumbers(theId,first,min,max) {
  48. var buildArray = [first];
  49. for (var i=min; i<=max; i++) { buildArray.push(i) }
  50. return createSelect(theId,buildArray);
  51. }
  52. function parseVersionString (str) {
  53. var x = str.split('.');
  54. var maj = parseInt(x[0]) || 0; var min = parseInt(x[1]) || 0; var pat = parseInt(x[2]) || 0;
  55. return {"major": maj, "minor": min, "patch": pat};
  56. }
  57. (function($){ $.fn.disableSelection = function() { return this.attr('unselectable', 'on').css('user-select', 'none').on('selectstart', false); }; })(jQuery);
  58. function saveSessionData() {
  59. sessionStorage.setItem("JR_PC_QueueHelper",JSON.stringify(gQueueSessOptions));
  60. }
  61. function loadSessionData() {
  62. gQueueSessOptions = JSON.parse(sessionStorage.getItem('JR_PC_QueueHelper'));
  63. if (!gQueueSessOptions) {
  64. sessionStorage.setItem("JR_PC_QueueHelper",JSON.stringify(gQueueSessOptionsDef));
  65. gQueueSessOptions = JSON.parse(sessionStorage.getItem('JR_PC_QueueHelper'));
  66. }
  67. }
  68. function checkVersion(str) {
  69. var versionObj = parseVersionString(str);
  70. if (versionObj.major>=0 && versionObj.minor>=3 && versionObj.patch>=7) return true;
  71. else return false;
  72. }
  73. function checkMode() {
  74. if (gLocation.indexOf("mturk/myhits") != -1) return "queuePageStart";
  75. else if (gLocation.indexOf("mturk/continue") != -1) return "hitPage";
  76. else if (gLocation.indexOf("mturk/accept") != -1 && gLocation.indexOf("&prevHitSubmitted=") != -1) return "accepted";
  77. else if (gLocation.indexOf("mturk/previewandaccept") != -1) return "accepted";
  78. else if (gLocation.indexOf("mturk/preview") != -1) return "previewPage";
  79. else if (gLocation.indexOf("mturk/submit") != -1) return "submitPage";
  80. else if (gLocation.indexOf("mturk/return") != -1) return "returnPage";
  81. return null;
  82. }
  83. function getTimeLeft(theTime) {
  84. if (theTime) {
  85. var tempArray = (theTime.indexOf("second") != -1) ? theTime.split("second")[0].trim().split(" ") : null;
  86. var seconds = (tempArray) ? tempArray[tempArray.length-1] : "0";
  87. tempArray = (theTime.indexOf("minute") != -1) ? theTime.split("minute")[0].trim().split(" ") : null;
  88. var minutes = (tempArray) ? tempArray[tempArray.length-1] : "0";
  89. tempArray = (theTime.indexOf("hour") != -1) ? theTime.split("hour")[0].trim().split(" ") : null;
  90. var hours = (tempArray) ? tempArray[tempArray.length-1] : "0";
  91. tempArray = (theTime.indexOf("day") != -1) ? theTime.split("day")[0].trim().split(" ") : null;
  92. var days = (tempArray) ? tempArray[tempArray.length-1] : "0";
  93. tempArray = (theTime.indexOf("week") != -1) ? theTime.split("week")[0].trim().split(" ") : null;
  94. var weeks = (tempArray) ? tempArray[tempArray.length-1] : "0";
  95. return( {"weeks":weeks,"days":days,"hours":hours,"minutes":minutes,"seconds":seconds} );
  96. } else return null;
  97. }
  98. function grabJobData() {
  99. var jobData = jQuery.extend(true, {}, jobDataDefault);
  100. var capsulelinkElement = $("form[name='hitForm'] .capsulelink_bold");
  101. var tableInfo = $(capsulelinkElement).closest("table").closest("table");
  102. jobData.groupId = (gLocation.indexOf("groupId=") != -1) ? gLocation.split("groupId=")[1].split("&")[0] : "";
  103. jobData.title = $(capsulelinkElement).find("div").text().trim();
  104. jobData.requesterId = ($("input[name='requesterId']:first").val()) ? $("input[name='requesterId']:first").attr("value") : "";
  105. jobData.requesterName = $("#requester\\.tooltip").closest("td").next().text().trim();
  106. jobData.duration = $("#time_left\\.tooltip").closest("td").next().text().trim();
  107. jobData.hitsAvailable = $("#number_of_hits\\.tooltip").closest("td").next().text().trim();
  108. jobData.pay = $("#reward\\.tooltip").closest("td").next().text().replace("$","").replace(" per HIT","").trim();
  109. return jobData;
  110. }
  111. function createMessageData(command,data,target,idNum,tabNum) { return {"time":(new Date().getTime()),"command":command,"data":data,"theTarget":target,"idNum":idNum,"tabNum":tabNum}; }
  112. function sendCommandMessage(data) { localStorage.setItem("JR_message_" + gScriptName, JSON.stringify(data)); }
  113.  
  114. function sendMessageData(command,theData) {
  115. var messageData = createMessageData(command,theData);
  116. sendCommandMessage(messageData);
  117. }
  118. function sendPingMessage(target) { localStorage.setItem("JR_message_ping_" + gScriptName, JSON.stringify(createMessageData("areYouThere",null,target,null,null))); }
  119. function sendPongMessage(target,idNum) { localStorage.setItem("JR_message_pong_" + gScriptName, JSON.stringify(createMessageData("iAmHere",null,target,idNum,null))); }
  120. function sendQueuePageMessage(target,idNum) { localStorage.setItem("JR_message_" + gScriptName, JSON.stringify(createMessageData("getQueueData",null,target,idNum,null))); }
  121. function sendSubmittedMessage(target,thisHitID,idNum) { localStorage.setItem("JR_message_" + gScriptName,
  122. JSON.stringify(createMessageData("submitted",{"hitId":thisHitID},target,idNum,null))); }
  123. function sendAcceptedHitMessage(target,thisJobData,idNum) { localStorage.setItem("JR_message_" + gScriptName,
  124. JSON.stringify(createMessageData("acceptedhit",{"jobData":thisJobData},target,idNum,null))); }
  125. function sendDoingHitMessage(tabNum,target,thisHitID,mode,idNum) { localStorage.setItem("JR_message_" + mode + "_" + tabNum + "_" + gScriptName,
  126. JSON.stringify(createMessageData("doinghit",{"hitId":thisHitID},target,idNum,tabNum))); }
  127. function setSessionData() {
  128. if ($("#JRPCQAdvSel").length) gQueueSessOptions.nextPosition = $("#JRPCQAdvSel").val();
  129. if ($("#JRPCQEasySel").val()=="Last") gQueueSessOptions.nextIsLast = true; else gQueueSessOptions.nextIsLast = false;
  130. saveSessionData();
  131. }
  132. function setupOptionsMenu() {
  133. var insideContent = $("#JR_QueueOptionsMenu");
  134. createDiv("Queue Options Window").css({"font-size":"22px","text-align":"center","margin":"8px 0"}).appendTo(insideContent);
  135. var optionsArea = createDiv("").css({"font-size":"14px","text-align":"left","padding":"0 10px","margin-top":"20px"}).appendTo(insideContent);
  136. createDiv(createSpan("Go to the ").append(createSelect("JRPCQEasySel",["First","Last","--"])).append(createSpan(" hit in the queue."))).appendTo(optionsArea);
  137. createDiv(createSpan("Go to the next hit at position ").css({}).append(createSelectNumbers("JRPCQAdvSel","--",1,24)).append(createSpan(" in the queue."))).appendTo(optionsArea);
  138. createButton("OK").css({"margin-top":"40px","margin-left":"10px"}).click(function() { toggleOptionsMenu(); }).appendTo(optionsArea);
  139. $("#JRPCQEasySel").val((gQueueSessOptions.nextPosition!="--") ? "--" : ((gQueueSessOptions.nextIsLast) ? "Last" : "First"));
  140. $("#JRPCQAdvSel").val(gQueueSessOptions.nextPosition);
  141. $("#JRPCQEasySel").change(function() {
  142. if ($(this).val()!="--") $("#JRPCQAdvSel").val("--"); else $("#JRPCQAdvSel").val("1");
  143. setSessionData();
  144. });
  145. $("#JRPCQAdvSel").change(function() {
  146. if ($(this).val()!="--") $("#JRPCQEasySel").val("--"); else $("#JRPCQEasySel").val("First");
  147. setSessionData();
  148. });
  149. }
  150. function toggleOptionsMenu() {
  151. toggleOptionsMenu.display = toggleOptionsMenu.display || false;
  152. toggleOptionsMenu.display = !toggleOptionsMenu.display;
  153. if (toggleOptionsMenu.display) $("#JR_QueueOptionsMenu").show();
  154. else $("#JR_QueueOptionsMenu").hide();
  155. }
  156. function inOtherTabs(hitId) {
  157. var foundIt=false;
  158. $.each(gTabHitIds, function(key,value) { if (value==hitId) foundIt=true; });
  159. return foundIt;
  160. }
  161. function firstOrLast(i) { return (gQueueSessOptions.nextIsLast) ? (i>0) : (i<gQueueData.length-1); }
  162. function letsFindNext() {
  163. }
  164. function findNextHitQueue(currentHitID) {
  165. if (!gQueueData || currentHitID === "") return null;
  166. var i=-1, skip=false, targetHit=null, nextPosition = (gQueueSessOptions.nextPosition=="--") ? -2 : parseInt(gQueueSessOptions.nextPosition);
  167. var getLast = (gQueueSessOptions.nextIsLast) ? true : ((nextPosition>gQueueData.length-1) ? true : false);
  168. for (j=0,len=gQueueData.length;j<len;j++) {
  169. if (gQueueData[j].hitId==currentHitID) gCurrentPostion = j+1; // find the currenthit position
  170. if (!targetHit || getLast) { // if targethit not found or looking for last hit then keep on looking for target hit!
  171. if (gQueueData[j].hitId!=currentHitID && gQueueData[j].hitId!=gSubmitHitID && gQueueData[j].hitId!=gReturnedHitID && !inOtherTabs(gQueueData[j].hitId))
  172. targetHit = gQueueData[j];
  173. if (!getLast && nextPosition>0 && j<nextPosition) targetHit=null;
  174. }
  175. }
  176. if (!targetHit && gQueueData.length>1 && !getLast) targetHit = gQueueData[0];
  177. else if (!targetHit && gQueueData.length>1 && getLast) targetHit = gQueueData[gQueueData.length-1];
  178. return targetHit;
  179. }
  180. function findNextPositionQueue(position) {
  181. }
  182. function queuePageStart() {
  183. var sortresultsForm = $("#sortresults_form");
  184. var hitsText = $(sortresultsForm).prev();
  185. $(hitsText).find(".title_orange_text_bold").append(createSpan("PC Enhanced Mode").css({"font-size":"11px","padding":"1px 2px","background-color":"#FFE4C4","color":"black"}));
  186. var lastTables = $(sortresultsForm).next().next();
  187. var displayHits = $(sortresultsForm).next();
  188. var afterThis = $("#subtabs_and_searchbar");
  189. theMainContainer = createDiv().attr({"id":"JRMQContainer"}).insertAfter(afterThis);
  190. //createDiv().attr({"id":"JRNormalQueue"}).append(hitsText).append(displayHits).appendTo(theMainContainer);
  191. createDiv().attr({"id":"JRNormalQueue"}).appendTo(theMainContainer);
  192. $("#JRNormalQueue").append($(hitsText));
  193. $("#JRNormalQueue").append($(sortresultsForm));
  194. $("#JRNormalQueue").append($(displayHits));
  195. createDiv().attr({"id":"JREnhancedQueue1"}).appendTo(theMainContainer);
  196. createDiv().attr({"id":"JREnhancedQueue"}).appendTo(theMainContainer);
  197. gScriptMode = "queuePage";
  198. }
  199. function mainListener(e) {
  200. var returnedStorage = JSON.parse(e.newValue);
  201. if (!gPandaCrazyLives) {
  202. if ( e.key.substring(0,16) == 'JR_message_pong_' && e.key.substr(e.key.length - gScriptName.length) == gScriptName && // Receiving correct message from Panda Crazy
  203. returnedStorage && returnedStorage.theTarget == gThisTarget) { // Receiving as a targeted message for first message from Panda Crazy
  204. gPandaCrazyLives = true; // We now know Panda Crazy is running.
  205. gPandaCrazyVersion = returnedStorage.version;
  206. gIdNum = returnedStorage.idNum;
  207. gQueueSessOptions.tabNum = (gQueueSessOptions.tabNum!=-1) ? gQueueSessOptions.tabNum : gIdNum;
  208. saveSessionData();
  209. gCorrectVersion = checkVersion(gPandaCrazyVersion);
  210. if (gScriptMode=="queuePageStart" || gScriptMode=="hitPage" || gScriptMode=="previewPage") { sendQueuePageMessage(gThisTarget,gIdNum); }
  211. }
  212. } else if (gHitId!="" && e.key.substring(0,13) == "JR_message_D_") {
  213. var messageSender = returnedStorage.tabNum;
  214. if (messageSender && messageSender!=-1) delete gTabHitIds[messageSender];
  215. } else if (gHitId!="" && e.key.substring(0,13) == "JR_message_S_") {
  216. var messageSender = returnedStorage.tabNum;
  217. gTabHitIds[messageSender] = returnedStorage.data.hitId;
  218. sendDoingHitMessage(gQueueSessOptions.tabNum,gThisTarget,gHitId,"R",gIdNum);
  219. } else if (gHitId!="" && e.key.substring(0,13) == "JR_message_R_") {
  220. var messageSender = returnedStorage.tabNum;
  221. gTabHitIds[messageSender] = returnedStorage.data.hitId;
  222. } else if (gCorrectVersion && returnedStorage && returnedStorage.data) { // Panda Crazy is up and running with correct version.
  223. if (e.key.substring(0,11) == 'JR_message_' && e.key.substr(e.key.length - gScriptName.length) == gScriptName && // Receiving correct message from Panda Crazy
  224. (returnedStorage.theTarget == gThisTarget || returnedStorage.theTarget===null)) { // Receiving as a targeted message or a ping message
  225. if (returnedStorage.data.queue) gQueueData = returnedStorage.data.queue;
  226. if (returnedStorage.data.PE) gPE = returnedStorage.data.PE;
  227. if (returnedStorage.command=="ping") sendPongMessage(gThisTarget,gIdNum);
  228. if (gScriptMode=="queuePageStart") queuePageStart();
  229. else if (gSubmitButton.length && (gScriptMode=="hitPage" || gScriptMode=="previewPage")) { //console.log("This is a hitpage so looking for next target hit");
  230. if (!gButtonSet) {
  231. gButtonSet = true;
  232. createDiv("Q").css({"position":"fixed","width":"10px","text-align":"right","height":"20px","top":"22px","right":"2px","float":"right","padding":"0px 3px","background-color":"black","color":"white","opacity": "0.3","cursor":"pointer"}).disableSelection().click(function() { toggleOptionsMenu(); }).appendTo("body");
  233. createDiv("").css({"position":"fixed","width":"400px","height":"200px","top":"25px","right":"20px","float":"right","background-color":"#eceadf","border":"3px solid #000"}).attr({"id":"JR_QueueOptionsMenu"}).hide().appendTo("body");
  234. setupOptionsMenu();
  235. if (gHitId!="") sendDoingHitMessage(gQueueSessOptions.tabNum,gThisTarget,gHitId,"S",gIdNum);
  236. }
  237. targetHit = findNextHitQueue(gHitId);
  238. if (targetHit) {
  239. var realHitId = $("input[name='hitId']").eq(0).val();
  240. if (gHitExternalNextLink) $(gHitExternalNextLink).attr('href', targetHit.continueURL + "&prevsubmithitId=" + gHitId);
  241. if (gHitExternalNextAcceptLink) $(gHitExternalNextAcceptLink).attr('href', targetHit.continueURL + "&prevsubmithitId=" + gHitId);
  242. var theReturnLinkHref = "https://www.mturk.com/mturk/return?hitId=" + realHitId + "&hitNextId=" + targetHit.hitId + "&inPipeline=false";
  243. if (gHitReturnLink.length) $(gHitReturnLink).attr('href', theReturnLinkHref);
  244. } else {
  245. if (gHitExternalNextLink) {
  246. var theNextLinkHref = $(gHitExternalNextLink).attr('href');
  247. if (theNextLinkHref.indexOf("&prevsubmithitId=") == -1) theNextLinkHref = theNextLinkHref + "&prevsubmithitId=" + gHitId;
  248. $(gHitExternalNextLink).attr('href', theNextLinkHref );
  249. }
  250. }
  251. //console.log("Next link is now: " + $(gHitExternalNextLink).attr('href'));
  252. } else if (gScriptMode=="accepted" && gThisJob===null && !gNoHits) {
  253. gThisJob = grabJobData();
  254. sendAcceptedHitMessage(gThisTarget,gThisJob,idNum)
  255. }
  256. }
  257. }
  258. }
  259. loadSessionData();
  260. gScriptMode = checkMode();
  261. if (gLocation.indexOf("&prevsubmithitId=") != -1) { gSubmitHitID = (gLocation.split("&prevsubmithitId=")[1]).split("&")[0]; sendSubmittedMessage(gThisTarget,gSubmitHitID,gIdNum); }
  262. if (gScriptMode=="returnPage") {
  263. gReturnedHitID = (gLocation.indexOf("hitId=") != -1) ? (gLocation.split("hitId=")[1]).split("&")[0] : "";
  264. sendSubmittedMessage(gThisTarget,gReturnedHitID,gIdNum);
  265. }
  266. if (gScriptMode=="submitPage" || gScriptMode=="returnPage") {
  267. var goToNext = "";
  268. if (gLocation.indexOf("&hitNextId=") != -1) goToNext = "https://www.mturk.com/mturk/continue?hitId=" + (gLocation.split("&hitNextId=")[1]).split("&")[0];
  269. if (goToNext!=="") {
  270. $("body").html("");
  271. setTimeout(function() { window.location.replace(goToNext); },1100);
  272. }
  273. }
  274. if (gScriptMode) {
  275. window.addEventListener("storage", mainListener, false);
  276. gHitReturnLink = $("a[href*='mturk/return']");
  277. gNoHits = ($("#alertboxMessage").length) ? ( (($("#alertboxMessage").html()).indexOf("There are no HITs in this group available") != -1) ? true : false) : false;
  278. gSubmitButton = document.getElementsByName("/submit");
  279. if (gSubmitButton.length) {
  280. gHitExternalNextLink = $("#hitExternalNextLink");
  281. gHitExternalNextAcceptLink = $("#hitExternalNextAcceptLink");
  282. var hitIdNode = $("input[name='hitId']");
  283. gHitId = (hitIdNode.length>0) ? $("input[name='hitId']").eq(0).val() : "";
  284. setInterval( function() {
  285. var theTimer = $("#theTime").text();
  286. var theTimeLeftNode = $("a#time_left\\.tooltip");
  287. var theDuration = (theTimeLeftNode.length) ? theTimeLeftNode.parent().next().html().trim() : "";
  288. var requesterTip = $("#requester\\.tooltip");
  289. var requesterNameNode = (requesterTip.length) ? $(requesterTip).parent().next() : null;
  290. var theRequesterName = (requesterNameNode.length) ? $(requesterNameNode).text().trim() : "";
  291. gTabTextTimer+=1000;
  292. if (gTabTextTimer>=4000) {
  293. gTabTextTimer=0;
  294. gTabTextMode = 1 - gTabTextMode;
  295. }
  296. //if (gQueueData===null) document.title = "( " + theTimer + " of " + theDuration + " ) " + theRequesterName + " :: " + gOriginalTitle;
  297. if (gQueueData!==null) document.title = "( " + gCurrentPostion + "/" + gQueueData.length + " ) " + theRequesterName + " :: " + gOriginalTitle;
  298. },1000);
  299. }
  300. window.onbeforeunload = function() { sendDoingHitMessage(gQueueSessOptions.tabNum,gThisTarget,gHitId,"D",gIdNum); };
  301. sendPingMessage(gThisTarget);
  302. }