mTurkWiki.net Great HITs Export

Export HIT description as phpBB formatted text with turkopticon link and all relevant data.

  1. // ==UserScript==
  2. // @name mTurkWiki.net Great HITs Export
  3. // @namespace localhost
  4. // @author ThirdClassInternationalMasterTurker and DCI modded for mTurk Wiki by ChrisM77
  5. // @description Export HIT description as phpBB formatted text with turkopticon link and all relevant data.
  6. // @include https://www.mturk.com/mturk/searchbar*
  7. // @include https://www.mturk.com/mturk/findhits*
  8. // @include https://www.mturk.com/mturk/viewhits*
  9. // @include https://www.mturk.com/mturk/viewsearchbar*
  10. // @include https://www.mturk.com/mturk/sortsearchbar*
  11. // @include https://www.mturk.com/mturk/sorthits*
  12. // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js
  13. // @grant GM_getValue
  14. // @grant GM_setValue
  15. // @grant GM_deleteValue
  16. // @version 2.1
  17. // ==/UserScript==
  18.  
  19. //
  20. // 2012-11-19 0.2: Added customizable template
  21. //
  22. // 2012-11-20 0.3: Fixed: verifies that HIT link is correct
  23. //
  24. // 2012-11-26 0.4: Try to get requester and requesterId right even if some other script has changed the page.
  25. // Added requester link to default template.
  26. //
  27. // 2012-11-26 0.5: Partially works with Opera (No customisation)
  28. //
  29. // 2012-12-02 1.0: Added @downloadURL and @updateURL
  30. //
  31. // 2013-05-20 1.2: Added a Time field that will need to be manually changed after pasting into forum thread. Changed name of scipt.
  32.  
  33. (function () {
  34. var EDIT = false;
  35. var HITS = [];
  36. var HIT;
  37.  
  38. DEFAULT_TEMPLATE = '[b]Title:[/b] [url={link}][COLOR=#be2c2a]{title}[/COLOR][/url]\n';
  39. DEFAULT_TEMPLATE += '[b]Requester:[/b] [url=https://www.mturk.com/mturk/searchbar?selectedSearchType=hitgroups&requesterId={requesterId}][COLOR=#be2c2a]{requester}[/COLOR][/url]';
  40. DEFAULT_TEMPLATE += ' [{requesterId}] ([url=http://turkopticon.differenceengines.com/{requesterId}][COLOR=#be2c2a]TO[/COLOR][/url])';
  41. DEFAULT_TEMPLATE += '{to_stuff}';
  42. DEFAULT_TEMPLATE += '\n[b]Description:[/b] {description}';
  43. DEFAULT_TEMPLATE += '\n[b]Reward:[/b] [COLOR=green][b]{reward}[/b][/COLOR]';
  44. DEFAULT_TEMPLATE += '\n[b]Qualifications:[/b] {qualifications}';
  45. var TO_BASE = "http://turkopticon.ucsd.edu/";
  46. var API_BASE = "https://mturk-api.istrack.in/";
  47. var API_MULTI_ATTRS_URL = API_BASE + "multi-attrs.php?ids=";
  48.  
  49. var TEMPLATE;
  50. var EASYLINK;
  51.  
  52. if (typeof GM_getValue === 'undefined')
  53. TEMPLATE = null;
  54. else {
  55. TEMPLATE = GM_getValue('HITExport Template');
  56. EASYLINK = GM_getValue('HITExport Easylink');
  57. }
  58. if (TEMPLATE == null) {
  59. TEMPLATE = DEFAULT_TEMPLATE;
  60. }
  61.  
  62. function get_requester_id(s) {
  63. var idx = 12 + s.search('requesterId=');
  64. return s.substr(idx);
  65. }
  66.  
  67. function getRequesterAnchorsAndIds(a) {
  68. var rai = {};
  69. var re = new RegExp(/requesterId/);
  70. var rf = new RegExp(/contact/);
  71. for (var i = 0; i < a.length; i++) {
  72. var href = a[i].getAttribute('href');
  73. if (re.test(href) && !rf.test(href)) {
  74. var id = a[i].href.split('requesterId=')[1].split('&')[0]
  75. if (!rai.hasOwnProperty(id)) {
  76. rai[id] = [];
  77. }
  78. rai[id].push(a[i]);
  79. }
  80. }
  81. return rai;
  82. }
  83.  
  84. function buildXhrUrl(rai) {
  85. var url = API_MULTI_ATTRS_URL;
  86. var ri = Object.keys(rai);
  87. for (var i = 0; i < ri.length; i++) {
  88. url += ri[i];
  89. if (i < ri.length - 1) {
  90. url += ",";
  91. }
  92. }
  93. return url;
  94. }
  95.  
  96. function makeXhrQuery(url) {
  97. var xhr = new XMLHttpRequest();
  98. xhr.open('GET', url, false);
  99. xhr.send(null);
  100. return $.parseJSON(xhr.response);
  101. }
  102.  
  103. function getNamesForEmptyResponses(rai, resp) {
  104. for (var rid in rai) {
  105. if (rai.hasOwnProperty(rid) && resp[rid] == "") {
  106. resp[rid] = $.parseJSON('{"name": "' + rai[rid][0].innerHTML + '"}');
  107. }
  108. }
  109. return resp;
  110. }
  111.  
  112. function getKeys(obj) {
  113. var keys = [];
  114. for (var key in obj) {
  115. keys.push(key);
  116. }
  117. return keys;
  118. }
  119.  
  120. function apply_template(hit_data) {
  121. var txt = TEMPLATE;
  122.  
  123. var vars = ['title', 'requester', 'requesterId', 'description', 'reward', 'qualifications', 'link', 'time', 'hits_available', 'to_stuff', 'to_text', 'adfly'];
  124.  
  125. var a = document.getElementsByTagName('a');
  126. var rai = getRequesterAnchorsAndIds(a);
  127. var url = buildXhrUrl(rai);
  128. var resp = makeXhrQuery(url);
  129. resp = getNamesForEmptyResponses(rai, resp);
  130. //var toText = "[list]";
  131. var toText = "";
  132. var toStuff = "";
  133. var toData = "";
  134. var numResp = resp[hit_data["requesterId"]].reviews;
  135. if (resp[hit_data["requesterId"]].attrs == null) {
  136. toStuff = " No TO ";
  137. toText = " No TO ";
  138. }
  139. else {
  140. for (var key in resp[hit_data["requesterId"]].attrs) {
  141. //toText += "\n[*]"+key+": "+resp[hit_data["requesterId"]].attrs[key]+"\n";
  142. var i = 0;
  143. var color = "green";
  144. var name = key;
  145. var num = Math.floor(resp[hit_data["requesterId"]].attrs[key]);
  146. toText += " "+Number(resp[hit_data["requesterId"]].attrs[key]).toFixed(2)+" "+name;
  147. toData += resp[hit_data["requesterId"]].attrs[key] + ",";
  148. }
  149. //toText += "[/list]";
  150. toText += (txt.indexOf('{to_stuff}') >= 0 ? "" : "\nNumber of Responses: "+numResp+"\n[URL=\"http://turkopticon.differenceengines.com/report?requester[amzn_id]=" + hit_data['requesterId'] + "&requester[amzn_name]=" + hit_data['requester'] + "\"](Submit a new TO rating for this requester)[/URL]");
  151. toStuff = '\n[img]http://data.istrack.in/turkopticon.php?data=' + toData.slice(0,-1) + '[/img]';
  152. toStuff += "\nNumber of Responses: "+numResp;
  153. }
  154.  
  155. for (var i = 0; i < vars.length; i++) {
  156. t = new RegExp('\{' + vars[i] + '\}', 'g');
  157. if (vars[i] == "to_stuff") {
  158. txt = txt.replace(t, toStuff);
  159. }
  160. else if (vars[i] == "to_text"){
  161. txt = txt.replace(t, toText);
  162. }
  163. else
  164. txt = txt.replace(t, hit_data[vars[i]]);
  165. }
  166. textarea.value = txt;
  167. }
  168.  
  169. function export_func(item) {
  170. return function () {
  171. HIT = item;
  172. EDIT = false;
  173. edit_button.textContent = 'Edit Template';
  174. apply_template(HITS[HIT]);
  175. div.style.display = 'block';
  176. textarea.select();
  177. }
  178. }
  179.  
  180. function hide_func(div) {
  181. return function () {
  182. if (EDIT == false)
  183. div.style.display = 'none';
  184. }
  185. }
  186.  
  187. function default_func() {
  188. return function () {
  189. GM_deleteValue('HITExport Template');
  190. TEMPLATE = DEFAULT_TEMPLATE;
  191. EDIT = false;
  192. edit_button.textContent = 'Edit Template';
  193. apply_template(HITS[HIT]);
  194. }
  195. }
  196.  
  197.  
  198. function edit_func() {
  199. return function () {
  200. if (EDIT == true) {
  201. EDIT = false;
  202. TEMPLATE = textarea.value;
  203. edit_button.textContent = 'Edit Template';
  204. apply_template(HITS[HIT]);
  205. }
  206. else {
  207. EDIT = true;
  208. edit_button.textContent = 'Show Changes';
  209. save_button.disabled = false;
  210. textarea.value = TEMPLATE;
  211. }
  212. }
  213. }
  214.  
  215. function easy_func() {
  216. return function () {
  217. var input = prompt("Please enter your adfly easy link, or leave blank if you don't have one.");
  218. if (input.substring(input.length - 1) != "/")
  219. input += "/";
  220. EASYLINK = input;
  221. GM_setValue('HITExport Easylink', EASYLINK);
  222. var url = EASYLINK + hit_data["link"];
  223. hit_data["adfly"] = url;
  224. apply_template(hit_data);
  225. }
  226. }
  227.  
  228. function save_func() {
  229. return function () {
  230. if (EDIT)
  231. TEMPLATE = textarea.value;
  232. GM_setValue('HITExport Template', TEMPLATE);
  233. }
  234. }
  235.  
  236. for (var item = 0; item < 10; item++) {
  237. var tooltip = document.getElementById('requester.tooltip--' + item);
  238. if (tooltip == null)
  239. break; // no need to continue
  240. var titleElement = document.getElementById('capsule' + item + '-0');
  241. var requesterId = tooltip.parentNode.parentNode.getElementsByTagName('a');
  242.  
  243. var hit_data = {};
  244. hit_data.title = titleElement.textContent.trim();
  245.  
  246. for (var i = 0; i < requesterId.length; i++) {
  247. if (requesterId[i].href.match(/requesterId=/)) {
  248. hit_data.requesterId = get_requester_id(requesterId[i].href);
  249. hit_data.requester = requesterId[i].textContent.trim();
  250. break;
  251. }
  252. }
  253.  
  254. hit_data.description = document.getElementById('description.tooltip--' + item).parentNode.parentNode.childNodes[3].textContent.trim();
  255. hit_data.reward = document.getElementById('reward.tooltip--' + item).parentNode.parentNode.childNodes[3].textContent.trim();
  256. hit_data.time = document.getElementById('duration_to_complete.tooltip--' + item).parentNode.parentNode.childNodes[3].textContent.trim();
  257. hit_data.hits_available = document.getElementById('number_of_hits.tooltip--' + item).parentNode.parentNode.childNodes[3].textContent.trim();
  258. //hit_data.to_stuff = toStuff;
  259.  
  260. var linkElements = titleElement.parentNode.parentNode.getElementsByTagName('a');
  261.  
  262. if (linkElements != null)
  263. for (var i = 0; i < linkElements.length; i++) {
  264. if (linkElements[i] != null && linkElements[i].textContent == 'View a HIT in this group') {
  265. var url = EASYLINK + linkElements[i].href;
  266. hit_data.adfly = url;
  267. hit_data.link = linkElements[i].href;
  268. break;
  269. }
  270. }
  271.  
  272. var qElements = document.getElementById('qualificationsRequired.tooltip--' + item).parentNode.parentNode.parentNode.getElementsByTagName('tr');
  273.  
  274. var qualifications = [];
  275. for (var i = 1; i < qElements.length; i++) {
  276. qualifications.push(qElements[i].childNodes[1].textContent.trim().replace(/\s+/g, ' '));
  277. }
  278. hit_data.qualifications = (qualifications.join(', ') ? qualifications.join(', ') : "None");
  279.  
  280. HITS[item] = hit_data;
  281. button = document.createElement('button');
  282. button.textContent = 'MTW';
  283. button.title = 'Export this HIT description as MTW formatted text';
  284.  
  285. button.style.height = '14px';
  286. button.style.width = '30px';
  287. button.style.fontSize = '8px';
  288. button.style.border = '1px solid';
  289. button.style.padding = '2px';
  290. button.style.backgroundColor = 'transparent';
  291.  
  292. button.addEventListener("click", export_func(item), false);
  293. titleElement.parentNode.appendChild(button);
  294. }
  295.  
  296. var div = document.createElement('div');
  297. var textarea = document.createElement('textarea');
  298. var div2 = document.createElement('label');
  299.  
  300. div.style.position = 'fixed';
  301. div.style.width = '500px';
  302. div.style.height = '235px';
  303. div.style.left = '50%';
  304. div.style.right = '50%';
  305. div.style.margin = '-250px 0px 0px -250px';
  306. div.style.top = '300px';
  307. div.style.padding = '5px';
  308. div.style.border = '2px';
  309. div.style.backgroundColor = '#be2c2a';
  310. div.style.color = 'white';
  311. div.style.zIndex = '100';
  312.  
  313. textarea.style.padding = '2px';
  314. textarea.style.width = '500px';
  315. textarea.style.height = '200px';
  316. textarea.title = '{title}\n{requester}\n{requesterId}\n{description}\n{reward}\n{qualifications}\n{link}\n{time}\n{hits_available}';
  317.  
  318. div.textContent = 'Press Ctrl+C to copy to clipboard. Click textarea to close';
  319. div.style.fontSize = '12px';
  320. div.appendChild(textarea);
  321. div2.style.position = 'fixed';
  322. div2.style.right = '230px';
  323. div2.style.fontSize = '9px';
  324.  
  325. var edit_button = document.createElement('button');
  326. var save_button = document.createElement('button');
  327. var default_button = document.createElement('button');
  328.  
  329. edit_button.textContent = 'Edit Template';
  330. edit_button.setAttribute('id', 'edit_button');
  331. edit_button.style.height = '18px';
  332. edit_button.style.width = '100px';
  333. edit_button.style.fontSize = '10px';
  334. edit_button.style.paddingLeft = '3px';
  335. edit_button.style.paddingRight = '3px';
  336. edit_button.style.backgroundColor = 'white';
  337. save_button.textContent = 'Save Template';
  338. save_button.setAttribute('id', 'save_button');
  339. save_button.style.height = '18px';
  340. save_button.style.width = '100px';
  341. save_button.style.fontSize = '10px';
  342. save_button.style.paddingLeft = '3px';
  343. save_button.style.paddingRight = '3px';
  344. save_button.style.backgroundColor = 'white';
  345. save_button.style.marginLeft = '5px';
  346.  
  347. default_button.textContent = ' D ';
  348. default_button.setAttribute('id', 'default_button');
  349. default_button.style.height = '18px';
  350. default_button.style.width = '20px';
  351. default_button.style.fontSize = '10px';
  352. default_button.style.paddingLeft = '3px';
  353. default_button.style.paddingRight = '3px';
  354. default_button.style.backgroundColor = 'white';
  355. default_button.style.marginLeft = '5px';
  356. default_button.title = 'Return default template';
  357.  
  358. div.appendChild(edit_button);
  359. div.appendChild(save_button);
  360. div.appendChild(default_button);
  361. save_button.disabled = true;
  362.  
  363. div.appendChild(div2);
  364. div.style.display = 'none';
  365. textarea.addEventListener("click", hide_func(div), false);
  366. edit_button.addEventListener("click", edit_func(), false);
  367. save_button.addEventListener("click", save_func(), false);
  368. default_button.addEventListener("click", default_func(), false);
  369. document.body.insertBefore(div,document.body.firstChild);
  370. })();