MTurk HIT DataBase Testing

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

当前为 2014-11-24 提交的版本,查看 最新版本

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