NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name MTurk HIT DataBase // @namespace https://greasyfork.org/users/710 // @description Extended ability to search HITs you have worked on and other useful tools (CSV export/import, requester notes, requester block, pending/projected earnings) // @include https://www.mturk.com/mturk/searchbar* // @include https://www.mturk.com/mturk/findhits* // @include https://www.mturk.com/mturk/viewhits* // @include https://www.mturk.com/mturk/viewsearchbar* // @include https://www.mturk.com/mturk/sortsearchbar* // @include https://www.mturk.com/mturk/sorthits* // @include https://www.mturk.com/mturk/dashboard // @include https://www.mturk.com/mturk/preview?* // @version 1.9.6.1 // @grant none // @require http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js // @require http://code.highcharts.com/highcharts.js // @require https://greasyfork.org/scripts/2351-jsdiff/code/jsdiff.js?version=6256 // @require https://greasyfork.org/scripts/2350-filesaver-js/code/filesaverjs.js?version=6255 // ==/UserScript== // // 2012-10-03 0.9.7: This is rewrite of MTurk Extended HIT Search (http://userscripts.org/scripts/show/146277) // with some extra features (and some missing for now: search by date). // It now uses IndexedDB (http://en.wikipedia.org/wiki/Indexed_Database_API) // // 2012-10-04 0.9.8: Improved use of indexes, check Pending Payment HITs // 0.9.9: Minor improvements // // 2012-10-04 0.10: Added date options // // 2012-10-07 0.11: Requester notes, bug fixes // 0.12: CSV export // // 2012-10-09 0.13: "Block" requesters or specific HITs // // 2012-10-10 0.14: Requester Overview, shows summary of all requesters in DB // // 2012-10-11 0.15: Blocked HITs are always on bottom of the page // // 2012-10-14 0.16: Requester Overview improvements // // 2012-10-17 0.17: Bug fixes and error checks // // 2012-10-18 0.18: Import HIT data from MTurk Extended HIT Search script // // 2012-10-21 0.19: Moved main interface to dashboard, show pending earnings on dashboard, // summary of all requesters with pending HITs. // // 2012-10-23 0.20: Added Turkopticon (http://turkopticon.differenceengines.com/) links to overview pages // 0.21: Fixed overview pages reward to include only 'Paid' and 'Approved - Pending Payment' HITs. // // 2012-10-28 0.22: Limited Auto Update. // 0.23: Minor improvements // // 2012-10-30 0.24: Projected earnings for today // // 2012-11-02 0.25: Smarter Auto Update // // 2012-11-03 0.26: GUI update // // 2012-11-05 0.30: Extra non-amazonian script monkeys // // 2012-11-06 0.31: Projected earnings progress bar // // 2012-11-08 0.32: Minor GUI fixes to look better on Chrome. Looks like it now works on stable Chrome! // // 2012-11-13 0.33: Time limits now work with Requester Overview // // 2012-11-15 0.34: Bug/compatibility fixes // // 2012-11-18 0.40: Daily Overview, update database to use YYYY-MM-DD date format. // // 2012-11-22 0.41: R and T button on HIT preview page. Auto-Approval time. // // 2012-11-30 0.42: Changes on MTurk pages. Status page in now on one page! // // 2012-12-02 1.0: Added @downloadURL and @updateURL // // 2012-12-06 1.1: Requester details. // Try to fetch few extra days at first update (not showing on status page). // // 2012-12-11 1.2: Import HITs from previously exported CSV-files. // Removed Extended HIT Search import. // // 2012-12-13 1.3: Fix CSV-import to put empty string instead if undefined if feedback is empty. // // 2012-12-14 1.4: Rewritten database update more properly. // // 2012-12-16 1.5: Fixed broken Auto Update (forgot to check that on pervious update). // // 2013-02-26 1.6: Fixed IDBTransactionModes for Chrome (note this breaks it for Firefox) // // 2013-02-27 1.7: Changed UI bars back to what they used to be. // var DAYS_TO_FETCH = []; var DAYS_TO_FETCH_CHECK; var HITStorage = {}; var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB; window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.mozIDBTransaction; window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.mozIDBKeyRange; HITStorage.IDBTransactionModes = { "READ_ONLY": "readonly", "READ_WRITE": "readwrite", "VERSION_CHANGE": "versionchange" }; var IDBKeyRange = window.IDBKeyRange; HITStorage.indexedDB = {}; HITStorage.indexedDB = {}; HITStorage.indexedDB.db = null; HITStorage.indexedDB.onerror = function(e) { console.log(e); }; var v = 4; HITStorage.indexedDB.create = function() { var request = indexedDB.open("HITDB", v); request.onupgradeneeded = function (e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var new_empty_db = false; if(!db.objectStoreNames.contains("HIT")) { var store = db.createObjectStore("HIT", { keyPath: "hitId" }); store.createIndex("date", "date", { unique: false }); store.createIndex("requesterName", "requesterName", { unique: false }); store.createIndex("title", "title", { unique: false }); store.createIndex("reward", "reward", { unique: false }); store.createIndex("status", "status", { unique: false }); store.createIndex("requesterId", "requesterId", { unique: false }); new_empty_db = true; // At first update try to get few extra days that do not show on status page localStorage['HITDB TRY_EXTRA_DAYS'] = 'YES'; } if(!db.objectStoreNames.contains("STATS")) { var store = db.createObjectStore("STATS", { keyPath: "date" }); } if(!db.objectStoreNames.contains("NOTES")) { var store = db.createObjectStore("NOTES", { keyPath: "requesterId" }); } if(!db.objectStoreNames.contains("BLOCKS")) { var store = db.createObjectStore("BLOCKS", { keyPath: "id", autoIncrement: true }); store.createIndex("requesterId", "requesterId", { unique: false }); } if (new_empty_db == false) { alert("HIT DataBase date format must be upgraded (MMDDYYYY => YYYY-MM-DD)\n" + "Please don't close or reload this page until it's done.\n" + "Press OK to start. This shouldn't take long. (few minutes max)" + "Sorry for the inconvenience."); HITStorage.update_date_format(true); } db.close(); //alert("DataBase upgraded to version " + v + '!'); } request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; db.close(); }; request.onerror = HITStorage.indexedDB.onerror; } HITStorage.indexedDB.addHIT = function(hitData) { // Temporary extra check if (hitData.date.indexOf('-') < 0) { alert('Wrong date format in addHIT()!'); return; } var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_WRITE); var store = trans.objectStore("HIT"); var request = store.put(hitData); request.onsuccess = function(e) { db.close(); }; request.onerror = function(e) { console.log("Error Adding: ", e); }; }; request.onerror = HITStorage.indexedDB.onerror; }; HITStorage.indexedDB.importHITs = function(hitData) { var hits = hitData.length; var label = document.getElementById('status_label'); var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_WRITE); var store = trans.objectStore("HIT"); putNextHIT(); function putNextHIT() { if (hitData.length > 0) { store.put(hitData.pop()).onsuccess = putNextHIT; label.innerHTML = progress_bar(((hits-hitData.length)/hits*50), 50, '█', '█', '#7fb448', 'grey') + ' (' + hitData.length + ')'; } else { HITStorage.enable_inputs(); HITStorage.update_status_label('Import done', 'green'); db.close(); } } }; request.onerror = HITStorage.indexedDB.onerror; }; HITStorage.indexedDB.addHITs = function(hitData, day_to_fetch, days_to_update) { var hits = hitData.length; if (day_to_fetch) var label = document.getElementById('status_label'); var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_WRITE); var store = trans.objectStore("HIT"); putNextHIT(); function putNextHIT() { if (hitData.length > 0) { store.put(hitData.pop()).onsuccess = putNextHIT; if (day_to_fetch) label.innerHTML = 'Saving ' + day_to_fetch.date + ': ' + progress_bar(((hits-hitData.length)/hits*40), 40, '█', '█', '#7fb448', 'grey'); } else { // move to next day if (day_to_fetch) { HITStorage.indexedDB.updateHITstats(day_to_fetch); setTimeout(function() { HITStorage.do_update(days_to_update); }, 2000); HITStorage.update_status_label('Please wait: script monkeys are taking naps 😌', 'red'); } db.close(); } } }; request.onerror = HITStorage.indexedDB.onerror; }; HITStorage.indexedDB.updateHITstats = function(date) { // Temporary extra check if (date.date.indexOf('-') < 0) { alert('Wrong date format in updateHITstats()!'); return; } var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["STATS"], HITStorage.IDBTransactionModes.READ_WRITE); var store = trans.objectStore("STATS"); var request = store.put(date); request.onsuccess = function(e) { db.close(); }; request.onerror = function(e) { console.log("Error Adding: ", e); }; }; request.onerror = HITStorage.indexedDB.onerror; }; HITStorage.prepare_update_and_check_pending_payments = function() { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("HIT"); var index = store.index('status'); var range = IDBKeyRange.only('Approved - Pending Payment'); index.openCursor(range).onsuccess = function(event) { var cursor = event.target.result; if (cursor && DAYS_TO_FETCH.length > 0) { for (var i=0; i<DAYS_TO_FETCH.length; i++) { if ( cursor.value.date == DAYS_TO_FETCH[i].date && cursor.value.reward>0 ) { DAYS_TO_FETCH[i].pending_payments = true; } } cursor.continue(); } else { if (DAYS_TO_FETCH.length>0) { db.close(); HITStorage.update_status_label('Please wait: script monkeys are planning to fetch relevant status pages', 'red'); setTimeout(function() { HITStorage.prepare_update(); }, 100); } else { db.close(); HITStorage.update_done(); } } }; } }; // check that number of hits in DB matches what is available HITStorage.check_update = function() { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.update_status_label('Please wait: checking database', 'red'); HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("HIT"); var index = store.index('date'); var range = IDBKeyRange.bound(DAYS_TO_FETCH_CHECK[DAYS_TO_FETCH_CHECK.length-1].date, DAYS_TO_FETCH_CHECK[0].date, false, false); index.count(range).onsuccess = function(event) { var count = event.target.result; var submitted_hits = 0; for (var i=0; i<DAYS_TO_FETCH_CHECK.length; i++) { submitted_hits += DAYS_TO_FETCH_CHECK[i].submitted; } if (submitted_hits == count) { db.close(); HITStorage.update_done(); } else { if (confirm("😞 ERROR! Number of HITs in DataBase does not match number of HITs available! (" + count + " != " + submitted_hits + ")\n" + "Would you like to refetch all status pages now?")) { db.close(); DAYS_TO_FETCH = DAYS_TO_FETCH_CHECK.slice(0); HITStorage.update_status_label('Please wait: new script monkeys are fetching relevant status pages', 'red'); setTimeout(function() { HITStorage.do_update(DAYS_TO_FETCH.length); }, 100); } else { db.close(); HITStorage.update_done(); } } }; } }; HITStorage.prepare_update = function() { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["STATS"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("STATS"); var range = IDBKeyRange.bound(DAYS_TO_FETCH[DAYS_TO_FETCH.length-1].date, DAYS_TO_FETCH[0].date, false, false); store.openCursor(range).onsuccess = function(event) { var cursor = event.target.result; if (cursor && DAYS_TO_FETCH.length > 0) { for (var i=0; i<DAYS_TO_FETCH.length; i++) { if ( cursor.value.date == DAYS_TO_FETCH[i].date && cursor.value.submitted == DAYS_TO_FETCH[i].submitted && cursor.value.approved == DAYS_TO_FETCH[i].approved && cursor.value.rejected == DAYS_TO_FETCH[i].rejected && cursor.value.pending == DAYS_TO_FETCH[i].pending) { // This day is already in DB and stats match => no need to fetch // unless there are 'Approved - Pending Payment' HITs if (DAYS_TO_FETCH[i].pending_payments === undefined || DAYS_TO_FETCH[i].pending_payments == false) DAYS_TO_FETCH.splice(i,1); } } cursor.continue(); } else { if (DAYS_TO_FETCH.length>0) { db.close(); setTimeout(function() { HITStorage.do_update(DAYS_TO_FETCH.length); }, 100); } else { db.close(); HITStorage.update_done(); } } }; } }; HITStorage.indexedDB.term_matches_HIT = function(term, hit) { var keys = ['date', 'requesterName', 'title', 'feedback', 'hitId', 'requesterId']; var re = new RegExp(escapeRegExp(term),"ig"); for (var k in keys) { //for testing if (hit[keys[k]] != null && re.test(hit[keys[k]].trim())) { return true; } } return false; } function escapeRegExp(str) { return str.trim().replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); } HITStorage.indexedDB.matchHIT = function(hit, options) { if (options.status == '---' || hit.status.match(options.status)) { if (options.search_term == '' || HITStorage.indexedDB.term_matches_HIT(options.term, hit)) { return true; } } return false; } function hit_sort_func() { return function(a,b) { if (a.date == b.date) { if (a.requesterName < b.requesterName) return -1; if (a.requesterName > b.requesterName) return 1; if (a.title < b.title) return -1; if (a.title > b.title) return 1; if (a.status < b.status) return -1; if (a.status > b.status) return 1; } if (a.date > b.date) return 1; if (a.date < b.date) return -1; }; } HITStorage.indexedDB.getHITs = function(options) { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("HIT"); var req; var results = []; var index; var range; if (options.from_date || options.to_date) { if (options.from_date != '' || options.to_date != '') { index = store.index('date'); if (options.from_date == options.to_date) { range = IDBKeyRange.only(options.from_date); } else if (options.from_date != '' && options.to_date != '') { range = IDBKeyRange.bound(options.from_date, options.to_date, false, false); } else if (options.from_date == '' && options.to_date != '') { range = IDBKeyRange.upperBound(options.to_date, false); } else { range = IDBKeyRange.lowerBound(options.from_date, false); } req = index.openCursor(range); } } else if (options.index && options.index != '') { index = store.index(options.index); range = IDBKeyRange.only(options.term); req = index.openCursor(range); } else if (options.status == 'Rejected' || options.status == 'Pending Approval' || options.status == 'Approved' || options.status == 'Paid') { var s = (options.status == 'Approved')? 'Approved - Pending Payment' : options.status; options.index = 'status'; index = store.index(options.index); range = IDBKeyRange.only(s); req = index.openCursor(range); } else { req = store.openCursor(); } req.onsuccess = function(event) { var cursor = event.target.result; if (cursor) { if (HITStorage.indexedDB.matchHIT(cursor.value, options)) results.push(cursor.value); cursor.continue(); } else { results.sort(hit_sort_func()); if (options.export_csv && options.export_csv == true) HITStorage.export_csv(results); else HITStorage.show_results(results); if (options.donut == '---') document.getElementById('container').style.display = 'none'; else if (options.donut != '') HITStorage.prepare_donut(results, options.donut); } db.close(); }; }; request.onerror = HITStorage.indexedDB.onerror; }; // // Show summary of all requesters // HITStorage.indexedDB.requesterOverview = function(options) { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("HIT"); var index; var req; // [ requesterId, requesterName, sum(hits), sum(rewards), rejected, pending ] var results = []; var tmp_results = {}; if (options.from_date || options.to_date) { if (options.from_date != '' || options.to_date != '') { index = store.index('date'); if (options.from_date == options.to_date) { range = IDBKeyRange.only(options.from_date); } else if (options.from_date != '' && options.to_date != '') { range = IDBKeyRange.bound(options.from_date, options.to_date, false, false); } else if (options.from_date == '' && options.to_date != '') { range = IDBKeyRange.upperBound(options.to_date, false); } else { range = IDBKeyRange.lowerBound(options.from_date, false); } req = index.openCursor(range); } req.onsuccess = function(event) { var cursor = event.target.result; if (cursor) { var hit = cursor.value; var rejected = (hit.status == 'Rejected') ? 1 : 0; var pending = (hit.status.match(/Approved|Paid|Rejected/) == null) ? 1 : 0; var reward = (pending>0 || rejected>0 )? 0: hit.reward; if (tmp_results[hit.requesterId] === undefined) { tmp_results[hit.requesterId] = []; tmp_results[hit.requesterId][0] = hit.requesterId; tmp_results[hit.requesterId][1] = hit.requesterName; tmp_results[hit.requesterId][2] = 1; tmp_results[hit.requesterId][3] = reward; tmp_results[hit.requesterId][4] = rejected; tmp_results[hit.requesterId][5] = pending; } else { tmp_results[hit.requesterId][1] = hit.requesterName; tmp_results[hit.requesterId][2] += 1; tmp_results[hit.requesterId][3] += reward; tmp_results[hit.requesterId][4] += rejected; tmp_results[hit.requesterId][5] += pending; } cursor.continue(); } else { for (var key in tmp_results) { results.push(tmp_results[key]); } // sort by total reward results.sort(function(a,b) { return b[3]-a[3]; }); if (options.export_csv == true) HITStorage.show_requester_overview_csv(results); else HITStorage.show_requester_overview(results, '(' + options.from_date + '–' + options.to_date + ')'); HITStorage.update_status_label('Script monkeys are ready', 'green'); setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000); HITStorage.enable_inputs(); } db.close(); }; } else { index = store.index('requesterId'); req = index.openCursor(); req.onsuccess = function(event) { var cursor = event.target.result; if (cursor) { var hit = cursor.value; var rejected = (hit.status == 'Rejected') ? 1 : 0; var pending = (hit.status.match(/Approved|Paid|Rejected/) == null) ? 1 : 0; var reward = (pending>0 || rejected>0 )? 0: hit.reward; if (results.length == 0) { results.push([hit.requesterId, hit.requesterName, 1, reward, rejected, pending]); } else if (results[0][0] == hit.requesterId) { results[0][2] += 1; results[0][3] += reward; results[0][4] += rejected; results[0][5] += pending; } else { results.unshift([hit.requesterId, hit.requesterName, 1, reward, rejected, pending]); } cursor.continue(); } else { // sort by total reward results.sort(function(a,b) { return b[3]-a[3]; }); if (options.export_csv == true) HITStorage.show_requester_overview_csv(results); else HITStorage.show_requester_overview(results); HITStorage.update_status_label('Script monkeys are ready', 'green'); setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000); HITStorage.enable_inputs(); } db.close(); }; } }; request.onerror = HITStorage.indexedDB.onerror; }; // // Show summary of one requester // HITStorage.indexedDB.showRequester = function(requesterId) { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("HIT"); var index; var results = []; index = store.index('requesterId'); var range = IDBKeyRange.only(requesterId); index.openCursor(range).onsuccess = function(event) { var cursor = event.target.result; if (cursor) { results.push(cursor.value); cursor.continue(); } else { results.sort(function(a,b) { if (a.date > b.date) return -1; if (a.date < b.date) return 1; return 0; }); HITStorage.show_requester(results); HITStorage.update_status_label('Script monkeys are ready', 'green'); setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000); HITStorage.enable_inputs(); } db.close(); }; }; request.onerror = HITStorage.indexedDB.onerror; }; // Show summary of pending HITs HITStorage.indexedDB.pendingOverview = function(options) { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("HIT"); var index; var req; // [ requesterId, requesterName, sum(pendings), sum(rewards) ] var results = []; var tmp_results = {}; index = store.index('status'); range = IDBKeyRange.only('Pending Approval'); index.openCursor(range).onsuccess = function(event) { var cursor = event.target.result; if (cursor) { var hit = cursor.value; console.log(hit); if (tmp_results[hit.requesterId] === undefined) { tmp_results[hit.requesterId] = []; tmp_results[hit.requesterId][0] = hit.requesterId; tmp_results[hit.requesterId][1] = hit.requesterName; tmp_results[hit.requesterId][2] = 1; tmp_results[hit.requesterId][3] = hit.reward; } else { tmp_results[hit.requesterId][1] = hit.requesterName; tmp_results[hit.requesterId][2] += 1; tmp_results[hit.requesterId][3] += hit.reward; } cursor.continue(); } else { for (var key in tmp_results) { results.push(tmp_results[key]); } // sort by pending hits results.sort(function(a,b) { return b[2]-a[2]; }); if (options.export_csv == true) HITStorage.show_pending_overview_csv(results); else HITStorage.show_pending_overview(results); HITStorage.update_status_label('Script monkeys are ready', 'green'); setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000); HITStorage.enable_inputs(); } db.close(); }; }; request.onerror = HITStorage.indexedDB.onerror; }; // Show summary of daily stats HITStorage.indexedDB.statusOverview = function(options) { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["STATS"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("STATS"); var req; var results = []; if (options.from_date || options.to_date) { if (options.from_date != '' || options.to_date != '') { if (options.from_date == options.to_date) { range = IDBKeyRange.only(options.from_date); } else if (options.from_date != '' && options.to_date != '') { range = IDBKeyRange.bound(options.from_date, options.to_date, false, false); } else if (options.from_date == '' && options.to_date != '') { range = IDBKeyRange.upperBound(options.to_date, false); } else { range = IDBKeyRange.lowerBound(options.from_date, false); } req = store.openCursor(range); } } else { req = store.openCursor(); } req.onsuccess = function(event) { var cursor = event.target.result; if (cursor) { if (cursor.value.submitted > 0) results.push(cursor.value); cursor.continue(); } else { if (options.export_csv == true) HITStorage.show_status_overview_csv(results); else HITStorage.show_status_overview(results, '(' + options.from_date + '–' + options.to_date + ')'); HITStorage.update_status_label('Script monkeys are ready', 'green'); setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000); HITStorage.enable_inputs(); } db.close(); }; }; request.onerror = HITStorage.indexedDB.onerror; }; HITStorage.indexedDB.getHIT = function(id) { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("HIT"); var request = store.get(id); request.onsuccess = function(e) { db.close(); showDetails(e.target.result.note); }; request.onerror = function(e) { console.log("Error Getting: ", e); }; }; request.onerror = HITStorage.indexedDB.onerror; }; HITStorage.indexedDB.addNote = function(id, note) { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["NOTES"], HITStorage.IDBTransactionModes.READ_WRITE); var store = trans.objectStore("NOTES"); var request; if (note == '') request = store.delete(id); else request = store.put({requesterId: id, note: note}); request.onsuccess = function(e) { db.close(); }; request.onerror = function(e) { console.log("Error Adding: ", e); }; }; request.onerror = HITStorage.indexedDB.onerror; }; HITStorage.indexedDB.blockHITS = function(requesterId, title, hitElement, titleElement) { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; if (!db.objectStoreNames.contains("BLOCKS")) { db.close(); return; } var trans = db.transaction(["BLOCKS"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("BLOCKS"); var index = store.index("requesterId"); var range = IDBKeyRange.only(requesterId); index.openCursor(range).onsuccess = function(event) { var cursor = event.target.result; if (cursor && cursor.value.re) { if (cursor.value.re.test(title)) { hitElement.style.display = 'none'; titleElement.addEventListener("click", unblock_func(requesterId, title)); titleElement.style.fontSize = 'small'; // move blocked hits to the bottom var table = hitElement.parentNode.parentNode.parentNode.parentNode.parentNode; var hit = hitElement.parentNode.parentNode.parentNode.parentNode; table.removeChild(hit); table.appendChild(hit); } cursor.continue(); } else { db.close(); } }; }; request.onerror = HITStorage.indexedDB.onerror; }; HITStorage.indexedDB.addBlock = function(requesterId, re) { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["BLOCKS"], HITStorage.IDBTransactionModes.READ_WRITE); var store = trans.objectStore("BLOCKS"); var request; request = store.put({requesterId: requesterId, re: re}); request.onsuccess = function(e) { db.close(); }; }; request.onerror = HITStorage.indexedDB.onerror; }; // Removes all blocks for requesterId, where RE matches this HIT title HITStorage.indexedDB.removeBlocks = function(requesterId, title) { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; if (!db.objectStoreNames.contains("BLOCKS")) { db.close(); return; } var trans = db.transaction(["BLOCKS"], HITStorage.IDBTransactionModes.READ_WRITE); var store = trans.objectStore("BLOCKS"); var index = store.index("requesterId"); var range = IDBKeyRange.only(requesterId); index.openCursor(range).onsuccess = function(event) { var cursor = event.target.result; if (cursor) { if (cursor.value.re.test(title)) store.delete(cursor.value.id); db.close(); } }; }; request.onerror = HITStorage.indexedDB.onerror; }; HITStorage.indexedDB.updateNoteButton = function(id, label) { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; if (!db.objectStoreNames.contains("NOTES")) { label.title = 'Update HIT database on statusdetail page to use this feature'; db.close(); return; } var trans = db.transaction(["NOTES"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("NOTES"); store.get(id).onsuccess = function(event) { if (event.target.result === undefined) { label.textContent = ''; } else { var note = event.target.result.note; label.textContent = note; label.style.border = '1px dotted'; if (note.indexOf('!') >= 0) label.style.color = 'red'; else label.style.color = 'black'; } db.close(); }; }; request.onerror = HITStorage.indexedDB.onerror; }; HITStorage.indexedDB.colorRequesterButton = function(id, button) { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; if (!db.objectStoreNames.contains("HIT")) { button.title = 'Update HIT database on statusdetail page to use this feature'; db.close(); return; } var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("HIT"); var index = store.index("requesterId"); index.get(id).onsuccess = function(event) { if (event.target.result === undefined) { button.style.backgroundColor = 'pink'; } else { button.style.backgroundColor = 'lightgreen'; button.style.fontWeight = 'bold'; } db.close(); }; }; request.onerror = HITStorage.indexedDB.onerror; }; HITStorage.indexedDB.colorTitleButton = function(title, button) { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; if (!db.objectStoreNames.contains("HIT")) { button.title = 'Update HIT database on statusdetail page to use this feature'; db.close(); return; } var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("HIT"); var index = store.index("title"); index.get(title).onsuccess = function(event) { if (event.target.result === undefined) { button.style.backgroundColor = 'pink'; } else { button.style.backgroundColor = 'lightgreen'; button.style.fontWeight = 'bold'; } db.close(); }; }; request.onerror = HITStorage.indexedDB.onerror; }; HITStorage.indexedDB.deleteDB = function () { var deleteRequest = indexedDB.deleteDatabase("HITDB"); deleteRequest.onsuccess = function (e) { alert("deleted"); } deleteRequest.onblocked = function (e) { alert("blocked"); } deleteRequest.onerror = HITStorage.indexedDB.onerror; } HITStorage.indexedDB.get_pending_approvals = function() { var element = document.getElementById('pending_earnings_value'); var header_element = document.getElementById('pending_earnings_header'); if (element == null) return; var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("HIT"); var result = 0; var index; var range; index = store.index('status'); range = IDBKeyRange.only('Pending Approval'); index.openCursor(range).onsuccess = function(event) { var cursor = event.target.result; if (cursor) { result += cursor.value.reward; cursor.continue(); } else { element.textContent = '$' + result.toFixed(2); if (header_element != null) header_element.textContent = 'Pending earnings (HITDB updated: ' + localStorage['HITDB UPDATED']+ ')'; } db.close(); }; }; request.onerror = HITStorage.indexedDB.onerror; }; HITStorage.indexedDB.get_pending_payments = function() { var element = document.getElementById('pending_earnings_value'); if (element == null) return; var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("HIT"); var result = 0; var index; var range; index = store.index('status'); range = IDBKeyRange.only('Approved - Pending Payment'); index.openCursor(range).onsuccess = function(event) { var cursor = event.target.result; if (cursor) { result += cursor.value.reward; cursor.continue(); } else { element.title = 'Approved - Pending Payment: $' + result.toFixed(2); } } db.close(); }; request.onerror = HITStorage.indexedDB.onerror; }; HITStorage.indexedDB.get_todays_projected_earnings = function(date) { var element = document.getElementById('projected_earnings_value'); if (element == null) return; var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY); var store = trans.objectStore("HIT"); var result = 0; var rejected = 0; var index; var range; index = store.index('date'); range = IDBKeyRange.only(date); index.openCursor(range).onsuccess = function(event) { var cursor = event.target.result; if (cursor) { if (cursor.value.status == 'Rejected') rejected += cursor.value.reward; else result += cursor.value.reward; cursor.continue(); } else { element.textContent = '$' + result.toFixed(2); element.title = '$' + rejected.toFixed(2) + ' rejected'; if (localStorage['TODAYS TARGET'] !== undefined) { var target = parseFloat(localStorage['TODAYS TARGET']).toFixed(2); var my_target = document.getElementById('my_target'); var progress = Math.floor(result/target*40); if (progress > 40) progress = 40; my_target.innerHTML = progress_bar(progress, 40, '█', '█', '#7fb448', 'grey') + ' ' + ((result>target)? '+' : '') + (result-target).toFixed(2); my_target.style.fontSize = '9px'; } } } db.close(); }; request.onerror = HITStorage.indexedDB.onerror; }; // Update database date format from MMDDYYYY to YYYY-MM-DD // Shouldn't break anything even if used on already updated db HITStorage.update_date_format = function(verbose) { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_WRITE); var store = trans.objectStore("HIT"); store.openCursor().onsuccess = function(event) { var cursor = event.target.result; if (cursor) { if (cursor.value.date.indexOf('-') < 0) { var i = cursor.value; i.date = convert_date(i.date); i.requesterName = i.requesterName.trim(); i.title = i.title.trim(); cursor.update(i); } cursor.continue(); } else { db.close(); HITStorage.update_stats_date_format(verbose); } }; } } HITStorage.update_stats_date_format = function(verbose) { var request = indexedDB.open("HITDB", v); request.onsuccess = function(e) { HITStorage.indexedDB.db = e.target.result; var db = HITStorage.indexedDB.db; var trans = db.transaction(["STATS"], HITStorage.IDBTransactionModes.READ_WRITE); var store = trans.objectStore("STATS"); store.openCursor().onsuccess = function(event) { var cursor = event.target.result; if (cursor) { if (cursor.value.date.indexOf('-') < 0) { var i = cursor.value; i.date = convert_date(i.date); cursor.delete(); store.put(i); } cursor.continue(); } else { // DB should be fully updated db.close(); if (verbose == true) alert('Date conversion done.'); } }; } }; /* ------------------------------------------------------------- */ HITStorage.prepare_donut = function (donutData, type) { if (type == '---') return; var countHits = true; if (type.match('REWARDS')) countHits = false; var tmpData = {}; var topRequesters = []; var topHits = []; var sum = 0; for (var i=0; i < donutData.length; i++) { var requesterName = donutData[i].requesterName.trim() + " (" + donutData[i].requesterId + ")"; var hitTitle = donutData[i].title; var hitReward = donutData[i].reward; sum += (countHits) ? 1 : hitReward; if (tmpData[requesterName]) { tmpData[requesterName]['HITS'] += (countHits) ? 1 : hitReward; } else { tmpData[requesterName] = {}; tmpData[requesterName]['HITS'] = (countHits) ? 1 : hitReward; } if (tmpData[requesterName][hitTitle]) tmpData[requesterName][hitTitle] += (countHits) ? 1 : hitReward; else tmpData[requesterName][hitTitle] = (countHits) ? 1 : hitReward; } for (var key in tmpData) { topRequesters.push({name: key, y: tmpData[key]['HITS']}); } topRequesters.sort(function(a,b){return b.y-a.y}); var colors = Highcharts.getOptions().colors; for (var i=0; i<topRequesters.length; i++) { var tmpHits = []; topRequesters[i].color = colors[i]; for (var key2 in tmpData[topRequesters[i].name]) { if (key2 != 'HITS') { tmpHits.push({name: key2, y: tmpData[topRequesters[i].name][key2], color: colors[i]}); } } tmpHits.sort(function(a,b){return b.y-a.y}); for (var j=0; j<tmpHits.length ; j++) { var brightness = 0.2 - (j / tmpHits.length) / 5; tmpHits[j].color = Highcharts.Color(colors[i]).brighten(brightness).get(); } topHits = topHits.concat(tmpHits); } document.getElementById('container').style.display = 'block'; chart = new Highcharts.Chart({ chart: { renderTo: 'container', type: 'pie' }, title: { text: 'Requesters and HITs matching your latest search' }, yAxis: { title: { text: '' } }, plotOptions: { pie: { shadow: false, dataLabels: { enabled: true} } }, tooltip: { animation: false, valuePrefix: (countHits)? '' : '$', valueSuffix: (countHits)? ' HITs' : '', valueDecimals: (countHits)? 0 : 2, pointFormat: (countHits)? '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> (of all ' + sum + ' HITs)<br/>' : '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> (of all $' + sum.toFixed(2) + ')<br/>' }, series: [{ name: 'Requesters', data: topRequesters, size: '60%', dataLabels: { formatter: function() { if (countHits) { return this.y/sum >= 0.20 ? this.point.name: null; } else { return this.y/sum >= 0.20 ? this.point.name : null; } }, color: 'black', distance: -10 } }, { name: 'HITs', data: topHits, innerSize: '60%', dataLabels: { formatter: function() { if (countHits) { return this.y/sum > 0.05 ? this.point.name : null; } else { return this.y/sum > 0.05 ? this.point.name : null; } }, color: 'black', } }] }); } // Stolen from Today's Projected Earnings (http://userscripts.org/scripts/show/95331) HITStorage.getHTTPObject = function() { if (typeof XMLHttpRequest != 'undefined') { return new XMLHttpRequest(); } try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {} } return false; } // Stolen from Today's Projected Earnings (http://userscripts.org/scripts/show/95331) // date format MMDDYYYY! HITStorage.process_page = function(link, date, hitData) { var page = HITStorage.getHTTPObject(); page.open("GET", link, false); page.send(null); return HITStorage.parse_data(page.responseText, date, hitData); } // Partly stolen from Today's Projected Earnings (http://userscripts.org/scripts/show/95331) // date format MMDDYYYY! HITStorage.parse_data = function(page_text, date, hitData) { var index = 0; var index2 = 0; var page_html = document.createElement('div'); page_html.innerHTML = page_text; var requesters = page_html.getElementsByClassName('statusdetailRequesterColumnValue'); var titles = page_html.getElementsByClassName('statusdetailTitleColumnValue'); var amounts = page_html.getElementsByClassName('statusdetailAmountColumnValue'); var statuses = page_html.getElementsByClassName('statusdetailStatusColumnValue'); var feedbacks = page_html.getElementsByClassName('statusdetailRequesterFeedbackColumnValue'); var requesterName; var hitTitle; var hitReward; var hitStatus; var requesterId; var hitId; for(var k = 0; k < amounts.length; k++) { requesterName = requesters[k].textContent; requesterLink = requesters[k].childNodes[1].href; hitTitle = titles[k].textContent; index = amounts[k].innerHTML.indexOf('$'); hitReward = parseFloat(amounts[k].innerHTML.substring(index+1)); hitStatus = statuses[k].innerHTML; hitFeedback = feedbacks[k].textContent; requesterId = getQueryVariable(requesterLink,"requesterId"); subject = getQueryVariable(requesterLink,"subject"); subject = subject.split("+"); hitId = subject[subject.length-1]; var hit = { hitId : hitId, date : convert_date(date), requesterName : requesterName.trim(), requesterLink : requesterLink.trim(), title : hitTitle.trim(), reward : hitReward, status : hitStatus, feedback : hitFeedback.trim(), requesterId : requesterId }; //HITStorage.indexedDB.addHIT(hitData); hitData.push(hit); } return amounts.length; } //Used to simplify getting requester ID's and such function getQueryVariable(url,variable) { var query = url.substring(1); var vars = query.split("?")[1].split("&"); for (var i=0;i<vars.length;i++) { var pair = vars[i].split("="); if(pair[0] == variable) { return pair[1]; } } return(false); } // Returns available days (YYYY-MM-DD) HITStorage.getAllAvailableDays = function(try_extra_days) { var days = []; var page = HITStorage.getHTTPObject(); page.open("GET", 'https://www.mturk.com/mturk/status', false); page.send(null); var page_html = document.createElement('div'); page_html.innerHTML = page.responseText; var dateElements = page_html.getElementsByClassName('statusDateColumnValue'); var submittedElements = page_html.getElementsByClassName('statusSubmittedColumnValue'); var approvedElements = page_html.getElementsByClassName('statusApprovedColumnValue'); var rejectedElements = page_html.getElementsByClassName('statusRejectedColumnValue'); var pendingElements = page_html.getElementsByClassName('statusPendingColumnValue'); var earningsElements = page_html.getElementsByClassName('statusEarningsColumnValue'); for (var i=0; i<dateElements.length; i++) { var date = dateElements[i].childNodes[1].href.substr(53); date = convert_date(date); days.push( { date: date, submitted: parseInt(submittedElements[i].textContent), approved : parseInt(approvedElements[i].textContent), rejected : parseInt(rejectedElements[i].textContent), pending : parseInt(pendingElements[i].textContent), earnings : parseFloat(earningsElements[i].textContent.slice(1)) }); } if (try_extra_days > 0) { var date = days[days.length-1].date; var d = new Date(); d.setFullYear(parseInt(date.substr(0,4)), parseInt(date.substr(5,2))-1, parseInt(date.substr(8,2))); for (var i=0; i<try_extra_days; i++) { d.setDate(d.getDate()-1); var month = '0' + (d.getMonth() + 1); var day = '0' + d.getDate(); if (month.length > 2) month = month.substr(1); if (day.length > 2) day = day.substr(1); date = '' + d.getFullYear() + '-' + month + '-' + day; days.push( { date: date, submitted: -1, approved : -1, rejected : -1, pending : -1, earnings : -1 } ); } } return days; } HITStorage.getLatestHITs = function() { if (localStorage['HITDB AUTO UPDATE'] === undefined || localStorage['HITDB AUTO UPDATE'] == 'OFF') return; if (localStorage['HITDB TIMESTAMP'] !== undefined) { if (new Date().getTime() < new Date(parseInt(localStorage['HITDB TIMESTAMP'])).getTime() + 90000) { return; } } localStorage['HITDB TIMESTAMP'] = new Date().getTime(); var auto_button = document.getElementById('auto_button'); var page = HITStorage.getHTTPObject(); page.open("GET", 'https://www.mturk.com/mturk/status', false); page.send(null); auto_button.textContent += ' +'; var page_html = document.createElement('div'); page_html.innerHTML = page.responseText; var dateElements = page_html.getElementsByClassName('statusDateColumnValue'); var submittedElements = page_html.getElementsByClassName('statusSubmittedColumnValue'); var approvedElements = page_html.getElementsByClassName('statusApprovedColumnValue'); var rejectedElements = page_html.getElementsByClassName('statusRejectedColumnValue'); var pendingElements = page_html.getElementsByClassName('statusPendingColumnValue'); var earningsElements = page_html.getElementsByClassName('statusEarningsColumnValue'); if (dateElements[0].childNodes[1].textContent.trim() != 'Today') return; var url = dateElements[0].childNodes[1].href; var date = url.substr(53); // keep MMDDYYYY var submitted = parseInt(submittedElements[0].textContent); //var approved = parseInt(approvedElements[0].textContent); //var rejected = parseInt(rejectedElements[0].textContent); //var pending = parseInt(pendingElements[0].textContent); //var earnings = parseFloat(earningsElements[0].textContent.slice(1)); var pages_done = null; if (localStorage['HITDB AUTOUPDATE PAGES'] !== undefined) { pages_done = JSON.parse(localStorage['HITDB AUTOUPDATE PAGES']); } if (pages_done == null || pages_done.date != date) pages_done = {date: date}; var new_hits = 0; var page = 1 + Math.floor(submitted/25); page = (page<1) ? 1 : page; var hitData = []; if (submitted != pages_done.submitted) { url = "https://www.mturk.com/mturk/statusdetail?sortType=All&pageNumber=" + page + "&encodedDate=" + date; HITStorage.process_page(url, date, hitData); new_hits += submitted - pages_done.submitted; pages_done.submitted = submitted; localStorage['HITDB AUTOUPDATE PAGES'] = JSON.stringify(pages_done); auto_button.textContent += '+'; } if (page > 1) { extra_page = page-1; while (extra_page >= 1) { if (pages_done[extra_page] != true) { url = "https://www.mturk.com/mturk/statusdetail?sortType=All&pageNumber=" + extra_page + "&encodedDate=" + date; if (HITStorage.process_page(url, date, hitData) == 25) { pages_done[extra_page] = true; localStorage['HITDB AUTOUPDATE PAGES'] = JSON.stringify(pages_done); auto_button.textContent += '+'; } break; } extra_page -= 1; } } HITStorage.indexedDB.addHITs(hitData); } // Gets status details for given date (MMDDYYYY) // Collects all HITs for given date to hitData array HITStorage.getHITData = function(day_to_fetch, hitData, page, days_to_update) { var dataDate = convert_iso_date(day_to_fetch.date); page = page || 1; detailed_status_page_link = "https://www.mturk.com/mturk/statusdetail?sortType=All&pageNumber=" + page + "&encodedDate=" + dataDate; if (HITStorage.process_page(detailed_status_page_link, dataDate, hitData) == 0) { if (day_to_fetch.submitted == -1 || hitData.length == day_to_fetch.submitted) { setTimeout(function(){ HITStorage.indexedDB.addHITs(hitData, day_to_fetch, days_to_update); }, 1000); } else { alert("There was an error while fetching HITs for date: " + day_to_fetch.date + ".\n" + "Script monkeys expected " + day_to_fetch.submitted + " bananas, but got " + hitData.length + "! 😞"); HITStorage.update_done(); } } else { HITStorage.update_status_label('Please wait: script monkeys are fetching status pages (' + day_to_fetch.date + ', page ' + page + ')', 'red'); setTimeout(function(){ HITStorage.getHITData(day_to_fetch, hitData, page+1, days_to_update); }, 1000); } } HITStorage.formatTime = function(msec) { if (isNaN(msec)) return "-"; var seconds = Math.floor(msec / 1000) % 60; var minutes = Math.floor((msec / 1000) / 60) % 60; var hours = Math.floor(((msec / 1000) / 60) / 60) % 24; var days = Math.floor(((msec / 1000) / 60) / 60 / 24); if (hours > 0) seconds = ""; else seconds = "" + seconds + "s"; minutes == 0 ? minutes = "" : minutes = "" + minutes + "m "; hours == 0 ? hours = "" : hours = "" + hours + "h "; if (days > 0) return '' + days + ' day' + ((days>1)? 's' : ' ') + hours; return hours + minutes + seconds; } HITStorage.update_status_label = function(new_status, color) { var label = document.getElementById('status_label'); label.innerHTML = new_status; label.style.color = color || 'black'; } // validate input field dates // Accept YYYY-MM-DD HITStorage.validate_date = function(input) { date = input.value; if (date.match(/^[01]\d\/[0123]\d\/20\d\d$/) != null) { var d = date.split('\/'); date = d[2] + '-' + d[0] + '-' + d[1]; input.value = date; } if (date.match(/^$|^20\d\d\-[01]\d\-[0123]\d$/) != null) { input.style.backgroundColor = 'white'; return true; } input.style.backgroundColor = 'pink'; return false; } HITStorage.validate_dates = function() { from = document.getElementById('from_date'); to = document.getElementById('to_date'); if (HITStorage.validate_date(from) && HITStorage.validate_date(to)) { if (from.value > to.value && to.value != '') { alert('Invalid date!'); return false; } return true; } alert('Invalid date!'); return false; } HITStorage.start_search = function() { if (HITStorage.validate_dates() == false) return; HITStorage.update_status_label('Using local HIT database', 'green'); var options = {}; options.term = document.getElementById('search_term').value; options.status = document.getElementById('status_select').value; options.donut = document.getElementById('donut_select').value; options.from_date = document.getElementById('from_date').value; options.to_date = document.getElementById('to_date').value; options.export_csv = document.getElementById('export_csv').checked; HITStorage.disable_inputs(); setTimeout(function(){ HITStorage.do_search(options); }, 500); } HITStorage.disable_inputs = function() { document.getElementById('delete_button').disabled = true; document.getElementById('search_button').disabled = true; document.getElementById('update_button').disabled = true; document.getElementById('overview_button').disabled = true; document.getElementById('import_button').disabled = true; document.getElementById('pending_button').disabled = true; document.getElementById('status_button').disabled = true; document.getElementById('from_date').disabled = true; document.getElementById('to_date').disabled = true; document.getElementById('search_term').disabled = true; document.getElementById('status_select').disabled = true; document.getElementById('donut_select').disabled = true; } HITStorage.enable_inputs = function() { document.getElementById('delete_button').disabled = false; document.getElementById('search_button').disabled = false; document.getElementById('update_button').disabled = false; document.getElementById('overview_button').disabled = false; document.getElementById('import_button').disabled = false; document.getElementById('pending_button').disabled = false; document.getElementById('status_button').disabled = false; document.getElementById('from_date').disabled = false; document.getElementById('to_date').disabled = false; document.getElementById('search_term').disabled = false; document.getElementById('status_select').disabled = false; document.getElementById('donut_select').disabled = false; } HITStorage.do_search = function(options) { HITStorage.indexedDB.getHITs(options); setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000); HITStorage.enable_inputs(); } HITStorage.show_results = function(results) { resultsWindow = window.open(); resultsWindow.document.write("<html><head><title>Status Detail Search Results</title></head><body>\n"); resultsWindow.document.write("<h1>HITs matching your search:</h1>\n"); resultsWindow.document.write('<table style="border: 1px solid black;border-collapse:collapse;width:90%;margin-left:auto;margin-right:auto;">\n'); 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'); var odd = true; var sum = 0; var sum_rejected = 0; var sum_approved = 0; var sum_pending = 0; var new_day = false; for (var i=0; i<results.length; i++) { odd = !odd; sum += results[i].reward; if (results[i].status == 'Rejected') sum_rejected += results[i].reward; else if (results[i].status == 'Pending Approval') sum_pending += results[i].reward; else sum_approved += results[i].reward; if (i>0 && (results[i-1].date != results[i].date)) new_day = true; else new_day = false; resultsWindow.document.write(HITStorage.format_hit_line(results[i], odd, HITStorage.status_color(results[i].status), new_day )); } 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'); resultsWindow.document.write("</table>"); resultsWindow.document.write("<p>Found " + results.length + " matching HITs. $" + sum_approved.toFixed(2) + " approved, " + "$" + sum_rejected.toFixed(2) + " rejected and $" + sum_pending.toFixed(2) + " pending.</p>"); resultsWindow.document.write("</body></html>") resultsWindow.document.close(); } HITStorage.status_color = function(status) { var color = "green"; if (status.match("Pending Approval")) color = "orange"; else if (status.match("Rejected")) color = "red"; return color; } HITStorage.format_hit_line = function(hit, odd, status_color, new_day) { var line = '<tr style="background-color:'; if (odd) line += '#f1f3eb;'; else line += 'white;'; line += ' valign=top;'; if (new_day) line += ' border: 0px dotted #000000; border-width: 2px 0px 0px 0px">'; else line += '">'; line += '<td>' + hit.date + '</td>'; if (hit.requesterLink != null) line += '<td style="width:165px"><a href="' + hit.requesterLink + '" title="Contact this Requester">' + hit.requesterName + '</a></td>'; else line += '<td style="width:165px">' + hit.requesterName + '</td>'; line += '<td style="width:213px">' + hit.title + '</td>'; line += '<td style="width:45px">$' + hit.reward.toFixed(2) + '</td>'; line += '<td style="color:' + status_color + '; width:55px">' + hit.status + '</td>'; line += '<td><div style="width:225px; overflow:hidden">' + hit.feedback + '</div></td>'; line += '</tr>\n'; return line; } HITStorage.show_pending_overview = function(results) { resultsWindow = window.open(); resultsWindow.document.write("<html><head><title>Summary of Pending HITs</title></head><body>\n"); resultsWindow.document.write("<h1>Summary of Pending HITs</h1>\n"); resultsWindow.document.write('<table style="border: 1px solid black;border-collapse:collapse;width:90%;margin-left:auto;margin-right:auto;">\n'); resultsWindow.document.write('<tr style="background-color:lightgrey"><th>requesterId</th><th>Requester</th><th></th><th>Pending</th><th>Rewards</th>\n'); // 'requesterId,requesterName,pending,reward'; var odd = false; var sum = 0; var pending = 0; for (var i=0; i<results.length; i++) { odd = !odd; sum += results[i][3]; pending += results[i][2]; resultsWindow.document.write(HITStorage.format_pending_line(results[i], odd, i)); } 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'); resultsWindow.document.write("</table>"); resultsWindow.document.write("</body></html>") resultsWindow.document.close(); for (var i=0; i<results.length; i++) { resultsWindow.document.getElementById('id-' + i).addEventListener("click", search_func(results[i][0], 'requesterId'), false); resultsWindow.document.getElementById('id2-' + i).addEventListener("click", show_requester_func(results[i][0]) , false); } } HITStorage.show_status_overview = function(results, date) { resultsWindow = window.open(); resultsWindow.document.write("<html><head><title>Daily HIT stats</title></head><body>\n"); if (date) resultsWindow.document.write("<h1>Daily HIT stats</h1>\n"); else resultsWindow.document.write("<h1>Daily HIT stats (' + date + ')</h1>\n"); resultsWindow.document.write('<table style="border: 1px solid black;border-collapse:collapse;width:90%;margin-left:auto;margin-right:auto;">\n'); 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'); var odd = false; var sum = 0; var submitted = 0; var approved = 0; var rejected = 0; var pending = 0; var new_month = false; for (var i=results.length-1; i>=0; i--) { odd = !odd; sum += results[i].earnings; submitted += results[i].submitted; approved += results[i].approved; rejected += results[i].rejected; pending += results[i].pending; if (i<results.length-1) new_month = (results[i].date.substr(0,7) != results[i+1].date.substr(0,7)); resultsWindow.document.write(HITStorage.format_status_line(results[i], odd, new_month)); } resultsWindow.document.write('<tr style="background-color:lightgrey"><th>' + results.length + ' days</th><th style="text-align: left">' + submitted + '</th><th style="text-align: left">' + approved + '</th><th style="text-align: left">' + rejected + '</th><th style="text-align: left">' + pending + '</th><th style="text-align: left">$' + sum.toFixed(2) + '</th>\n'); resultsWindow.document.write("</table>"); resultsWindow.document.write("</body></html>") resultsWindow.document.close(); for (var i=0; i<results.length; i++) resultsWindow.document.getElementById(results[i].date).addEventListener("click", search_func('', 'date', results[i].date, results[i].date), false); } HITStorage.show_requester_overview = function(results, date) { resultsWindow = window.open(); resultsWindow.document.write("<html><head><title>Requester Overview</title></head><body>\n"); if (date) resultsWindow.document.write("<h1>Requester Overview " + date + "</h1>\n"); else resultsWindow.document.write("<h1>Requester Overview</h1>\n"); resultsWindow.document.write('<table style="border: 1px solid black;border-collapse:collapse;width:90%;margin-left:auto;margin-right:auto;">\n'); 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'); // 'requesterId,requesterName,hits,pending,reward,rejected'; var odd = false; var sum = 0; var hits = 0; var rejected = 0; var pending = 0; var new_day = false; var top = true; var dot_line; for (var i=0; i<results.length; i++) { odd = !odd; sum += results[i][3]; hits += results[i][2]; rejected += results[i][4]; pending += results[i][5]; dot_line = false; if (i==10) { dot_line = true; top = false; } if (i>10 && results[i][3] == 0 && results[i-1][3] != 0) dot_line = true; resultsWindow.document.write(HITStorage.format_overview_line(results[i], odd, dot_line, top, i)); } resultsWindow.document.write('<tr style="background-color:lightgrey"><th>' + results.length + ' different requesterIds</th>' + '<th></th><th></th><th style="text-align: right">' + hits + '<th style="text-align: right">' + pending + '</th><th style="text-align: right">$' + sum.toFixed(2) + '</th><th style="text-align: right">' + rejected + '</th>' + '<th style="text-align: right">' + (rejected/hits*100).toFixed(2) + '%</th></tr>\n'); resultsWindow.document.write("</table>"); resultsWindow.document.write("<p>Reward includes all 'Paid' and 'Approved - Pending Payment' HITs. " + "Reward does not include any bonuses.</p>"); resultsWindow.document.write("</body></html>") resultsWindow.document.close(); for (var i=0; i<results.length; i++) { resultsWindow.document.getElementById('id-' + i).addEventListener("click", search_func(results[i][0], 'requesterId'), false); resultsWindow.document.getElementById('id2-' + i).addEventListener("click", show_requester_func(results[i][0]) , false); } } HITStorage.show_requester = function(results) { resultsWindow = window.open(); resultsWindow.document.write('<html><head><title>' + results[0].requesterName + '</title></head><body>\n'); resultsWindow.document.write('<h1>' + results[0].requesterName + ' (' + results[0].requesterId + ')</h1>\n'); resultsWindow.document.write('You have submitted ' + results.length + ' HITs for this requester. Earliest ' + results[results.length-1].date + ', latest ' + results[0].date); resultsWindow.document.write('<p><a href="https://www.mturk.com/mturk/searchbar?selectedSearchType=hitgroups&requesterId=' + results[0].requesterId + '">' + 'Search HITs created by this requester</a></p>'); resultsWindow.document.write('<p><a href="http://turkopticon.differenceengines.com/' + results[0].requesterId + '">' + 'See reviews about this requester on Turkopticon</a> or '); resultsWindow.document.write('<a href="' + TO_report_link(results[0].requesterId,results[0].requesterName) + '">' + 'review this requester on Turkopticon</a></p>'); var reward = 0; var hits = 0; var sum = 0; var rejected = 0; var approved = 0; var pending = 0; var all_rejected = 0; var all_approved = 0; var all_pending = 0; resultsWindow.document.write('<table style="border: 1px solid black;border-collapse:collapse;margin-left:10px;margin-right:auto;">\n'); resultsWindow.document.write('<tr style="background-color:lightgrey"><th>Month' + '</th><th>Submitted' + '</th><th>Approved' + '</th><th>Rejected' + '</th><th>Pending' + '</th><th>Earnings</th></tr>\n'); for (var i=0; i<results.length; i++) { hits++; if (results[i].status == 'Rejected') { all_rejected++; rejected++; } else if (results[i].status == 'Pending Approval') { all_pending++; pending++; } else { all_approved++; approved++; sum += results[i].reward; reward += results[i].reward; } if (i==results.length-1 || (i<results.length-1 && (results[i].date.substr(0,7) != results[i+1].date.substr(0,7)))) { resultsWindow.document.write('<tr><td style="text-align: right">' + results[i].date.substr(0,7) + '</td><td style="text-align: right">' + hits + '</td><td style="text-align: right">' + approved + '</td><td style="text-align: right">' + rejected + '</td><td style="text-align: right">' + pending + '</td><td style="text-align: right">$' + reward.toFixed(2) + '</td></tr>\n'); reward = 0; hits = 0; approved = 0; rejected = 0; pending = 0; } } resultsWindow.document.write('<tr style="background-color:lightgrey"><th>' + '</th><th style="text-align: right">' + results.length + '</th><th style="text-align: right">' + all_approved + '</th><th style="text-align: right">' + all_rejected + '</th><th style="text-align: right">' + all_pending + '</th><th style="text-align: right">$' + sum.toFixed(2) + '</th></tr>\n'); resultsWindow.document.write('</table>'); resultsWindow.document.write('<p>Rewards do not include any bonuses</p>'); resultsWindow.document.write("</body></html>"); resultsWindow.document.close(); } function TO_report_link(requesterId, requesterName) { return 'http://turkopticon.differenceengines.com/report?requester[amzn_id]=' + requesterId + '&requester[amzn_name]=' + encodeURI(requesterName.trim()); } HITStorage.format_overview_line = function(req, odd, dot_line, top, i) { var color; if (top) color = (odd)? 'ffffe0;' : '#eee8aa;'; else color = (odd)? 'white;' : '#f1f3eb;'; var line = '<tr style="background-color:' + color; if (dot_line) line += ' border: 0px dotted #000000; border-width: 2px 0px 0px 0px'; line += '">'; line += '<td><button type="button" title="Show all HITs" style="height: 16px;font-size: 8px; padding: 0px;" id="id-' + i + '">>></button>' + '<button type="button" title="Show details about requester" style="height: 16px;font-size: 8px; padding: 0px;" id="id2-' + i + '">+</button> ' + req[0].trim() + '</td>'; line += '<td><a title="Requesters Turkopticon page" target="_blank" href="http://turkopticon.differenceengines.com/' + req[0].trim() + '">[TO]</a> '; line += req[1].trim() + '</td>'; line += '<td style="width: 50px"><a title="Report requester to Turkopticon" target="_blank" href="' + TO_report_link(req[0], req[1]) + '">[report]</a></td>'; line += '<td style="text-align: right">' + req[2] + '</td>'; line += '<td style="text-align: right">' + req[5] + '</td>'; line += '<td style="text-align: right">$' + req[3].toFixed(2) + '</td>'; var p = (req[4]/req[2]*100).toFixed(1); var pc = (p>0)? 'red' : 'green'; line += '<td style="text-align: right; color:' + pc + ';">' + req[4] + '</td>'; line += '<td style="text-align: right; color:' + pc + ';">' + p + '%</td>'; line += '</tr>\n'; return line; } HITStorage.format_pending_line = function(req, odd, i) { console.log(req); var color = (odd)? 'white;' : '#f1f3eb;'; var line = '<tr style="background-color:' + color; line += '">'; 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-' + i + '">>>></button>' + '<button type="button" title="Show details about requester" style="height: 16px;font-size: 8px; padding: 0px;" id="id2-' + i + '">+</button> ' + req[0].trim() + '</td>'; line += '<td><a title="Requesters Turkopticon page" target="_blank" href="http://turkopticon.differenceengines.com/' + req[0].trim() + '">[TO]</a> '; line += req[1].trim() + '</td>'; line += '<td style="width: 50px"><a title="Report requester to Turkopticon" target="_blank" href="' + TO_report_link(req[0], req[1]) + '">[report]</a></td>'; line += '<td style="text-align: right">' + req[2] + '</td>'; line += '<td style="text-align: right">$' + req[3].toFixed(2) + '</td>'; line += '</tr>\n'; return line; } HITStorage.format_status_line = function(d, odd, new_month) { var color = (odd)? 'white;' : '#f1f3eb;'; var line = '<tr style="background-color:' + color; if (new_month) line += ' border: 0px dotted #000000; border-width: 2px 0px 0px 0px">'; else line += '">'; line += '<td><button type="button" title="Show all HITs" style="height: 16px;font-size: 8px; padding: 0px;" id="' + d.date + '">>>></button> ' + d.date + '</td>'; line += '<td>' + d.submitted + '</td>'; line += '<td>' + d.approved + '</td>'; line += '<td>' + d.rejected + '</td>'; line += '<td>' + d.pending + '</td>'; line += '<td>$' + d.earnings.toFixed(2) + '</td>'; line += '</tr>\n'; return line; } HITStorage.show_pending_overview_csv = function(results) { var csvData = []; csvData.push(["requesterId","requesterName","pending","reward","\n"]); for (var i=0; i<results.length; i++) { csvData.push(HITStorage.format_pending_line_csv(results[i])); } var blob = new Blob(csvData, {type: "text/csv;charset=utf-8"}); saveAs(blob, "pending_overview.csv"); } HITStorage.format_pending_line_csv = function(req) { var line = []; line.push(req[0].trim()); line.push('"' + req[1].trim() + '"'); line.push(req[2]); line.push(req[3].toFixed(2)); line.push('\n'); return line; } HITStorage.show_requester_overview_csv = function(results) { var csvData = []; csvData.push(['requesterId','requesterName','hits','reward','rejected','pending','\n']); for (var i=0; i<results.length; i++) { csvData.push(HITStorage.format_overview_line_csv(results[i])); } var blob = new Blob(csvData, {type: "text/csv;charset=utf-8"}); saveAs(blob, "requester_overview.csv"); } HITStorage.format_overview_line_csv = function(req) { var line = []; line.push(req[0].trim()); line.push('"' + req[1].trim() + '"'); line.push(req[2]); line.push(req[3].toFixed(2)); line.push(req[4]); line.push(req[5]); line.push('\n'); return line; } HITStorage.show_status_overview_csv = function(results) { var csvData = []; csvData.push(['Date','Submitted','Approved','Rejected','Pending','Earnings','\n']); for (var i=results.length-1; i>=0; i--) { csvData.push(HITStorage.format_status_line_csv(results[i])); } var blob = new Blob(csvData, {type: "text/csv;charset=utf-8"}); //location.href='data:text/csv;charset=utf8,' + encodeURIComponent(csvData); saveAs(blob, "status_overview.csv"); } HITStorage.format_status_line_csv = function(d) { var line = []; line.push('"' + d.date + '"'); line.push(d.submitted); line.push(d.approved); line.push(d.rejected); line.push(d.pending); line.push(d.earnings.toFixed(2)); line.push('\n'); return line; } HITStorage.export_csv = function(results) { var csvData = []; csvData.push(['hitId','date','requesterName','requesterId','title','reward','status','feedback','\n']); for (var i=0; i<results.length; i++) { csvData.push(HITStorage.format_csv_line(results[i])); } var blob = new Blob(csvData, {type: "text/csv;charset=utf-8"}); //location.href='data:text/csv;charset=utf8,' + encodeURIComponent(csvData); saveAs(blob, "hit_database.csv"); } HITStorage.format_csv_line = function(hit) { var line = []; line.push('"' + hit.hitId.trim() + '"'); line.push('"' + hit.date.trim() + '"'); line.push('"' + hit.requesterName.trim() + '"'); line.push('"' + hit.requesterId.trim() + '"'); line.push('"' + hit.title.trim() + '"'); line.push(hit.reward.toFixed(2)); line.push('"' + hit.status.trim().replace(/\ /g,' ') + '"'); line.push('"' + hit.feedback.trim() + '"'); line.push('\n'); return line; } HITStorage.do_update = function(days_to_update) { if (DAYS_TO_FETCH.length<1) { HITStorage.check_update(); return; } HITStorage.update_status_label('Please wait: ' + progress_bar(days_to_update-DAYS_TO_FETCH.length, days_to_update) + ' (' + (days_to_update-DAYS_TO_FETCH.length) + '/' + days_to_update + ')', 'red'); var hits = []; setTimeout(function(){ HITStorage.getHITData( DAYS_TO_FETCH.shift(), hits, 1, days_to_update); }, 2000); } HITStorage.update_done = function() { HITStorage.update_status_label('Script monkeys have updated your local database', 'green'); setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 5000); HITStorage.enable_inputs(); localStorage['HITDB UPDATED'] = new Date().toString(); var e = document.getElementById('user_activities.date_column_header.tooltip').parentNode.parentNode.childNodes[2].childNodes[1].childNodes[1]; if (e != null && e.textContent.trim() == 'Today') { var today = e.href.slice(-8); today = convert_date(today); HITStorage.indexedDB.get_todays_projected_earnings(today); } HITStorage.indexedDB.get_pending_approvals(); HITStorage.indexedDB.get_pending_payments(); } HITStorage.update_database = function() { HITStorage.disable_inputs(); if (localStorage['HITDB TRY_EXTRA_DAYS'] == 'YES') { DAYS_TO_FETCH = HITStorage.getAllAvailableDays(20); delete localStorage['HITDB TRY_EXTRA_DAYS']; } else { DAYS_TO_FETCH = HITStorage.getAllAvailableDays(); } DAYS_TO_FETCH_CHECK = DAYS_TO_FETCH.slice(0); // remove extra days from checklist for (var i=0; i<DAYS_TO_FETCH_CHECK.length; i++) { if (DAYS_TO_FETCH_CHECK[i].submitted == -1) { DAYS_TO_FETCH_CHECK = DAYS_TO_FETCH_CHECK.slice(0,i); break; } } DAYS_TO_FETCH = DAYS_TO_FETCH_CHECK.slice(0); HITStorage.update_status_label('Please wait: script monkeys are preparing to start working', 'red'); setTimeout(function(){ HITStorage.prepare_update_and_check_pending_payments(); }, 100); } HITStorage.show_overview = function() { if (HITStorage.validate_dates() == false) return; var options = {}; options.term = document.getElementById('search_term').value; options.status = document.getElementById('status_select').value; options.donut = document.getElementById('donut_select').value; options.from_date = document.getElementById('from_date').value; options.to_date = document.getElementById('to_date').value; options.export_csv = document.getElementById('export_csv').checked; HITStorage.update_status_label('Please wait: script monkeys are picking bananas 😋', 'red'); HITStorage.disable_inputs(); HITStorage.indexedDB.requesterOverview(options); } HITStorage.show_pendings = function() { var options = {}; options.term = document.getElementById('search_term').value; options.status = document.getElementById('status_select').value; options.donut = document.getElementById('donut_select').value; options.from_date = document.getElementById('from_date').value; options.to_date = document.getElementById('to_date').value; options.export_csv = document.getElementById('export_csv').checked; HITStorage.update_status_label('Please wait: script monkeys are picking bananas 😋', 'red'); HITStorage.disable_inputs(); HITStorage.indexedDB.pendingOverview(options); } HITStorage.show_status = function() { if (HITStorage.validate_dates() == false) return; var options = {}; options.term = document.getElementById('search_term').value; options.status = document.getElementById('status_select').value; options.donut = document.getElementById('donut_select').value; options.from_date = document.getElementById('from_date').value; options.to_date = document.getElementById('to_date').value; options.export_csv = document.getElementById('export_csv').checked; HITStorage.update_status_label('Please wait: script monkeys are picking bananas 😋', 'red'); HITStorage.disable_inputs(); HITStorage.indexedDB.statusOverview(options); } var IMPORT_DIALOG = null; function import_dialog() { if (IMPORT_DIALOG == null) { IMPORT_DIALOG = document.createElement('div'); IMPORT_DIALOG.style.display = 'block'; IMPORT_DIALOG.style.position = 'fixed'; IMPORT_DIALOG.style.width = '600px'; //IMPORT_DIALOG.style.height = '400px'; IMPORT_DIALOG.style.height = '90%'; IMPORT_DIALOG.style.left = '50%'; IMPORT_DIALOG.style.right = '50%'; IMPORT_DIALOG.style.margin = '-300px 0px 0px -300px'; //IMPORT_DIALOG.style.top = '400px'; IMPORT_DIALOG.style.bottom = '10px'; IMPORT_DIALOG.style.padding = '10px'; IMPORT_DIALOG.style.border = '2px'; IMPORT_DIALOG.style.textAlign = 'center'; IMPORT_DIALOG.style.verticalAlign = 'middle'; IMPORT_DIALOG.style.borderStyle = 'solid'; IMPORT_DIALOG.style.borderColor = 'black'; IMPORT_DIALOG.style.backgroundColor = 'white'; IMPORT_DIALOG.style.color = 'black'; IMPORT_DIALOG.style.zIndex = '100'; var table = document.createElement('table'); var input = document.createElement('textarea'); var input2 = document.createElement('input'); var label = document.createElement('label'); var label2 = document.createElement('label'); label.textContent = 'Paste CSV-file in the textarea below.'; label2.textContent = 'CVS separator: '; input.style.width = '100%'; input.style.height = '90%'; input2.maxLength = '1'; input2.size = '1'; input2.defaultValue = ','; var import_button = document.createElement('button'); import_button.textContent = 'Import HITs'; import_button.addEventListener("click", import_dialog_close_func(true, input, input2), false); import_button.style.margin = '5px'; var cancel_button = document.createElement('button'); cancel_button.textContent = 'Cancel'; cancel_button.addEventListener("click", import_dialog_close_func(false, input, input2), false); cancel_button.style.margin = '5px'; IMPORT_DIALOG.appendChild(label); IMPORT_DIALOG.appendChild(document.createElement('br')); IMPORT_DIALOG.appendChild(label2); IMPORT_DIALOG.appendChild(input2); IMPORT_DIALOG.appendChild(document.createElement('br')); IMPORT_DIALOG.appendChild(input); IMPORT_DIALOG.appendChild(document.createElement('br')); IMPORT_DIALOG.appendChild(cancel_button); IMPORT_DIALOG.appendChild(import_button); document.body.appendChild(IMPORT_DIALOG); } else { IMPORT_DIALOG.style.display = 'block'; } } /* * CSVToArray() function is taken from: * * Blog Entry: * Ask Ben: Parsing CSV Strings With Javascript Exec() Regular Expression Command * * Author: * Ben Nadel / Kinky Solutions * * Link: * http://www.bennadel.com/index.cfm?event=blog.view&id=1504 * * Date Posted: * Feb 19, 2009 at 10:03 AM */ // This will parse a delimited string into an array of // arrays. The default delimiter is the comma, but this // can be overriden in the second argument. function CSVToArray( strData, strDelimiter ) { // Check to see if the delimiter is defined. If not, // then default to comma. strDelimiter = (strDelimiter || ","); // Create a regular expression to parse the CSV values. var objPattern = new RegExp( ( // Delimiters. "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" + // Quoted fields. "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" + // Standard fields. "([^\"\\" + strDelimiter + "\\r\\n]*))" ), "gi" ); // Create an array to hold our data. Give the array // a default empty first row. var arrData = [[]]; // Create an array to hold our individual pattern // matching groups. var arrMatches = null; // Keep looping over the regular expression matches // until we can no longer find a match. while (arrMatches = objPattern.exec( strData )){ // Get the delimiter that was found. var strMatchedDelimiter = arrMatches[ 1 ]; // Check to see if the given delimiter has a length // (is not the start of string) and if it matches // field delimiter. If id does not, then we know // that this delimiter is a row delimiter. if ( strMatchedDelimiter.length && (strMatchedDelimiter != strDelimiter) ){ // Since we have reached a new row of data, // add an empty row to our data array. arrData.push( [] ); } // Now that we have our delimiter out of the way, // let's check to see which kind of value we // captured (quoted or unquoted). if (arrMatches[ 2 ]){ // We found a quoted value. When we capture // this value, unescape any double quotes. var strMatchedValue = arrMatches[ 2 ].replace( new RegExp( "\"\"", "g" ), "\"" ); } else { // We found a non-quoted value. var strMatchedValue = arrMatches[ 3 ]; } // Now that we have our value string, let's add // it to the data array. arrData[ arrData.length - 1 ].push( strMatchedValue ); } // Return the parsed data. return( arrData ); } function import_dialog_close_func(save, input, separator) { return function() { if (save == true) { var lines = []; var hits = []; var dates = []; if (input.value.length > 0) lines = CSVToArray(input.value, separator.value); var errors = 0; for (var i = 0; i<lines.length; i++) { var error = false; try { if (lines[i][0] == null || lines[i][0] == 'hitId') continue; if(lines[i][6] == 'Approved - Pending Payment') lines[i][6] = 'Approved - Pending Payment'; if (lines[i].length != 8) error = true; var hit = { hitId : lines[i][0], date : convert_date(lines[i][1]), requesterName : lines[i][2], //This line was null in the version I was using. I added it in, giving it the proper format. //This setting is for the links to contact the requester in the status window 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(" ","+"), requesterId : lines[i][3], title : lines[i][4], reward : parseFloat(lines[i][5]), status : lines[i][6], feedback : lines[i][7] || "" // If no feedback, put empty string }; //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. //This sets up a simple associative array for the initial "date" entry for the hit stats. See below for implementation var status = { date : hit.date, approved : (hit.status != "Rejected" ? 1 : 0), earnings : (hit.status != "Rejected" ? hit.reward : 0), pending : (hit.status != "Pending" ? 0 : 1), rejected : (hit.status == "Rejected" ? 1 : 0), submitted : 1 }; } catch(err) { error = true; } if (error == false){ hits.push(hit); //Implementation of status stuff. First I see if the object exists in my "dates" array, var index = lookup(hit.date, "date", dates); if (index != -1){ //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 for (var key in dates[index]){ if (key != "date") dates[index][key] += status[key]; } } else dates.push(status); //if the date doesn't exist in the array, add it as an initial object } else errors++; } if (hits.length < 1) { alert('No HITs found!'); return; } else if (confirm('Found ' + hits.length + ' HITs' + (errors>0? ' and ' + errors + (errors==1? ' error' : ' errors') : '') + '.\nDo not reload this page until import is ready.\n' + 'Press Ok to start.') == true) { HITStorage.disable_inputs(); HITStorage.update_status_label('Please wait: importing HITs', 'red'); IMPORT_DIALOG.style.display = 'none'; input.value = ''; HITStorage.indexedDB.importHITs(hits); //You have to call updateHITstats on a date object: //object = { date:"yyyy-mm-dd", (approved|pending|rejected):int num(Approved|Pending|Rejected), earnings:float totalEarningsForDay, submitted:int numHitsSubmittedThatDay } //Easiest hack to do so, parse over the dates objects I manipulated above, call update hit stats on each of them. for (var i = 0; i < dates.length; i++){ HITStorage.indexedDB.updateHITstats(dates[i]); } return; } else { return; } } IMPORT_DIALOG.style.display = 'none'; input.value = ''; }; } //simple lookup function for searching I'm reusing. function lookup (needle, key, haystack) { for (var i = 0; i < haystack.length; i++){ if (haystack[i][key] == needle) return i; } return -1; } function get_requester_id(s) { var idx = 12 + s.search('requesterId='); return s.substr(idx); } function show_requester_func(requesterId) { return function() { HITStorage.indexedDB.showRequester(requesterId); }; } function search_func(key, index, d1, d2) { d1 = d1 || ''; d2 = d2 || d1; return function() { HITStorage.indexedDB.getHITs({term: key, index: index, status: '---', from_date: d1, to_date: d2, donut: '', this_day: ''}); }; } function visible_func(element, visible) { return function() { element.style.visibility = (visible)? 'visible' : 'hidden'; }; } function delete_func() { return function() { if (confirm('This will remove your local HIT DataBase!\nContinue?')) { HITStorage.indexedDB.deleteDB(); } }; } function import_func() { return function() { import_dialog(); }; } function note_func(id, label) { return function() { note = prompt('Note for requesterId \'' + id + '\':', label.textContent); if (note == null) { return; } HITStorage.indexedDB.addNote(id, note); label.textContent = note; label.style.border = '1px dotted'; if (note.indexOf('!') >= 0) label.style.color = 'red'; else label.style.color = 'black'; }; } function block_func(requesterId, title, hitElement) { return function() { re = prompt('Block HITs from requesterId \'' + requesterId + '\' matching:\n' + '(default matches only exactly same HIT title, leave empty to match all HITS)', '^' + title.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1") + '$'); if (re == null) { return; } re = new RegExp(re); if (!re.test(title)) { if (confirm("Your regular expression does not match current HIT title.\nSave it anyway?") == false) return; } HITStorage.indexedDB.addBlock(requesterId, re); }; } function unblock_func(requesterId, title) { return function() { var unblock = confirm('Unblocking removes all blocks that match this HITs title and requesterId.'); if (unblock == true) { HITStorage.indexedDB.removeBlocks(requesterId, title); } }; } function auto_update_func() { return function() { var button = document.getElementById('auto_button'); if (localStorage['HITDB AUTO UPDATE'] === undefined) { alert('Enable Hit DataBase Auto Update\nWhen enabled, script will fetch last ' + 'statusdetail pages and add them to database when this page is reloaded ' + 'and at least two minutes have passed from last update. You still need to ' + 'do full update from dashboard every now and then.'); button.textContent = 'Auto Update is ON'; button.style.color = 'green'; localStorage['HITDB AUTO UPDATE'] = 'ON'; } else if (localStorage['HITDB AUTO UPDATE'] == 'ON') { button.textContent = 'Auto Update is OFF'; button.style.color = 'red'; localStorage['HITDB AUTO UPDATE'] = 'OFF'; } else { button.textContent = 'Auto Update is ON'; button.style.color = 'green'; localStorage['HITDB AUTO UPDATE'] = 'ON'; } }; } function set_target_func(date) { return function() { var target = localStorage['TODAYS TARGET']; if (target === undefined) target = ''; else target = parseFloat(localStorage['TODAYS TARGET']).toFixed(2); target = prompt('Set your target:', target); if (target == null) return; target = parseFloat(target); localStorage['TODAYS TARGET'] = target.toFixed(2); if (date != null) HITStorage.indexedDB.get_todays_projected_earnings(date); }; } function random_face() { var faces = ['😁','😃','😄','😇','😈','😉','😊','😋','😌','😍','😐','😎','😸','😹','😺','😻']; var n = Math.floor((Math.random()*faces.length)); return '<span style="color: black; font-weight: normal;" title="Featured non-amazonian script ' + ((n>11) ? '... kitten?': 'monkey') + '">' + faces[n] + '</span>'; } function progress_bar(done, max, full, empty, c1, c2) { max = (max<1)? 1 : max; done = (done<0)? 0 : done; done = (done>max)? max : done; var bar = '<span style="color: ' + (c1||'green') + '">'; for (var i=0; i<done; i++) { bar += full || '■'; } bar += '</span><span style="color: ' + (c2||'black') + '">'; for (var i=done; i<max; i++) { bar += empty || '⬜'; } bar += '</span>'; return bar; } // convert date to more practical form (MMDDYYYY => YYYY-MM-DD) function convert_date(date) { if (date.indexOf('-') > 0) return date; var day = date.substr(2,2); var month = date.substr(0,2); var year = date.substr(4,4); return (year + '-' + month + '-' + day); } // convert date from YYYY-MM-DD to MMDDYYYY if it isn't already function convert_iso_date(date) { if (date.indexOf('-') < 0) return date; var t = date.split('-'); return t[1] + t[2] + t[0]; } // Format date for display YYYY-MM-DD, DD/MM/YYYY or DD.MM.YYYY function display_date(date, format) { if (format === undefined || format == null) return date; var d = date.split('-'); if (format == 'little') { return d[2] + '.' + d[1] + '.' + d[0]; } if (format == 'middle') { return d[1] + '/' + d[2] + '/' + d[0]; } } HITStorage.indexedDB.create(); // Backup plan //HITStorage.update_date_format(true); if (document.location.href.match('https://www.mturk.com/mturk/dashboard')) { var footer = document.getElementsByClassName('footer_separator')[0]; if (footer == null) return; var extra_table = document.createElement('table'); extra_table.width = '700'; extra_table.style.boder = '1px solid black'; extra_table.align = 'center'; extra_table.cellSpacing = '0px'; extra_table.cellPadding = '0px'; var row1 = document.createElement('tr'); var row2 = document.createElement('tr'); var td1 = document.createElement('td'); var content_td = document.createElement('td'); var whatsthis = document.createElement('a'); row1.style.height = '25px'; td1.setAttribute('class', 'white_text_14_bold'); td1.style.backgroundColor = '#7fb448';//'#7fb4cf'; td1.style.paddingLeft = '10px'; td1.innerHTML = 'HIT DataBase' + random_face() + ' '; content_td.setAttribute('class', 'container-content'); whatsthis.href = 'http://userscripts.org/scripts/show/149548'; whatsthis.setAttribute('class', 'whatis'); whatsthis.textContent = '(What\'s this?)'; extra_table.appendChild(row1); row1.appendChild(td1); td1.appendChild(whatsthis); extra_table.appendChild(row2); row2.appendChild(content_td); footer.parentNode.insertBefore(extra_table, footer); var my_bar = document.createElement('div'); var search_button = document.createElement('button'); var status_select = document.createElement('select'); var label = document.createElement('label'); var label2 = document.createElement('label'); var input = document.createElement('input'); var donut_select = document.createElement('select'); var csv_label = document.createElement('label'); var csv = document.createElement('input'); var update_button = document.createElement('button'); var delete_button = document.createElement('button'); var pending_button = document.createElement('button'); var overview_button = document.createElement('button'); var import_button = document.createElement('button'); var status_button = document.createElement('button'); var from_input = document.createElement('input'); var to_input = document.createElement('input'); var date_label1 = document.createElement('label'); var date_label2 = document.createElement('label'); date_label1.textContent = 'from date '; date_label2.textContent = ' to '; from_input.setAttribute('id', "from_date"); to_input.setAttribute('id', "to_date"); to_input.setAttribute('maxlength', "10"); from_input.setAttribute('maxlength', "10"); to_input.setAttribute('size', "10"); from_input.setAttribute('size', "10"); from_input.title = 'Date format YYYY-MM-DD\nOr leave empty.'; to_input.title = 'Date format YYYY-MM-DD\nOr leave empty.'; var donut_options = []; donut_options[0] = document.createElement("option"); donut_options[1] = document.createElement("option"); donut_options[2] = document.createElement("option"); donut_options[0].text = "---"; donut_options[1].text = "Donut Chart HITS"; donut_options[2].text = "Donut Chart REWARDS"; donut_options[0].value = "---"; donut_options[1].value = "HITS"; donut_options[2].value = "REWARDS"; var status_options = []; status_options[0] = document.createElement("option"); status_options[1] = document.createElement("option"); status_options[2] = document.createElement("option"); status_options[3] = document.createElement("option"); status_options[4] = document.createElement("option"); status_options[5] = document.createElement("option"); status_options[0].text = "Pending Approval"; status_options[0].style.color = "orange"; status_options[1].text = "Rejected"; status_options[1].style.color = "red"; status_options[2].text = "Approved - Pending Payment"; status_options[2].style.color = "green"; status_options[3].text = "Paid"; status_options[3].style.color = "green"; status_options[4].text = "Paid AND Approved"; status_options[4].style.color = "green"; status_options[5].text = "ALL"; status_options[0].value = "Pending Approval"; status_options[1].value = "Rejected"; status_options[2].value = "Approved"; status_options[3].value = "Paid"; status_options[4].value = "Paid|Approved"; status_options[5].value = "---"; search_button.setAttribute('id', "search_button"); input.setAttribute('id', "search_term"); status_select.setAttribute('id', "status_select"); label.setAttribute('id', "status_label"); donut_select.setAttribute('id', "donut_select"); delete_button.setAttribute('id', "delete_button"); update_button.setAttribute('id', "update_button"); overview_button.setAttribute('id', "overview_button"); import_button.setAttribute('id', "import_button"); pending_button.setAttribute('id', "pending_button"); status_button.setAttribute('id', "status_button"); my_bar.style.marginLeft = 'auto'; my_bar.style.marginRight = 'auto'; my_bar.style.textAlign = 'center'; label.style.marginLeft = 'auto'; label.style.marginRight = 'auto'; label.style.textAlign = 'center'; var donut = document.createElement('div'); donut.setAttribute('id', "container"); donut.style.display = 'none'; content_td.appendChild(my_bar); my_bar.appendChild(delete_button); my_bar.appendChild(import_button); my_bar.appendChild(update_button); my_bar.appendChild(document.createElement("br")); my_bar.appendChild(pending_button); my_bar.appendChild(overview_button); my_bar.appendChild(status_button); my_bar.appendChild(document.createElement("br")); my_bar.appendChild(donut_select); my_bar.appendChild(status_select); my_bar.appendChild(label2); my_bar.appendChild(input); my_bar.appendChild(search_button); my_bar.appendChild(document.createElement("br")); my_bar.appendChild(date_label1); my_bar.appendChild(from_input); my_bar.appendChild(date_label2); my_bar.appendChild(to_input); my_bar.appendChild(csv_label); my_bar.appendChild(csv); my_bar.appendChild(document.createElement("br")); my_bar.appendChild(label); my_bar.appendChild(document.createElement("br")); (footer.parentNode).insertBefore(donut, footer); my_bar.style.textAlign = "float"; search_button.textContent = "Search"; search_button.title = "Search from local HIT database\nYou can set time limits and export as CSV-file"; label2.textContent = " HITs matching: "; input.value = ""; label.textContent = "Search powered by non-amazonian script monkeys"; for (var i=0; i<status_options.length; i++) status_select.options.add(status_options[i]); for (var i=0; i<donut_options.length; i++) donut_select.options.add(donut_options[i]); update_button.title = "Fetch status pages and copy HITs to local indexed database.\nFirst time may take several minutes!"; update_button.textContent = "Update database"; update_button.style.color = 'green'; update_button.style.margin = '5px 5px 5px 5x'; delete_button.textContent = "Delete database"; delete_button.style.color = 'red'; delete_button.style.margin = '5px 5px 5px 5px'; delete_button.title = "Delete Local DataBase!"; import_button.textContent = "Import"; import_button.style.margin = '5px 5px 5px 5px'; import_button.title = "Import HIT data from exported CSV-file"; overview_button.textContent = "Requester Overview"; overview_button.style.margin = '0px 5px 5px 5px'; overview_button.title = "Summary of all requesters you have worked for\nYou can set time limit and export as CSV-file"; pending_button.textContent = "Pending Overview"; pending_button.style.margin = '0px 5px 5px 5px'; pending_button.title = "Summary of all pending HITs\nYou can export as CSV-file"; status_button.textContent = "Daily Overview"; status_button.style.margin = '0px 5px 5px 5px'; status_button.title = "Summary of each day you have worked on MTurk\nYou can set time limit and export as CSV-file"; pending_button.addEventListener("click", HITStorage.show_pendings, false); overview_button.addEventListener("click", HITStorage.show_overview, false); search_button.addEventListener("click", HITStorage.start_search, false); update_button.addEventListener("click", HITStorage.update_database, false); delete_button.addEventListener("click", delete_func(), false); import_button.addEventListener("click", import_func(), false); status_button.addEventListener("click", HITStorage.show_status, false); csv_label.textContent = 'export CSV'; csv_label.title = 'Export results as comma-separated values'; csv_label.style.verticalAlign = 'middle'; csv_label.style.marginLeft = '50px'; csv.title = 'Export results as comma-separated values'; csv.setAttribute('type', 'checkbox'); csv.setAttribute('id', 'export_csv'); csv.style.verticalAlign = 'middle'; from_input.value = ''; to_input.value = ''; var table = document.getElementById('bonus_earnings_amount'); if (table != null) { table = table.parentNode.parentNode.parentNode.parentNode; var pending_tr = document.createElement('tr'); var pending_td1 = document.createElement('td'); var pending_td2 = document.createElement('td'); var today_tr = document.createElement('tr'); var today_td1 = document.createElement('td'); var today_td2 = document.createElement('td'); pending_tr.setAttribute('class', 'even'); pending_td1.setAttribute('class', 'metrics-table-first-value'); pending_td1.setAttribute('id', 'pending_earnings_header'); pending_td2.setAttribute('id', 'pending_earnings_value'); today_tr.setAttribute('class', 'odd'); today_td1.setAttribute('class', 'metrics-table-first-value'); today_td1.setAttribute('id', 'projected_earnings_header'); today_td2.setAttribute('id', 'projected_earnings_value'); pending_tr.appendChild(pending_td1); pending_tr.appendChild(pending_td2); today_tr.appendChild(today_td1); today_tr.appendChild(today_td2); table.appendChild(pending_tr); table.appendChild(today_tr); pending_td1.style.borderTop = '1px dotted darkgrey'; pending_td2.style.borderTop = '1px dotted darkgrey'; today_td1.style.borderBottom = '1px dotted darkgrey'; today_td2.style.borderBottom = '1px dotted darkgrey'; today_td1.title = 'This value can be inaccurate if HITDB has not been updated recently'; pending_td1.title = 'This value can be inaccurate if HITDB has not been updated recently'; if (localStorage['HITDB UPDATED'] === undefined) pending_td1.textContent = 'Pending earnings'; else pending_td1.textContent = 'Pending earnings (HITDB updated: ' + localStorage['HITDB UPDATED'] + ')'; today_td1.innerHTML = 'Projected earnings for today '; today_td2.textContent = 'ಠ_ಠ'; pending_td2.textContent = 'ಠ_ಠ'; var e = document.getElementById('user_activities.date_column_header.tooltip').parentNode.parentNode.childNodes[2].childNodes[1].childNodes[1]; var today = null; if (e != null && e.textContent.trim() == 'Today') { today = convert_date(e.href.slice(-8)); HITStorage.indexedDB.get_todays_projected_earnings(today); } HITStorage.indexedDB.get_pending_approvals(); HITStorage.indexedDB.get_pending_payments(); var target = document.createElement('span'); target.setAttribute('id', 'my_target'); target.textContent = 'click here to set your target'; target.style.fontSize = 'small'; target.style.color = 'blue'; today_td1.appendChild(target); target.addEventListener("click", set_target_func(today), false); } } else if (document.location.href.match('https://www.mturk.com/mturk/preview')) { var table = document.getElementById('requester.tooltip'); if (table == null) return; table = table.parentNode.parentNode.parentNode; var title = table.parentNode.parentNode.parentNode.parentNode.getElementsByTagName('div')[0].textContent.trim(); var extra_row = document.createElement('tr'); var td_1 = document.createElement('td'); var td_2 = document.createElement('td'); var requesterId = document.getElementsByName('requesterId')[0].value; var auto_approve = parseInt(document.getElementsByName('hitAutoAppDelayInSeconds')[0].value); var buttons = []; var b = ['Requester', 'HIT Title']; for (var i=0; i<b.length; i++) { buttons[i] = document.createElement('button'); buttons[i].textContent = b[i]; buttons[i].id = b[i] + 'Button' + i; buttons[i].style.fontSize = '10px'; buttons[i].style.height = '18px'; buttons[i].style.width = '80px'; buttons[i].style.border = '1px solid'; buttons[i].style.paddingLeft = '3px'; buttons[i].style.paddingRight = '3px'; buttons[i].style.backgroundColor = 'lightgrey'; buttons[i].setAttribute('form', 'NOThitForm'); td_1.appendChild(buttons[i]); } buttons[0].title = 'Search requester ' + requesterId + ' from HIT database'; buttons[1].title = 'Search title \'' + title + '\' from HIT database'; HITStorage.indexedDB.colorRequesterButton(requesterId, buttons[0]); HITStorage.indexedDB.colorTitleButton(title, buttons[1]); buttons[0].addEventListener("click", search_func(requesterId, 'requesterId'), false); buttons[1].addEventListener("click", search_func(title, 'title'), false); td_2.innerHTML = '<span class="capsule_field_title">Auto-Approval:</span>  ' + HITStorage.formatTime(auto_approve*1000) + ''; td_1.colSpan = '3'; td_2.colSpan = '8'; extra_row.appendChild(td_1); extra_row.appendChild(td_2); table.appendChild(extra_row); } else { for (var item=0; item<10; item++) { var tooltip = document.getElementById('requester.tooltip--' + item); if (tooltip == null) break; // no need to continue var titleElement = document.getElementById('capsule' + item + '-0'); var emptySpace = tooltip.parentNode.parentNode.parentNode.parentNode.parentNode; var hitItem = tooltip.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;//.parentNode; var requesterLabel = tooltip.parentNode; var requesterId = tooltip.parentNode.parentNode.getElementsByTagName('a'); var title = titleElement.textContent.trim(); requesterId = get_requester_id(requesterId[1].href); var buttons = []; var row = document.createElement('tr'); var div = document.createElement('div'); emptySpace.appendChild(row); row.appendChild(div); /* Turkopticon link next to requester name */ //to_link = document.createElement('a'); //to_link.textContent = ' TO '; //to_link.href = 'http://turkopticon.differenceengines.com/' + requesterId; //to_link.target = '_blank'; //to_link.title = requesterId + ' on Turkopticon'; //tooltip.parentNode.parentNode.appendChild(to_link); /*-----------------------------------------*/ HITStorage.indexedDB.blockHITS(requesterId, title, hitItem, titleElement); var b = ['R', 'T', 'N', 'B']; for (var i=0; i<b.length; i++) { buttons[i] = document.createElement('button'); buttons[i].textContent = b[i]; buttons[i].id = b[i] + 'Button' + i; buttons[i].style.height = '18px'; buttons[i].style.fontSize = '10px'; buttons[i].style.border = '1px solid'; buttons[i].style.paddingLeft = '3px'; buttons[i].style.paddingRight = '3px'; buttons[i].style.backgroundColor = 'lightgrey'; div.appendChild(buttons[i]); } buttons[0].title = 'Search requester ' + requesterId + ' from HIT database'; buttons[1].title = 'Search title \'' + title + '\' from HIT database'; buttons[2].title = 'Add a requester note'; buttons[3].title = '"Block" requester'; var notelabel = document.createElement('label'); notelabel.textContent = ''; notelabel.id = b[i] + 'notelabel' + item; notelabel.style.height = '18px'; notelabel.style.fontSize = '10px'; notelabel.style.marginLeft = '10px'; notelabel.style.padding = '1px'; notelabel.style.backgroundColor = 'transparent'; HITStorage.indexedDB.updateNoteButton(requesterId, notelabel); div.appendChild(notelabel); HITStorage.indexedDB.colorRequesterButton(requesterId, buttons[0]); HITStorage.indexedDB.colorTitleButton(title, buttons[1]); buttons[0].addEventListener("click", search_func(requesterId, 'requesterId'), false); buttons[1].addEventListener("click", search_func(title, 'title'), false); buttons[2].addEventListener("click", note_func(requesterId, notelabel), false); buttons[3].addEventListener("click", block_func(requesterId, title, hitItem), false); div.style.margin = "0px"; buttons[2].style.visibility = "hidden"; // "visible" buttons[2].parentNode.addEventListener("mouseover", visible_func(buttons[2], true), false); buttons[2].parentNode.addEventListener("mouseout", visible_func(buttons[2], false), false); buttons[2].addEventListener("mouseout", visible_func(buttons[2], false), false); buttons[3].style.visibility = "hidden"; // "visible" buttons[3].parentNode.addEventListener("mouseover", visible_func(buttons[3], true), false); buttons[3].parentNode.addEventListener("mouseout", visible_func(buttons[3], false), false); buttons[3].addEventListener("mouseout", visible_func(buttons[3], false), false); } var auto_button = document.createElement('button'); auto_button.setAttribute('id', 'auto_button'); auto_button.title = 'HIT DataBase Auto Update\nAutomagically update newest HITs to database when reloading this page'; if (localStorage['HITDB AUTO UPDATE'] === undefined) { auto_button.textContent = 'Auto update ?'; auto_button.style.color = 'red'; } else if (localStorage['HITDB AUTO UPDATE'] == 'ON') { auto_button.textContent = 'Auto Update is ON'; auto_button.style.color = 'green'; } else { auto_button.textContent = 'Auto Update is OFF'; auto_button.style.color = 'red'; } //var element = document.body.childNodes[13].childNodes[3].childNodes[1].childNodes[0].childNodes[5]; var element = document.getElementsByName("/sort")[0]; //element.insertBefore(auto_button, element.firstChild); element.parentNode.insertBefore(auto_button, element.nextSibling); auto_button.addEventListener("click", auto_update_func(), false); setTimeout(HITStorage.getLatestHITs, 100); }