Advertisement
Guest User

Untitled

a guest
Nov 2nd, 2013
236
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name        MTurk HIT DataBase
  3. // @namespace   localhost
  4. // @description Extended ability to search HITs you have worked on and other useful tools (CSV export/import, requester notes, requester block, pending/projected earnings)
  5. // @include     https://www.mturk.com/mturk/searchbar*
  6. // @include     https://www.mturk.com/mturk/findhits*
  7. // @include     https://www.mturk.com/mturk/viewhits*
  8. // @include     https://www.mturk.com/mturk/viewsearchbar*
  9. // @include     https://www.mturk.com/mturk/sortsearchbar*
  10. // @include     https://www.mturk.com/mturk/sorthits*
  11. // @include     https://www.mturk.com/mturk/dashboard
  12. // @include     https://www.mturk.com/mturk/preview?*
  13. // @version     1.7
  14. // @grant       none
  15. // @require     http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js
  16. // @require     http://code.highcharts.com/highcharts.js
  17. // @downloadURL https://userscripts.org/scripts/source/160501.user.js
  18. // @updateURL   https://userscripts.org/scripts/source/160501.user.js
  19. // ==/UserScript==
  20.  
  21. //
  22. // 2012-10-03 0.9.7: This is rewrite of MTurk Extended HIT Search (http://userscripts.org/scripts/show/146277)
  23. //                   with some extra features (and some missing for now: search by date).
  24. //                   It now uses IndexedDB (http://en.wikipedia.org/wiki/Indexed_Database_API)
  25. //
  26. // 2012-10-04 0.9.8: Improved use of indexes, check Pending Payment HITs
  27. //            0.9.9: Minor improvements
  28. //
  29. // 2012-10-04 0.10:  Added date options
  30. //
  31. // 2012-10-07 0.11:  Requester notes, bug fixes
  32. //            0.12:  CSV export
  33. //
  34. // 2012-10-09 0.13: "Block" requesters or specific HITs
  35. //
  36. // 2012-10-10 0.14: Requester Overview, shows summary of all requesters in DB
  37. //
  38. // 2012-10-11 0.15: Blocked HITs are always on bottom of the page
  39. //
  40. // 2012-10-14 0.16: Requester Overview improvements
  41. //
  42. // 2012-10-17 0.17: Bug fixes and error checks
  43. //
  44. // 2012-10-18 0.18: Import HIT data from MTurk Extended HIT Search script
  45. //
  46. // 2012-10-21 0.19: Moved main interface to dashboard, show pending earnings on dashboard,
  47. //                  summary of all requesters with pending HITs.
  48. //
  49. // 2012-10-23 0.20: Added Turkopticon (http://turkopticon.differenceengines.com/) links to overview pages
  50. //            0.21: Fixed overview pages reward to include only 'Paid' and 'Approved - Pending Payment' HITs.
  51. //
  52. // 2012-10-28 0.22: Limited Auto Update.
  53. //            0.23: Minor improvements
  54. //
  55. // 2012-10-30 0.24: Projected earnings for today
  56. //
  57. // 2012-11-02 0.25: Smarter Auto Update
  58. //
  59. // 2012-11-03 0.26: GUI update
  60. //
  61. // 2012-11-05 0.30: Extra non-amazonian script monkeys
  62. //
  63. // 2012-11-06 0.31: Projected earnings progress bar
  64. //
  65. // 2012-11-08 0.32: Minor GUI fixes to look better on Chrome. Looks like it now works on stable Chrome!
  66. //
  67. // 2012-11-13 0.33: Time limits now work with Requester Overview
  68. //
  69. // 2012-11-15 0.34: Bug/compatibility fixes
  70. //
  71. // 2012-11-18 0.40: Daily Overview, update database to use YYYY-MM-DD date format.
  72. //
  73. // 2012-11-22 0.41: R and T button on HIT preview page. Auto-Approval time.
  74. //
  75. // 2012-11-30 0.42: Changes on MTurk pages. Status page in now on one page!
  76. //
  77. // 2012-12-02 1.0: Added @downloadURL and @updateURL
  78. //
  79. // 2012-12-06 1.1: Requester details.
  80. //                 Try to fetch few extra days at first update (not showing on status page).
  81. //
  82. // 2012-12-11 1.2: Import HITs from previously exported CSV-files.
  83. //                 Removed Extended HIT Search import.
  84. //
  85. // 2012-12-13 1.3: Fix CSV-import to put empty string instead if undefined if feedback is empty.
  86. //
  87. // 2012-12-14 1.4: Rewritten database update more properly.
  88. //
  89. // 2012-12-16 1.5: Fixed broken Auto Update (forgot to check that on pervious update).
  90. //
  91. // 2013-02-26 1.6: Fixed IDBTransactionModes for Chrome (note this breaks it for Firefox)
  92. //
  93. // 2013-02-27 1.7: Changed UI bars back to what they used to be.
  94. //
  95.  
  96. var DAYS_TO_FETCH = [];
  97. var DAYS_TO_FETCH_CHECK;
  98.  
  99. var HITStorage = {};
  100. var indexedDB = window.indexedDB || window.webkitIndexedDB ||
  101.                 window.mozIndexedDB;
  102. window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.mozIDBTransaction;
  103. window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.mozIDBKeyRange;
  104. HITStorage.IDBTransactionModes = { "READ_ONLY": "readonly", "READ_WRITE": "readwrite", "VERSION_CHANGE": "versionchange" };
  105. var IDBKeyRange = window.IDBKeyRange;
  106.  
  107. HITStorage.indexedDB = {};
  108. HITStorage.indexedDB = {};
  109. HITStorage.indexedDB.db = null;
  110.  
  111. HITStorage.indexedDB.onerror = function(e) {
  112.   console.log(e);
  113. };
  114. var v = 4;
  115.  
  116. HITStorage.indexedDB.create = function() {
  117.  
  118.   var request = indexedDB.open("HITDB", v);
  119.  
  120.   request.onupgradeneeded = function (e) {
  121.     HITStorage.indexedDB.db = e.target.result;
  122.     var db = HITStorage.indexedDB.db;
  123.     var new_empty_db = false;
  124.  
  125.     if(!db.objectStoreNames.contains("HIT")) {
  126.       var store = db.createObjectStore("HIT", { keyPath: "hitId" });
  127.  
  128.       store.createIndex("date", "date", { unique: false });
  129.       store.createIndex("requesterName", "requesterName", { unique: false });
  130.       store.createIndex("title", "title", { unique: false });
  131.       store.createIndex("reward", "reward", { unique: false });
  132.       store.createIndex("status", "status", { unique: false });
  133.       store.createIndex("requesterId", "requesterId", { unique: false });
  134.  
  135.       new_empty_db = true;
  136.      
  137.       // At first update try to get few extra days that do not show on status page
  138.       localStorage['HITDB TRY_EXTRA_DAYS'] = 'YES';
  139.     }
  140.     if(!db.objectStoreNames.contains("STATS")) {
  141.       var store = db.createObjectStore("STATS", { keyPath: "date" });
  142.     }
  143.     if(!db.objectStoreNames.contains("NOTES")) {
  144.       var store = db.createObjectStore("NOTES", { keyPath: "requesterId" });
  145.     }
  146.     if(!db.objectStoreNames.contains("BLOCKS")) {
  147.       var store = db.createObjectStore("BLOCKS", { keyPath: "id", autoIncrement: true });
  148.  
  149.       store.createIndex("requesterId", "requesterId", { unique: false });
  150.     }
  151.  
  152.     if (new_empty_db == false)
  153.     {
  154.       alert("HIT DataBase date format must be upgraded (MMDDYYYY => YYYY-MM-DD)\n" +
  155.             "Please don't close or reload this page until it's done.\n" +
  156.             "Press OK to start. This shouldn't take long. (few minutes max)" +
  157.             "Sorry for the inconvenience.");
  158.       HITStorage.update_date_format(true);
  159.     }
  160.     db.close();
  161.     //alert("DataBase upgraded to version " + v + '!');
  162.   }
  163.  
  164.   request.onsuccess = function(e) {
  165.     HITStorage.indexedDB.db = e.target.result;
  166.     var db = HITStorage.indexedDB.db;
  167.     db.close();
  168.   };
  169.  
  170.   request.onerror = HITStorage.indexedDB.onerror;
  171. }
  172.  
  173. HITStorage.indexedDB.addHIT = function(hitData) {
  174.   // Temporary extra check
  175.   if (hitData.date.indexOf('-') < 0)
  176.   {
  177.     alert('Wrong date format in addHIT()!');
  178.     return;  
  179.   }  
  180.  
  181. var request = indexedDB.open("HITDB", v);
  182.     request.onsuccess = function(e) {
  183.     HITStorage.indexedDB.db = e.target.result;
  184.       var db = HITStorage.indexedDB.db;
  185.       var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_WRITE);
  186.       var store = trans.objectStore("HIT");
  187.  
  188.       var request = store.put(hitData);
  189.  
  190.       request.onsuccess = function(e) {
  191.         db.close();
  192.       };
  193.  
  194.       request.onerror = function(e) {
  195.       console.log("Error Adding: ", e);
  196.       };
  197.     };
  198.     request.onerror = HITStorage.indexedDB.onerror;
  199. };
  200.  
  201. HITStorage.indexedDB.importHITs = function(hitData) {
  202.   var hits = hitData.length;
  203.   var label = document.getElementById('status_label');
  204.  
  205.   var request = indexedDB.open("HITDB", v);
  206.     request.onsuccess = function(e) {
  207.   HITStorage.indexedDB.db = e.target.result;
  208.     var db = HITStorage.indexedDB.db;
  209.     var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_WRITE);
  210.     var store = trans.objectStore("HIT");
  211.  
  212.     putNextHIT();
  213.  
  214.     function putNextHIT()
  215.     {
  216.       if (hitData.length > 0)
  217.       {
  218.         store.put(hitData.pop()).onsuccess = putNextHIT;
  219.         label.innerHTML = progress_bar(((hits-hitData.length)/hits*50), 50, '█', '█', '#7fb448', 'grey') + ' (' + hitData.length + ')';
  220.       }
  221.       else
  222.       {
  223.         HITStorage.enable_inputs();
  224.         HITStorage.update_status_label('Import done', 'green');
  225.         db.close();
  226.       }
  227.     }
  228.     };
  229.     request.onerror = HITStorage.indexedDB.onerror;
  230. };
  231.  
  232. HITStorage.indexedDB.addHITs = function(hitData, day_to_fetch, days_to_update) {
  233.   var hits = hitData.length;
  234.   if (day_to_fetch)
  235.     var label = document.getElementById('status_label');
  236.  
  237.   var request = indexedDB.open("HITDB", v);
  238.     request.onsuccess = function(e) {
  239.   HITStorage.indexedDB.db = e.target.result;
  240.     var db = HITStorage.indexedDB.db;
  241.     var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_WRITE);
  242.     var store = trans.objectStore("HIT");
  243.  
  244.     putNextHIT();
  245.  
  246.     function putNextHIT()
  247.     {
  248.       if (hitData.length > 0)
  249.       {
  250.         store.put(hitData.pop()).onsuccess = putNextHIT;
  251.         if (day_to_fetch)
  252.           label.innerHTML = 'Saving ' + day_to_fetch.date + ': ' + progress_bar(((hits-hitData.length)/hits*40), 40, '█', '█', '#7fb448', 'grey');
  253.       }
  254.       else
  255.       {
  256.         // move to next day
  257.         if (day_to_fetch)
  258.         {
  259.           HITStorage.indexedDB.updateHITstats(day_to_fetch);
  260.           setTimeout(function() { HITStorage.do_update(days_to_update); }, 2000);
  261.           HITStorage.update_status_label('Please wait: script monkeys are taking naps 😌', 'red');
  262.         }
  263.         db.close();
  264.       }
  265.     }
  266.     };
  267.     request.onerror = HITStorage.indexedDB.onerror;
  268. };
  269.  
  270.  
  271. HITStorage.indexedDB.updateHITstats = function(date)
  272. {
  273.   // Temporary extra check
  274.   if (date.date.indexOf('-') < 0)
  275.   {
  276.     alert('Wrong date format in updateHITstats()!');
  277.     return;
  278.   }
  279.  
  280.   var request = indexedDB.open("HITDB", v);
  281.   request.onsuccess = function(e) {
  282.     HITStorage.indexedDB.db = e.target.result;
  283.     var db = HITStorage.indexedDB.db;
  284.     var trans = db.transaction(["STATS"], HITStorage.IDBTransactionModes.READ_WRITE);
  285.     var store = trans.objectStore("STATS");
  286.  
  287.     var request = store.put(date);
  288.  
  289.     request.onsuccess = function(e) {
  290.       db.close();
  291.     };
  292.  
  293.     request.onerror = function(e) {
  294.       console.log("Error Adding: ", e);
  295.     };
  296.   };
  297.   request.onerror = HITStorage.indexedDB.onerror;
  298. };
  299.  
  300. HITStorage.prepare_update_and_check_pending_payments = function()
  301. {
  302.   var request = indexedDB.open("HITDB", v);
  303.     request.onsuccess = function(e) {
  304.         HITStorage.indexedDB.db = e.target.result;
  305.         var db = HITStorage.indexedDB.db;
  306.         var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  307.         var store = trans.objectStore("HIT");
  308.     var index = store.index('status');
  309.     var range = IDBKeyRange.only('Approved&nbsp;- Pending&nbsp;Payment');
  310.  
  311.     index.openCursor(range).onsuccess = function(event) {
  312.       var cursor = event.target.result;
  313.       if (cursor && DAYS_TO_FETCH.length > 0)
  314.       {
  315.         for (var i=0; i<DAYS_TO_FETCH.length; i++)
  316.         {
  317.           if ( cursor.value.date == DAYS_TO_FETCH[i].date )
  318.           {
  319.             DAYS_TO_FETCH[i].pending_payments = true;
  320.           }
  321.         }
  322.         cursor.continue();
  323.       }
  324.       else
  325.       {
  326.         if (DAYS_TO_FETCH.length>0) {
  327.           db.close();
  328.           HITStorage.update_status_label('Please wait: script monkeys are planning to fetch relevant status pages', 'red');
  329.           setTimeout(function() { HITStorage.prepare_update(); }, 100);
  330.         }
  331.         else
  332.         {
  333.           db.close();
  334.           HITStorage.update_done();
  335.         }
  336.       }
  337.     };
  338.   }
  339. };
  340.  
  341. // check that number of hits in DB matches what is available
  342. HITStorage.check_update = function()
  343. {
  344.   var request = indexedDB.open("HITDB", v);
  345.      request.onsuccess = function(e) {
  346.        HITStorage.update_status_label('Please wait: checking database', 'red');
  347.     HITStorage.indexedDB.db = e.target.result;
  348.     var db = HITStorage.indexedDB.db;
  349.     var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  350.     var store = trans.objectStore("HIT");
  351.     var index = store.index('date');
  352.     var range = IDBKeyRange.bound(DAYS_TO_FETCH_CHECK[DAYS_TO_FETCH_CHECK.length-1].date, DAYS_TO_FETCH_CHECK[0].date, false, false);
  353.  
  354.     index.count(range).onsuccess = function(event) {
  355.       var count = event.target.result;
  356.       var submitted_hits = 0;
  357.      
  358.       for (var i=0; i<DAYS_TO_FETCH_CHECK.length; i++)
  359.       {
  360.         submitted_hits += DAYS_TO_FETCH_CHECK[i].submitted;
  361.       }
  362.  
  363.       if (submitted_hits == count)
  364.       {
  365.         db.close();        
  366.         HITStorage.update_done();
  367.       }
  368.       else
  369.       {
  370.         if (confirm("😞 ERROR! Number of HITs in DataBase does not match number of HITs available! (" + count + " != " + submitted_hits + ")\n"
  371.                    + "Would you like to refetch all status pages now?"))
  372.         {
  373.           db.close();        
  374.           DAYS_TO_FETCH = DAYS_TO_FETCH_CHECK.slice(0);
  375.           HITStorage.update_status_label('Please wait: new script monkeys are fetching relevant status pages', 'red');
  376.           setTimeout(function() { HITStorage.do_update(DAYS_TO_FETCH.length); }, 100);
  377.         }
  378.         else
  379.         {
  380.           db.close();        
  381.           HITStorage.update_done();
  382.         }
  383.       }
  384.     };
  385.   }
  386. };
  387.  
  388. HITStorage.prepare_update = function()
  389. {
  390.   var request = indexedDB.open("HITDB", v);
  391.     request.onsuccess = function(e) {
  392.         HITStorage.indexedDB.db = e.target.result;
  393.         var db = HITStorage.indexedDB.db;
  394.         var trans = db.transaction(["STATS"], HITStorage.IDBTransactionModes.READ_ONLY);
  395.         var store = trans.objectStore("STATS");
  396.     var range = IDBKeyRange.bound(DAYS_TO_FETCH[DAYS_TO_FETCH.length-1].date, DAYS_TO_FETCH[0].date, false, false);
  397.  
  398.     store.openCursor(range).onsuccess = function(event) {
  399.       var cursor = event.target.result;
  400.       if (cursor && DAYS_TO_FETCH.length > 0)
  401.       {
  402.         for (var i=0; i<DAYS_TO_FETCH.length; i++)
  403.         {
  404.           if ( cursor.value.date == DAYS_TO_FETCH[i].date
  405.                 && cursor.value.submitted == DAYS_TO_FETCH[i].submitted
  406.                 && cursor.value.approved == DAYS_TO_FETCH[i].approved
  407.                 && cursor.value.rejected == DAYS_TO_FETCH[i].rejected
  408.                 && cursor.value.pending == DAYS_TO_FETCH[i].pending)
  409.           {
  410.             // This day is already in DB and stats match => no need to fetch
  411.             // unless there are 'Approved - Pending Payment' HITs
  412.             if (DAYS_TO_FETCH[i].pending_payments === undefined || DAYS_TO_FETCH[i].pending_payments == false)
  413.               DAYS_TO_FETCH.splice(i,1);
  414.           }
  415.         }
  416.         cursor.continue();
  417.       }
  418.       else
  419.       {
  420.         if (DAYS_TO_FETCH.length>0) {
  421.           db.close();
  422.           setTimeout(function() { HITStorage.do_update(DAYS_TO_FETCH.length); }, 100);
  423.         }
  424.         else
  425.         {
  426.           db.close();
  427.           HITStorage.update_done();
  428.         }
  429.       }
  430.     };
  431.   }
  432. };
  433.  
  434. HITStorage.indexedDB.term_matches_HIT = function(term, hit)
  435. {
  436.   var keys = ['date', 'requesterName', 'title', 'feedback', 'hitId', 'requesterId'];
  437.   for (var k in keys)
  438.   {
  439.     if (hit[keys[k]] != null && hit[keys[k]].match(term))
  440.     {
  441.       return true;
  442.     }
  443.   }
  444.   return false;
  445. }
  446.  
  447. HITStorage.indexedDB.matchHIT = function(hit, options)
  448. {
  449.   if (options.status == '---' || hit.status.match(options.status))
  450.   {
  451.     if (options.search_term == '' || HITStorage.indexedDB.term_matches_HIT(options.term, hit))
  452.     {
  453.       return true;
  454.     }
  455.   }
  456.   return false;
  457. }
  458.  
  459. function hit_sort_func()
  460. {
  461.   return function(a,b) {
  462.     if (a.date == b.date) {
  463.       if (a.requesterName < b.requesterName)
  464.         return -1;
  465.       if (a.requesterName > b.requesterName)
  466.         return 1;
  467.       if (a.title < b.title)
  468.         return -1;
  469.       if (a.title > b.title)
  470.         return 1;
  471.       if (a.status < b.status)
  472.         return -1;
  473.       if (a.status > b.status)
  474.         return 1;
  475.     }
  476.     if (a.date > b.date)
  477.       return 1;
  478.     if (a.date < b.date)
  479.       return -1;
  480.   };
  481. }
  482.  
  483. HITStorage.indexedDB.getHITs = function(options) {
  484.   var request = indexedDB.open("HITDB", v);
  485.     request.onsuccess = function(e) {
  486.     HITStorage.indexedDB.db = e.target.result;
  487.     var db = HITStorage.indexedDB.db;
  488.     var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  489.     var store = trans.objectStore("HIT");
  490.    
  491.     var req;
  492.     var results = [];
  493.     var index;
  494.     var range;
  495.  
  496.     if (options.from_date || options.to_date)
  497.     {
  498.       if (options.from_date != '' || options.to_date != '')
  499.       {
  500.         index = store.index('date');
  501.         if (options.from_date == options.to_date)
  502.         {
  503.           range = IDBKeyRange.only(options.from_date);
  504.         }
  505.         else if (options.from_date != '' && options.to_date != '')
  506.         {
  507.           range = IDBKeyRange.bound(options.from_date, options.to_date, false, false);
  508.         }
  509.         else if (options.from_date == '' && options.to_date != '')
  510.         {
  511.           range = IDBKeyRange.upperBound(options.to_date, false);
  512.         }
  513.         else
  514.         {
  515.           range = IDBKeyRange.lowerBound(options.from_date, false);
  516.         }
  517.         req = index.openCursor(range);
  518.       }
  519.     }
  520.     else if (options.index && options.index != '')
  521.     {
  522.       index = store.index(options.index);
  523.       range = IDBKeyRange.only(options.term);
  524.       req = index.openCursor(range);
  525.     }
  526.     else if (options.status == 'Rejected' || options.status == 'Pending Approval'
  527.               || options.status == 'Approved' || options.status == 'Paid')
  528.     {
  529.       var s = (options.status == 'Approved')? 'Approved&nbsp;- Pending&nbsp;Payment' : options.status;
  530.       options.index = 'status';
  531.       index = store.index(options.index);
  532.       range = IDBKeyRange.only(s);
  533.       req = index.openCursor(range);
  534.     }
  535.     else
  536.     {
  537.       req = store.openCursor();
  538.     }
  539.    
  540.     req.onsuccess = function(event) {
  541.       var cursor = event.target.result;
  542.       if (cursor) {
  543.         if (HITStorage.indexedDB.matchHIT(cursor.value, options))
  544.           results.push(cursor.value);
  545.  
  546.         cursor.continue();
  547.       }
  548.       else {
  549.         results.sort(hit_sort_func());
  550.  
  551.         if (options.export_csv && options.export_csv == true)
  552.           HITStorage.export_csv(results);
  553.         else
  554.           HITStorage.show_results(results);
  555.        
  556.         if (options.donut == '---')
  557.           document.getElementById('container').style.display = 'none';
  558.         else if (options.donut != '')
  559.           HITStorage.prepare_donut(results, options.donut);
  560.       }
  561.       db.close();
  562.     };
  563.   };
  564.   request.onerror = HITStorage.indexedDB.onerror;
  565. };
  566.  
  567. //
  568. // Show summary of all requesters
  569. //
  570. HITStorage.indexedDB.requesterOverview = function(options) {
  571.   var request = indexedDB.open("HITDB", v);
  572.     request.onsuccess = function(e) {
  573.     HITStorage.indexedDB.db = e.target.result;
  574.     var db = HITStorage.indexedDB.db;
  575.     var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  576.     var store = trans.objectStore("HIT");
  577.     var index;
  578.     var req;
  579.    
  580.     // [ requesterId, requesterName, sum(hits), sum(rewards), rejected, pending ]
  581.     var results = [];
  582.     var tmp_results = {};
  583.     if (options.from_date || options.to_date)
  584.     {
  585.       if (options.from_date != '' || options.to_date != '')
  586.       {
  587.         index = store.index('date');
  588.         if (options.from_date == options.to_date)
  589.         {
  590.           range = IDBKeyRange.only(options.from_date);
  591.         }
  592.         else if (options.from_date != '' && options.to_date != '')
  593.         {
  594.           range = IDBKeyRange.bound(options.from_date, options.to_date, false, false);
  595.         }
  596.         else if (options.from_date == '' && options.to_date != '')
  597.         {
  598.           range = IDBKeyRange.upperBound(options.to_date, false);
  599.         }
  600.         else
  601.         {
  602.           range = IDBKeyRange.lowerBound(options.from_date, false);
  603.         }
  604.         req = index.openCursor(range);
  605.       }
  606.       req.onsuccess = function(event) {
  607.         var cursor = event.target.result;
  608.         if (cursor) {
  609.           var hit = cursor.value;
  610.           var rejected = (hit.status == 'Rejected') ? 1 : 0;
  611.           var pending = (hit.status.match(/Approved|Paid|Rejected/) == null) ? 1 : 0;
  612.           var reward = (pending>0 || rejected>0 )? 0: hit.reward;
  613.  
  614.           if (tmp_results[hit.requesterId] === undefined)
  615.           {
  616.             tmp_results[hit.requesterId] = [];
  617.             tmp_results[hit.requesterId][0] = hit.requesterId;
  618.             tmp_results[hit.requesterId][1] = hit.requesterName;
  619.             tmp_results[hit.requesterId][2] = 1;
  620.             tmp_results[hit.requesterId][3] = reward;
  621.             tmp_results[hit.requesterId][4] = rejected;
  622.             tmp_results[hit.requesterId][5] = pending;
  623.           }
  624.           else
  625.           {
  626.             tmp_results[hit.requesterId][1] = hit.requesterName;
  627.             tmp_results[hit.requesterId][2] += 1;
  628.             tmp_results[hit.requesterId][3] += reward;
  629.             tmp_results[hit.requesterId][4] += rejected;
  630.             tmp_results[hit.requesterId][5] += pending;
  631.           }
  632.           cursor.continue();
  633.         }
  634.         else {
  635.           for (var key in tmp_results) {
  636.             results.push(tmp_results[key]);
  637.           }
  638.           // sort by total reward
  639.           results.sort(function(a,b) { return b[3]-a[3]; });
  640.           if (options.export_csv == true)
  641.             HITStorage.show_requester_overview_csv(results);
  642.           else        
  643.             HITStorage.show_requester_overview(results, '(' + options.from_date + '–' + options.to_date + ')');
  644.           HITStorage.update_status_label('Script monkeys are ready', 'green');
  645.           setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000);
  646.           HITStorage.enable_inputs();
  647.         }
  648.         db.close();
  649.       };
  650.     }
  651.     else {
  652.       index = store.index('requesterId');
  653.       req = index.openCursor();
  654.       req.onsuccess = function(event) {
  655.         var cursor = event.target.result;
  656.         if (cursor) {
  657.           var hit = cursor.value;
  658.           var rejected = (hit.status == 'Rejected') ? 1 : 0;
  659.           var pending = (hit.status.match(/Approved|Paid|Rejected/) == null) ? 1 : 0;
  660.           var reward = (pending>0 || rejected>0 )? 0: hit.reward;
  661.           if (results.length == 0)
  662.           {
  663.             results.push([hit.requesterId, hit.requesterName, 1, reward, rejected, pending]);
  664.           }
  665.           else if (results[0][0] == hit.requesterId)
  666.           {
  667.             results[0][2] += 1;
  668.             results[0][3] += reward;
  669.             results[0][4] += rejected;
  670.             results[0][5] += pending;
  671.           }
  672.           else
  673.           {
  674.             results.unshift([hit.requesterId, hit.requesterName, 1, reward, rejected, pending]);
  675.           }
  676.           cursor.continue();
  677.         }
  678.         else {
  679.           // sort by total reward
  680.           results.sort(function(a,b) { return b[3]-a[3]; });
  681.           if (options.export_csv == true)
  682.             HITStorage.show_requester_overview_csv(results);
  683.           else        
  684.             HITStorage.show_requester_overview(results);
  685.           HITStorage.update_status_label('Script monkeys are ready', 'green');
  686.           setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000);
  687.           HITStorage.enable_inputs();
  688.         }
  689.         db.close();
  690.       };
  691.     }
  692.   };
  693.   request.onerror = HITStorage.indexedDB.onerror;
  694. };
  695.  
  696. //
  697. // Show summary of one requester
  698. //
  699. HITStorage.indexedDB.showRequester = function(requesterId) {
  700.   var request = indexedDB.open("HITDB", v);
  701.   request.onsuccess = function(e) {
  702.     HITStorage.indexedDB.db = e.target.result;
  703.     var db = HITStorage.indexedDB.db;
  704.     var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  705.     var store = trans.objectStore("HIT");
  706.     var index;
  707.     var results = [];
  708.  
  709.     index = store.index('requesterId');
  710.     var range = IDBKeyRange.only(requesterId);
  711.     index.openCursor(range).onsuccess = function(event) {
  712.       var cursor = event.target.result;
  713.       if (cursor) {
  714.         results.push(cursor.value);
  715.         cursor.continue();
  716.       }
  717.       else {
  718.         results.sort(function(a,b)
  719.           {
  720.             if (a.date > b.date)
  721.               return -1;
  722.             if (a.date < b.date)
  723.               return 1;
  724.             return 0;
  725.           });
  726.         HITStorage.show_requester(results);
  727.         HITStorage.update_status_label('Script monkeys are ready', 'green');
  728.         setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000);
  729.         HITStorage.enable_inputs();
  730.       }
  731.       db.close();
  732.     };
  733.   };
  734.   request.onerror = HITStorage.indexedDB.onerror;
  735. };
  736.  
  737.  
  738. // Show summary of pending HITs
  739. HITStorage.indexedDB.pendingOverview = function(options) {
  740.   var request = indexedDB.open("HITDB", v);
  741.     request.onsuccess = function(e) {
  742.     HITStorage.indexedDB.db = e.target.result;
  743.     var db = HITStorage.indexedDB.db;
  744.     var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  745.     var store = trans.objectStore("HIT");
  746.     var index;
  747.     var req;
  748.    
  749.     // [ requesterId, requesterName, sum(pendings), sum(rewards) ]
  750.     var results = [];
  751.     var tmp_results = {};
  752.  
  753.     index = store.index('status');
  754.     range = IDBKeyRange.only('Pending Approval');
  755.     index.openCursor(range).onsuccess = function(event) {
  756.       var cursor = event.target.result;
  757.       if (cursor) {
  758.         var hit = cursor.value;
  759.         if (tmp_results[hit.requesterId] === undefined)
  760.         {
  761.           tmp_results[hit.requesterId] = [];
  762.           tmp_results[hit.requesterId][0] = hit.requesterId;
  763.           tmp_results[hit.requesterId][1] = hit.requesterName;
  764.           tmp_results[hit.requesterId][2] = 1;
  765.           tmp_results[hit.requesterId][3] = hit.reward;
  766.         }
  767.         else
  768.         {
  769.           tmp_results[hit.requesterId][1] = hit.requesterName;
  770.           tmp_results[hit.requesterId][2] += 1;
  771.           tmp_results[hit.requesterId][3] += hit.reward;
  772.         }
  773.         cursor.continue();
  774.       }
  775.       else {
  776.         for (var key in tmp_results) {
  777.           results.push(tmp_results[key]);
  778.         }
  779.         // sort by pending hits
  780.         results.sort(function(a,b) { return b[2]-a[2]; });
  781.         if (options.export_csv == true)
  782.           HITStorage.show_pending_overview_csv(results);
  783.         else        
  784.           HITStorage.show_pending_overview(results);
  785.         HITStorage.update_status_label('Script monkeys are ready', 'green');
  786.         setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000);
  787.         HITStorage.enable_inputs();
  788.       }
  789.       db.close();
  790.     };
  791.   };
  792.   request.onerror = HITStorage.indexedDB.onerror;
  793. };
  794.  
  795. // Show summary of daily stats
  796. HITStorage.indexedDB.statusOverview = function(options) {
  797.   var request = indexedDB.open("HITDB", v);
  798.   request.onsuccess = function(e) {
  799.     HITStorage.indexedDB.db = e.target.result;
  800.     var db = HITStorage.indexedDB.db;
  801.     var trans = db.transaction(["STATS"], HITStorage.IDBTransactionModes.READ_ONLY);
  802.     var store = trans.objectStore("STATS");
  803.     var req;
  804.    
  805.     var results = [];
  806.  
  807.     if (options.from_date || options.to_date)
  808.     {
  809.       if (options.from_date != '' || options.to_date != '')
  810.       {
  811.         if (options.from_date == options.to_date)
  812.         {
  813.           range = IDBKeyRange.only(options.from_date);
  814.         }
  815.         else if (options.from_date != '' && options.to_date != '')
  816.         {
  817.           range = IDBKeyRange.bound(options.from_date, options.to_date, false, false);
  818.         }
  819.         else if (options.from_date == '' && options.to_date != '')
  820.         {
  821.           range = IDBKeyRange.upperBound(options.to_date, false);
  822.         }
  823.         else
  824.         {
  825.           range = IDBKeyRange.lowerBound(options.from_date, false);
  826.         }
  827.         req = store.openCursor(range);
  828.       }
  829.     }
  830.     else
  831.     {
  832.       req = store.openCursor();
  833.     }
  834.     req.onsuccess = function(event) {
  835.       var cursor = event.target.result;
  836.       if (cursor) {
  837.         if (cursor.value.submitted > 0)
  838.           results.push(cursor.value);
  839.         cursor.continue();
  840.       }
  841.       else {
  842.         if (options.export_csv == true)
  843.           HITStorage.show_status_overview_csv(results);
  844.         else        
  845.           HITStorage.show_status_overview(results, '(' + options.from_date + '–' + options.to_date + ')');
  846.         HITStorage.update_status_label('Script monkeys are ready', 'green');
  847.         setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000);
  848.         HITStorage.enable_inputs();
  849.       }
  850.       db.close();
  851.     };
  852.   };
  853.   request.onerror = HITStorage.indexedDB.onerror;
  854. };
  855.  
  856. HITStorage.indexedDB.getHIT = function(id) {
  857. var request = indexedDB.open("HITDB", v);
  858.     request.onsuccess = function(e) {
  859.         HITStorage.indexedDB.db = e.target.result;
  860.     var db = HITStorage.indexedDB.db;
  861.       var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  862.       var store = trans.objectStore("HIT");
  863.  
  864.       var request = store.get(id);
  865.  
  866.       request.onsuccess = function(e) {
  867.             db.close();
  868.             showDetails(e.target.result.note);
  869.       };
  870.  
  871.       request.onerror = function(e) {
  872.             console.log("Error Getting: ", e);
  873.       };
  874.     };
  875.     request.onerror = HITStorage.indexedDB.onerror;
  876. };
  877.  
  878. HITStorage.indexedDB.addNote = function(id, note) {
  879. var request = indexedDB.open("HITDB", v);
  880.     request.onsuccess = function(e) {
  881.     HITStorage.indexedDB.db = e.target.result;
  882.       var db = HITStorage.indexedDB.db;
  883.       var trans = db.transaction(["NOTES"], HITStorage.IDBTransactionModes.READ_WRITE);
  884.       var store = trans.objectStore("NOTES");
  885.     var request;
  886.  
  887.     if (note == '')
  888.       request = store.delete(id);
  889.     else
  890.       request = store.put({requesterId: id, note: note});
  891.  
  892.       request.onsuccess = function(e) {
  893.         db.close();
  894.       };
  895.  
  896.       request.onerror = function(e) {
  897.       console.log("Error Adding: ", e);
  898.       };
  899.     };
  900.     request.onerror = HITStorage.indexedDB.onerror;
  901. };
  902.  
  903. HITStorage.indexedDB.blockHITS = function(requesterId, title, hitElement, titleElement) {
  904.   var request = indexedDB.open("HITDB", v);
  905.     request.onsuccess = function(e) {
  906.         HITStorage.indexedDB.db = e.target.result;
  907.         var db = HITStorage.indexedDB.db;
  908.        
  909.     if (!db.objectStoreNames.contains("BLOCKS"))
  910.     {
  911.       db.close();
  912.       return;
  913.     }
  914.         var trans = db.transaction(["BLOCKS"], HITStorage.IDBTransactionModes.READ_ONLY);
  915.         var store = trans.objectStore("BLOCKS");
  916.     var index = store.index("requesterId");
  917.     var range = IDBKeyRange.only(requesterId);
  918.    
  919.     index.openCursor(range).onsuccess = function(event) {
  920.       var cursor = event.target.result;
  921.       if (cursor && cursor.value.re)
  922.       {
  923.         if (cursor.value.re.test(title))
  924.         {
  925.           hitElement.style.display = 'none';
  926.           titleElement.addEventListener("click", unblock_func(requesterId, title));
  927.          
  928.           titleElement.style.fontSize = 'small';          
  929.          
  930.           // move blocked hits to the bottom
  931.           var table = hitElement.parentNode.parentNode.parentNode.parentNode.parentNode;
  932.           var hit = hitElement.parentNode.parentNode.parentNode.parentNode;
  933.           table.removeChild(hit);
  934.           table.appendChild(hit);
  935.         }
  936.         cursor.continue();
  937.       }
  938.       else
  939.       {
  940.         db.close();
  941.       }
  942.     };
  943.     };
  944.     request.onerror = HITStorage.indexedDB.onerror;
  945. };
  946.  
  947. HITStorage.indexedDB.addBlock = function(requesterId, re) {
  948. var request = indexedDB.open("HITDB", v);
  949.     request.onsuccess = function(e) {
  950.     HITStorage.indexedDB.db = e.target.result;
  951.       var db = HITStorage.indexedDB.db;
  952.       var trans = db.transaction(["BLOCKS"], HITStorage.IDBTransactionModes.READ_WRITE);
  953.       var store = trans.objectStore("BLOCKS");
  954.     var request;
  955.  
  956.     request = store.put({requesterId: requesterId, re: re});
  957.  
  958.       request.onsuccess = function(e) {
  959.         db.close();
  960.       };
  961.     };
  962.     request.onerror = HITStorage.indexedDB.onerror;
  963. };
  964.  
  965. // Removes all blocks for requesterId, where RE matches this HIT title
  966. HITStorage.indexedDB.removeBlocks = function(requesterId, title) {
  967.   var request = indexedDB.open("HITDB", v);
  968.     request.onsuccess = function(e) {
  969.         HITStorage.indexedDB.db = e.target.result;
  970.         var db = HITStorage.indexedDB.db;
  971.     if (!db.objectStoreNames.contains("BLOCKS"))
  972.     {
  973.       db.close();
  974.       return;
  975.     }
  976.         var trans = db.transaction(["BLOCKS"], HITStorage.IDBTransactionModes.READ_WRITE);
  977.         var store = trans.objectStore("BLOCKS");
  978.         var index = store.index("requesterId");
  979.     var range = IDBKeyRange.only(requesterId);
  980.  
  981.     index.openCursor(range).onsuccess = function(event)
  982.     {
  983.       var cursor = event.target.result;
  984.       if (cursor)
  985.       {
  986.         if (cursor.value.re.test(title))
  987.           store.delete(cursor.value.id);
  988.         db.close();
  989.       }
  990.     };
  991.     };
  992.     request.onerror = HITStorage.indexedDB.onerror;
  993. };
  994.  
  995. HITStorage.indexedDB.updateNoteButton = function(id, label) {
  996.   var request = indexedDB.open("HITDB", v);
  997.     request.onsuccess = function(e) {
  998.         HITStorage.indexedDB.db = e.target.result;
  999.         var db = HITStorage.indexedDB.db;
  1000.        
  1001.     if (!db.objectStoreNames.contains("NOTES"))
  1002.     {
  1003.       label.title = 'Update HIT database on statusdetail page to use this feature';
  1004.       db.close();
  1005.       return;
  1006.     }
  1007.         var trans = db.transaction(["NOTES"], HITStorage.IDBTransactionModes.READ_ONLY);
  1008.         var store = trans.objectStore("NOTES");
  1009.  
  1010.     store.get(id).onsuccess = function(event)
  1011.     {
  1012.       if (event.target.result === undefined)
  1013.       {
  1014.         label.textContent = '';
  1015.       }
  1016.       else
  1017.       {
  1018.         var note = event.target.result.note;
  1019.         label.textContent = note;
  1020.         label.style.border = '1px dotted';
  1021.         if (note.indexOf('!') >= 0)
  1022.           label.style.color = 'red';
  1023.         else
  1024.           label.style.color = 'black';
  1025.       }
  1026.       db.close();
  1027.     };
  1028.     };
  1029.     request.onerror = HITStorage.indexedDB.onerror;
  1030. };
  1031.  
  1032.  
  1033. HITStorage.indexedDB.colorRequesterButton = function(id, button) {
  1034.   var request = indexedDB.open("HITDB", v);
  1035.     request.onsuccess = function(e) {
  1036.         HITStorage.indexedDB.db = e.target.result;
  1037.         var db = HITStorage.indexedDB.db;
  1038.     if (!db.objectStoreNames.contains("HIT"))
  1039.     {
  1040.       button.title = 'Update HIT database on statusdetail page to use this feature';
  1041.       db.close();
  1042.       return;
  1043.     }
  1044.         var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  1045.         var store = trans.objectStore("HIT");
  1046.  
  1047.         var index = store.index("requesterId");
  1048.     index.get(id).onsuccess = function(event)
  1049.     {
  1050.       if (event.target.result === undefined)
  1051.       {
  1052.         button.style.backgroundColor = 'pink';
  1053.       }
  1054.       else
  1055.       {
  1056.         button.style.backgroundColor = 'lightgreen';
  1057.         button.style.fontWeight = 'bold';
  1058.       }
  1059.       db.close();
  1060.     };
  1061.     };
  1062.     request.onerror = HITStorage.indexedDB.onerror;
  1063. };
  1064.  
  1065. HITStorage.indexedDB.colorTitleButton = function(title, button) {
  1066.   var request = indexedDB.open("HITDB", v);
  1067.     request.onsuccess = function(e) {
  1068.         HITStorage.indexedDB.db = e.target.result;
  1069.         var db = HITStorage.indexedDB.db;
  1070.     if (!db.objectStoreNames.contains("HIT"))
  1071.     {
  1072.       button.title = 'Update HIT database on statusdetail page to use this feature';
  1073.       db.close();
  1074.       return;
  1075.     }
  1076.         var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  1077.         var store = trans.objectStore("HIT");
  1078.  
  1079.         var index = store.index("title");
  1080.     index.get(title).onsuccess = function(event)
  1081.     {
  1082.       if (event.target.result === undefined)
  1083.       {
  1084.         button.style.backgroundColor = 'pink';
  1085.       }
  1086.       else
  1087.       {
  1088.         button.style.backgroundColor = 'lightgreen';
  1089.         button.style.fontWeight = 'bold';
  1090.       }
  1091.      
  1092.       db.close();
  1093.     };
  1094.     };
  1095.     request.onerror = HITStorage.indexedDB.onerror;
  1096. };
  1097.  
  1098. HITStorage.indexedDB.deleteDB = function () {
  1099.   var deleteRequest = indexedDB.deleteDatabase("HITDB");
  1100.   deleteRequest.onsuccess = function (e)
  1101.   {
  1102.     alert("deleted");
  1103.   }
  1104.   deleteRequest.onblocked = function (e)
  1105.   {
  1106.     alert("blocked");
  1107.   }
  1108.   deleteRequest.onerror = HITStorage.indexedDB.onerror;
  1109. }
  1110.  
  1111. HITStorage.indexedDB.get_pending_approvals = function() {
  1112.   var element = document.getElementById('pending_earnings_value');
  1113.   var header_element = document.getElementById('pending_earnings_header');
  1114.   if (element == null)
  1115.     return;  
  1116.  
  1117.   var request = indexedDB.open("HITDB", v);
  1118.     request.onsuccess = function(e) {
  1119.     HITStorage.indexedDB.db = e.target.result;
  1120.     var db = HITStorage.indexedDB.db;
  1121.     var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  1122.     var store = trans.objectStore("HIT");
  1123.    
  1124.     var result = 0;
  1125.     var index;
  1126.     var range;
  1127.  
  1128.     index = store.index('status');
  1129.     range = IDBKeyRange.only('Pending Approval');
  1130.    
  1131.     index.openCursor(range).onsuccess = function(event) {
  1132.       var cursor = event.target.result;
  1133.       if (cursor) {
  1134.         result += cursor.value.reward;
  1135.         cursor.continue();
  1136.       }
  1137.       else {
  1138.         element.textContent = '$' + result.toFixed(2);
  1139.         if (header_element != null)
  1140.           header_element.textContent = 'Pending earnings (HITDB updated: ' + localStorage['HITDB UPDATED']+ ')';
  1141.       }
  1142.       db.close();
  1143.     };
  1144.   };
  1145.   request.onerror = HITStorage.indexedDB.onerror;
  1146. };
  1147.  
  1148. HITStorage.indexedDB.get_pending_payments = function() {
  1149.   var element = document.getElementById('pending_earnings_value');
  1150.   if (element == null)
  1151.     return;  
  1152.  
  1153.   var request = indexedDB.open("HITDB", v);
  1154.     request.onsuccess = function(e) {
  1155.     HITStorage.indexedDB.db = e.target.result;
  1156.     var db = HITStorage.indexedDB.db;
  1157.     var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  1158.     var store = trans.objectStore("HIT");
  1159.    
  1160.     var result = 0;
  1161.     var index;
  1162.     var range;
  1163.  
  1164.     index = store.index('status');
  1165.     range = IDBKeyRange.only('Approved&nbsp;- Pending&nbsp;Payment');
  1166.    
  1167.     index.openCursor(range).onsuccess = function(event) {
  1168.       var cursor = event.target.result;
  1169.       if (cursor) {
  1170.         result += cursor.value.reward;
  1171.         cursor.continue();
  1172.       }
  1173.       else {
  1174.         element.title = 'Approved - Pending Payment: $' + result.toFixed(2);
  1175.       }
  1176.     }
  1177.     db.close();
  1178.   };
  1179.   request.onerror = HITStorage.indexedDB.onerror;
  1180. };
  1181.  
  1182. HITStorage.indexedDB.get_todays_projected_earnings = function(date) {
  1183.   var element = document.getElementById('projected_earnings_value');
  1184.   if (element == null)
  1185.     return;  
  1186.  
  1187.   var request = indexedDB.open("HITDB", v);
  1188.   request.onsuccess = function(e) {
  1189.     HITStorage.indexedDB.db = e.target.result;
  1190.     var db = HITStorage.indexedDB.db;
  1191.     var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  1192.     var store = trans.objectStore("HIT");
  1193.    
  1194.     var result = 0;
  1195.     var rejected = 0;
  1196.     var index;
  1197.     var range;
  1198.  
  1199.     index = store.index('date');
  1200.     range = IDBKeyRange.only(date);
  1201.    
  1202.     index.openCursor(range).onsuccess = function(event) {
  1203.       var cursor = event.target.result;
  1204.       if (cursor) {
  1205.         if (cursor.value.status == 'Rejected')
  1206.           rejected += cursor.value.reward;
  1207.         else
  1208.           result += cursor.value.reward;
  1209.         cursor.continue();
  1210.       }
  1211.       else {
  1212.         element.textContent = '$' + result.toFixed(2);
  1213.         element.title = '$' + rejected.toFixed(2) + ' rejected';
  1214.        
  1215.         if (localStorage['TODAYS TARGET'] !== undefined)
  1216.         {
  1217.           var target = parseFloat(localStorage['TODAYS TARGET']).toFixed(2);
  1218.           var my_target = document.getElementById('my_target');
  1219.          
  1220.           var progress = Math.floor(result/target*40);
  1221.           if (progress > 40)
  1222.             progress = 40;
  1223.           my_target.innerHTML = progress_bar(progress, 40, '█', '█', '#7fb448', 'grey') + '&nbsp;' +
  1224.             ((result>target)? '+' : '') + (result-target).toFixed(2);
  1225.           my_target.style.fontSize = '9px';
  1226.         }
  1227.       }
  1228.     }
  1229.     db.close();
  1230.   };
  1231.   request.onerror = HITStorage.indexedDB.onerror;
  1232. };
  1233.  
  1234. // Update database date format from MMDDYYYY to YYYY-MM-DD
  1235. // Shouldn't break anything even if used on already updated db
  1236. HITStorage.update_date_format = function(verbose)
  1237. {
  1238.   var request = indexedDB.open("HITDB", v);
  1239.   request.onsuccess = function(e) {
  1240.     HITStorage.indexedDB.db = e.target.result;
  1241.          var db = HITStorage.indexedDB.db;
  1242.           var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_WRITE);
  1243.           var store = trans.objectStore("HIT");
  1244.    
  1245.     store.openCursor().onsuccess = function(event) {
  1246.       var cursor = event.target.result;
  1247.       if (cursor)
  1248.       {
  1249.         if (cursor.value.date.indexOf('-') < 0)
  1250.         {
  1251.           var i = cursor.value;
  1252.           i.date = convert_date(i.date);
  1253.           i.requesterName = i.requesterName.trim();
  1254.           i.title = i.title.trim();
  1255.           cursor.update(i);
  1256.         }
  1257.         cursor.continue();
  1258.       }
  1259.       else
  1260.       {
  1261.         db.close();
  1262.         HITStorage.update_stats_date_format(verbose);
  1263.       }
  1264.     };
  1265.   }
  1266. }
  1267.  
  1268. HITStorage.update_stats_date_format = function(verbose)
  1269. {
  1270.   var request = indexedDB.open("HITDB", v);
  1271.   request.onsuccess = function(e) {
  1272.     HITStorage.indexedDB.db = e.target.result;
  1273.          var db = HITStorage.indexedDB.db;
  1274.           var trans = db.transaction(["STATS"], HITStorage.IDBTransactionModes.READ_WRITE);
  1275.           var store = trans.objectStore("STATS");
  1276.  
  1277.     store.openCursor().onsuccess = function(event) {
  1278.       var cursor = event.target.result;
  1279.       if (cursor)
  1280.       {
  1281.         if (cursor.value.date.indexOf('-') < 0)
  1282.         {
  1283.           var i = cursor.value;
  1284.           i.date = convert_date(i.date);
  1285.           cursor.delete();
  1286.           store.put(i);
  1287.         }
  1288.         cursor.continue();
  1289.       }
  1290.       else
  1291.       {
  1292.         // DB should be fully updated
  1293.         db.close();
  1294.         if (verbose == true)
  1295.           alert('Date conversion done.');
  1296.       }
  1297.     };
  1298.   }
  1299. };
  1300.  
  1301. /* ------------------------------------------------------------- */
  1302.  
  1303. HITStorage.prepare_donut = function (donutData, type)
  1304. {
  1305.   if (type == '---')
  1306.     return;
  1307.   var countHits = true;
  1308.   if (type.match('REWARDS'))
  1309.     countHits = false;
  1310.  
  1311.   var tmpData = {};
  1312.   var topRequesters = [];
  1313.   var topHits = [];
  1314.   var sum = 0;
  1315.  
  1316.   for (var i=0; i < donutData.length; i++) {
  1317.     var requesterName = donutData[i].requesterName.trim() + " (" + donutData[i].requesterId + ")";
  1318.     var hitTitle = donutData[i].title;
  1319.     var hitReward = donutData[i].reward;
  1320.     sum += (countHits) ? 1 : hitReward;
  1321.  
  1322.     if (tmpData[requesterName]) {
  1323.       tmpData[requesterName]['HITS'] += (countHits) ? 1 : hitReward;
  1324.     }
  1325.     else {
  1326.       tmpData[requesterName] = {};
  1327.       tmpData[requesterName]['HITS'] = (countHits) ? 1 : hitReward;
  1328.     }
  1329.     if (tmpData[requesterName][hitTitle])
  1330.       tmpData[requesterName][hitTitle] += (countHits) ? 1 : hitReward;
  1331.     else
  1332.       tmpData[requesterName][hitTitle] = (countHits) ? 1 : hitReward;
  1333.  
  1334.   }
  1335.  
  1336.   for (var key in tmpData) {
  1337.     topRequesters.push({name: key, y: tmpData[key]['HITS']});
  1338.   }
  1339.   topRequesters.sort(function(a,b){return b.y-a.y});
  1340.  
  1341.   var colors = Highcharts.getOptions().colors;
  1342.  
  1343.   for (var i=0; i<topRequesters.length; i++) {
  1344.     var tmpHits = [];
  1345.     topRequesters[i].color = colors[i];
  1346.     for (var key2 in tmpData[topRequesters[i].name]) {
  1347.       if (key2 != 'HITS') {
  1348.         tmpHits.push({name: key2, y: tmpData[topRequesters[i].name][key2], color: colors[i]});
  1349.       }
  1350.     }
  1351.     tmpHits.sort(function(a,b){return b.y-a.y});
  1352.     for (var j=0; j<tmpHits.length ; j++) {
  1353.       var brightness = 0.2 - (j / tmpHits.length) / 5;
  1354.       tmpHits[j].color = Highcharts.Color(colors[i]).brighten(brightness).get();
  1355.     }
  1356.     topHits = topHits.concat(tmpHits);
  1357.   }
  1358.  
  1359.   document.getElementById('container').style.display = 'block';
  1360.  
  1361.  
  1362.   chart = new Highcharts.Chart({
  1363.             chart: {
  1364.                 renderTo: 'container',
  1365.                 type: 'pie'
  1366.             },
  1367.             title: {
  1368.                 text: 'Requesters and HITs matching your latest search'
  1369.             },
  1370.             yAxis: {
  1371.                 title: {
  1372.                     text: ''
  1373.                 }
  1374.             },
  1375.             plotOptions: {
  1376.                 pie: {
  1377.                     shadow: false,
  1378.                     dataLabels: { enabled: true}
  1379.                 }
  1380.             },
  1381.             tooltip: {
  1382.                     animation: false,
  1383.                 valuePrefix: (countHits)? '' : '$',
  1384.                 valueSuffix: (countHits)? ' HITs' : '',
  1385.                     valueDecimals: (countHits)? 0 : 2,
  1386.                     pointFormat: (countHits)? '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> (of all ' + sum + ' HITs)<br/>' :
  1387.                                               '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> (of all $' + sum.toFixed(2) + ')<br/>'
  1388.             },
  1389.             series: [{
  1390.                 name: 'Requesters',
  1391.                 data: topRequesters,
  1392.                 size: '60%',
  1393.                 dataLabels: {
  1394.                     formatter: function() {
  1395.                         if (countHits) {
  1396.                           return this.y/sum >= 0.20 ? this.point.name: null;
  1397.                         }
  1398.                         else {
  1399.                           return this.y/sum >= 0.20 ? this.point.name : null;
  1400.                         }
  1401.                     },
  1402.                     color: 'black',
  1403.                     distance: -10
  1404.                 }
  1405.             }, {
  1406.                 name: 'HITs',
  1407.                 data: topHits,
  1408.                 innerSize: '60%',
  1409.                 dataLabels: {
  1410.                     formatter: function() {
  1411.                         if (countHits) {
  1412.                           return this.y/sum > 0.05 ? this.point.name : null;
  1413.                         }
  1414.                         else {
  1415.                           return this.y/sum > 0.05 ? this.point.name : null;
  1416.                         }
  1417.                     },
  1418.                     color: 'black',
  1419.                 }
  1420.             }]
  1421.   });
  1422. }
  1423.  
  1424. // Stolen from Today's Projected Earnings (http://userscripts.org/scripts/show/95331)
  1425. HITStorage.getHTTPObject = function()  
  1426. {
  1427.    if (typeof XMLHttpRequest != 'undefined')
  1428.    {
  1429.       return new XMLHttpRequest();
  1430.    }
  1431.    try
  1432.    {
  1433.       return new ActiveXObject("Msxml2.XMLHTTP");
  1434.    }
  1435.    catch (e)
  1436.    {
  1437.       try
  1438.       {
  1439.          return new ActiveXObject("Microsoft.XMLHTTP");
  1440.       }
  1441.       catch (e) {}
  1442.    }
  1443.    return false;
  1444. }
  1445.  
  1446. // Stolen from Today's Projected Earnings (http://userscripts.org/scripts/show/95331)
  1447. // date format MMDDYYYY!
  1448. HITStorage.process_page = function(link, date, hitData)
  1449. {
  1450.    var page = HITStorage.getHTTPObject();
  1451.    page.open("GET", link, false);
  1452.    page.send(null);
  1453.    return HITStorage.parse_data(page.responseText, date, hitData);
  1454. }
  1455.  
  1456. // Partly stolen from Today's Projected Earnings (http://userscripts.org/scripts/show/95331)
  1457. // date format MMDDYYYY!
  1458. HITStorage.parse_data = function(page_text, date, hitData)
  1459. {
  1460.    var index  = 0;
  1461.    var index2 = 0;
  1462.    var page_html = document.createElement('div');
  1463.    page_html.innerHTML = page_text;
  1464.  
  1465.    var requesters = page_html.getElementsByClassName('statusdetailRequesterColumnValue');
  1466.    var titles = page_html.getElementsByClassName('statusdetailTitleColumnValue');
  1467.    var amounts = page_html.getElementsByClassName('statusdetailAmountColumnValue');
  1468.    var statuses = page_html.getElementsByClassName('statusdetailStatusColumnValue');
  1469.    var feedbacks = page_html.getElementsByClassName('statusdetailRequesterFeedbackColumnValue');
  1470.  
  1471.    var requesterName;
  1472.    var hitTitle;
  1473.    var hitReward;
  1474.    var hitStatus;
  1475.    var requesterId;
  1476.    var hitId;
  1477.  
  1478.    for(var k = 0; k < amounts.length; k++)
  1479.    {
  1480.       requesterName = requesters[k].textContent;
  1481.       requesterLink = requesters[k].childNodes[1].href;
  1482.       hitTitle      = titles[k].textContent;
  1483.       index = amounts[k].innerHTML.indexOf('$');
  1484.       hitReward     = parseFloat(amounts[k].innerHTML.substring(index+1));
  1485.       hitStatus     = statuses[k].innerHTML;
  1486.       hitFeedback   = feedbacks[k].textContent;
  1487.  
  1488.  
  1489.       index = requesterLink.search("requesterId=");
  1490.       requesterId = requesterLink.substring(index+12, requesterLink.lastIndexOf('&'));
  1491.       hitId = requesterLink.substring(81, index-1);
  1492.  
  1493.       var hit = {
  1494.         hitId         : hitId,
  1495.         date          : convert_date(date),
  1496.         requesterName : requesterName.trim(),
  1497.         requesterLink : requesterLink.trim(),
  1498.         title         : hitTitle.trim(),
  1499.         reward        : hitReward,
  1500.         status        : hitStatus,
  1501.         feedback      : hitFeedback.trim(),
  1502.         requesterId   : requesterId
  1503.       };
  1504.  
  1505.       //HITStorage.indexedDB.addHIT(hitData);
  1506.       hitData.push(hit);
  1507.    }
  1508.  
  1509.    return amounts.length;
  1510. }
  1511.  
  1512. // Returns available days (YYYY-MM-DD)
  1513. HITStorage.getAllAvailableDays = function(try_extra_days)
  1514. {
  1515.   var days = [];
  1516.  
  1517.   var page = HITStorage.getHTTPObject();
  1518.   page.open("GET", 'https://www.mturk.com/mturk/status', false);
  1519.   page.send(null);
  1520.    
  1521.   var page_html = document.createElement('div');
  1522.   page_html.innerHTML = page.responseText;
  1523.  
  1524.   var dateElements = page_html.getElementsByClassName('statusDateColumnValue');
  1525.   var submittedElements = page_html.getElementsByClassName('statusSubmittedColumnValue');
  1526.   var approvedElements = page_html.getElementsByClassName('statusApprovedColumnValue');
  1527.   var rejectedElements = page_html.getElementsByClassName('statusRejectedColumnValue');
  1528.   var pendingElements = page_html.getElementsByClassName('statusPendingColumnValue');
  1529.   var earningsElements = page_html.getElementsByClassName('statusEarningsColumnValue');
  1530.  
  1531.   for (var i=0; i<dateElements.length; i++)
  1532.   {
  1533.     var date = dateElements[i].childNodes[1].href.substr(53);
  1534.     date = convert_date(date);
  1535.      
  1536.     days.push( { date: date,
  1537.                  submitted: parseInt(submittedElements[i].textContent),
  1538.                  approved : parseInt(approvedElements[i].textContent),
  1539.                  rejected : parseInt(rejectedElements[i].textContent),
  1540.                  pending  : parseInt(pendingElements[i].textContent),
  1541.                  earnings : parseFloat(earningsElements[i].textContent.slice(1)) });
  1542.   }
  1543.  
  1544.   if (try_extra_days > 0)
  1545.   {
  1546.     var date = days[days.length-1].date;
  1547.     var d = new Date();
  1548.     d.setFullYear(parseInt(date.substr(0,4)), parseInt(date.substr(5,2))-1, parseInt(date.substr(8,2)));
  1549.    
  1550.     for (var i=0; i<try_extra_days; i++)
  1551.     {      
  1552.       d.setDate(d.getDate()-1);
  1553.       var month = '0' + (d.getMonth() + 1);
  1554.       var day = '0' + d.getDate();
  1555.       if (month.length > 2)
  1556.         month = month.substr(1);
  1557.       if (day.length > 2)
  1558.         day = day.substr(1);
  1559.       date = '' + d.getFullYear() + '-' + month + '-' + day;
  1560.      
  1561.       days.push( { date: date,
  1562.                    submitted: -1,
  1563.                    approved : -1,
  1564.                    rejected : -1,
  1565.                    pending  : -1,
  1566.                    earnings : -1 } );
  1567.     }
  1568.   }
  1569.  
  1570.   return days;
  1571. }
  1572.  
  1573. HITStorage.getLatestHITs = function()
  1574. {
  1575.   if (localStorage['HITDB AUTO UPDATE'] === undefined || localStorage['HITDB AUTO UPDATE'] == 'OFF')
  1576.     return;  
  1577.  
  1578.   if (localStorage['HITDB TIMESTAMP'] !== undefined)
  1579.   {
  1580.     if (new Date().getTime() < new Date(parseInt(localStorage['HITDB TIMESTAMP'])).getTime() + 90000)
  1581.     {
  1582.       return;
  1583.     }
  1584.   }
  1585.   localStorage['HITDB TIMESTAMP'] = new Date().getTime();
  1586.  
  1587.   var auto_button = document.getElementById('auto_button');
  1588.   var page = HITStorage.getHTTPObject();
  1589.   page.open("GET", 'https://www.mturk.com/mturk/status', false);
  1590.   page.send(null);
  1591.   auto_button.textContent += ' +';
  1592.    
  1593.   var page_html = document.createElement('div');
  1594.   page_html.innerHTML = page.responseText;
  1595.  
  1596.   var dateElements = page_html.getElementsByClassName('statusDateColumnValue');
  1597.   var submittedElements = page_html.getElementsByClassName('statusSubmittedColumnValue');
  1598.   var approvedElements = page_html.getElementsByClassName('statusApprovedColumnValue');
  1599.   var rejectedElements = page_html.getElementsByClassName('statusRejectedColumnValue');
  1600.   var pendingElements = page_html.getElementsByClassName('statusPendingColumnValue');
  1601.   var earningsElements = page_html.getElementsByClassName('statusEarningsColumnValue');
  1602.  
  1603.   if (dateElements[0].childNodes[1].textContent.trim() != 'Today')
  1604.     return;
  1605.  
  1606.   var url = dateElements[0].childNodes[1].href;
  1607.   var date = url.substr(53); // keep MMDDYYYY
  1608.   var submitted = parseInt(submittedElements[0].textContent);
  1609.   //var approved = parseInt(approvedElements[0].textContent);
  1610.   //var rejected = parseInt(rejectedElements[0].textContent);
  1611.   //var pending  = parseInt(pendingElements[0].textContent);
  1612.   //var earnings = parseFloat(earningsElements[0].textContent.slice(1));
  1613.   var pages_done = null;
  1614.   if (localStorage['HITDB AUTOUPDATE PAGES'] !== undefined)
  1615.   {
  1616.     pages_done = JSON.parse(localStorage['HITDB AUTOUPDATE PAGES']);
  1617.   }
  1618.   if (pages_done == null || pages_done.date != date)
  1619.     pages_done = {date: date};
  1620.  
  1621.   var new_hits = 0;
  1622.   var page = 1 + Math.floor(submitted/25);
  1623.   page = (page<1) ? 1 : page;
  1624.  
  1625.   var hitData = [];  
  1626.   if (submitted != pages_done.submitted)
  1627.   {
  1628.     url = "https://www.mturk.com/mturk/statusdetail?sortType=All&pageNumber=" + page + "&encodedDate=" + date;
  1629.     HITStorage.process_page(url, date, hitData);
  1630.     new_hits += submitted - pages_done.submitted;
  1631.     pages_done.submitted = submitted;
  1632.     localStorage['HITDB AUTOUPDATE PAGES'] = JSON.stringify(pages_done);
  1633.     auto_button.textContent += '+';
  1634.   }
  1635.  
  1636.   if (page > 1)
  1637.   {
  1638.     extra_page = page-1;
  1639.    
  1640.     while (extra_page >= 1)
  1641.     {
  1642.       if (pages_done[extra_page] != true)
  1643.       {
  1644.         url = "https://www.mturk.com/mturk/statusdetail?sortType=All&pageNumber=" + extra_page + "&encodedDate=" + date;
  1645.         if (HITStorage.process_page(url, date, hitData) == 25)
  1646.         {
  1647.           pages_done[extra_page] = true;
  1648.           localStorage['HITDB AUTOUPDATE PAGES'] = JSON.stringify(pages_done);
  1649.           auto_button.textContent += '+';
  1650.         }
  1651.         break;
  1652.       }
  1653.       extra_page -= 1;
  1654.     }    
  1655.   }
  1656.   HITStorage.indexedDB.addHITs(hitData);
  1657. }
  1658.  
  1659. // Gets status details for given date (MMDDYYYY)
  1660. // Collects all HITs for given date to hitData array
  1661. HITStorage.getHITData = function(day_to_fetch, hitData, page, days_to_update)
  1662. {
  1663.   var dataDate = convert_iso_date(day_to_fetch.date);
  1664.   page = page || 1;
  1665.   detailed_status_page_link = "https://www.mturk.com/mturk/statusdetail?sortType=All&pageNumber=" + page + "&encodedDate=" + dataDate;            
  1666.  
  1667.   if (HITStorage.process_page(detailed_status_page_link, dataDate, hitData) == 0)
  1668.   {
  1669.     if (day_to_fetch.submitted == -1 || hitData.length == day_to_fetch.submitted)
  1670.     {
  1671.       setTimeout(function(){ HITStorage.indexedDB.addHITs(hitData, day_to_fetch, days_to_update); }, 1000);
  1672.     }
  1673.     else
  1674.     {
  1675.       alert("There was an error while fetching HITs for date: " + day_to_fetch.date + ".\n" +
  1676.             "Script monkeys expected " + day_to_fetch.submitted + " bananas, but got " + hitData.length + "! 😞");
  1677.       HITStorage.update_done();
  1678.     }
  1679.   }
  1680.   else
  1681.   {
  1682.     HITStorage.update_status_label('Please wait: script monkeys are fetching status pages (' +
  1683.                                    day_to_fetch.date + ', page ' + page + ')', 'red');
  1684.     setTimeout(function(){ HITStorage.getHITData(day_to_fetch, hitData, page+1, days_to_update); }, 1000);
  1685.   }  
  1686. }
  1687.  
  1688. HITStorage.formatTime = function(msec)
  1689. {
  1690.     if (isNaN(msec))
  1691.       return "-";
  1692.     var seconds = Math.floor(msec / 1000) % 60;
  1693.     var minutes = Math.floor((msec / 1000) / 60) % 60;
  1694.     var hours = Math.floor(((msec / 1000) / 60) / 60) % 24;
  1695.     var days = Math.floor(((msec / 1000) / 60) / 60 / 24);
  1696.  
  1697.     if (hours > 0)
  1698.       seconds = "";
  1699.     else
  1700.       seconds = "" + seconds + "s";
  1701.     minutes == 0 ? minutes = "" : minutes = "" + minutes + "m ";
  1702.     hours == 0   ? hours = "" : hours = "" + hours + "h ";
  1703.  
  1704.     if (days > 0)
  1705.       return '' + days + ' day' + ((days>1)? 's' : ' ') + hours;
  1706.     return hours + minutes + seconds;
  1707. }
  1708.  
  1709. HITStorage.update_status_label = function(new_status, color)
  1710. {
  1711.   var label = document.getElementById('status_label');
  1712.   label.innerHTML = new_status;
  1713.   label.style.color = color || 'black';
  1714. }
  1715.  
  1716. // validate input field dates
  1717. // Accept YYYY-MM-DD
  1718. HITStorage.validate_date = function(input)
  1719. {
  1720.   date = input.value;  
  1721.  
  1722.   if (date.match(/^[01]\d\/[0123]\d\/20\d\d$/) != null)
  1723.   {
  1724.     var d = date.split('\/');
  1725.     date = d[2] + '-' + d[0] + '-' + d[1];
  1726.     input.value = date;
  1727.   }
  1728.  
  1729.   if (date.match(/^$|^20\d\d\-[01]\d\-[0123]\d$/) != null)
  1730.   {
  1731.     input.style.backgroundColor = 'white';
  1732.     return true;
  1733.   }
  1734.   input.style.backgroundColor = 'pink';
  1735.   return false;
  1736. }
  1737.  
  1738. HITStorage.validate_dates = function()
  1739. {
  1740.   from = document.getElementById('from_date');
  1741.   to = document.getElementById('to_date');  
  1742.  
  1743.   if (HITStorage.validate_date(from) && HITStorage.validate_date(to))
  1744.   {
  1745.     if (from.value > to.value && to.value != '')
  1746.     {
  1747.       alert('Invalid date!');
  1748.       return false;
  1749.     }
  1750.    
  1751.     return true;
  1752.   }
  1753.   alert('Invalid date!');
  1754.   return false;
  1755. }
  1756.  
  1757. HITStorage.start_search = function()
  1758. {
  1759.   if (HITStorage.validate_dates() == false)
  1760.     return;
  1761.  
  1762.   HITStorage.update_status_label('Using local HIT database', 'green');
  1763.  
  1764.   var options = {};
  1765.   options.term = document.getElementById('search_term').value;
  1766.   options.status = document.getElementById('status_select').value;
  1767.   options.donut = document.getElementById('donut_select').value;
  1768.   options.from_date = document.getElementById('from_date').value;
  1769.   options.to_date = document.getElementById('to_date').value;
  1770.   options.export_csv = document.getElementById('export_csv').checked;
  1771.  
  1772.   HITStorage.disable_inputs();
  1773.   setTimeout(function(){ HITStorage.do_search(options); }, 500);
  1774. }
  1775.  
  1776. HITStorage.disable_inputs = function()
  1777. {
  1778.   document.getElementById('delete_button').disabled = true;
  1779.   document.getElementById('search_button').disabled = true;
  1780.   document.getElementById('update_button').disabled = true;
  1781.   document.getElementById('overview_button').disabled = true;
  1782.   document.getElementById('import_button').disabled = true;
  1783.   document.getElementById('pending_button').disabled = true;
  1784.   document.getElementById('status_button').disabled = true;
  1785.   document.getElementById('from_date').disabled = true;
  1786.   document.getElementById('to_date').disabled = true;
  1787.   document.getElementById('search_term').disabled = true;
  1788.   document.getElementById('status_select').disabled = true;
  1789.   document.getElementById('donut_select').disabled = true;
  1790. }
  1791.  
  1792. HITStorage.enable_inputs = function()
  1793. {
  1794.   document.getElementById('delete_button').disabled = false;
  1795.   document.getElementById('search_button').disabled = false;
  1796.   document.getElementById('update_button').disabled = false;
  1797.   document.getElementById('overview_button').disabled = false;
  1798.   document.getElementById('import_button').disabled = false;
  1799.   document.getElementById('pending_button').disabled = false;
  1800.   document.getElementById('status_button').disabled = false;
  1801.   document.getElementById('from_date').disabled = false;
  1802.   document.getElementById('to_date').disabled = false;
  1803.   document.getElementById('search_term').disabled = false;
  1804.   document.getElementById('status_select').disabled = false;
  1805.   document.getElementById('donut_select').disabled = false;
  1806. }
  1807.  
  1808.  
  1809. HITStorage.do_search = function(options)
  1810. {
  1811.   HITStorage.indexedDB.getHITs(options);
  1812.  
  1813.   setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000);
  1814.  
  1815.   HITStorage.enable_inputs();
  1816. }
  1817.  
  1818. HITStorage.show_results = function(results)
  1819. {
  1820.   var odd = true;
  1821.   var sum = 0;
  1822.   var sum_rejected = 0;
  1823.   var sum_approved = 0;
  1824.   var sum_pending = 0;
  1825.   var new_day = false;
  1826.   var HITSlines = '';
  1827.  
  1828.   resultsWindow = window.open();
  1829.  
  1830.   for (var i=0; i<results.length; i++) {
  1831.     odd = !odd;
  1832.     sum += results[i].reward;
  1833.     if (results[i].status == 'Rejected')
  1834.       sum_rejected += results[i].reward;
  1835.     else if (results[i].status == 'Pending Approval')
  1836.       sum_pending += results[i].reward;
  1837.     else
  1838.       sum_approved += results[i].reward;
  1839.  
  1840.     if (i>0 && (results[i-1].date != results[i].date))
  1841.       new_day = true;
  1842.     else
  1843.       new_day = false;
  1844.     HITSlines += HITStorage.format_hit_line(results[i], odd, HITStorage.status_color(results[i].status), new_day );
  1845.   }
  1846.  
  1847.   resultsWindow.document.documentElement.innerHTML =
  1848.     "<head><title>Status Detail Search Results</title></head><body>\n" +
  1849.     "<h1>HITs matching your search:</h1>\n" +
  1850.     '<table style="border: 1px solid black;border-collapse:collapse;width:90%;margin-left:auto;margin-right:auto;">'+"\n" +
  1851.     '<tr style="background-color:lightgrey"><th>Date</th><th>Requester</th><th>HIT Title</th><th>Reward</th><th>Status</th><th>Feedback</th></tr>'+"\n" +
  1852.     HITSlines +
  1853.     '<tr style="background-color:lightgrey"><th></th><th></th><th></th><th>$' + sum.toFixed(2) + '</th><th></th><th></th></tr>'+"\n" +
  1854.     "</table>" +
  1855.     "<p>Found " + results.length + " matching HITs. $" + sum_approved.toFixed(2) + " approved, " +
  1856.     "$" + sum_rejected.toFixed(2) + " rejected and $" + sum_pending.toFixed(2) + " pending.</p>" +
  1857.     "</body>";
  1858.  
  1859.   resultsWindow.document.close();
  1860. }
  1861.  
  1862. HITStorage.status_color = function(status)
  1863. {
  1864.   var color = "green";
  1865.  
  1866.   if (status.match("Pending Approval"))
  1867.     color = "orange";
  1868.   else if (status.match("Rejected"))
  1869.     color = "red";
  1870.  
  1871.   return color;
  1872. }
  1873.  
  1874. HITStorage.format_hit_line = function(hit, odd, status_color, new_day)
  1875. {
  1876.   var line = '<tr style="background-color:';
  1877.   if (odd)
  1878.     line += '#f1f3eb;';
  1879.   else
  1880.     line += 'white;';
  1881.   line += ' valign=top;';
  1882.   if (new_day)
  1883.     line += ' border: 0px dotted #000000; border-width: 2px 0px 0px 0px">';
  1884.   else
  1885.     line += '">';
  1886.  
  1887.   line += '<td>' + hit.date + '</td>';
  1888.   if (hit.requesterLink != null)
  1889.     line += '<td style="width:165px"><a href="' + hit.requesterLink + '" title="Contact this Requester">' + hit.requesterName + '</a></td>';
  1890.   else
  1891.     line += '<td style="width:165px">' + hit.requesterName + '</td>';
  1892.   line += '<td style="width:213px">' + hit.title + '</td>';
  1893.   line += '<td style="width:45px">$' + hit.reward.toFixed(2) + '</td>';
  1894.   line += '<td style="color:' + status_color + '; width:55px">' + hit.status + '</td>';
  1895.   line += '<td><div style="width:225px; overflow:hidden">' + hit.feedback + '</div></td>';
  1896.   line += '</tr>\n';
  1897.   return line;
  1898. }
  1899.  
  1900. HITStorage.show_pending_overview = function(results)
  1901. {
  1902.  
  1903.   // 'requesterId,requesterName,pending,reward';
  1904.   var odd = false;
  1905.   var sum = 0;
  1906.   var pending = 0;
  1907.   var HITSlines = '';
  1908.  
  1909.   resultsWindow = window.open();
  1910.  
  1911.   for (var i=0; i<results.length; i++) {
  1912.     odd = !odd;
  1913.     sum += results[i][3];
  1914.     pending += results[i][2];
  1915.     HITSlines += HITStorage.format_pending_line(results[i], odd, i);
  1916.   }
  1917.  
  1918.   resultsWindow.document.documentElement.innerHTML =
  1919.     "<html><head><title>Summary of Pending HITs</title></head><body>\n" +
  1920.     "<h1>Summary of Pending HITs</h1>\n" +
  1921.     '<table style="border: 1px solid black;border-collapse:collapse;width:90%;margin-left:auto;margin-right:auto;">' + "\n" +
  1922.     '<tr style="background-color:lightgrey"><th>requesterId</th><th>Requester</th><th></th><th>Pending</th><th>Rewards</th>' + "\n" +
  1923.     HITSlines +
  1924.     '<tr style="background-color:lightgrey"><th>' + results.length + ' different requesterIds</th><th></th><th></th><th style="text-align: right">' + pending + '</th><th style="text-align: right">$' + sum.toFixed(2) + '</th>' + "\n" +
  1925.     "</table>" +
  1926.     "</body></html>";
  1927.  
  1928.   resultsWindow.document.close();
  1929.  
  1930.   for (var i=0; i<results.length; i++)
  1931.   {
  1932.     resultsWindow.document.getElementById('id-' + i).addEventListener("click", search_func(results[i][0], 'requesterId'), false);
  1933.     resultsWindow.document.getElementById('id2-' + i).addEventListener("click", show_requester_func(results[i][0]) , false);
  1934.   }
  1935. }
  1936.  
  1937. HITStorage.show_status_overview = function(results, date)
  1938. {
  1939.  
  1940.   var odd = false;
  1941.   var sum = 0;
  1942.   var submitted = 0;
  1943.   var approved = 0;
  1944.   var rejected = 0;
  1945.   var pending = 0;
  1946.   var new_month = false;
  1947.   var title = '';
  1948.   var HITSlines = '';
  1949.  
  1950.   resultsWindow = window.open();
  1951.  
  1952.   for (var i=results.length-1; i>=0; i--) {
  1953.     odd = !odd;
  1954.     sum += results[i].earnings;
  1955.     submitted += results[i].submitted;
  1956.     approved += results[i].approved;
  1957.     rejected += results[i].rejected;
  1958.     pending += results[i].pending;
  1959.     if (i<results.length-1)
  1960.       new_month = (results[i].date.substr(0,7) != results[i+1].date.substr(0,7));
  1961.     HITSlines += HITStorage.format_status_line(results[i], odd, new_month);
  1962.   }
  1963.  
  1964.   if (date)
  1965.     title = "<h1>Daily HIT stats</h1>\n";
  1966.   else
  1967.     title = "<h1>Daily HIT stats (' + date + ')</h1>\n";
  1968.  
  1969.  
  1970.   resultsWindow.document.documentElement.innerHTML =
  1971.     "<html><head><title>Daily HIT stats</title></head><body>\n" +
  1972.     title +
  1973.     '<table style="border: 1px solid black;border-collapse:collapse;width:90%;margin-left:auto;margin-right:auto;">' + "\n" +
  1974.     '<tr style="background-color:lightgrey"><th>Date</th><th>Submitted</th><th>Approved</th><th>Rejected</th><th>Pending</th><th>Earnings</th>' + "\n" +
  1975.     HITSlines +
  1976.     '<tr style="background-color:lightgrey"><th>' + results.length + ' days</th><th style="text-align: left">' + submitted +
  1977.                                '</th><th style="text-align: left">' + approved +
  1978.                                '</th><th style="text-align: left">' + rejected +
  1979.                                '</th><th style="text-align: left">' + pending +
  1980.                                '</th><th style="text-align: left">$' + sum.toFixed(2) + '</th>' + "\n" +
  1981.     "</table>" +
  1982.     "</body></html>";
  1983.  
  1984.   resultsWindow.document.close();
  1985.  
  1986.   for (var i=0; i<results.length; i++)
  1987.     resultsWindow.document.getElementById(results[i].date).addEventListener("click", search_func('', 'date', results[i].date, results[i].date), false);
  1988. }
  1989.  
  1990. HITStorage.show_requester_overview = function(results, date)
  1991. {
  1992.   // 'requesterId,requesterName,hits,pending,reward,rejected';
  1993.   var odd = false;
  1994.   var sum = 0;
  1995.   var hits = 0;
  1996.   var rejected = 0;
  1997.   var pending = 0;
  1998.   var new_day = false;
  1999.   var top = true;
  2000.   var dot_line;
  2001.   var HITSlines = '';
  2002.   var title = '';
  2003.  
  2004.   resultsWindow = window.open();
  2005.  
  2006.   for (var i=0; i<results.length; i++) {
  2007.     odd = !odd;
  2008.     sum += results[i][3];
  2009.     hits += results[i][2];
  2010.     rejected += results[i][4];
  2011.     pending += results[i][5];
  2012.     dot_line = false;
  2013.     if (i==10)
  2014.     {
  2015.       dot_line = true;
  2016.       top = false;
  2017.     }
  2018.     if (i>10 && results[i][3] == 0 && results[i-1][3] != 0)
  2019.       dot_line = true;
  2020.    
  2021.     HITSlines += HITStorage.format_overview_line(results[i], odd, dot_line, top, i);
  2022.   }
  2023.  
  2024.   if (date)
  2025.     title = "<h1>Requester Overview " + date + "</h1>\n";
  2026.   else
  2027.     title = "<h1>Requester Overview</h1>\n";
  2028.  
  2029.   resultsWindow.document.documentElement.innerHTML =
  2030.  
  2031.   "<html><head><title>Requester Overview</title></head><body>\n" +
  2032.   title +
  2033.   '<table style="border: 1px solid black;border-collapse:collapse;width:90%;margin-left:auto;margin-right:auto;">' + "\n" +
  2034.   '<tr style="background-color:lightgrey"><th>requesterId</th><th>Requester</th><th></th><th>HITs</th><th>Pending</th><th>Rewards</th><th colspan="2">Rejected</th></tr>' + "\n" +
  2035.   HITSlines +
  2036.   '<tr style="background-color:lightgrey"><th>' + results.length + ' different requesterIds</th>' +
  2037.                                 '<th></th><th></th><th style="text-align: right">' + hits + '<th style="text-align: right">' + pending +
  2038.                                 '</th><th style="text-align: right">$' + sum.toFixed(2) + '</th><th style="text-align: right">' + rejected + '</th>' +
  2039.                                 '<th style="text-align: right">' +
  2040.                                 (rejected/hits*100).toFixed(2) + '%</th></tr>' + "\n" +
  2041.   "</table>" +
  2042.   "<p>Reward includes all 'Paid' and 'Approved - Pending Payment' HITs. " +
  2043.                                 "Reward does not include any bonuses.</p>" +
  2044.   "</body></html>";
  2045.  
  2046.   resultsWindow.document.close();
  2047.  
  2048.   for (var i=0; i<results.length; i++)
  2049.   {
  2050.     resultsWindow.document.getElementById('id-' + i).addEventListener("click", search_func(results[i][0], 'requesterId'), false);
  2051.     resultsWindow.document.getElementById('id2-' + i).addEventListener("click", show_requester_func(results[i][0]) , false);
  2052.   }
  2053. }
  2054.  
  2055. HITStorage.show_requester = function(results)
  2056. {
  2057.   resultsWindow = window.open();
  2058.   resultsWindow.document.write('<html><head><title>' + results[0].requesterName + '</title></head><body>\n');
  2059.   resultsWindow.document.write('<h1>' + results[0].requesterName + ' (' + results[0].requesterId + ')</h1>\n');
  2060.  
  2061.   resultsWindow.document.write('You have submitted ' + results.length + ' HITs for this requester. Earliest ' + results[results.length-1].date +
  2062.                                ', latest ' + results[0].date);
  2063.  
  2064.   resultsWindow.document.write('<p><a href="https://www.mturk.com/mturk/searchbar?selectedSearchType=hitgroups&requesterId=' + results[0].requesterId + '">' +
  2065.                                'Search HITs created by this requester</a></p>');
  2066.  
  2067.  
  2068.   resultsWindow.document.write('<p><a href="http://turkopticon.differenceengines.com/' + results[0].requesterId + '">' +
  2069.                                'See reviews about this requester on Turkopticon</a> or ');
  2070.   resultsWindow.document.write('<a href="' + TO_report_link(results[0].requesterId,results[0].requesterName) + '">' +
  2071.                                'review this requester on Turkopticon</a></p>');
  2072.  
  2073.   var reward = 0;
  2074.   var hits = 0;
  2075.   var sum = 0;
  2076.   var rejected = 0;
  2077.   var approved = 0;
  2078.   var pending  = 0;
  2079.   var all_rejected = 0;
  2080.   var all_approved = 0;
  2081.   var all_pending  = 0;
  2082.  
  2083.   resultsWindow.document.write('<table style="border: 1px solid black;border-collapse:collapse;margin-left:10px;margin-right:auto;">\n');
  2084.   resultsWindow.document.write('<tr style="background-color:lightgrey"><th>Month' +
  2085.                                '</th><th>Submitted' +
  2086.                                '</th><th>Approved' +
  2087.                                '</th><th>Rejected' +
  2088.                                '</th><th>Pending' +
  2089.                                '</th><th>Earnings</th></tr>\n');
  2090.  
  2091.   for (var i=0; i<results.length; i++) {
  2092.     hits++;
  2093.     if (results[i].status == 'Rejected')
  2094.     {
  2095.       all_rejected++;
  2096.       rejected++;
  2097.     }
  2098.     else if (results[i].status == 'Pending Approval')
  2099.     {
  2100.       all_pending++;
  2101.       pending++;
  2102.     }
  2103.     else
  2104.     {
  2105.       all_approved++;
  2106.       approved++;
  2107.       sum += results[i].reward;
  2108.       reward += results[i].reward;
  2109.     }
  2110.    
  2111.     if (i==results.length-1 || (i<results.length-1 && (results[i].date.substr(0,7) != results[i+1].date.substr(0,7))))
  2112.     {
  2113.       resultsWindow.document.write('<tr><td style="text-align: right">' + results[i].date.substr(0,7) +
  2114.                                    '</td><td style="text-align: right">' + hits +
  2115.                                    '</td><td style="text-align: right">' + approved +
  2116.                                    '</td><td style="text-align: right">' + rejected +
  2117.                                    '</td><td style="text-align: right">' + pending +
  2118.                                    '</td><td style="text-align: right">$' + reward.toFixed(2) + '</td></tr>\n');
  2119.       reward = 0;
  2120.       hits = 0;
  2121.       approved = 0;
  2122.       rejected = 0;
  2123.       pending = 0;
  2124.     }
  2125.   }
  2126.   resultsWindow.document.write('<tr style="background-color:lightgrey"><th>' +
  2127.                                '</th><th style="text-align: right">' + results.length +
  2128.                                '</th><th style="text-align: right">' + all_approved +
  2129.                                '</th><th style="text-align: right">' + all_rejected +
  2130.                                '</th><th style="text-align: right">' + all_pending +
  2131.                                '</th><th style="text-align: right">$' + sum.toFixed(2) + '</th></tr>\n');
  2132.   resultsWindow.document.write('</table>');
  2133.  
  2134.   resultsWindow.document.write('<p>Rewards do not include any bonuses</p>');
  2135.  
  2136.   resultsWindow.document.write("</body></html>");
  2137.   resultsWindow.document.close();
  2138. }
  2139.  
  2140. function TO_report_link(requesterId, requesterName)
  2141. {
  2142.   return 'http://turkopticon.differenceengines.com/report?requester[amzn_id]=' + requesterId +
  2143.           '&requester[amzn_name]=' + encodeURI(requesterName.trim());
  2144. }
  2145.  
  2146. HITStorage.format_overview_line = function(req, odd, dot_line, top, i)
  2147. {
  2148.   var color;
  2149.   if (top)
  2150.     color = (odd)? 'ffffe0;' : '#eee8aa;';
  2151.   else
  2152.     color = (odd)? 'white;' : '#f1f3eb;';
  2153.   var line = '<tr style="background-color:' + color;
  2154.   if (dot_line)
  2155.     line += ' border: 0px dotted #000000; border-width: 2px 0px 0px 0px';
  2156.   line += '">';
  2157.   line += '<td><button type="button" title="Show all HITs" style="height: 16px;font-size: 8px; padding: 0px;" id="id-' +
  2158.            i + '">&gt;&gt;</button>' +
  2159.            '<button type="button" title="Show details about requester" style="height: 16px;font-size: 8px; padding: 0px;" id="id2-' +
  2160.            i + '">+</button> ' + req[0].trim() +
  2161.            '</td>';
  2162.   line += '<td><a title="Requesters Turkopticon page" target="_blank" href="http://turkopticon.differenceengines.com/' + req[0].trim() + '">[TO]</a> ';
  2163.   line += req[1].trim() + '</td>';
  2164.   line += '<td style="width: 50px"><a title="Report requester to Turkopticon" target="_blank" href="' + TO_report_link(req[0], req[1]) + '">[report]</a></td>';
  2165.   line += '<td style="text-align: right">' + req[2] + '</td>';
  2166.   line += '<td style="text-align: right">' + req[5] + '</td>';
  2167.   line += '<td style="text-align: right">$' + req[3].toFixed(2) + '</td>';
  2168.   var p = (req[4]/req[2]*100).toFixed(1);
  2169.   var pc = (p>0)? 'red' : 'green';
  2170.   line += '<td style="text-align: right; color:' + pc + ';">' + req[4] + '</td>';
  2171.   line += '<td style="text-align: right; color:' + pc + ';">' + p + '%</td>';
  2172.   line += '</tr>\n';
  2173.   return line;
  2174. }
  2175.  
  2176. HITStorage.format_pending_line = function(req, odd, i)
  2177. {
  2178.   var color = (odd)? 'white;' : '#f1f3eb;';
  2179.   var line = '<tr style="background-color:' + color;
  2180.   line += '">';
  2181.   line += '<td style="white-space: nowrap; width: 150px; margin-right: 10px;"><button type="button" title="Show all HITs" style="height: 16px;font-size: 8px; padding: 0px;" id="id-' +
  2182.            i + '">&gt;&gt;&gt;</button>' +
  2183.            '<button type="button" title="Show details about requester" style="height: 16px;font-size: 8px; padding: 0px;" id="id2-' +
  2184.            i + '">+</button> ' + req[0].trim() + '</td>';
  2185.   line += '<td><a title="Requesters Turkopticon page" target="_blank" href="http://turkopticon.differenceengines.com/' + req[0].trim() + '">[TO]</a> ';
  2186.   line += req[1].trim() + '</td>';
  2187.   line += '<td style="width: 50px"><a title="Report requester to Turkopticon" target="_blank" href="' + TO_report_link(req[0], req[1]) + '">[report]</a></td>';
  2188.   line += '<td style="text-align: right">' + req[2] + '</td>';
  2189.   line += '<td style="text-align: right">$' + req[3].toFixed(2) + '</td>';
  2190.   line += '</tr>\n';
  2191.   return line;
  2192. }
  2193.  
  2194. HITStorage.format_status_line = function(d, odd, new_month)
  2195. {
  2196.   var color = (odd)? 'white;' : '#f1f3eb;';
  2197.   var line = '<tr style="background-color:' + color;
  2198.   if (new_month)
  2199.     line += ' border: 0px dotted #000000; border-width: 2px 0px 0px 0px">';
  2200.   else
  2201.     line += '">';
  2202.   line += '<td><button type="button" title="Show all HITs" style="height: 16px;font-size: 8px; padding: 0px;" id="' +
  2203.            d.date + '">&gt;&gt;&gt;</button> ' + d.date + '</td>';
  2204.   line += '<td>' + d.submitted + '</td>';
  2205.   line += '<td>' + d.approved + '</td>';
  2206.   line += '<td>' + d.rejected + '</td>';
  2207.   line += '<td>' + d.pending + '</td>';
  2208.   line += '<td>$' + d.earnings.toFixed(2) + '</td>';
  2209.   line += '</tr>\n';
  2210.   return line;
  2211. }
  2212.  
  2213. HITStorage.show_pending_overview_csv = function(results)
  2214. {
  2215.   var csvData = 'requesterId,requesterName,pending,reward\n';
  2216.   for (var i=0; i<results.length; i++) {
  2217.     csvData += HITStorage.format_pending_line_csv(results[i]);
  2218.   }
  2219.   location.href='data:text/csv;charset=utf8,' + encodeURIComponent(csvData);
  2220. }
  2221.  
  2222. HITStorage.format_pending_line_csv = function(req)
  2223. {
  2224.   var line = '';
  2225.   line += req[0].trim() + ',';
  2226.   line += '"' + req[1].trim() + '",';
  2227.   line += req[2] + ',';
  2228.   line += req[3].toFixed(2);
  2229.   line += '\n';
  2230.   return line;
  2231. }
  2232.  
  2233.  
  2234. HITStorage.show_requester_overview_csv = function(results)
  2235. {
  2236.   var csvData = 'requesterId,requesterName,hits,reward,rejected,pending\n';
  2237.   for (var i=0; i<results.length; i++) {
  2238.     csvData += HITStorage.format_overview_line_csv(results[i]);
  2239.   }
  2240.   location.href='data:text/csv;charset=utf8,' + encodeURIComponent(csvData);
  2241. }
  2242.  
  2243. HITStorage.format_overview_line_csv = function(req)
  2244. {
  2245.   var line = '';
  2246.   line += req[0].trim() + ',';
  2247.   line += '"' + req[1].trim() + '",';
  2248.   line += req[2] + ',';
  2249.   line += req[3].toFixed(2) + ',';
  2250.   line += req[4] + ',';
  2251.   line += req[5];
  2252.   line += '\n';
  2253.   return line;
  2254. }
  2255.  
  2256. HITStorage.show_status_overview_csv = function(results)
  2257. {
  2258.   var csvData = 'Date,Submitted,Approved,Rejected,Pending,Earnings\n';
  2259.   for (var i=results.length-1; i>=0; i--) {
  2260.     csvData += HITStorage.format_status_line_csv(results[i]);
  2261.   }
  2262.   location.href='data:text/csv;charset=utf8,' + encodeURIComponent(csvData);
  2263. }
  2264.  
  2265. HITStorage.format_status_line_csv = function(d)
  2266. {
  2267.   var line = '';
  2268.   line += '"' + d.date + '",';
  2269.   line += d.submitted + ',';
  2270.   line += d.approved + ',';
  2271.   line += d.rejected + ',';
  2272.   line += d.pending + ',';
  2273.   line += d.earnings.toFixed(2);
  2274.   line += '\n';
  2275.   return line;
  2276. }
  2277.  
  2278. HITStorage.export_csv = function(results)
  2279. {
  2280.   var csvData = 'hitId,date,requesterName,requesterId,title,reward,status,feedback\n';
  2281.   for (var i=0; i<results.length; i++) {
  2282.     csvData += HITStorage.format_csv_line(results[i]);
  2283.   }
  2284.   location.href='data:text/csv;charset=utf8,' + encodeURIComponent(csvData);
  2285. }
  2286.  
  2287. HITStorage.format_csv_line = function(hit)
  2288. {
  2289.   var line = '';
  2290.   line += '"' + hit.hitId.trim() + '",';
  2291.   line += '"' + hit.date.trim() + '",';
  2292.   line += '"' + hit.requesterName.trim() + '",';
  2293.   line += '"' + hit.requesterId.trim() + '",';
  2294.   line += '"' + hit.title.trim() + '",';
  2295.   line += hit.reward.toFixed(2) + ',';
  2296.   line += '"' + hit.status.trim().replace(/\&nbsp;/g,' ') + '",';
  2297.   line += '"' + hit.feedback.trim() + '"';
  2298.   line += '\n';
  2299.   return line;
  2300. }
  2301.  
  2302. HITStorage.do_update = function(days_to_update)
  2303. {
  2304.   if (DAYS_TO_FETCH.length<1)
  2305.   {
  2306.     HITStorage.check_update();
  2307.     return;
  2308.   }
  2309.   HITStorage.update_status_label('Please wait: ' + progress_bar(days_to_update-DAYS_TO_FETCH.length, days_to_update) +
  2310.                                  ' (' + (days_to_update-DAYS_TO_FETCH.length) + '/' + days_to_update + ')', 'red');
  2311.  
  2312.   var hits = [];
  2313.   setTimeout(function(){ HITStorage.getHITData( DAYS_TO_FETCH.shift(), hits, 1, days_to_update); }, 2000);
  2314. }
  2315.  
  2316. HITStorage.update_done = function()
  2317. {
  2318.   HITStorage.update_status_label('Script monkeys have updated your local database', 'green');
  2319.   setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 5000);
  2320.  
  2321.   HITStorage.enable_inputs();
  2322.  
  2323.   localStorage['HITDB UPDATED'] = new Date().toString();
  2324.  
  2325.   var e = document.getElementById('user_activities.date_column_header.tooltip').parentNode.parentNode.childNodes[2].childNodes[1].childNodes[1];
  2326.   if (e != null && e.textContent.trim() == 'Today') {
  2327.     var today = e.href.slice(-8);
  2328.     today = convert_date(today);
  2329.     HITStorage.indexedDB.get_todays_projected_earnings(today);
  2330.   }
  2331.   HITStorage.indexedDB.get_pending_approvals();
  2332.   HITStorage.indexedDB.get_pending_payments();
  2333. }
  2334.  
  2335.  
  2336. HITStorage.update_database = function()
  2337. {
  2338.   HITStorage.disable_inputs();
  2339.  
  2340.   if (localStorage['HITDB TRY_EXTRA_DAYS'] == 'YES') {
  2341.     DAYS_TO_FETCH = HITStorage.getAllAvailableDays(20);
  2342.     delete localStorage['HITDB TRY_EXTRA_DAYS'];
  2343.   }
  2344.   else
  2345.   {
  2346.     DAYS_TO_FETCH = HITStorage.getAllAvailableDays();
  2347.   }
  2348.   DAYS_TO_FETCH_CHECK = DAYS_TO_FETCH.slice(0);
  2349.  
  2350.   // remove extra days from checklist
  2351.   for (var i=0; i<DAYS_TO_FETCH_CHECK.length; i++)
  2352.   {
  2353.     if (DAYS_TO_FETCH_CHECK[i].submitted == -1) {
  2354.       DAYS_TO_FETCH_CHECK = DAYS_TO_FETCH_CHECK.slice(0,i);
  2355.       break;
  2356.     }
  2357.   }
  2358.  
  2359.  
  2360.   HITStorage.update_status_label('Please wait: script monkeys are preparing to start working', 'red');
  2361.   setTimeout(function(){ HITStorage.prepare_update_and_check_pending_payments(); }, 100);  
  2362. }
  2363.  
  2364. HITStorage.show_overview = function()
  2365. {
  2366.   if (HITStorage.validate_dates() == false)
  2367.     return;
  2368.   var options = {};
  2369.   options.term = document.getElementById('search_term').value;
  2370.   options.status = document.getElementById('status_select').value;
  2371.   options.donut = document.getElementById('donut_select').value;
  2372.   options.from_date = document.getElementById('from_date').value;
  2373.   options.to_date = document.getElementById('to_date').value;
  2374.   options.export_csv = document.getElementById('export_csv').checked;
  2375.  
  2376.   HITStorage.update_status_label('Please wait: script monkeys are picking bananas 😋', 'red');
  2377.   HITStorage.disable_inputs();
  2378.   HITStorage.indexedDB.requesterOverview(options);
  2379. }
  2380.  
  2381. HITStorage.show_pendings = function()
  2382. {
  2383.   var options = {};
  2384.   options.term = document.getElementById('search_term').value;
  2385.   options.status = document.getElementById('status_select').value;
  2386.   options.donut = document.getElementById('donut_select').value;
  2387.   options.from_date = document.getElementById('from_date').value;
  2388.   options.to_date = document.getElementById('to_date').value;
  2389.   options.export_csv = document.getElementById('export_csv').checked;
  2390.  
  2391.   HITStorage.update_status_label('Please wait: script monkeys are picking bananas 😋', 'red');
  2392.   HITStorage.disable_inputs();
  2393.   HITStorage.indexedDB.pendingOverview(options);
  2394. }
  2395.  
  2396. HITStorage.show_status = function()
  2397. {
  2398.   if (HITStorage.validate_dates() == false)
  2399.     return;
  2400.   var options = {};
  2401.   options.term = document.getElementById('search_term').value;
  2402.   options.status = document.getElementById('status_select').value;
  2403.   options.donut = document.getElementById('donut_select').value;
  2404.   options.from_date = document.getElementById('from_date').value;
  2405.   options.to_date = document.getElementById('to_date').value;
  2406.   options.export_csv = document.getElementById('export_csv').checked;
  2407.  
  2408.   HITStorage.update_status_label('Please wait: script monkeys are picking bananas 😋', 'red');
  2409.   HITStorage.disable_inputs();
  2410.   HITStorage.indexedDB.statusOverview(options);
  2411. }
  2412.  
  2413. var IMPORT_DIALOG = null;
  2414.  
  2415. function import_dialog()
  2416. {
  2417.   if (IMPORT_DIALOG == null)
  2418.   {
  2419.     IMPORT_DIALOG = document.createElement('div');
  2420.     IMPORT_DIALOG.style.display = 'block';
  2421.  
  2422.     IMPORT_DIALOG.style.position = 'fixed';
  2423.     IMPORT_DIALOG.style.width = '600px';
  2424.     //IMPORT_DIALOG.style.height = '400px';
  2425.     IMPORT_DIALOG.style.height = '90%';
  2426.     IMPORT_DIALOG.style.left = '50%';
  2427.     IMPORT_DIALOG.style.right = '50%';
  2428.     IMPORT_DIALOG.style.margin = '-300px 0px 0px -300px';
  2429.     //IMPORT_DIALOG.style.top = '400px';
  2430.     IMPORT_DIALOG.style.bottom = '10px';
  2431.     IMPORT_DIALOG.style.padding = '10px';
  2432.     IMPORT_DIALOG.style.border = '2px';
  2433.     IMPORT_DIALOG.style.textAlign = 'center';
  2434.     IMPORT_DIALOG.style.verticalAlign = 'middle';
  2435.     IMPORT_DIALOG.style.borderStyle = 'solid';
  2436.     IMPORT_DIALOG.style.borderColor = 'black';
  2437.     IMPORT_DIALOG.style.backgroundColor = 'white';
  2438.     IMPORT_DIALOG.style.color = 'black';
  2439.     IMPORT_DIALOG.style.zIndex = '100';
  2440.  
  2441.     var table = document.createElement('table');
  2442.     var input = document.createElement('textarea');
  2443.     var input2 = document.createElement('input');
  2444.     var label = document.createElement('label');
  2445.     var label2 = document.createElement('label');
  2446.  
  2447.     label.textContent = 'Paste CSV-file in the textarea below.';
  2448.     label2.textContent = 'CVS separator: ';
  2449.     input.style.width = '100%';
  2450.     input.style.height = '90%';
  2451.  
  2452.     input2.maxLength = '1';
  2453.     input2.size = '1';
  2454.     input2.defaultValue = ',';
  2455.  
  2456.     var import_button = document.createElement('button');
  2457.     import_button.textContent = 'Import HITs';
  2458.     import_button.addEventListener("click", import_dialog_close_func(true, input, input2), false);
  2459.     import_button.style.margin = '5px';
  2460.     var cancel_button = document.createElement('button');
  2461.     cancel_button.textContent = 'Cancel';
  2462.     cancel_button.addEventListener("click", import_dialog_close_func(false, input, input2), false);
  2463.     cancel_button.style.margin = '5px';
  2464.  
  2465.     IMPORT_DIALOG.appendChild(label);
  2466.     IMPORT_DIALOG.appendChild(document.createElement('br'));
  2467.     IMPORT_DIALOG.appendChild(label2);
  2468.     IMPORT_DIALOG.appendChild(input2);
  2469.     IMPORT_DIALOG.appendChild(document.createElement('br'));
  2470.     IMPORT_DIALOG.appendChild(input);
  2471.     IMPORT_DIALOG.appendChild(document.createElement('br'));
  2472.     IMPORT_DIALOG.appendChild(cancel_button);
  2473.     IMPORT_DIALOG.appendChild(import_button);
  2474.     document.body.appendChild(IMPORT_DIALOG);
  2475.   }
  2476.   else
  2477.   {
  2478.     IMPORT_DIALOG.style.display = 'block';
  2479.   }
  2480. }
  2481.  
  2482.  
  2483. /*
  2484.  * CSVToArray() function is taken from:
  2485.  *
  2486.  *  Blog Entry:
  2487.  *  Ask Ben: Parsing CSV Strings With Javascript Exec() Regular Expression Command
  2488.  * 
  2489.  *   Author:
  2490.  *   Ben Nadel / Kinky Solutions
  2491.  * 
  2492.  *   Link:
  2493.  *   http://www.bennadel.com/index.cfm?event=blog.view&id=1504
  2494.  * 
  2495.  *   Date Posted:
  2496.  *   Feb 19, 2009 at 10:03 AM
  2497.  */
  2498.     // This will parse a delimited string into an array of
  2499.     // arrays. The default delimiter is the comma, but this
  2500.     // can be overriden in the second argument.
  2501. function CSVToArray( strData, strDelimiter ) {
  2502.         // Check to see if the delimiter is defined. If not,
  2503.         // then default to comma.
  2504.         strDelimiter = (strDelimiter || ",");
  2505.  
  2506.         // Create a regular expression to parse the CSV values.
  2507.         var objPattern = new RegExp(
  2508.             (
  2509.                 // Delimiters.
  2510.                 "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
  2511.  
  2512.                 // Quoted fields.
  2513.                 "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
  2514.  
  2515.                 // Standard fields.
  2516.                 "([^\"\\" + strDelimiter + "\\r\\n]*))"
  2517.             ),
  2518.             "gi"
  2519.             );
  2520.  
  2521.         // Create an array to hold our data. Give the array
  2522.         // a default empty first row.
  2523.         var arrData = [[]];
  2524.  
  2525.         // Create an array to hold our individual pattern
  2526.         // matching groups.
  2527.         var arrMatches = null;
  2528.  
  2529.  
  2530.         // Keep looping over the regular expression matches
  2531.         // until we can no longer find a match.
  2532.         while (arrMatches = objPattern.exec( strData )){
  2533.  
  2534.             // Get the delimiter that was found.
  2535.             var strMatchedDelimiter = arrMatches[ 1 ];
  2536.  
  2537.             // Check to see if the given delimiter has a length
  2538.             // (is not the start of string) and if it matches
  2539.             // field delimiter. If id does not, then we know
  2540.             // that this delimiter is a row delimiter.
  2541.             if (
  2542.                 strMatchedDelimiter.length &&
  2543.                 (strMatchedDelimiter != strDelimiter)
  2544.                 ){
  2545.  
  2546.                 // Since we have reached a new row of data,
  2547.                 // add an empty row to our data array.
  2548.                 arrData.push( [] );
  2549.  
  2550.             }
  2551.  
  2552.  
  2553.             // Now that we have our delimiter out of the way,
  2554.             // let's check to see which kind of value we
  2555.             // captured (quoted or unquoted).
  2556.             if (arrMatches[ 2 ]){
  2557.  
  2558.                 // We found a quoted value. When we capture
  2559.                 // this value, unescape any double quotes.
  2560.                 var strMatchedValue = arrMatches[ 2 ].replace(
  2561.                     new RegExp( "\"\"", "g" ),
  2562.                     "\""
  2563.                     );
  2564.  
  2565.             } else {
  2566.  
  2567.                 // We found a non-quoted value.
  2568.                 var strMatchedValue = arrMatches[ 3 ];
  2569.  
  2570.             }
  2571.  
  2572.  
  2573.             // Now that we have our value string, let's add
  2574.             // it to the data array.
  2575.             arrData[ arrData.length - 1 ].push( strMatchedValue );
  2576.         }
  2577.  
  2578.         // Return the parsed data.
  2579.         return( arrData );
  2580. }
  2581.  
  2582. function import_dialog_close_func(save, input, separator)
  2583. {
  2584.   return function()
  2585.   {
  2586.     if (save == true)
  2587.     {
  2588.      
  2589.       var lines = [];
  2590.       var hits = [];
  2591.  
  2592.       if (input.value.length > 0)
  2593.         lines = CSVToArray(input.value, separator.value);
  2594.  
  2595.       var errors = 0;
  2596.       for (var i = 0; i<lines.length; i++)
  2597.       {
  2598.         var error = false;
  2599.         try {
  2600.           if (lines[i][0] == null || lines[i][0] == 'hitId')
  2601.             continue;
  2602.  
  2603.           if(lines[i][6] == 'Approved - Pending Payment')
  2604.             lines[i][6] = 'Approved&nbsp;- Pending&nbsp;Payment';        
  2605.        
  2606.           if (lines[i].length != 8)
  2607.             error = true;
  2608.        
  2609.           var hit = {
  2610.                 hitId         : lines[i][0],
  2611.                 date          : convert_date(lines[i][1]),
  2612.                 requesterName : lines[i][2],
  2613.                 //requesterLink : null,
  2614.                 requesterId   : lines[i][3],
  2615.                 title         : lines[i][4],
  2616.                 reward        : parseFloat(lines[i][5]),
  2617.                 status        : lines[i][6],
  2618.                 feedback      : lines[i][7] || "" // If no feedback, put empty string
  2619.           };
  2620.         } catch(err) { error = true; }
  2621.  
  2622.         if (error == false)
  2623.           hits.push(hit);
  2624.         else
  2625.           errors++;
  2626.       }
  2627.       if (hits.length < 1)
  2628.       {
  2629.         alert('No HITs found!');
  2630.         return;
  2631.       }
  2632.       else if (confirm('Found ' + hits.length + ' HITs' + (errors>0? ' and ' + errors + (errors==1? ' error' : ' errors') : '') +
  2633.                   '.\nDo not reload this page until import is ready.\n' +
  2634.                   'Press Ok to start.') == true)
  2635.       {
  2636.         HITStorage.disable_inputs();
  2637.         HITStorage.update_status_label('Please wait: importing HITs', 'red');
  2638.         IMPORT_DIALOG.style.display = 'none';
  2639.         input.value = '';
  2640.         HITStorage.indexedDB.importHITs(hits);
  2641.         return;
  2642.       }
  2643.       else { return; }
  2644.     }
  2645.    
  2646.     IMPORT_DIALOG.style.display = 'none';
  2647.     input.value = '';
  2648.   };
  2649. }
  2650.  
  2651. function get_requester_id(s) {
  2652.   var idx = 12 + s.search('requesterId=');
  2653.   return s.substr(idx);
  2654. }
  2655.  
  2656. function show_requester_func(requesterId)
  2657. {
  2658.   return function()
  2659.   {
  2660.     HITStorage.indexedDB.showRequester(requesterId);
  2661.   };  
  2662. }
  2663.  
  2664. function search_func(key, index, d1, d2)
  2665. {
  2666.   d1 = d1 || '';
  2667.   d2 = d2 || d1;
  2668.   return function()
  2669.   {
  2670.     HITStorage.indexedDB.getHITs({term: key, index: index, status: '---', from_date: d1, to_date: d2, donut: '', this_day: ''});
  2671.   };  
  2672. }
  2673.  
  2674. function visible_func(element, visible)
  2675. {
  2676.   return function()
  2677.   {
  2678.     element.style.visibility = (visible)? 'visible' : 'hidden';
  2679.   };  
  2680. }
  2681.  
  2682. function delete_func()
  2683. {
  2684.   return function()
  2685.   {
  2686.     if (confirm('This will remove your local HIT DataBase!\nContinue?'))
  2687.     {
  2688.       HITStorage.indexedDB.deleteDB();
  2689.     }
  2690.   };  
  2691. }
  2692.  
  2693. function import_func()
  2694. {
  2695.   return function()
  2696.   {
  2697.     import_dialog();
  2698.   };  
  2699. }
  2700.  
  2701. function note_func(id, label)
  2702. {
  2703.   return function()
  2704.   {
  2705.     note = prompt('Note for requesterId \'' + id + '\':', label.textContent);
  2706.    
  2707.     if (note == null)
  2708.     {
  2709.       return;  
  2710.     }    
  2711.    
  2712.     HITStorage.indexedDB.addNote(id, note);
  2713.     label.textContent = note;
  2714.    
  2715.     label.style.border = '1px dotted';
  2716.     if (note.indexOf('!') >= 0)
  2717.       label.style.color = 'red';
  2718.     else
  2719.       label.style.color = 'black';
  2720.   };  
  2721. }
  2722.  
  2723. function block_func(requesterId, title, hitElement)
  2724. {
  2725.   return function()
  2726.   {
  2727.     re = prompt('Block HITs from requesterId \'' + requesterId + '\' matching:\n' +
  2728.                 '(default matches only exactly same HIT title, leave empty to match all HITS)', '^'
  2729.                  + title.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1") + '$');
  2730.    
  2731.     if (re == null)
  2732.     {
  2733.       return;  
  2734.     }
  2735.     re = new RegExp(re);
  2736.  
  2737.     if (!re.test(title)) {
  2738.       if (confirm("Your regular expression does not match current HIT title.\nSave it anyway?") == false)
  2739.         return;
  2740.     }
  2741.  
  2742.     HITStorage.indexedDB.addBlock(requesterId, re);
  2743.   };  
  2744. }
  2745.  
  2746. function unblock_func(requesterId, title)
  2747. {
  2748.   return function()
  2749.   {
  2750.     var unblock = confirm('Unblocking removes all blocks that match this HITs title and requesterId.');
  2751.     if (unblock == true)
  2752.     {
  2753.       HITStorage.indexedDB.removeBlocks(requesterId, title);
  2754.     }
  2755.   };  
  2756. }
  2757.  
  2758. function auto_update_func()
  2759. {
  2760.   return function()
  2761.   {
  2762.     var button = document.getElementById('auto_button');    
  2763.    
  2764.     if (localStorage['HITDB AUTO UPDATE'] === undefined)
  2765.     {
  2766.       alert('Enable Hit DataBase Auto Update\nWhen enabled, script will fetch last ' +
  2767.             'statusdetail pages and add them to database when this page is reloaded ' +
  2768.             'and at least two minutes have passed from last update. You still need to ' +
  2769.             'do full update from dashboard every now and then.');
  2770.       button.textContent = 'Auto Update is ON';
  2771.       button.style.color = 'green';
  2772.       localStorage['HITDB AUTO UPDATE'] = 'ON';
  2773.     }
  2774.     else if (localStorage['HITDB AUTO UPDATE'] == 'ON')
  2775.     {
  2776.       button.textContent = 'Auto Update is OFF';
  2777.       button.style.color = 'red';
  2778.       localStorage['HITDB AUTO UPDATE'] = 'OFF';
  2779.     }
  2780.     else
  2781.     {
  2782.       button.textContent = 'Auto Update is ON';
  2783.       button.style.color = 'green';
  2784.       localStorage['HITDB AUTO UPDATE'] = 'ON';
  2785.     }
  2786.   };  
  2787. }
  2788.  
  2789. function set_target_func(date)
  2790. {
  2791.   return function()
  2792.   {
  2793.     var target = localStorage['TODAYS TARGET'];
  2794.     if (target === undefined)
  2795.       target = '';
  2796.     else
  2797.       target = parseFloat(localStorage['TODAYS TARGET']).toFixed(2);
  2798.     target = prompt('Set your target:', target);
  2799.    
  2800.     if (target == null)
  2801.       return;    
  2802.     target = parseFloat(target);
  2803.    
  2804.     localStorage['TODAYS TARGET'] = target.toFixed(2);
  2805.     if (date != null)
  2806.       HITStorage.indexedDB.get_todays_projected_earnings(date);
  2807.   };  
  2808. }
  2809.  
  2810. function random_face()
  2811. {
  2812.   var faces = ['😁','😃','😄','😇','😈','😉','😊','😋','😌','😍','😐','😎','😸','😹','😺','😻'];
  2813.   var n = Math.floor((Math.random()*faces.length));
  2814.   return '<span style="color: black; font-weight: normal;" title="Featured non-amazonian script ' + ((n>11) ? '... kitten?': 'monkey') + '">' + faces[n] + '</span>';
  2815. }
  2816.  
  2817. function progress_bar(done, max, full, empty, c1, c2)
  2818. {
  2819.   max = (max<1)? 1 : max;  
  2820.   done = (done<0)? 0 : done;  
  2821.   done = (done>max)? max : done;  
  2822.  
  2823.   var bar = '<span style="color: ' + (c1||'green') + '">';
  2824.   for (var i=0; i<done; i++)
  2825.   {
  2826.     bar += full || '■';
  2827.   }
  2828.   bar += '</span><span style="color: ' + (c2||'black') + '">';
  2829.   for (var i=done; i<max; i++)
  2830.   {
  2831.     bar += empty || '⬜';
  2832.   }
  2833.   bar += '</span>';
  2834.   return bar;
  2835. }
  2836.  
  2837. // convert date to more practical form (MMDDYYYY => YYYY-MM-DD)
  2838. function convert_date(date)
  2839. {
  2840.   if (date.indexOf('-') > 0)
  2841.     return date;
  2842.   var day   = date.substr(2,2);
  2843.   var month = date.substr(0,2);
  2844.   var year  = date.substr(4,4);
  2845.   return (year + '-' + month + '-' + day);
  2846. }
  2847.  
  2848. // convert date from YYYY-MM-DD to MMDDYYYY if it isn't already
  2849. function convert_iso_date(date)
  2850. {
  2851.   if (date.indexOf('-') < 0)
  2852.     return date;
  2853.   var t = date.split('-');
  2854.   return t[1] + t[2] + t[0];
  2855. }
  2856.  
  2857. // Format date for display YYYY-MM-DD, DD/MM/YYYY or DD.MM.YYYY
  2858. function display_date(date, format)
  2859. {
  2860.   if (format === undefined || format == null)
  2861.     return date;
  2862.  
  2863.   var d = date.split('-');
  2864.  
  2865.   if (format == 'little')
  2866.   {
  2867.     return d[2] + '.' + d[1] + '.' + d[0];
  2868.   }
  2869.   if (format == 'middle')
  2870.   {
  2871.     return d[1] + '/' + d[2] + '/' + d[0];
  2872.   }
  2873. }
  2874.  
  2875. HITStorage.indexedDB.create();
  2876.  
  2877. // Backup plan
  2878. //HITStorage.update_date_format(true);
  2879.  
  2880. if (document.location.href.match('https://www.mturk.com/mturk/dashboard'))
  2881. {  
  2882.   var footer = document.getElementsByClassName('footer_separator')[0];
  2883.   if (footer == null)
  2884.     return;
  2885.  
  2886.   var extra_table = document.createElement('table');
  2887.   extra_table.width = '700';
  2888.   extra_table.style.boder = '1px solid black';
  2889.   extra_table.align = 'center';
  2890.   extra_table.cellSpacing = '0px';
  2891.   extra_table.cellPadding = '0px';
  2892.   var row1 = document.createElement('tr');
  2893.   var row2 = document.createElement('tr');
  2894.   var td1 = document.createElement('td');
  2895.   var content_td = document.createElement('td');
  2896.   var whatsthis = document.createElement('a');
  2897.  
  2898.   row1.style.height = '25px';
  2899.   td1.setAttribute('class', 'white_text_14_bold');
  2900.   td1.style.backgroundColor = '#7fb448';//'#7fb4cf';
  2901.   td1.style.paddingLeft = '10px';
  2902.   td1.innerHTML = 'HIT DataBase' + random_face() + ' ';
  2903.   content_td.setAttribute('class', 'container-content');  
  2904.  
  2905.   whatsthis.href = 'http://userscripts.org/scripts/show/149548';
  2906.   whatsthis.setAttribute('class', 'whatis');
  2907.   whatsthis.textContent = '(What\'s this?)';
  2908.  
  2909.   extra_table.appendChild(row1);
  2910.   row1.appendChild(td1);
  2911.   td1.appendChild(whatsthis);
  2912.   extra_table.appendChild(row2);
  2913.   row2.appendChild(content_td);
  2914.   footer.parentNode.insertBefore(extra_table, footer);  
  2915.  
  2916.   var my_bar = document.createElement('div');
  2917.   var search_button = document.createElement('button');
  2918.   var status_select = document.createElement('select');
  2919.   var label = document.createElement('label');
  2920.   var label2 = document.createElement('label');
  2921.   var input = document.createElement('input');
  2922.   var donut_select = document.createElement('select');
  2923.   var csv_label = document.createElement('label');
  2924.   var csv = document.createElement('input');
  2925.  
  2926.   var update_button = document.createElement('button');
  2927.   var delete_button = document.createElement('button');
  2928.   var pending_button = document.createElement('button');
  2929.   var overview_button = document.createElement('button');
  2930.   var import_button = document.createElement('button');
  2931.   var status_button = document.createElement('button');
  2932.  
  2933.   var from_input = document.createElement('input');
  2934.   var to_input = document.createElement('input');
  2935.   var date_label1 = document.createElement('label');
  2936.   var date_label2 = document.createElement('label');
  2937.   date_label1.textContent = 'from date ';
  2938.   date_label2.textContent = ' to ';
  2939.   from_input.setAttribute('id', "from_date");
  2940.   to_input.setAttribute('id', "to_date");
  2941.   to_input.setAttribute('maxlength', "10");
  2942.   from_input.setAttribute('maxlength', "10");
  2943.   to_input.setAttribute('size', "10");
  2944.   from_input.setAttribute('size', "10");
  2945.   from_input.title = 'Date format YYYY-MM-DD\nOr leave empty.';
  2946.   to_input.title = 'Date format YYYY-MM-DD\nOr leave empty.';
  2947.  
  2948.   var donut_options = [];
  2949.   donut_options[0] = document.createElement("option");
  2950.   donut_options[1] = document.createElement("option");
  2951.   donut_options[2] = document.createElement("option");
  2952.   donut_options[0].text = "---";
  2953.   donut_options[1].text = "Donut Chart HITS";
  2954.   donut_options[2].text = "Donut Chart REWARDS";
  2955.   donut_options[0].value = "---";
  2956.   donut_options[1].value = "HITS";
  2957.   donut_options[2].value = "REWARDS";
  2958.  
  2959.   var status_options = [];
  2960.   status_options[0] = document.createElement("option");
  2961.   status_options[1] = document.createElement("option");
  2962.   status_options[2] = document.createElement("option");
  2963.   status_options[3] = document.createElement("option");
  2964.   status_options[4] = document.createElement("option");
  2965.   status_options[5] = document.createElement("option");
  2966.   status_options[0].text = "Pending Approval";
  2967.   status_options[0].style.color = "orange";
  2968.   status_options[1].text = "Rejected";
  2969.   status_options[1].style.color = "red";
  2970.   status_options[2].text = "Approved - Pending Payment";
  2971.   status_options[2].style.color = "green";
  2972.   status_options[3].text = "Paid";
  2973.   status_options[3].style.color = "green";
  2974.   status_options[4].text = "Paid AND Approved";
  2975.   status_options[4].style.color = "green";
  2976.   status_options[5].text = "ALL";
  2977.   status_options[0].value = "Pending Approval";
  2978.   status_options[1].value = "Rejected";
  2979.   status_options[2].value = "Approved";
  2980.   status_options[3].value = "Paid";
  2981.   status_options[4].value = "Paid|Approved";
  2982.   status_options[5].value = "---";
  2983.  
  2984.   search_button.setAttribute('id', "search_button");
  2985.   input.setAttribute('id', "search_term");
  2986.   status_select.setAttribute('id', "status_select");
  2987.   label.setAttribute('id', "status_label");
  2988.   donut_select.setAttribute('id', "donut_select");
  2989.   delete_button.setAttribute('id', "delete_button");
  2990.   update_button.setAttribute('id', "update_button");
  2991.   overview_button.setAttribute('id', "overview_button");
  2992.   import_button.setAttribute('id', "import_button");
  2993.   pending_button.setAttribute('id', "pending_button");
  2994.   status_button.setAttribute('id', "status_button");
  2995.  
  2996.   my_bar.style.marginLeft = 'auto';
  2997.   my_bar.style.marginRight = 'auto';
  2998.   my_bar.style.textAlign = 'center';
  2999.   label.style.marginLeft = 'auto';
  3000.   label.style.marginRight = 'auto';
  3001.   label.style.textAlign = 'center';
  3002.  
  3003.   var donut = document.createElement('div');
  3004.   donut.setAttribute('id', "container");
  3005.   donut.style.display = 'none';
  3006.  
  3007.   content_td.appendChild(my_bar);
  3008.   my_bar.appendChild(delete_button);
  3009.   my_bar.appendChild(import_button);
  3010.   my_bar.appendChild(update_button);
  3011.   my_bar.appendChild(document.createElement("br"));
  3012.   my_bar.appendChild(pending_button);
  3013.   my_bar.appendChild(overview_button);
  3014.   my_bar.appendChild(status_button);
  3015.   my_bar.appendChild(document.createElement("br"));
  3016.   my_bar.appendChild(donut_select);
  3017.   my_bar.appendChild(status_select);
  3018.   my_bar.appendChild(label2);
  3019.   my_bar.appendChild(input);
  3020.   my_bar.appendChild(search_button);
  3021.   my_bar.appendChild(document.createElement("br"));
  3022.   my_bar.appendChild(date_label1);
  3023.   my_bar.appendChild(from_input);
  3024.   my_bar.appendChild(date_label2);
  3025.   my_bar.appendChild(to_input);
  3026.   my_bar.appendChild(csv_label);
  3027.   my_bar.appendChild(csv);
  3028.   my_bar.appendChild(document.createElement("br"));  
  3029.   my_bar.appendChild(label);
  3030.   my_bar.appendChild(document.createElement("br"));  
  3031.   (footer.parentNode).insertBefore(donut, footer);
  3032.  
  3033.   my_bar.style.textAlign = "float";
  3034.   search_button.textContent = "Search";
  3035.   search_button.title = "Search from local HIT database\nYou can set time limits and export as CSV-file";
  3036.   label2.textContent = " HITs matching: ";
  3037.   input.value = "";
  3038.  
  3039.   label.textContent = "Search powered by non-amazonian script monkeys";
  3040.  
  3041.   for (var i=0; i<status_options.length; i++)
  3042.     status_select.options.add(status_options[i]);
  3043.   for (var i=0; i<donut_options.length; i++)
  3044.     donut_select.options.add(donut_options[i]);
  3045.  
  3046.   update_button.title = "Fetch status pages and copy HITs to local indexed database.\nFirst time may take several minutes!";
  3047.   update_button.textContent = "Update database";
  3048.   update_button.style.color = 'green';
  3049.   update_button.style.margin = '5px 5px 5px 5x';
  3050.   delete_button.textContent = "Delete database";
  3051.   delete_button.style.color = 'red';
  3052.   delete_button.style.margin = '5px 5px 5px 5px';
  3053.   delete_button.title = "Delete Local DataBase!";
  3054.   import_button.textContent = "Import";
  3055.   import_button.style.margin = '5px 5px 5px 5px';
  3056.   import_button.title = "Import HIT data from exported CSV-file";
  3057.   overview_button.textContent = "Requester Overview";
  3058.   overview_button.style.margin = '0px 5px 5px 5px';
  3059.   overview_button.title = "Summary of all requesters you have worked for\nYou can set time limit and export as CSV-file";
  3060.   pending_button.textContent = "Pending Overview";
  3061.   pending_button.style.margin = '0px 5px 5px 5px';
  3062.   pending_button.title = "Summary of all pending HITs\nYou can export as CSV-file";
  3063.   status_button.textContent = "Daily Overview";
  3064.   status_button.style.margin = '0px 5px 5px 5px';
  3065.   status_button.title = "Summary of each day you have worked on MTurk\nYou can set time limit and export as CSV-file";
  3066.  
  3067.   pending_button.addEventListener("click", HITStorage.show_pendings, false);
  3068.   overview_button.addEventListener("click", HITStorage.show_overview, false);
  3069.   search_button.addEventListener("click", HITStorage.start_search, false);
  3070.   update_button.addEventListener("click", HITStorage.update_database, false);
  3071.   delete_button.addEventListener("click", delete_func(), false);
  3072.   import_button.addEventListener("click", import_func(), false);
  3073.   status_button.addEventListener("click", HITStorage.show_status, false);
  3074.  
  3075.   csv_label.textContent = 'export CSV';
  3076.   csv_label.title = 'Export results as comma-separated values';
  3077.   csv_label.style.verticalAlign = 'middle';
  3078.   csv_label.style.marginLeft = '50px';
  3079.   csv.title = 'Export results as comma-separated values';
  3080.   csv.setAttribute('type', 'checkbox');
  3081.   csv.setAttribute('id', 'export_csv');
  3082.   csv.style.verticalAlign = 'middle';
  3083.  
  3084.   from_input.value = '';
  3085.   to_input.value = '';
  3086.  
  3087.   var table = document.getElementById('lnk_show_earnings_details');
  3088.   if (table != null)
  3089.   {
  3090.     table = table.parentNode.parentNode.parentNode.parentNode;
  3091.     var pending_tr = document.createElement('tr');
  3092.     var pending_td1 = document.createElement('td');
  3093.     var pending_td2 = document.createElement('td');
  3094.     var today_tr = document.createElement('tr');
  3095.     var today_td1 = document.createElement('td');
  3096.     var today_td2 = document.createElement('td');
  3097.  
  3098.     pending_tr.setAttribute('class', 'even');
  3099.     pending_td1.setAttribute('class', 'metrics-table-first-value');
  3100.     pending_td1.setAttribute('id', 'pending_earnings_header');
  3101.     pending_td2.setAttribute('id', 'pending_earnings_value');
  3102.     today_tr.setAttribute('class', 'odd');
  3103.     today_td1.setAttribute('class', 'metrics-table-first-value');
  3104.     today_td1.setAttribute('id', 'projected_earnings_header');
  3105.     today_td2.setAttribute('id', 'projected_earnings_value');
  3106.  
  3107.     pending_tr.appendChild(pending_td1);
  3108.     pending_tr.appendChild(pending_td2);
  3109.     today_tr.appendChild(today_td1);
  3110.     today_tr.appendChild(today_td2);
  3111.     table.appendChild(pending_tr);
  3112.     table.appendChild(today_tr);
  3113.  
  3114.     pending_td1.style.borderTop = '1px dotted darkgrey';
  3115.     pending_td2.style.borderTop = '1px dotted darkgrey';
  3116.     today_td1.style.borderBottom = '1px dotted darkgrey';
  3117.     today_td2.style.borderBottom = '1px dotted darkgrey';
  3118.  
  3119.     today_td1.title = 'This value can be inaccurate if HITDB has not been updated recently';
  3120.     pending_td1.title = 'This value can be inaccurate if HITDB has not been updated recently';
  3121.  
  3122.     if (localStorage['HITDB UPDATED'] === undefined)
  3123.       pending_td1.textContent = 'Pending earnings';
  3124.     else
  3125.       pending_td1.textContent = 'Pending earnings (HITDB updated: ' + localStorage['HITDB UPDATED'] + ')';
  3126.     today_td1.innerHTML = 'Projected earnings for today &nbsp;&nbsp;';
  3127.     today_td2.textContent = '$-.--';
  3128.     pending_td2.textContent = '$-.--';
  3129.  
  3130.  
  3131.     var e = document.getElementById('user_activities.date_column_header.tooltip').parentNode.parentNode.childNodes[2].childNodes[1].childNodes[1];
  3132.     var today = null;
  3133.     if (e != null && e.textContent.trim() == 'Today') {
  3134.       today = convert_date(e.href.slice(-8));
  3135.       HITStorage.indexedDB.get_todays_projected_earnings(today);
  3136.     }
  3137.     HITStorage.indexedDB.get_pending_approvals();
  3138.     HITStorage.indexedDB.get_pending_payments();
  3139.  
  3140.     var target = document.createElement('span');
  3141.     target.setAttribute('id', 'my_target');
  3142.     target.textContent = 'click here to set your target';
  3143.     target.style.fontSize = 'small';
  3144.     target.style.color = 'blue';
  3145.     today_td1.appendChild(target);
  3146.     target.addEventListener("click", set_target_func(today), false);
  3147.   }
  3148. }
  3149. else if (document.location.href.match('https://www.mturk.com/mturk/preview'))
  3150. {
  3151.   var table = document.getElementById('requester.tooltip');
  3152.   if (table == null)
  3153.     return;
  3154.   table = table.parentNode.parentNode.parentNode;
  3155.   var title = table.parentNode.parentNode.parentNode.parentNode.getElementsByTagName('div')[0].textContent.trim();
  3156.  
  3157.   var extra_row = document.createElement('tr');
  3158.   var td_1 = document.createElement('td');
  3159.   var td_2 = document.createElement('td');
  3160.  
  3161.   var requesterId = document.getElementsByName('requesterId')[0].value;
  3162.   var auto_approve = parseInt(document.getElementsByName('hitAutoAppDelayInSeconds')[0].value);
  3163.  
  3164.   var buttons = [];
  3165.   var b = ['Requester', 'HIT Title'];
  3166.   for (var i=0; i<b.length; i++)
  3167.   {
  3168.     buttons[i] = document.createElement('button');
  3169.     buttons[i].textContent = b[i];
  3170.     buttons[i].id = b[i] + 'Button' + i;
  3171.     buttons[i].style.fontSize = '10px';
  3172.     buttons[i].style.height = '18px';
  3173.     buttons[i].style.width = '80px';
  3174.     buttons[i].style.border = '1px solid';
  3175.     buttons[i].style.paddingLeft = '3px';
  3176.     buttons[i].style.paddingRight = '3px';
  3177.     buttons[i].style.backgroundColor = 'lightgrey';
  3178.     buttons[i].setAttribute('form', 'NOThitForm');
  3179.     td_1.appendChild(buttons[i]);
  3180.   }
  3181.   buttons[0].title = 'Search requester ' + requesterId + ' from HIT database';
  3182.   buttons[1].title = 'Search title \'' + title + '\' from HIT database';
  3183.  
  3184.   HITStorage.indexedDB.colorRequesterButton(requesterId, buttons[0]);
  3185.   HITStorage.indexedDB.colorTitleButton(title, buttons[1]);
  3186.   buttons[0].addEventListener("click", search_func(requesterId, 'requesterId'), false);
  3187.   buttons[1].addEventListener("click", search_func(title, 'title'), false);
  3188.  
  3189.   td_2.innerHTML = '<span class="capsule_field_title">Auto-Approval:</span>&nbsp&nbsp' + HITStorage.formatTime(auto_approve*1000) + '';
  3190.   td_1.colSpan = '3';
  3191.   td_2.colSpan = '8';
  3192.  
  3193.   extra_row.appendChild(td_1);
  3194.   extra_row.appendChild(td_2);
  3195.   table.appendChild(extra_row);
  3196. }
  3197. else
  3198. {
  3199.   for (var item=0; item<10; item++) {
  3200.     var tooltip = document.getElementById('requester.tooltip--' + item);
  3201.     if (tooltip == null)
  3202.       break; // no need to continue
  3203.     var titleElement = document.getElementById('capsule' + item + '-0');
  3204.     var emptySpace = tooltip.parentNode.parentNode.parentNode.parentNode.parentNode;
  3205.    
  3206.     var hitItem = tooltip.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;//.parentNode;
  3207.  
  3208.     var requesterLabel = tooltip.parentNode;
  3209.     var requesterId = tooltip.parentNode.parentNode.getElementsByTagName('a');
  3210.     var title = titleElement.textContent.trim();
  3211.  
  3212.     requesterId = get_requester_id(requesterId[1].href);
  3213.  
  3214.     var buttons = [];
  3215.     var row = document.createElement('tr');
  3216.     var div = document.createElement('div');
  3217.     emptySpace.appendChild(row);
  3218.     row.appendChild(div);
  3219.  
  3220.     /* Turkopticon link next to requester name */
  3221.     //to_link = document.createElement('a');
  3222.     //to_link.textContent = ' TO ';
  3223.     //to_link.href = 'http://turkopticon.differenceengines.com/' + requesterId;
  3224.     //to_link.target = '_blank';
  3225.     //to_link.title = requesterId + ' on Turkopticon';
  3226.     //tooltip.parentNode.parentNode.appendChild(to_link);
  3227.     /*-----------------------------------------*/
  3228.    
  3229.     HITStorage.indexedDB.blockHITS(requesterId, title, hitItem, titleElement);
  3230.          
  3231.     var b = ['R', 'T', 'N', 'B'];
  3232.     for (var i=0; i<b.length; i++)
  3233.     {
  3234.       buttons[i] = document.createElement('button');
  3235.       buttons[i].textContent = b[i];
  3236.       buttons[i].id = b[i] + 'Button' + i;
  3237.       buttons[i].style.height = '18px';
  3238.       buttons[i].style.fontSize = '10px';
  3239.       buttons[i].style.border = '1px solid';
  3240.       buttons[i].style.paddingLeft = '3px';
  3241.       buttons[i].style.paddingRight = '3px';
  3242.       buttons[i].style.backgroundColor = 'lightgrey';
  3243.       div.appendChild(buttons[i]);
  3244.     }
  3245.     buttons[0].title = 'Search requester ' + requesterId + ' from HIT database';
  3246.     buttons[1].title = 'Search title \'' + title + '\' from HIT database';
  3247.     buttons[2].title = 'Add a requester note';
  3248.     buttons[3].title = '"Block" requester';
  3249.  
  3250.     var notelabel = document.createElement('label');
  3251.     notelabel.textContent = '';
  3252.     notelabel.id = b[i] + 'notelabel' + item;
  3253.     notelabel.style.height = '18px';
  3254.     notelabel.style.fontSize = '10px';
  3255.     notelabel.style.marginLeft = '10px';
  3256.     notelabel.style.padding = '1px';
  3257.     notelabel.style.backgroundColor = 'transparent';
  3258.     HITStorage.indexedDB.updateNoteButton(requesterId, notelabel);
  3259.     div.appendChild(notelabel);
  3260.  
  3261.     HITStorage.indexedDB.colorRequesterButton(requesterId, buttons[0]);
  3262.     HITStorage.indexedDB.colorTitleButton(title, buttons[1]);
  3263.     buttons[0].addEventListener("click", search_func(requesterId, 'requesterId'), false);
  3264.     buttons[1].addEventListener("click", search_func(title, 'title'), false);
  3265.     buttons[2].addEventListener("click", note_func(requesterId, notelabel), false);
  3266.     buttons[3].addEventListener("click", block_func(requesterId, title, hitItem), false);
  3267.  
  3268.     div.style.margin = "0px";
  3269.    
  3270.     buttons[2].style.visibility = "hidden"; // "visible"
  3271.     buttons[2].parentNode.addEventListener("mouseover", visible_func(buttons[2], true), false);
  3272.     buttons[2].parentNode.addEventListener("mouseout", visible_func(buttons[2], false), false);
  3273.     buttons[2].addEventListener("mouseout", visible_func(buttons[2], false), false);
  3274.     buttons[3].style.visibility = "hidden"; // "visible"
  3275.     buttons[3].parentNode.addEventListener("mouseover", visible_func(buttons[3], true), false);
  3276.     buttons[3].parentNode.addEventListener("mouseout", visible_func(buttons[3], false), false);
  3277.     buttons[3].addEventListener("mouseout", visible_func(buttons[3], false), false);
  3278.   }
  3279.  
  3280.   var auto_button = document.createElement('button');
  3281.   auto_button.setAttribute('id', 'auto_button');
  3282.   auto_button.title = 'HIT DataBase Auto Update\nAutomagically update newest HITs to database when reloading this page';
  3283.  
  3284.   if (localStorage['HITDB AUTO UPDATE'] === undefined)
  3285.   {
  3286.     auto_button.textContent = 'Auto update ?';
  3287.     auto_button.style.color = 'red';
  3288.   }
  3289.   else if (localStorage['HITDB AUTO UPDATE'] == 'ON')
  3290.   {
  3291.     auto_button.textContent = 'Auto Update is ON';
  3292.     auto_button.style.color = 'green';
  3293.   }
  3294.   else
  3295.   {
  3296.     auto_button.textContent = 'Auto Update is OFF';
  3297.     auto_button.style.color = 'red';
  3298.   }
  3299.  
  3300.   var element = document.body.childNodes[13].childNodes[3].childNodes[1].childNodes[0].childNodes[5];
  3301.   element.insertBefore(auto_button, element.firstChild);
  3302.   auto_button.addEventListener("click", auto_update_func(), false);
  3303.  
  3304.   setTimeout(HITStorage.getLatestHITs, 100);
  3305. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement