mTurk Title Bar Timer / Wage - Fork

Title bar timer/counter. Fork of salembeats's script since he deleted his.

当前为 2018-01-28 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name mTurk Title Bar Timer / Wage - Fork
  3. // @author parseHex
  4. // @namespace https://greasyfork.org/users/8394
  5. // @version 40
  6. // @description Title bar timer/counter. Fork of salembeats's script since he deleted his.
  7. // @include https://www.mturk.com/mturk/preview?*
  8. // @include https://www.mturk.com/mturk/accept?*
  9. // @include https://www.mturk.com/mturk/continue?*
  10. // @include https://www.mturk.com/mturk/return?*
  11. // @include https://www.mturk.com/mturk/previewandaccept*
  12. // @include https://worker.mturk.com/projects/*
  13. // @icon https://i.imgur.com/Y68Qxdd.png
  14. // @grant GM_setValue
  15. // @grant GM_getValue
  16. // ==/UserScript==
  17.  
  18. // Changing this to true will base wages on the total time passed since the tab has opened.
  19. // False means wages are based on the total time that the tab has been in focus.
  20. const ALWAYS_TICK_WAGE_TIMER = true;
  21.  
  22. // Whether to show the "Big Emoji", and configurable parameters related to its display (i.e., size and transparency).
  23. const SHOW_BIG_EMOJI = true;
  24. const BIG_EMOJI_SIZE = "250px";
  25. const BIG_EMOJI_OPACITY_ZERO_TO_ONE = "0.03";
  26.  
  27. // No need to change this. One second is alwyas 1000ms. Just here to add meaning to some code.
  28. const ONE_SECOND_IN_MS = 1000;
  29.  
  30. // The wage amounts. Change these to your liking.
  31. const LOVE_WAGE_LOWER_LIMIT = 12.00;
  32. const HAPPY_WAGE_LOWER_LIMIT = 8.00;
  33. const OK_WAGE_LOWER_LIMIT = 6.00;
  34. const SAD_WAGE_LOWER_LIMIT = 4.00;
  35. const DISGUSTED_WAGE_LOWER_LIMIT = 0.00;
  36.  
  37. // The emojis. Change these to whatever you want to display for each wage range.
  38. const LOVE_WAGE_EMOJI = "?";
  39. const HAPPY_WAGE_EMOJI = "?";
  40. const OK_WAGE_EMOJI = "?";
  41. const SAD_WAGE_EMOJI = "?";
  42. const DISGUSTED_WAGE_EMOJI = "?";
  43.  
  44. // Changing this to a bigger number will save you CPU cycles.
  45. // Changing it to a smaller number will give you more timely data.
  46. // Chrome automatically slows down the interval timers that this is based on in background tabs, though, so this is influenced strongly by that.
  47. // TODO: Fix this so we can change to a different update rate without breaking "seconds visible / invisible" (I know how to do it, but haven't bothered writing it yet since it doesn't earn me money to do so.)
  48. // The eventual fix will be to keep two separate stores of "seconds visible/invisible", managed by taking snapshots of the last time visibility status was changed.
  49. const UPDATE_RATE_IN_MILLISECONDS = (ALWAYS_TICK_WAGE_TIMER ? (ONE_SECOND_IN_MS / 3) : ONE_SECOND_IN_MS);
  50.  
  51. var Utility = (function createUtility() {
  52.  
  53. const _scriptStartTime = new Date().getTime();
  54. const _fallbackHITStartTime = (unsafeWindow.pageTimes === undefined ? _scriptStartTime : unsafeWindow.pageTimes.beginPageLoad);
  55. const _HITStartTime = (performance.timing === undefined ? _fallbackHitStartTime : performance.timing.navigationStart);
  56. const _HITLoadTime = _scriptStartTime - _HITStartTime;
  57.  
  58. let _HITLoadTimeAccuracy;
  59. if (performance.timing === undefined) {
  60. if (unsafeWindow.pageTimes === undefined) {
  61. _HITLoadTimeAccuracy = "low";
  62. }
  63. else {
  64. _HITLoadTimeAccuracy = "medium";
  65. }
  66. }
  67. else {
  68. _HITLoadTimeAccuracy = "high";
  69. }
  70.  
  71. let _allContentLoadedTime;
  72. let _allContentLoadedCallback = () => { };
  73. document.addEventListener('readystatechange', function (event) {
  74. if (document.readyState === "complete") {
  75. _allContentLoadedTime = performance.now();
  76. _allContentLoadedCallback();
  77. }
  78. });
  79.  
  80. var _secondsVisible = 0;
  81. var _pauseOnInvisible = true;
  82.  
  83. function getHITStartTime() {
  84. return _HITStartTime;
  85. }
  86.  
  87. function getHITLoadTime() {
  88. return _HITLoadTime;
  89. }
  90.  
  91. function getAllContentLoadedTime() {
  92. return _allContentLoadedTime;
  93. }
  94.  
  95. function setAllContentLoadedCallback(newFn) {
  96. _allContentLoadedCallback = newFn;
  97. }
  98.  
  99. function getHITLoadTimeAccuracy() {
  100. return _HITLoadTimeAccuracy;
  101. }
  102.  
  103. function getSecondsSinceStartTime() {
  104. return (new Date().getTime() - _HITStartTime) / 1000;
  105. }
  106.  
  107. function isWindowTopFrame() {
  108. return (window === window.top);
  109. }
  110.  
  111. function updateSecondsVisible() {
  112. if (!document.hidden && document.hasFocus()) { incrementSecondsVisible(); }
  113. }
  114.  
  115. function incrementSecondsVisible() {
  116. _secondsVisible += (UPDATE_RATE_IN_MILLISECONDS / ONE_SECOND_IN_MS);
  117. }
  118.  
  119. function getSecondsVisible() {
  120. return _secondsVisible;
  121. }
  122.  
  123. function hourlyWageDollars(secondsPassed, rewardCents) {
  124. return (rewardCents / secondsPassed) * 36;
  125. }
  126.  
  127. function hmsToSeconds(hours, minutes, seconds) {
  128. return hoursToSeconds(hours) + minutesToSeconds(minutes) + seconds;
  129. }
  130.  
  131. function minutesToSeconds(minutes) {
  132. return (minutes * 60);
  133. }
  134.  
  135. function toMMSSString(secondsRemaining) {
  136. let minutesRemaining = Math.floor(secondsRemaining / 60);
  137. let remainderSeconds = secondsRemaining - (minutesRemaining * 60);
  138. const localeOptions = { useGrouping: false, minimumIntegerDigits: 2, maximumFractionDigits: 0 };
  139. return minutesRemaining.toLocaleString(undefined, localeOptions) + ":" + remainderSeconds.toLocaleString(undefined, localeOptions);
  140. }
  141.  
  142. function daysToSeconds(days) {
  143. return (days * 86400);
  144. }
  145.  
  146. function hoursToSeconds(hours) {
  147. return (hours * 3600);
  148. }
  149.  
  150. function secondsRemainingInHHMMSSCountdown(hhmmssString) {
  151. let values = hhmmssString.split(":");
  152. return hmsToSeconds(Number(values[0]), Number(values[1]), Number(values[2]));
  153. }
  154.  
  155. function secondsRemainingInMMSSCountdown(mmssString) {
  156. let values = mmssString.split(":");
  157. let minutes = Number(values[0]);
  158. let seconds = Number(values[1]);
  159. return minutesToSeconds(minutes) + seconds;
  160. }
  161.  
  162. function secondsRemainingInMMSSCountup(mmssString, totalSeconds) {
  163. let countupValues = mmssString.split(":");
  164. let countupMinutes = Number(countupValues[0]);
  165. let countupSeconds = Number(countupValues[1]);
  166. let totalSecondsCounted = (countupMinutes * 60) + countupSeconds;
  167. return totalSeconds - totalSecondsCounted;
  168. }
  169.  
  170. function secondsRemainingInHHMMSSCountup(hhmmssString, totalSeconds) {
  171. let values = hhmmssString.split(":");
  172. return totalSeconds - hmsToSeconds(Number(values[0]), Number(values[1]), Number(values[2]));
  173. }
  174.  
  175. function secondsGoalForTargetWage(desiredCentsPerHour, jobCents) {
  176. let desiredCentsPerSecond = desiredCentsPerHour / 3600;
  177. return jobCents / desiredCentsPerSecond;
  178. }
  179.  
  180. function secondsPassed(totalSeconds, secondsRemaining) {
  181. return totalSeconds - secondsRemaining;
  182. }
  183.  
  184. function dollarsToCents(dollars) {
  185. return dollars * 100;
  186. }
  187.  
  188. function askWorkerFrameWhetherSurvey() {
  189. document.querySelector("iframe").contentWindow.postMessage({ msg: "Are you a survey HIT?" }, "*");
  190. }
  191.  
  192. function disablePauseOnInvisible() {
  193. _pauseOnInvisible = false;
  194. }
  195.  
  196. function isPausingOnInvisible() {
  197. return _pauseOnInvisible;
  198. }
  199.  
  200. function highlightWageRange(cssSelectorString) {
  201. document.querySelector(cssSelectorString).style = "font-weight: bold; text-decoration: underline;";
  202. }
  203.  
  204. function unhighlightWageRange(cssSelectorString) {
  205. document.querySelector(cssSelectorString).style = "font-weight: normal; text-decoration: none;";
  206. }
  207.  
  208. return {
  209. getSecondsSinceStartTime,
  210. getHITStartTime,
  211. isWindowTopFrame,
  212. updateSecondsVisible,
  213. getSecondsVisible,
  214. hourlyWageDollars,
  215. minutesToSeconds,
  216. toMMSSString,
  217. secondsRemainingInMMSSCountdown,
  218. secondsRemainingInMMSSCountup,
  219. secondsPassed,
  220. dollarsToCents,
  221. askWorkerFrameWhetherSurvey,
  222. disablePauseOnInvisible,
  223. isPausingOnInvisible,
  224. hmsToSeconds,
  225. secondsRemainingInHHMMSSCountdown,
  226. secondsRemainingInHHMMSSCountup,
  227. daysToSeconds,
  228. secondsGoalForTargetWage,
  229. highlightWageRange,
  230. unhighlightWageRange,
  231. getHITLoadTime,
  232. getHITLoadTimeAccuracy,
  233. getAllContentLoadedTime,
  234. setAllContentLoadedCallback
  235. };
  236.  
  237. })();
  238.  
  239. function updateActiveHITTabTimer(requesterName, secondsRemaining, rewardInCents) {
  240. if (secondsRemaining <= 0) {
  241. document.title = `EXPIRED - ${requesterName}`; return;
  242. }
  243.  
  244. Utility.updateSecondsVisible();
  245.  
  246. let newTitle = `${Utility.toMMSSString(secondsRemaining)} `;
  247.  
  248. let secondsPassed;
  249. if (Utility.isPausingOnInvisible() && (!ALWAYS_TICK_WAGE_TIMER)) {
  250. secondsPassed = Utility.getSecondsVisible();
  251. }
  252. else {
  253. secondsPassed = Utility.getSecondsSinceStartTime();
  254. }
  255.  
  256. document.querySelector("#timeWorkedSpan").innerText = Utility.toMMSSString(secondsPassed);
  257.  
  258. let hourlyWageDollars = Utility.hourlyWageDollars(secondsPassed, rewardInCents + (document.querySelector("#bonusWageCents").value !== "" ? Utility.dollarsToCents(document.querySelector("#bonusWageCents").value) : 0));
  259.  
  260. if (hourlyWageDollars > LOVE_WAGE_LOWER_LIMIT) {
  261. newTitle += LOVE_WAGE_EMOJI;
  262. if (SHOW_BIG_EMOJI) { document.querySelector("#pageEmoji").innerText = LOVE_WAGE_EMOJI; }
  263. Utility.highlightWageRange("#loveWageSpan");
  264. Utility.unhighlightWageRange("#happyWageSpan");
  265. Utility.unhighlightWageRange("#okWageSpan");
  266. Utility.unhighlightWageRange("#sadWageSpan");
  267. Utility.unhighlightWageRange("#disgustedWageSpan");
  268. }
  269. else if (hourlyWageDollars > HAPPY_WAGE_LOWER_LIMIT) {
  270. newTitle += HAPPY_WAGE_EMOJI;
  271. if (SHOW_BIG_EMOJI) { document.querySelector("#pageEmoji").innerText = HAPPY_WAGE_EMOJI; }
  272. Utility.unhighlightWageRange("#loveWageSpan");
  273. Utility.highlightWageRange("#happyWageSpan");
  274. Utility.unhighlightWageRange("#okWageSpan");
  275. Utility.unhighlightWageRange("#sadWageSpan");
  276. Utility.unhighlightWageRange("#disgustedWageSpan");
  277. }
  278. else if (hourlyWageDollars > OK_WAGE_LOWER_LIMIT) {
  279. newTitle += OK_WAGE_EMOJI;
  280. if (SHOW_BIG_EMOJI) { document.querySelector("#pageEmoji").innerText = OK_WAGE_EMOJI; }
  281. Utility.unhighlightWageRange("#loveWageSpan");
  282. Utility.unhighlightWageRange("#happyWageSpan");
  283. Utility.highlightWageRange("#okWageSpan");
  284. Utility.unhighlightWageRange("#sadWageSpan");
  285. Utility.unhighlightWageRange("#disgustedWageSpan");
  286. }
  287. else if (hourlyWageDollars > SAD_WAGE_LOWER_LIMIT) {
  288. newTitle += SAD_WAGE_EMOJI;
  289. if (SHOW_BIG_EMOJI) { document.querySelector("#pageEmoji").innerText = SAD_WAGE_EMOJI; }
  290. Utility.unhighlightWageRange("#loveWageSpan");
  291. Utility.unhighlightWageRange("#happyWageSpan");
  292. Utility.unhighlightWageRange("#okWageSpan");
  293. Utility.highlightWageRange("#sadWageSpan");
  294. Utility.unhighlightWageRange("#disgustedWageSpan");
  295. }
  296. else if (hourlyWageDollars > DISGUSTED_WAGE_LOWER_LIMIT) {
  297. newTitle += DISGUSTED_WAGE_EMOJI;
  298. if (SHOW_BIG_EMOJI) { document.querySelector("#pageEmoji").innerText = DISGUSTED_WAGE_EMOJI; }
  299. Utility.unhighlightWageRange("#loveWageSpan");
  300. Utility.unhighlightWageRange("#happyWageSpan");
  301. Utility.unhighlightWageRange("#okWageSpan");
  302. Utility.unhighlightWageRange("#sadWageSpan");
  303. Utility.highlightWageRange("#disgustedWageSpan");
  304. }
  305.  
  306. let pageLoadSpan = document.querySelector("#timeLostToPageLoad");
  307. if (pageLoadSpan.innerText.trim() === "") {
  308. pageLoadSpan.innerText = Utility.getHITLoadTime().toFixed(0);
  309. }
  310.  
  311. let fullPageLoadSpan = document.querySelector("#timeLostToFullPageLoad");
  312. if (fullPageLoadSpan.innerText.trim() === "") {
  313. if (Utility.getAllContentLoadedTime()) {
  314. fullPageLoadSpan.innerText = Utility.getAllContentLoadedTime().toFixed(0);
  315. }
  316. }
  317.  
  318. newTitle = newTitle + ` \$${hourlyWageDollars.toFixed(2)}/hr.`;
  319.  
  320. newTitle += ` ${requesterName}`;
  321.  
  322. if (!ALWAYS_TICK_WAGE_TIMER && (!(document.visible && document.hasFocus()))) {
  323. newTitle += `(\$ timer paused)`;
  324. }
  325.  
  326. document.title = newTitle;
  327. }
  328.  
  329. function swapToExpanderBar() {
  330. document.querySelector("#titleBarTimerBar").style.left = "-100%";
  331. document.querySelector("#expanderBar").style.left = "50%";
  332. GM_setValue("timerBarVisible", false);
  333. }
  334.  
  335. function swapToTimerBar() {
  336. document.querySelector("#titleBarTimerBar").style.left = "50%";
  337. document.querySelector("#expanderBar").style.left = "-100%";
  338. GM_setValue("timerBarVisible", true);
  339. }
  340.  
  341. function restoreTimerBarCollapseState() {
  342. let isTimerBarVisible = GM_getValue("timerBarVisible");
  343.  
  344. if (isTimerBarVisible !== undefined) {
  345. if (!isTimerBarVisible) {
  346. swapToExpanderBar();
  347. }
  348. }
  349. else {
  350. }
  351. }
  352.  
  353. function workerSiteMain() {
  354. if (Utility.isWindowTopFrame()) {
  355.  
  356. const divID = document.querySelector(`div[data-react-class*="require('reactComponents/common/ShowModal')"]`);
  357. const reactProps = divID.getAttribute("data-react-props");
  358. const json = JSON.parse(reactProps);
  359. const requesterName = json.modalOptions.requesterName;
  360.  
  361. const notAcceptedSpan = document.querySelector(`span[data-react-props*='"text":"accept"']`);
  362.  
  363. if (notAcceptedSpan) {
  364. document.title = `NOT ACCEPTED - ${requesterName}`;
  365. }
  366. else {
  367. const countdownTimer = document.querySelector("span[data-react-class*='reactComponents/common/CompletionTimer'] span:last-of-type");
  368. const rewardInDollars = json.modalOptions.monetaryReward.amountInDollars;
  369. const rewardInCents = Utility.dollarsToCents(rewardInDollars);
  370.  
  371. const timeGivenMinutesSplit = (countdownTimer.innerText.trim().match(/(\d+) Min/) || [0, 0])[1];
  372. const timeGivenSecondsSplit = 0; // TODO: Change this when I figure out what a HIT with <1 min reads out as.
  373. const maximumTimeGivenInSeconds = Utility.hmsToSeconds(0, timeGivenMinutesSplit, timeGivenSecondsSplit);
  374.  
  375. const secondsForLoveWage = Utility.secondsGoalForTargetWage(Utility.dollarsToCents(LOVE_WAGE_LOWER_LIMIT), rewardInCents);
  376. const secondsForHappyWage = Utility.secondsGoalForTargetWage(Utility.dollarsToCents(HAPPY_WAGE_LOWER_LIMIT), rewardInCents);
  377. const secondsForOKWage = Utility.secondsGoalForTargetWage(Utility.dollarsToCents(OK_WAGE_LOWER_LIMIT), rewardInCents);
  378. const secondsForSadWage = Utility.secondsGoalForTargetWage(Utility.dollarsToCents(SAD_WAGE_LOWER_LIMIT), rewardInCents);
  379. const secondsForDisgustedWage = Utility.secondsGoalForTargetWage(Utility.dollarsToCents(DISGUSTED_WAGE_LOWER_LIMIT), rewardInCents);
  380.  
  381. document.body.insertAdjacentHTML('afterend',
  382. `<div id='titleBarTimerBar' style="position: fixed !important; pointer-events: none !important; background: white !important; opacity: 0.95 !important; width: 100% !important; left: 50% !important; margin-left: -50% !important; top: 0px !important; z-index: ${Number.MAX_SAFE_INTEGER} !important; text-align: center !important;">` +
  383. `<span id="loveWageSpan">${LOVE_WAGE_EMOJI}: (<= ${Utility.toMMSSString(secondsForLoveWage)})</span> ` +
  384. `<span id="happyWageSpan">${HAPPY_WAGE_EMOJI}: (${Utility.toMMSSString(secondsForLoveWage)} - ${Utility.toMMSSString(secondsForHappyWage)})</span> ` +
  385. `<span id="okWageSpan">${OK_WAGE_EMOJI}: (${Utility.toMMSSString(secondsForHappyWage)} - ${Utility.toMMSSString(secondsForOKWage)})</span> ` +
  386. `<span id="sadWageSpan">${SAD_WAGE_EMOJI}: (${Utility.toMMSSString(secondsForOKWage)} - ${Utility.toMMSSString(secondsForSadWage)})</span> ` +
  387. `<span id="disgustedWageSpan">${DISGUSTED_WAGE_EMOJI}: (>= ${Utility.toMMSSString(secondsForSadWage)})</span>` +
  388. `<div style="text-align: center !important; margin-left: auto !important; margin-right: auto !important; font-style: italic !important;">Time Worked: <span id="timeWorkedSpan">00:00</span> <input type="text" id="bonusWageCents" style="pointer-events: auto !important;" placeholder="Bonus $" size="5"> <div>Pageload: <span id="timeLostToPageLoad"></span>ms Complete: <span id="timeLostToFullPageLoad"></span>ms</div></div>` +
  389. `<button id='hideBarButton' style="pointer-events: auto !important;">Hide</button>` +
  390. '</div>'
  391. );
  392.  
  393. document.body.insertAdjacentHTML('afterend',
  394. `<div id='expanderBar' style="position: fixed; pointer-events: none; background: white; opacity: 0.95; width: 100%; left: -100%; margin-left: -50%; top: 0px; z-index: ${Number.MAX_SAFE_INTEGER}; text-align: center;">` +
  395. `<button id='showBarButton' style="pointer-events: auto;">Show $/hr. Details</button>` +
  396. '</div>'
  397. );
  398.  
  399. document.querySelector('#hideBarButton').addEventListener('click', function hideBar() {
  400. swapToExpanderBar();
  401. });
  402.  
  403. document.querySelector('#showBarButton').addEventListener('click', function hideBar() {
  404. swapToTimerBar();
  405. });
  406.  
  407. restoreTimerBarCollapseState();
  408.  
  409. if (SHOW_BIG_EMOJI) {
  410. document.body.insertAdjacentHTML('afterend',
  411. `<div id="pageEmoji" draggable="false" style="position: fixed; opacity: ${BIG_EMOJI_OPACITY_ZERO_TO_ONE}; font-size: ${BIG_EMOJI_SIZE}; pointer-events: none; user-drag: none; user-select: none; width: 100%; left: 0px; height: 50%; top: 50%; z-index: ${Number.MAX_SAFE_INTEGER}; text-align: center;">` +
  412. `` +
  413. `</div>`);
  414. }
  415.  
  416.  
  417. setInterval(function workerSiteUpdate() {
  418. let countdownTimerText = countdownTimer.innerText.match(/\d+:\d+\b/)[0];
  419. // let secondsRemaining = Utility.secondsRemainingInHHMMSSCountdown(countdownTimerText);
  420. let secondsRemaining = Utility.secondsRemainingInMMSSCountup(countdownTimerText, maximumTimeGivenInSeconds);
  421. updateActiveHITTabTimer(requesterName, secondsRemaining, rewardInCents);
  422. }, UPDATE_RATE_IN_MILLISECONDS);
  423. } // End of HIT accepted check.
  424.  
  425. } // End of isWindowTopFrame().
  426. }
  427.  
  428. function originalSiteMain() {
  429. if (Utility.isWindowTopFrame()) {
  430. const countdownTimer = document.querySelector("#theTime");
  431.  
  432. const acceptHITInput = document.querySelector("input[name='/accept']");
  433.  
  434. const capsuleTexts = document.querySelectorAll(".capsule_field_text");
  435. const requesterName = capsuleTexts[0].innerText.trim();
  436. const rewardInDollars = capsuleTexts[1].innerText.trim().match(/\$([0-9.]+)/)[1];
  437. const rewardInCents = Utility.dollarsToCents(rewardInDollars);
  438. const timeGivenDaysSplit = (capsuleTexts[3].innerText.trim().match(/(\d+) days/) || [0, 0])[1];
  439. const timeGivenHoursSplit = (capsuleTexts[3].innerText.trim().match(/(\d+) hours/) || [0, 0])[1];
  440. const timeGivenMinutesSplit = (capsuleTexts[3].innerText.trim().match(/(\d+) minutes/) || [0, 0])[1];
  441. const timeGivenSecondsSplit = (capsuleTexts[3].innerText.trim().match(/(\d+) seconds/) || [0, 0])[1];
  442. const maximumTimeGivenInSeconds = Utility.daysToSeconds(timeGivenDaysSplit) + Utility.hmsToSeconds(timeGivenHoursSplit, timeGivenMinutesSplit, timeGivenSecondsSplit);
  443.  
  444. const secondsForLoveWage = Utility.secondsGoalForTargetWage(Utility.dollarsToCents(LOVE_WAGE_LOWER_LIMIT), rewardInCents);
  445. const secondsForHappyWage = Utility.secondsGoalForTargetWage(Utility.dollarsToCents(HAPPY_WAGE_LOWER_LIMIT), rewardInCents);
  446. const secondsForOKWage = Utility.secondsGoalForTargetWage(Utility.dollarsToCents(OK_WAGE_LOWER_LIMIT), rewardInCents);
  447. const secondsForSadWage = Utility.secondsGoalForTargetWage(Utility.dollarsToCents(SAD_WAGE_LOWER_LIMIT), rewardInCents);
  448. const secondsForDisgustedWage = Utility.secondsGoalForTargetWage(Utility.dollarsToCents(DISGUSTED_WAGE_LOWER_LIMIT), rewardInCents);
  449.  
  450. console.log("You lost", Utility.getHITLoadTime() / 1000, "seconds to HIT loading, with accuracy:", Utility.getHITLoadTimeAccuracy());
  451. console.log("For a theoretical 1-second submission, this means that loading time cut your wage from", `\$${Utility.hourlyWageDollars(1, rewardInCents).toFixed(2)}/hr.`, "to", `\$${Utility.hourlyWageDollars(1 + (Utility.getHITLoadTime() / 1000), rewardInCents).toFixed(2)}/hr.`);
  452. console.log("For a theoretical 5-second submission, this means that loading time cut your wage from", `\$${Utility.hourlyWageDollars(5, rewardInCents).toFixed(2)}/hr.`, "to", `\$${Utility.hourlyWageDollars(5 + (Utility.getHITLoadTime() / 1000), rewardInCents).toFixed(2)}/hr.`);
  453.  
  454. Utility.setAllContentLoadedCallback(() => {
  455. console.log("You lost", Utility.getAllContentLoadedTime() / 1000, "seconds to HIT the full page load, with accuracy:", Utility.getHITLoadTimeAccuracy());
  456. console.log("If you waited for the whole page to load, this means that the most you could earn with an instant submission would be", `\$${Utility.hourlyWageDollars(Utility.getAllContentLoadedTime() / 1000, rewardInCents).toFixed(2)}/hr.`);
  457. });
  458.  
  459. document.body.insertAdjacentHTML('afterend',
  460. `<div id="titleBarTimerBar" style="position: fixed; pointer-events: none; background: white; opacity: 0.95; width: 100%; left: 50%; margin-left: -50%; top: 0px; z-index: ${Number.MAX_SAFE_INTEGER}; text-align: center;">` +
  461. `<span id="loveWageSpan">${LOVE_WAGE_EMOJI}: (<= ${Utility.toMMSSString(secondsForLoveWage)})</span> ` +
  462. `<span id="happyWageSpan">${HAPPY_WAGE_EMOJI}: (${Utility.toMMSSString(secondsForLoveWage)} - ${Utility.toMMSSString(secondsForHappyWage)})</span> ` +
  463. `<span id="okWageSpan">${OK_WAGE_EMOJI}: (${Utility.toMMSSString(secondsForHappyWage)} - ${Utility.toMMSSString(secondsForOKWage)})</span> ` +
  464. `<span id="sadWageSpan">${SAD_WAGE_EMOJI}: (${Utility.toMMSSString(secondsForOKWage)} - ${Utility.toMMSSString(secondsForSadWage)})</span> ` +
  465. `<span id="disgustedWageSpan">${DISGUSTED_WAGE_EMOJI}: (>= ${Utility.toMMSSString(secondsForSadWage)})</span>` +
  466. `<div style="text-align: center; margin-left: auto; margin-right: auto; font-style: italic;">Time Worked: <span id="timeWorkedSpan">00:00</span> <input type="text" style="pointer-events: auto;" id="bonusWageCents" placeholder="Bonus $" size="5"><div>Pageload: <span id="timeLostToPageLoad"></span>ms Complete: <span id="timeLostToFullPageLoad"></span>ms</div></div>` +
  467. `<button id='hideBarButton' style="pointer-events: auto;">Hide</button>` +
  468. '</div>'
  469. );
  470.  
  471. document.body.insertAdjacentHTML('afterend',
  472. `<div id='expanderBar' style="position: fixed; pointer-events: none; background: white; opacity: 0.95; width: 100%; left: -100%; margin-left: -50%; top: 0px; z-index: ${Number.MAX_SAFE_INTEGER}; text-align: center;">` +
  473. `<button id='showBarButton' style="pointer-events: auto;">Show $/hr. Details</button>` +
  474. '</div>'
  475. );
  476.  
  477. document.querySelector('#hideBarButton').addEventListener('click', function hideBar() {
  478. swapToExpanderBar();
  479. });
  480.  
  481. document.querySelector('#showBarButton').addEventListener('click', function hideBar() {
  482. swapToTimerBar();
  483. });
  484.  
  485. restoreTimerBarCollapseState();
  486.  
  487. if (SHOW_BIG_EMOJI) {
  488. document.body.insertAdjacentHTML('afterend',
  489. `<div id="pageEmoji" draggable="false" style="position: fixed !important; opacity: ${BIG_EMOJI_OPACITY_ZERO_TO_ONE} !important; font-size: ${BIG_EMOJI_SIZE} !important; pointer-events: none !important; user-drag: none !important; user-select: none !important; width: 100% !important; left: 0px !important; height: 50% !important; top: 50% !important; z-index: ${Number.MAX_SAFE_INTEGER} !important; text-align: center !important;">` +
  490. `?` +
  491. `</div>`);
  492. }
  493.  
  494. if (acceptHITInput) {
  495. document.title = `NOT ACCEPTED - ${requesterName}`;
  496. }
  497. else {
  498. setInterval(function mainSiteUpdate() {
  499. let countupTimerText = countdownTimer.innerText.trim();
  500. let secondsRemaining = Utility.secondsRemainingInHHMMSSCountup(countupTimerText, maximumTimeGivenInSeconds);
  501. updateActiveHITTabTimer(requesterName, secondsRemaining, rewardInCents);
  502. }, UPDATE_RATE_IN_MILLISECONDS);
  503. } // End of HIT accepted check.
  504. } // End of isWindowTopFrame().
  505. }
  506.  
  507. function isWorkerSite() {
  508. return window.location.href.toLowerCase().includes("worker.mturk.com");
  509. }
  510.  
  511. (function main() {
  512. window.addEventListener("message", function handleMessage(event) {
  513. if (event.data.msg === "I'm ready for you to ask me questions.") {
  514. console.log("PARENT FRAME: Kid said it's ready for questions.");
  515. Utility.askWorkerFrameWhetherSurvey();
  516. console.log("PARENT FRAME: I asked whether the kid was a survey.");
  517. }
  518. else if (event.data.msg === "I am a survey HIT.") {
  519. console.log("PARENT FRAME: Kid said he was a survey.");
  520. Utility.disablePauseOnInvisible();
  521. console.log("PARENT FRAME: I disabled pausing on invisible, since the kid said he was a survey.");
  522. }
  523.  
  524. if (event.data.msg === "Submit relay.") {
  525. console.log("PARENT FRAME: I detected a form submit.");
  526. alert("Detected a form submit.");
  527. }
  528. });
  529.  
  530. if (isWorkerSite()) {
  531. workerSiteMain();
  532. }
  533. else {
  534. originalSiteMain();
  535. }
  536. })();