您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds an Export button to MTurk HIT capsules to share HITs on forums, reddit, etc.
当前为
// ==UserScript== // @name Hermes HIT exporter // @namespace mobiusevalon.tibbius.com // @version 2.4 // @author Mobius Evalon <[email protected]> // @description Adds an Export button to MTurk HIT capsules to share HITs on forums, reddit, etc. // @license Creative Commons Attribution-ShareAlike 4.0; http://creativecommons.org/licenses/by-sa/4.0/ // @require https://code.jquery.com/jquery-1.12.4.min.js // @require https://greasyfork.org/scripts/22593-mount-olympus/code/Mount%20Olympus.js?version=144091 // @include /^https{0,1}:\/\/\w{0,}\.?mturk\.com\/mturk\/(?:searchbar|viewsearchbar|sortsearchbar|findhits|viewhits|sorthits)/ // @exclude /&hit_scraper$/ // @grant none // ==/UserScript== $(document).ready(function() { script_version = "2.4 beta"; function hit_info($element) { function scrape_from_tooltip(tt) { return $("a[id*='"+tt+".tooltip']",$element).parent().next().text().collapse_whitespace(); } // basic HIT info that must be scraped off the page var obj = { hit_name:$("a.capsulelink[href='#']",$element).first().text().collapse_whitespace(), hit_id:$("a[href*='groupId']",$element).first().attr("href").match(/groupId=([A-Z0-9]{30})(?:&|$)/)[1], hit_desc:scrape_from_tooltip("description"), //$("a[id*='description.tooltip']",$element).parent().next().text().collapse_whitespace(), hit_time:scrape_from_tooltip("duration_to_complete"), //$("a[id*='duration_to_complete.tooltip']",$element).parent().next().text().collapse_whitespace(), hits_available:scrape_from_tooltip("number_of_hits"), //$("a[id*='number_of_hits.tooltip']",$element).parent().next().text().collapse_whitespace(), hit_reward:scrape_from_tooltip("reward"), //$("a[id*='reward.tooltip']",$element).parent().next().text().collapse_whitespace(), requester_name:$("a[href*='selectedSearchType=hitgroups']",$element).first().text().collapse_whitespace(), requester_id:$("a[href*='requesterId']",$element).first().attr("href").match(/requesterId=([A-Z0-9]{12,14})(?:&|$)/)[1] }; // link properties for convenience, since these are long URLs that only use one bit of previously collected info obj.preview_link = ("https://www.mturk.com/mturk/preview?groupId="+obj.hit_id); obj.panda_link = ("https://www.mturk.com/mturk/previewandaccept?groupId="+obj.hit_id); obj.requester_hits = ("https://www.mturk.com/mturk/searchbar?selectedSearchType=hitgroups&requesterId="+obj.requester_id); obj.contact_requester = ("https://www.mturk.com/mturk/contact?requesterId="+obj.requester_id+"&requesterName="+obj.requester_name); obj.to_reviews = ("https://turkopticon.ucsd.edu/"+obj.requester_id); // parse qualifications var $qual_anchor = $("a[id*='qualificationsRequired.tooltip']",$element); if($qual_anchor.parent().next().text().trim() === "None") obj.quals = "None"; else { var quals = []; $("tr:not(:first-of-type) td:first-of-type",$qual_anchor.closest("table")).each(function() {quals.push($(this).text().collapse_whitespace());}); obj.quals = quals.join("; "); } obj.author_url = "https://greasyfork.org/en/users/9665-mobius-evalon"; obj.shortened_author_url = "http://goo.gl/jqpg0h"; obj.hermes_version = script_version; obj.hermes_url = "https://greasyfork.org/en/scripts/21175-hermes-hit-exporter"; obj.shortened_hermes_url = "http://goo.gl/bNdTBj"; // by default the user has provided no completion time estimate, so this block needs to not be displayed until the user provides that info obj.pph_block = ""; localStorage.hermes_hit = JSON.stringify(obj); return obj; } function get_template(t) { var format = (t || $("#hhe_export_format").val()); return (localStorage.getItem("hermes_"+format+"_template") || default_template(format)); } function display_template() { var obj = localstorage_obj("hermes_hit"), template = get_template($("#hhe_export_format").val()); $("#hhe_update_time, #hhe_export_format").prop("disabled",false); $.each(obj,function(key,val) { if(key.slice(-6) === "_block") template = template.replace(new RegExp(("<"+key+":.*?>"),"gi"),val); else template = template.replace(new RegExp(("\\{"+key+"\\}"),"gi"),val); }); // date_time has to handled here because putting it in the hit obj would cache the data instead of // using current information. the other replace is to remove unused blocks template = template.replace(/{date_time}/ig,new Date().toString()).replace(/<.+?_block:(.*?)>/gi,"$1"); $("#hhe_export_output").show().text(template); } function default_template(format) { if(format === "vbulletin") return "[url={preview_link}][color=blue]{hit_name}[/color][/url] [[url={panda_link}][color=blue]PANDA[/color][/url]]\n"+ "[b]Reward[/b]: {hit_reward}<pph_block:/{my_time} ({hourly_rate}/hour)>\n"+ "[b]Time allowed[/b]: {hit_time}\n"+ "[b]Available[/b]: {hits_available}\n"+ "[b]Description[/b]: {hit_desc}\n"+ "[b]Qualifications[/b]: {quals}\n\n"+ "[b]Requester[/b]: [url={requester_hits}][color=blue]{requester_name}[/color][/url] [[url={contact_requester}][color=blue]Contact[/color][/url]]\n"+ "[url={to_reviews}][color=blue][b]Turkopticon[/b][/color][/url]: <to_block:[Pay: [color={to_pay_color}]{to_pay}[/color]] [Fair: [color={to_fair_color}]{to_fair}[/color]] [Fast: [color={to_fast_color}]{to_fast}[/color]] [Comm: [color={to_comm_color}]{to_comm}[/color]]>\n"+ "[size=8px]Generated {date_time} with [url={hermes_url}][color=blue]Hermes HIT Exporter[/color][/url] {hermes_version} by [url={author_url}][color=blue]Mobius Evalon[/color][/url][/size]"; else if(format === "markdown") return "[{hit_name}]({preview_link}) \\[[PANDA]({panda_link})\\] \n"+ "**Reward**: {hit_reward}<pph_block:/{my_time} ({hourly_rate}/hour)> \n"+ "**Time allowed**: {hit_time} \n"+ "**Available**: {hits_available} \n"+ "**Description**: {hit_desc} \n"+ "**Qualifications**: {quals}\n\n"+ "**Requester**: [{requester_name}]({requester_hits}) \\[[Contact]({contact_requester})\\] \n"+ "[**Turkopticon**]({to_reviews}): <to_block:\\[Pay: {to_pay}\\] \\[Fair: {to_fair}\\] \\[Fast: {to_fast}\\] \\[Comm: {to_comm}\\]> \n"+ "^Generated {date_time} with [Hermes HIT Exporter]({hermes_url}) {hermes_version} by [Mobius Evalon]({author_url})"; else if(format === "plaintext") return "{hit_name} [{preview_link}] \n"+ "Reward: {hit_reward}<pph_block:/{my_time} ({hourly_rate}/hour)> \n"+ "Time allowed: {hit_time} \n"+ "Available: {hits_available} \n"+ "Description: {hit_desc} \n"+ "Qualifications: {quals}\n\n"+ "Requester: {requester_name} [{requester_hits}] \n"+ "Turkopticon: <to_block:[Pay: {to_pay}] [Fair: {to_fair}] [Fast: {to_fast}] [Comm: {to_comm}]> [{to_reviews}] \n"+ "Generated {date_time} with Hermes HIT Exporter {hermes_version} by Mobius Evalon [{hermes_url}]"; else if(format === "irc") return "{hit_name} [View: {shortened_preview_link}, PANDA: {shortened_panda_link}] Reward: {hit_reward}<pph_block:/{my_time} ({hourly_rate}/hour)> Time: {hit_time} | Requester: {requester_name} [HITs: {shortened_requester_hits}, TO: {shortened_to_reviews}] Pay={to_pay} Fair={to_fair} Fast={to_fast} Comm={to_comm}"; } function reset_interface() { $("#hhe_edit_template").hide(); $("#hhe_export_output").hide(); $("#hhe_mode_swap").text("Edit"); $("#hhe_update_time").prop("disabled",true); $("#hhe_completion_time").val(""); $("#hhe_export_format").prop("disabled",true).val(localStorage.hermes_export_format || "vbulletin"); } function url_shortener(callback) { var mirrors = [ "https://ns4t.net/yourls-api.php?action=bulkshortener&title=MTurk&signature=39f6cf4959" ], params = "", hit = localstorage_obj("hermes_hit"), template = get_template($("#hhe_export_format").val()), tokens = [], result = {}; function exit() { if($.type(callback) === "function") callback(result); } // since we can never know which or how many shortened url tokens will appear because of // template editing, and since this function may be called when url shortening has already // been completed (switching template format), we have to check to make sure that we actually need // to query for shortened urls in the first place tokens = template.match(/(\{shortened_[^}]+\})/gi); if(tokens.length) { $.each(tokens,function(key,val) { if(!hit.hasOwnProperty(val.slice(1,-1))) params += ("&urls[]="+hit[val.slice(11,-1)]); }); } if(params.length) { olympian_get_request(mirrors,params,function(response) { if(response.length) { response = response.split(";"); $.each(tokens,function(key,val) { result[val.slice(1,-1)] = response[key]; }); } else console.log("Hermes HIT exporter: url shortening service appeared to be queried successfully but returned no data"); exit(); }); } else exit(); } function url_shortening_complete(result) { var hit = localstorage_obj("hermes_hit"), template = get_template($("#hhe_export_format").val()); $.each(result,function(key,val) { hit[key] = val; }); localStorage.hermes_hit = JSON.stringify(hit); display_template(); } function to_complete(result) { function color_prop(n) { switch(Math.round(n*1)) { case 0: return "black"; case 1: return "red"; case 2: return "red"; case 3: return "orange"; default: return "green"; } } function symbol_prop(n) { n = Math.round(n*1); var filled = "⚫⚫⚫⚫⚫", empty = "⚪⚪⚪⚪⚪"; return (filled.slice(0,n)+empty.slice(n)); } var hit_obj = localstorage_obj("hermes_hit"), template = get_template($("#hhe_export_format").val()); if(result.hasOwnProperty(hit_obj.requester_id)) { if($.type(result[hit_obj.requester_id]) === "object") { $.each(result[hit_obj.requester_id].attrs,function(k,v) { hit_obj["to_"+k] = v; hit_obj["to_"+k+"_color"] = color_prop(v); hit_obj["to_"+k+"_symbols"] = symbol_prop(v); }); } else hit_obj.to_block = "[None]"; } else hit_obj.to_block = "[Error]"; localStorage.hermes_hit = JSON.stringify(hit_obj); if(contains_short_url_tokens(template)) { status("Shortening URLs..."); url_shortener(url_shortening_complete); } else display_template(); } function json_obj(json) { var obj; if(typeof json === "string" && json.trim().length) { try {obj = JSON.parse(json);} catch(e) {console.log("Malformed JSON object. Error message from JSON library: ["+e.message+"]");} } return obj; } function localstorage_obj(key) { var obj = json_obj(localStorage.getItem(key)); if(typeof obj !== "object") localStorage.removeItem(key); return obj; } function zero_pad(a,l,r) { function repeat(g) { var s = ""; while(s.length < (l-a.length)) s += g.charAt(0); return s; } if($.type(a) !== "string") a = (""+a); if(r) return (a+repeat("0")); else return (repeat("0")+a); } function contains_to_tokens(tmp) { return /{to_(?:pay|fair|fast|comm|graphic)(?:_symbols|_color)?}/i.test(tmp); } function contains_short_url_tokens(tmp) { return /{shortened_.+}/i.test(tmp); } function status(s) { $("div#hermes_export_window textarea#hhe_export_output").show().text(s); } Date.prototype.toString = function() { return (""+this.getDate()+" "+this.getMonthString().slice(0,3)+" "+this.getFullYear()+", "+this.getTwoDigitUTCHours()+":"+this.getTwoDigitUTCMinutes()+"."+this.getTwoDigitUTCSeconds()+" UTC"); }; Date.prototype.getMonthString = function() { switch(this.getMonth()) { case 0: return "January"; case 1: return "February"; case 2: return "March"; case 3: return "April"; case 4: return "May"; case 5: return "June"; case 6: return "July"; case 7: return "August"; case 8: return "September"; case 9: return "October"; case 10: return "November"; case 11: return "December"; } }; Date.prototype.getTwoDigitUTCHours = function() { return zero_pad(this.getUTCHours(),2); }; Date.prototype.getTwoDigitUTCMinutes = function() { return zero_pad(this.getUTCMinutes(),2); }; Date.prototype.getTwoDigitUTCSeconds = function() { return zero_pad(this.getUTCSeconds(),2); }; String.prototype.collapse_whitespace = function() { // both regular expressions could go in the same statement, but removing html may leave extraneous space behind return this.replace(/<[^>]*>/g,"").replace(/\s{2,}/g," ").trim(); }; $("head").append( $("<style/>") .attr("type","text/css") .text("#hermes_export_button {height: 16px; font-size: 10px; font-weight: bold; border: 1px solid; margin-left: 5px; padding: 0px 5px; background-color: transparent;} "+ "#hermes_export_window {position: fixed; left: 15vw; top: 10vh; background-color: #a5ccdd; border: 2px solid #5c9ebf; border-radius: 10px;} "+ "#hermes_export_window textarea {width: 400px; height: 250px; margin: 0px auto; display: block;} "+ "#hermes_export_window h1 {margin: 10px 0px; padding: 0px; font-size: 150%; font-weight: bold; text-align: center;} "+ "#hermes_export_window input#hhe_completion_time {width: 40px; text-align: center; margin: 0px 5px;} "+ "#hermes_export_window button#hhe_close {display: block; margin: 10px auto; clear: both;} "+ "#hermes_export_window select#hhe_export_format {margin: 0px 5px;} "+ "#hermes_export_window div.hhe_options div.left {display: inline-block; float: left; text-align: left;} "+ "#hermes_export_window div.hhe_options div.right {display: inline-block; float: right; text-align: right;} "+ "#hermes_export_window button {font: inherit;} "+ ".noscroll {overflow: hidden;} ") ); $("body").append( $("<div/>") .attr("id","hermes_export_window") .append( $("<h1/>") .text("Hermes HIT exporter"), $("<textarea/>") .attr("id","hhe_export_output") .hide(), $("<textarea/>") .attr("id","hhe_edit_template") .hide(), $("<div/>") .attr("class","hhe_options") .append( $("<div/>") .attr("class","left") .append( $("<select/>") .attr("id","hhe_export_format") .append( $("<option/>") .attr("value","vbulletin") .text("vBulletin"), $("<option/>") .attr("value","markdown") .text("Markdown"), $("<option/>") .attr("value","plaintext") .text("Plain text"), $("<option/>") .attr("value","irc") .text("IRC") ) .change(function() { localStorage.hermes_export_format = $(this).val(); if($("#hhe_mode_swap").text() === "Edit") { var obj = localstorage_obj("hermes_hit"), tmp = get_template($(this).val()); if(!obj.hasOwnProperty("to_pay") && contains_to_tokens(tmp)) { status("Gathering Turkopticon data for "+obj.requester_name+"..."); olympian_turkopticon([obj.requester_id],to_complete); } else if(contains_short_url_tokens(tmp)) { status("Shortening URLs..."); url_shortener(url_shortening_complete); } else display_template(); } }), $("<button/>") .attr("id","hhe_mode_swap") .text("Edit") .click(function() { if($(this).text() === "Edit") { $("#hhe_edit_template").show().text(get_template($("#hhe_export_format").val())); $("#hhe_export_output").hide(); $("#hhe_reset_template").show(); $("#hhe_export_format, #hhe_update_time").prop("disabled",true); $(this).text("Done"); } else { localStorage.setItem(("hermes_"+$("#hhe_export_format").val()+"_template"),$("#hhe_edit_template").val()); $("#hhe_export_format, #hhe_update_time").prop("disabled",false); $("#hhe_edit_template, #hhe_reset_template").hide(); $(this).text("Edit"); display_template(); } }), $("<button/>") .attr("id","hhe_reset_template") .text("Reset") .click(function() { if($("#hhe_mode_swap").text() === "Done" && confirm("Are you sure you want to reset the "+$("#hhe_export_format option:selected").text()+" template to default?\n\nThis can't be undone.")) { localStorage.removeItem("hermes_"+$("#hhe_export_format").val()+"_template"); $("#hhe_edit_template").val(get_template($("#hhe_export_format").val())); } }) .hide() ), $("<div/>") .attr("class","right") .append( $("<label/>") .append( document.createTextNode("Time:"), $("<input/>") .attr({ "type":"text", "id":"hhe_completion_time" }) ), $("<button/>") .text("Update") .attr("id","hhe_update_time") .click(function() { var hit = localstorage_obj("hermes_hit"), time = $("#hhe_completion_time").val(), mins = 0, secs = 0; if(time.indexOf(":") > -1) { mins = Math.floor(time.split(":")[0]*1); secs = Math.floor(time.split(":")[1]*1); } else { mins = Math.floor(time*1); secs = (((time*1)-mins)*60); } // in case some smart aleck enters something like 1:75 while(secs > 59) { mins++; secs -= 60; } if((mins+secs) <= 0) { if(hit.hasOwnProperty("my_time")) delete hit.my_time; if(hit.hasOwnProperty("hourly_rate")) delete hit.hourly_rate; hit.pph_block = ""; } else { hit.my_time = (""+mins+":"+zero_pad(secs,2)); hit.hourly_rate = "$"+(((hit.hit_reward.slice(1)*1)/((mins*60)+secs))*3600).toFixed(2); if(hit.hasOwnProperty("pph_block")) delete hit.pph_block; } localStorage.hermes_hit = JSON.stringify(hit); display_template(); }) ) ), $("<button/>") .attr("id","hhe_close") .text("Close") .click(function() { $("#hermes_export_window").hide(); $("body").removeClass("noscroll"); }) ) .hide() ); $("a.capsulelink").after( $("<button/>") .attr("id","hermes_export_button") .text("Export") .click(function() { reset_interface(); var obj = hit_info($(this).closest("table").parent().closest("table")), template = get_template($("#hhe_export_format").val()); $("div#hermes_export_window").show(); $("body").addClass("noscroll"); // so that quick scroll flicks won't scroll the page behind the export window, which is really annoying // only query turkopticon if to tokens are present in the template if(contains_to_tokens(template)) { status("Gathering Turkopticon data for "+obj.requester_name+"..."); olympian_turkopticon([obj.requester_id],to_complete); } else if(contains_short_url_tokens(template)) { status("Shortening URLs..."); url_shortener(url_shortening_complete); } else display_template(); }) ); });