// ==UserScript== // @name MTurk Wage Reporter // @namespace localhost // @description Tracks a best-estimate hourly wage on active HITs being worked. // @include https://www.mturk.com/mturk/accept?* // @include https://www.mturk.com/mturk/continue?* // @include https://www.mturk.com/mturk/previewandaccept?* // @include https://www.mturk.com/mturk/preview?* // @include https://www.mturk.com/mturk/dashboard* // @include https://www.mturk.com/mturk/submit* // @version 0.7.5b // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_getResourceText // @require http://code.jquery.com/jquery-2.1.1.js // @require http://code.jquery.com/ui/1.10.3/jquery-ui.js // @require http://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.3.0/js/bootstrap-datepicker.min.js // @require http://cdn.jsdelivr.net/jqplot/1.0.8/jquery.jqplot.js // @require http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.dateAxisRenderer.min.js // @require http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.canvasAxisTickRenderer.js // @require http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.canvasAxisLabelRenderer.js // @require http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.canvasTextRenderer.js // @require http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.categoryAxisRenderer.js // @require http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.pointLabels.js // @require http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.barRenderer.min.js // @require https://greasyfork.org/scripts/3595-jqmodal/code/jqModal.js?version=10865 // @require http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.7.0/moment.min.js // @require http://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.2.0/moment-timezone.min.js // @resource jqmodal http://cdn.jsdelivr.net/jqmodal/0.1/jqModal.css // @resource jqplot http://cdn.jsdelivr.net/jqplot/1.0.8/jquery.jqplot.css // @resource jqdp http://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.3.0/css/datepicker.min.css // @author DeliriumTremens 2014 // @downloadURL https://update.greasyfork.org/scripts/3211/MTurk%20Wage%20Reporter.user.js // @updateURL https://update.greasyfork.org/scripts/3211/MTurk%20Wage%20Reporter.meta.js // ==/UserScript== // // 2014-07-10 0.1b Beginning development. Creating timer and tab tracker, as well as initial IndexedDB for storage of data. // // // 2014-07-15 0.4.2b Continued development. Not ready for live usage yet. Expanded math for wage calculation, added safeguards // to prevent adding expired or missing HITs. Added formatting for dashboard. Set up groundwork for additional // math to be included in the next update. Added a buttload of comments to the code. // // // 2014-07-16 0.5b More work! Added function to remove a HIT that expires from the database under the assumption that expired hits // were forgotten about rather than actively worked on until expiration. Updated wage calculation because I had a major // brainfart about DIVIDING DOLLARS BY HOURS -- don't ask... Yet more safeguards and checks put in place... // // 2014-07-16 0.5.1b Added a dropdown to select hourly wage by requester. Minor bug fixes. // // 2014-07-18 0.6b Added functions to crosscheck HITs in the database to ensure only submitted or returned HIT's appear in the calculations. // Without this, HITs that expired in queue would show up with insanely high rates of pay -- which we don't want. This update // was a massive thorn in my side, and not easy to accomplish -.- // // 2014-07-18 0.6.1b Fixed a bug that was causing some PandA HITs not to appear in wages, as well as producing inaccurate wages. // Cleaned up some database closing methods. // // 2014-07-23 0.6.2b Added ability to export, import, and delete the database. A few small bugfixes. // // 2014-07-23 0.6.3b Added cookie refresher button to clear cookies and rescrape status pages when hits arent "completing" correctly // // 2014-07-24 0.6.5b Fixed bug in calculation function that was reporting inaccurate wages. // // 2014-07-25 0.6.6b Fixed bug in CSV export and import, successfully moved the database between firefox and chrome, updating records appropriately // Added chart button and wage charts. // // 2014-07-28 0.7b Now reports in Amazon time. // // 2014-07-29 0.7.2b Added an element to the chart div -- when you hover the bar for a specific hour, it will show you a breakdown for that hour of: // Wage, # of HITs, and total Earned. // // 2014-07-30 0.7.4b Added a calendar button to choose the date you want to view for HITs recorded by Wage Reporter. // // ------------------------------------------------------------------------------------------------------------------------------------------------ // First, create indexedDB variables. // This sets up the various indexedDB functions and processes for interacting with WageDB. // Parts borrowed from HITdb // // // START CSS ADDSTYLE CODE // var jqmodCSS = GM_getResourceText("jqmodal"); var jqplotCSS = GM_getResourceText("jqplot"); var jqdpCSS = GM_getResourceText("jqdp"); GM_addStyle(jqdpCSS); GM_addStyle(jqmodCSS); GM_addStyle(jqplotCSS); moment.tz.add('America/Los_Angeles|PST PDT|80 70|0101|1Lzm0 1zb0 Op0'); // Add Amazon timezone to moment.js // // STOP CSS ADDSTYLE CODE // // // // // START INDEXEDDB CODE // var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB; // Variables used for updating the wage per requester chosen var reqList = []; var permHitArray = []; var wageReturn = 0; var WageStorage = {}; window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.mozIDBTransaction; window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.mozIDBKeyRange; var idbKeyRange = window.IDBKeyRange; WageStorage.indexedDB = {}; WageStorage.indexedDB.db = null; // Global catch for indexedDB errors WageStorage.indexedDB.onerror = function (e) { console.log(e); } // Check if database exists. If no database, create one. // Parts borrowed from HITdb. var inProgress = false; // Boolean for when a HIT is continued from queue var dbExists = true; // Boolean for when the database is already created var v = 7; // Version of the database WageStorage.indexedDB.create = function () { // Function that creates the database if it hasn't been created already. var request = indexedDB.open("WageDB", v); request.onupgradeneeded = function (e) { //console.log("CREATE THE FUCKING DATABSE, SHIT"); WageStorage.indexedDB.db = e.target.result; var db = WageStorage.indexedDB.db; var newDB = false; if (!db.objectStoreNames.contains("Wage")) { var store = db.createObjectStore("Wage", { keyPath: "hitId" // Primary key of the database (not an index) }); store.createIndex("date", "date", { // These are the other fields (or Indexes) of the database. unique: false }); store.createIndex("reqName", "reqName", { unique: false }); store.createIndex("reqId", "reqId", { unique: false }); store.createIndex("checkId", "checkId", { unique: false }); store.createIndex("reward", "reward", { unique: false }); store.createIndex("start", "start", { unique: false }); store.createIndex("stop", "stop", { unique: false }); store.createIndex("unworked", "unworked", { unique: false }); store.createIndex("submitted", "submitted", { unique: false }); newDB = true; } db.close(); } request.onsuccess = function (e) { WageStorage.indexedDB.db = e.target.result; var db = WageStorage.indexedDB.db; db.close(); } request.onerror = console.log(request.errorCode); } // Function for adding HIT data into database. // Includes logic for updating HITs which were worked on in several sittings (or from queue) WageStorage.indexedDB.addhit = function () { var request = indexedDB.open("WageDB", v); request.onsuccess = function (e) { WageStorage.indexedDB.db = e.target.result; var db = WageStorage.indexedDB.db; var newDB = false; if (!db.objectStoreNames.contains("Wage")) { // Make sure database is there... This should never fire. db.close(); } else { var trans = db.transaction(["Wage"], 'readwrite'); var store = trans.objectStore("Wage"); var request; request = store.put({ // Insert fresh hit into database hitId: hitId, date: date, reqName: reqName, reqId: reqId[1], checkId: checkId, reward: reward, start: wageStart, stop: wageEnd, unworked: wageExtra, submitted: submitted }); request.onsuccess = function (e) { //console.log(e.target.result); //console.log("Inserted HIT into database...") } request.onerror = function (e) { //console.log("Failed to add hit."); } } db.close(); } request.onerror = WageStorage.indexedDB.onerror; } // Function for getting todays data. // Gets reward amount, start time, stop time, and queue time (unworked time) WageStorage.indexedDB.getWage = function () { //console.log("Retrieving wage for dashboard..."); var request = indexedDB.open("WageDB", v); reqList.length = 0; request.onsuccess = function (e) { //console.log("getWage first request"); WageStorage.indexedDB.db = e.target.result; var db = WageStorage.indexedDB.db var transaction = db.transaction('Wage', 'readonly'); var store = transaction.objectStore('Wage'); var index = store.index('date'); var range = IDBKeyRange.only(date); var results = []; var tmp_results = {}; index.openCursor(range).onsuccess = function (event) { var cursor = event.target.result; if (cursor) { var hit = cursor.value; if (tmp_results[hit.hitId] === undefined) { tmp_results[hit.hitId] = []; tmp_results[hit.hitId][0] = hit.reward; tmp_results[hit.hitId][1] = hit.start; tmp_results[hit.hitId][2] = hit.stop; tmp_results[hit.hitId][3] = hit.unworked; tmp_results[hit.hitId][4] = hit.submitted; if (!hit.submitted) { var hitSubmitted = false; //console.log(hit.checkId); //console.log($.inArray(hit.checkId, permHitArray)); ($.inArray(hit.checkId, permHitArray) > -1 ? hitSubmitted = true : hitSubmitted = false); //console.log(hitSubmitted); if (hitSubmitted) { if ($.inArray(hit.reqName, reqList) === -1) { reqList.push(hit.reqName); } WageStorage.indexedDB.updateHit(hit.hitId, hitSubmitted); tmp_results[hit.hitId][4] = true; } } else if (hit.submitted) { if ($.inArray(hit.reqName, reqList) === -1) { reqList.push(hit.reqName); } } } cursor.continue(); } else { for (var key in tmp_results) { if (tmp_results[key][4]) { results.push(tmp_results[key]); } } //console.log("Calculating wage..."); //console.log(reqList); var wage = calculateWage(results); // Calls function to calculate wage //console.log("Wage calculated, showing results..." + wage); addTableElement(wage); // Calls function to add wage to dashboard after all data has been pulled } } db.close(); } request.onerror = WageStorage.indexedDB.onerror; } // Function to return a wage for a specific requester WageStorage.indexedDB.getReq = function (requesterName) { var request = indexedDB.open("WageDB", v); var requester = requesterName; request.onsuccess = function (e) { WageStorage.indexedDB.db = e.target.result; var db = WageStorage.indexedDB.db var transaction = db.transaction('Wage', 'readonly'); var store = transaction.objectStore('Wage'); var index = store.index('date'); var range = IDBKeyRange.only(date); var results = []; var tmp_results = {}; index.openCursor(range).onsuccess = function (event) { var cursor = event.target.result; if (cursor) { var hit = cursor.value; if (tmp_results[hit.hitId] === undefined) { tmp_results[hit.hitId] = []; tmp_results[hit.hitId][0] = hit.reward; tmp_results[hit.hitId][1] = hit.start; tmp_results[hit.hitId][2] = hit.stop; tmp_results[hit.hitId][3] = hit.unworked; tmp_results[hit.hitId][4] = hit.submitted; tmp_results[hit.hitId][5] = hit.reqName; } cursor.continue(); } else { //console.log(tmp_results); for (var key in tmp_results) { if (requester === 'All') { tmp_results[key].pop(); if (tmp_results[key][4]) { results.push(tmp_results[key]); } } else { if (tmp_results[key][5] === requester) { tmp_results[key].pop(); if (tmp_results[key][4]) { results.push(tmp_results[key]); } } } } //console.log("Calculating specific Requester wage..."); var wage = calculateWage(results); // Calls function to calculate wage //console.log("Requester Wage calculated, showing results..." + wage); $('td[name="wageCell"]').text("$" + wage + "/hr"); //wageReturn = wage; return; } } db.close(); return; } return; } // Function to check if HIT has already been started, if so then store the start and end time so it can be updated and have unworked time added to database WageStorage.indexedDB.getHit = function () { //console.log("Checking if HIT exists..."); var request = indexedDB.open("WageDB", v); request.onsuccess = function (e) { WageStorage.indexedDB.db = e.target.result; var hitId2 = $('input[name="hitId"]').val(); var db = WageStorage.indexedDB.db var transaction = db.transaction('Wage', 'readonly'); var store = transaction.objectStore('Wage'); var hitCheck = store.get(hitId2); hitCheck.onsuccess = function (event) { if (event.target.result === undefined) { inProgress = false; //console.log("New HIT..."); } else { inProgress = true; wageExtra = (event.target.result.unworked + (wageStart - event.target.result.stop)); wageStart = event.target.result.start; //console.log("HIT exists, continuing..."); } } db.close(); } } WageStorage.indexedDB.updateHit = function (updateId, submitted) { var request = indexedDB.open("WageDB", v); var remHit = remHit; var updater = updateId; //console.log("attempting to update hit " + updater); request.onsuccess = function (e) { WageStorage.indexedDB.db = e.target.result; var db = WageStorage.indexedDB.db var transaction = db.transaction(['Wage'], "readwrite"); var store = transaction.objectStore('Wage'); var updateRequest = store.get(updateId); updateRequest.onerror = function (e) { console.log(e) } updateRequest.onsuccess = function (e) { var data = e.target.result; data.submitted = submitted; var pushUpdate = store.put(data); pushUpdate.onerror = function (e) { console.log(e) } pushUpdate.onsuccess = function (e) { /* console.log("updated hit to submitted"); */ } } db.close(); } //request.onerror = console.log("nope"); } // Function to remove bad or unused HIT from database WageStorage.indexedDB.delHit = function (remHit) { //console.log("Expired HIT... Removing from DB..."); var request = indexedDB.open("WageDB", v); var remHit = remHit; request.onsuccess = function (e) { WageStorage.indexedDB.db = e.target.result; var db = WageStorage.indexedDB.db var transaction = db.transaction('Wage', 'readwrite'); var store = transaction.objectStore('Wage'); var hitDel = store.delete(remHit); hitDel.onsuccess = function (event) { //console.log("HIT successfully deleted from database..."); } db.close(); } } // Function to pull all data from DB and send to export function WageStorage.indexedDB.expDB = function () { var request = indexedDB.open("WageDB", v); request.onsuccess = function (e) { WageStorage.indexedDB.db = e.target.result; var db = WageStorage.indexedDB.db; var transaction = db.transaction('Wage', 'readonly'); var store = transaction.objectStore('Wage'); var results = []; var tmp_results = {}; store.openCursor().onsuccess = function (event) { var cursor = event.target.result; //console.log(cursor); if (cursor) { var hit = cursor.value; if (tmp_results[cursor.key] === undefined) { tmp_results[cursor.key] = []; tmp_results[cursor.key][0] = hit.hitId; tmp_results[cursor.key][1] = hit.date; tmp_results[cursor.key][2] = hit.checkId; tmp_results[cursor.key][3] = hit.reqId; tmp_results[cursor.key][4] = hit.reqName; tmp_results[cursor.key][5] = hit.start; tmp_results[cursor.key][6] = hit.stop; tmp_results[cursor.key][7] = hit.unworked; tmp_results[cursor.key][8] = hit.reward; tmp_results[cursor.key][9] = hit.submitted; } cursor.continue(); } else { for (var key in tmp_results) { results.push(tmp_results[key]); } exportToCSV(results); } } db.close(); } //request.onerror = console.log("error"); } // Function to import a CSV into the database WageStorage.indexedDB.impDB = function (hitData) { var hits = hitData.length; var request = indexedDB.open("WageDB", v); request.onsuccess = function (e) { var db = e.target.result; var trans = db.transaction('Wage', 'readwrite'); var store = trans.objectStore('Wage'); var req = store.openCursor().onsuccess = function (event) { var cursor = event.target.result; for (var i = 0; i < hits; i++) { if (cursor) { var putReq = store.put(hitData[i]); } else { var putReq = store.add(hitData[i]); } } } db.close(); } //request.onerror = console.log("error in import"); } // Function to delete the database WageStorage.indexedDB.deleteDB = function () { var deleteRequest = indexedDB.deleteDatabase("WageDB"); deleteRequest.onsuccess = function (e) { alert("deleted"); } deleteRequest.onblocked = function (e) { alert("blocked"); } deleteRequest.onerror = WageStorage.indexedDB.onerror; } // Function to get all dates from database WageStorage.indexedDB.getDates = function () { var request = indexedDB.open("WageDB", v); var hitDates = [] request.onsuccess = function (e) { WageStorage.indexedDB.db = e.target.result; var db = WageStorage.indexedDB.db var transaction = db.transaction('Wage', 'readonly'); var store = transaction.objectStore('Wage'); store.openCursor().onsuccess = function (event) { var cursor = event.target.result; //console.log(cursor); if (cursor) { var hit = cursor.value; var hitDate = hit.date.replace('"',''); hitDate = hitDate.replace('"',''); if (hitDates.indexOf(hitDate) === -1 && hitDate != 'date' && hitDate != "undefined") { hitDates.push(hitDate); } cursor.continue(); } else { hitDates = hitDates.sort(function (a, b) { // Function that sorts the list of HITs by time starting with the first HIT of the day if (a < b) return -1; if (a > b) return 1; return 0; }); addCalendarElement(hitDates); } } db.close(); } } // // // END INDEXEDDB CODE // // // // START EXPORT AND IMPORT CODE // exportToCSV = function (results) { var csvData = 'hitId,date,checkId,reqId,reqName,start,stop,unworked,reward,submitted'; for (var i = 0; i < results.length; i++) { csvData += formatLineCSV(results[i]); } location.href = 'data:text/csv;charset=utf8,' + encodeURIComponent(csvData); } importFromCSV = function (results) { } formatLineCSV = function (hit) { var line = ''; line += '"' + hit[0] + '",'; line += '"' + hit[1] + '",'; line += '"' + hit[2] + '",'; line += '"' + hit[3] + '",'; line += '"' + hit[4] + '",'; line += hit[5] + ','; line += hit[6] + ','; line += hit[7] + ','; line += hit[8] + ','; line += hit[9] + ','; line += '\n'; return line; } // // STOP EXPORT AND IMPORT CODE // // // // START WAGE CALCULATION AND INSERTION CODE // // // Script Variables var limit = 0; var submitted = false; var wageLost = false; // Boolean for a returned hit, and a wage that is abandoned. Will record the time with zero payment. var wageExtra = 0; // Placeholder for extra time that was not worked. var wageStart = null; // Placeholder for start time of HIT var wageEnd = null; // Placeholder for end time of HIT var wageNix = false; // Placeholder for determining if HIT was expired or unavailable or captcha'd var expiredHit = false; // Placeholder for expired HIT var reqId = document.URL.match(/requesterId=(.*?)&/i); // Parse requester ID out of URL if (reqId) { GM_setValue("reqId", reqId); // If no requester ID is available, it's because you accepted next hit in batch. Retrieves ID. } else { reqId = GM_getValue("reqId"); // Stores requester ID in a global script var to be pulled in case of batch work. } // HIT Data var hitId = $('input[name="assignmentId"]').val(); // Parses hit id from html var checkId = null; var submitted = false; var reward = parseFloat($('span[class="reward"]:eq(1)').text().replace('$', '')); // Parses reward from html var reqName = $('input[name="prevRequester"]').val(); // Parses requester name from html var date = new Date(); // Get todays date date = date.toLocaleDateString({ timeZone: 'America/Los_Angeles' }).replace(/\//g, '-'); // Convert date to usable string var todayDate = new Date(); todayDate = todayDate.toLocaleDateString({ timeZone: 'America/Los_Angeles' }).replace(/\//g, '-'); // Create table element for showing hourly wage. Parts borrowed from Today's Projected Earnings script var allTds, thisTd; var allTdhtml = []; calculateWage = function (wage) { // Function that does the meat of the wage calculation. Meaty. Array has [wage,num_hits,total_earned] wageChartArr = { // Array that holds all the hourly data for chart. 0: [ [], 0, 0], 1: [ [], 0, 0], 2: [ [], 0, 0], 3: [ [], 0, 0], 4: [ [], 0, 0], 5: [ [], 0, 0], 6: [ [], 0, 0], 7: [ [], 0, 0], 8: [ [], 0, 0], 9: [ [], 0, 0], 10: [ [], 0, 0], 11: [ [], 0, 0], 12: [ [], 0, 0], 13: [ [], 0, 0], 14: [ [], 0, 0], 15: [ [], 0, 0], 16: [ [], 0, 0], 17: [ [], 0, 0], 18: [ [], 0, 0], 19: [ [], 0, 0], 20: [ [], 0, 0], 21: [ [], 0, 0], 22: [ [], 0, 0], 23: [ [], 0, 0] }; var currStart = 0; var currEnd = 0; var currWages = []; var currHit = 0; var batch = []; var batchWage = 0; var currWage = 0; var hour = 0; var wage = wage.sort(function (a, b) { // Function that sorts the list of HITs by time starting with the first HIT of the day if (a[1] < b[1]) return -1; if (a[1] > b[1]) return 1; return 0; }); for (var x = 0; x < wage.length; x++) { currEnd = wage[x][2]; hour = new Date(currEnd); hour = moment(hour); hour = hour.tz('America/Los_Angeles').format('H'); wageChartArr[hour][1] += 1; wageChartArr[hour][2] += wage[x][0]; currEnd = 0; } for (var i = 0; i < wage.length; i++) { if (currStart < wage[i][1] && wage[i][1] < currEnd && moment(currEnd) === hour) { // If current HIT started before previous HIT was submitted during the same hour, start batch processing currEnd = wage[i][2]; currHit = (wage[i][0]) / ((((currEnd - currStart) - wage[i][3]) / 1000) / 3600); batch.push(currHit); } else if ((currStart >= wage[i][1] && wage[i][1] >= currEnd) || (currStart < wage[i][1] && wage[i][1] > currEnd)) { // Before starting a hit outside of a confirmed batch, add contents of batch to wages if (batch.length >= 1) { for (var j = 0; j < batch.length; j++) { batchWage += batch[j]; } batchWage = wageRound((batchWage / batch.length), 2); currWages.push(batchWage); hour = new Date(currEnd); hour = moment(hour); hour = hour.tz('America/Los_Angeles').format('H'); wageChartArr[hour][0].push(batchWage) batchWage = 0; batch.length = 0; currStart = wage[i][1]; currEnd = wage[i][2]; currHit = (wage[i][0]) / ((((currEnd - currStart) - wage[i][3]) / 1000) / 3600); batch.push(currHit); hour = new Date(currEnd); hour = moment(hour); hour = hour.tz('America/Los_Angeles').format('H'); } else { currStart = wage[i][1]; currEnd = wage[i][2]; currHit = (wage[i][0]) / ((((currEnd - currStart) - wage[i][3]) / 1000) / 3600); batch.push(currHit); hour = new Date(currEnd); hour = moment(hour); hour = hour.tz('America/Los_Angeles').format('H'); } } } if (batch.length >= 1) { for (var i = 0; i < batch.length; i++) { batchWage += batch[i]; } batchWage = wageRound((batchWage / batch.length), 2); currWages.push(batchWage); wageChartArr[hour][0].push(batchWage); batchWage = 0; batch = []; } currWage = 0; for (var i = 0; i < currWages.length; i++) { // Add up all of the calculated wages in the current wages array //console.log(currWages); currWage += currWages[i]; } // Find the rounded average of all wages in the array currWage = wageRound((currWage / currWages.length), 2); return currWage; } // Function to add the table cells to dashboard and displaying the wage addTableElement = function (wage) { var belowThisTD = (($.inArray('Today\'s Projected Earnings', allTdhtml) > -1) ? /Today's Projected Earnings/ : /Total Earnings/); // If Projected Earnings script is installed, this will be sure to place is in the correct spot var rowColor = (($.inArray('Today\'s Projected Earnings', allTdhtml) > -1) ? "#f1f3eb" : "FFFFFF"); // If Projected Earnings script is installed, this will ensure the correct color is used for the new row for (var i = 0; i < allTds.length; i++) { thisTd = allTds[i]; if (thisTd.innerHTML.match(belowThisTD) && thisTd.className.match(/metrics\-table\-first\-value/)) { var row = document.createElement('tr'); row.setAttribute("name", "wageRow"); row.className = "even"; row.setAttribute("style", ("background-color:" + rowColor)); var hourlyWageTitle = document.createElement('p'); hourlyWageTitle.setAttribute("name", "wageTitle"); hourlyWageTitle.innerHTML = ((date === todayDate) ? "Today's Hourly Wage    " : (date + " Hourly Wage ")); hourlyWageTitle.setAttribute("style", ("background-color:" + rowColor)); var cellLeft = document.createElement('td'); cellLeft.className = "metrics-table-first-value"; cellLeft.appendChild(hourlyWageTitle); row.appendChild(cellLeft); var cellRight = document.createElement('td'); cellRight.setAttribute("name", "wageCell"); cellRight.innerHTML = "$" + wage + "/hr"; row.appendChild(cellRight); thisTd.parentNode.parentNode.insertBefore(row, thisTd.parentNode.nextSibling); } } // Drop down menu for requesters $('p[name="wageTitle"]').append(''); // Buttons for exporting CSV, importing CSV, viewing graphs, and deleting the DB $('p[name="wageTitle"]').append('').button(); $('p[name="wageTitle"]').append('').button(); $('p[name="wageTitle"]').append(''); $('p[name="wageTitle"]').append('').button(); $('p[name="wageTitle"]').append('').button(); $('p[name="wageTitle"]').append('').button(); $('p[name="wageTitle"]').append('
'); $('p[name="wageTitle"]').append('').button(); // Div for chart(s) $("body").append(''); $('#wageChartHolder').jqm({ onShow: function (hash) { hash.w.show(); $('#wageChart').empty(); wageChart(wageChartArr); }, onHide: function (hash) { hash.w.hide(); hash.o.remove(); } }); } addCalendarElement = function (dateRange) { var firstDate = dateRange[0]; var lastDate = dateRange[dateRange.length - 1]; $('#datepicker').datepicker({ format: 'm-dd-yyyy', startDate: firstDate, endDate: lastDate, autoclose: true, }); $('#datepicker').datepicker().on('changeDate', function (e) { date = $('#datepicker').val(); if (date[2] === '0') { date = date.slice(0,2) + date.slice(3, date.length); } console.log(date); $('tr[name="wageRow"]').remove(); $('.datepicker').remove(); WageStorage.indexedDB.getWage(); WageStorage.indexedDB.getDates(); $('p[name="wageTitle"]').text(date + ' Hourly Wage '); }); } // Function(s) for graphs and charts creation, to be expanded for an on-click method var wageChartArr = { 0: [ [], 0, 0], 1: [ [], 0, 0], 2: [ [], 0, 0], 3: [ [], 0, 0], 4: [ [], 0, 0], 5: [ [], 0, 0], 6: [ [], 0, 0], 7: [ [], 0, 0], 8: [ [], 0, 0], 9: [ [], 0, 0], 10: [ [], 0, 0], 11: [ [], 0, 0], 12: [ [], 0, 0], 13: [ [], 0, 0], 14: [ [], 0, 0], 15: [ [], 0, 0], 16: [ [], 0, 0], 17: [ [], 0, 0], 18: [ [], 0, 0], 19: [ [], 0, 0], 20: [ [], 0, 0], 21: [ [], 0, 0], 22: [ [], 0, 0], 23: [ [], 0, 0] }; function wageChart(results) { //console.log(results); var hourlyWages = []; var hourlyHits = []; var hourlyTotal = []; var thisWage = 0; for (var i = 0; i < 24; i++) { if (results[i][1] === 0) { hourlyWages.push(0); hourlyHits.push(0); hourlyTotal.push(0); } else { for (var x = 0; x < results[i][0].length; x++) { thisWage += results[i][0][x]; } hourlyWages.push(wageRound((thisWage / results[i][1]), 2)); thisWage = 0; //hourlyWages.push(wageRound((results[i][0] / results[i][1]),2)); hourlyHits.push(results[i][1]); hourlyTotal.push(results[i][2]); } } //console.log(hourlyWages); var ticks = ['12a', '1a', '2a', '3a', '4a', '5a', '6a', '7a', '8a', '9a', '10a', '11a', '12p', '1p', '2p', '3p', '4p', '5p', '6p', '7p', '8p', '9p', '10p', '11p']; wagePlot = $.jqplot('wageChart', [hourlyWages], { seriesDefaults: { tickRenderer: $.jqplot.CanvasAxisTickRenderer, renderer: $.jqplot.BarRenderer, rendererOptions: { barWidth: 15, }, pointLabels: { show: false, hideZeros: true, formatString: '$%#.2f\/hr' } }, title: { text: 'Hourly Wage By Hour For ' + $('.wageDrop :selected').text(), fontFamily: '"Trebuchet MS", Arial, Helvetica, sans-serif', fontSize: '10pt', textColor: '#666666' }, axes: { xaxis: { renderer: $.jqplot.CategoryAxisRenderer, ticks: ticks, tickOptions: { showGridline: false }, }, yaxis: { tickRenderer: $.jqplot.CanvasAxisTickRenderer, tickOptions: { fontSize: '8pt', showMark: false, prefix: '$' }, pad: 1.2, min: 0 } } }); $('#wageChart').bind('jqplotDataHighlight', function (ev, seriesIndex, pointIndex, data) { var mouseX = ev.pageX; //these are going to be how jquery knows where to put the div that will be our tooltip var mouseY = ev.pageY; $('#chartpseudotooltip').html('Wage: | $' + data[1] + '\/hr |
HITs: | ' + hourlyHits[parseInt(pointIndex)] + ' |
Earned: | $' + wageRound(hourlyTotal[parseInt(pointIndex)], 2).toFixed(2) + ' |