MTurk HIT DataBase Legacy

Extended ability to search HITs you have worked on and other useful tools (CSV export/import, requester notes, requester block, pending/projected earnings)

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