Greasy Fork

Greasy Fork is available in English.

Hermes HIT exporter

Adds an Export button to MTurk HIT capsules to share HITs on forums, reddit, etc.

当前为 2016-09-10 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Hermes HIT exporter
// @namespace    mobiusevalon.tibbius.com
// @version      2.7
// @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/
// @include      /^https{0,1}:\/\/\w{0,}\.?mturk\.com\/mturk/
// @exclude      /&hit_scraper$/
// @grant        none
// ==/UserScript==

if(window.olympus === undefined) window.olympus = {};

olympus.hermes = {
	__name:"hermes",
	// properties
	_format:"",
	_template:"",
	tokens: {},
	// methods
	__init:function() {
		console.log("hermes init");
		olympus.style.add(
			".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;} "
		);

		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 olympus.utilities.pad_string(this.getUTCHours(),2);
		};

		Date.prototype.getTwoDigitUTCMinutes = function() {
			return olympus.utilities.pad_string(this.getUTCMinutes(),2);
		};

		Date.prototype.getTwoDigitUTCSeconds = function() {
			return olympus.utilities.pad_string(this.getUTCSeconds(),2);
		};

		$("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();
											olympus.hermes.switch_format($(this).val());
										}),
									$("<button/>")
										.attr("id","hhe_mode_swap")
										.text("Edit")
										.click(function() {
											if($(this).text() === "Edit") {
												$("#hhe_edit_template").show().text(olympus.hermes.get_raw_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");
												olympus.hermes.template_edited();
											}
										}),
									$("<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(olympus.hermes.default_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() {
											olympus.hermes.compute_pph($("#hhe_completion_time").val());
										})
								)
						),
					$("<button/>")
						.attr("id","hhe_close")
						.text("Close")
						.click(function() {
							olympus.hermes.hide();
						})
				)
				.hide()
		);

		this.add_buttons();
	},
	__reset_tokens:function() {
		this.tokens = {
			hermes_version:"2.6",
			author_url:"http://greasyfork.icu/en/users/9665-mobius-evalon",
			hermes_url:"http://greasyfork.icu/en/scripts/21175-hermes-hit-exporter",
			shortened_author_url:"http://goo.gl/jqpg0h",
			shortened_hermes_url:"http://goo.gl/bNdTBj"
		};
	},
	__reset_interface:function() {
		$("#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");
	},
	_async_turkopticon:function(result) {
		// used as a callback in the olympian library's turkopticon function
		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));
		}

		if(result.hasOwnProperty(this.tokens.requester_id)) {
			if($.type(result[this.tokens.requester_id]) === "object") {
				$.each(result[this.tokens.requester_id].attrs,function(k,v) {
					hermes_template.tokens["to_"+k] = v;
					hermes_template.tokens["to_"+k+"_color"] = color_prop(v);
					hermes_template.tokens["to_"+k+"_symbols"] = symbol_prop(v);
				});
			}
			else this.tokens.to_wrapper = "[None]";
		}
		else this.tokens.to_wrapper = "[Error]";

		this._process();
	},
	_async_url_shorten:function(result) {
		// used as a callback for url_shortener()
		if(Object.keys(result).length) {
			$.each(result,function(key,val) {
				hermes_template.tokens[key] = val;
			});
			if(this.tokens.hasOwnProperty("shortened_url_wrapper")) delete this.tokens.shortened_url_wrapper;
		}
		else this.tokens.shortened_url_wrapper = "[Error]";

		this._process();
	},
	_contains_short_url_tokens:function() {
		return /\{shortened_[^}]+\}/i.test(this._template);
	},
	_contains_to_tokens:function () {
		return /{to_(?:pay|fair|fast|comm|graphic)(?:_symbols|_color)?}/i.test(this._template);
	},
	_expand_tokens:function() {
		var tmp = this._template;
		$.each(this.tokens,function(key,val) {
			if(key.slice(-8) === "_wrapper") tmp = tmp.replace(new RegExp(("<"+key+":[^>]+>"),"gi"),val);
			else tmp = tmp.replace(new RegExp(("\\{"+key+"\\}"),"gi"),val);
		});
		tmp = tmp.replace(/{date_time}/ig,new Date().toString()).replace(/<[^>]+_wrapper:([^>]+)>/gi,"$1");
		return tmp;
	},
	_process:function() {
		// this function is called repeatedly to determine the status of the template display
		if(!this.tokens.hasOwnProperty("to_pay") && !this.tokens.hasOwnProperty("to_wrapper") && this._contains_to_tokens()) {
			this.output("Gathering Turkopticon data for "+this.tokens.requester_name+"...");
			olympus.utilities.turkopticon([this.tokens.requester_id],this._async_turkopticon,this);
		}
		else if(!this.tokens.hasOwnProperty("shortened_url_wrapper") && this._contains_short_url_tokens() && this.tokens_to_shorten().length) {
			this.output("Shortening URLs...");
			this._url_shortener(this._async_url_shorten,this);
		}
		else this.display();
	},
	_url_shortener:function(callback,scope) {
		var mirrors = [
				"https://ns4t.net/yourls-api.php?action=bulkshortener&title=MTurk&signature=39f6cf4959"
			],
			params = "",
			tokens = [],
			result = {};

		function exit() {
			if($.type(callback) === "function") callback.call(scope,result);
		}

		tokens = olympus.hermes.tokens_to_shorten();
		if(tokens.length) {
			params = (
				"&urls[]="+
				tokens
					.map(function(val,idx,arr) {
						return olympus.hermes.tokens[val];
					})
					.join("&urls[]=")
			);
			olympus.utilities.ajax_get(mirrors,params,function(response) {
				if(response.length) {
					response = response.split(";");
					$.each(tokens,function(key,val) {
						result["shortened_"+val] = response[key];
					});
				}
				else console.log("Hermes HIT exporter: url shortening service appeared to be queried successfully but returned no data");
				exit();
			});
		}
		else exit();
	},
	add_buttons:function() {
		$("a.capsulelink").each(function() {
			if(!$(this).siblings(".hermes_export_button").length) $(this).append(
				$("<button/>")
					.attr("class","hermes_export_button")
					.text("Export")
					.click(function() {
						olympus.hermes.__reset_interface();

						$("div#hermes_export_window").show();
						$("body").addClass("noscroll");

						olympus.hermes.begin({
							hit:$(this).closest("table").parent().closest("table"),
							format:$("#hhe_export_format").val()
						});
					})
				);
		});
	},
	begin:function(obj) {
		if($.type(obj) === "object" && obj.hasOwnProperty("hit") && obj.hasOwnProperty("format")) {
			this.__reset_tokens();
			this.mturk_info(obj.hit);
			this.switch_format(obj.format);
		}
		else throw new Error("Hermes HIT exporter: template was initialized with improper arguments");
	},
	compute_pph:function(time) {
		var mins = 0,
			secs = 0;

		if(time.indexOf(":") > -1) {
			mins = Math.floor(time.split(":")[0]*1);
			secs = Math.floor(time.split(":")[1]*1);

			// in case some smart aleck enters something like 1:75
			while(secs > 59) {
				mins++;
				secs -= 60;
			}
		}
		else {
			mins = Math.floor(time*1);
			secs = (((time*1)-mins)*60);
		}

		if((mins+secs) <= 0) {
			if(this.tokens.hasOwnProperty("my_time")) delete this.tokens.my_time;
			if(this.tokens.hasOwnProperty("hourly_rate")) delete this.tokens.hourly_rate;
			this.tokens.pph_wrapper = "";
		}
		else {
			this.tokens.my_time = (""+mins+":"+olympus.utilities.pad_string(secs,2));
			this.tokens.hourly_rate = "$"+(((this.tokens.hit_reward.slice(1)*1)/((mins*60)+secs))*3600).toFixed(2);
			if(this.tokens.hasOwnProperty("pph_wrapper")) delete this.tokens.pph_wrapper;
		}

		this._process();
	},
	default_template:function(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_wrapper:/{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_wrapper:[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_wrapper:/{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_wrapper:\\[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_wrapper:/{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_wrapper:[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_wrapper:/{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}";
	},
	display:function() {
		$("#hhe_update_time, #hhe_export_format").prop("disabled",false);
		this.output(this._expand_tokens(this._template));
	},
	get_raw_template:function(string) {
		return (localStorage.getItem("hermes_"+string+"_template") || this.default_template(string));
	},
	hide:function() {
		$("#hermes_export_window").hide();
		$("body").removeClass("noscroll");
	},
	mturk_info:function($element) {
		function scrape_from_tooltip(tt) {
			return $("a[id*='"+tt+".tooltip']",$element).parent().next().text().collapseWhitespace();
		}
		// basic HIT info that can be scraped off the page
		this.tokens.hit_name = $("a.capsulelink[href='#']",$element).first().text().collapseWhitespace();
		this.tokens.hit_id = $("a[href*='groupId']",$element).first().attr("href").match(/groupId=([A-Z0-9]{30})(?:&|$)/)[1];
		this.tokens.hit_desc = scrape_from_tooltip("description");
		this.tokens.hit_time = scrape_from_tooltip("duration_to_complete");
		this.tokens.hits_available = scrape_from_tooltip("number_of_hits");
		this.tokens.hit_reward = scrape_from_tooltip("reward");
		this.tokens.requester_name = $("a[href*='selectedSearchType=hitgroups']",$element).first().text().collapseWhitespace();
		this.tokens.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
		this.tokens.preview_link = ("https://www.mturk.com/mturk/preview?groupId="+this.tokens.hit_id);
		this.tokens.panda_link = ("https://www.mturk.com/mturk/previewandaccept?groupId="+this.tokens.hit_id);
		this.tokens.requester_hits = ("https://www.mturk.com/mturk/searchbar?selectedSearchType=hitgroups&requesterId="+this.tokens.requester_id);
		this.tokens.contact_requester = ("https://www.mturk.com/mturk/contact?requesterId="+this.tokens.requester_id+"&requesterName="+this.tokens.requester_name);
		this.tokens.to_reviews = ("https://turkopticon.ucsd.edu/"+this.tokens.requester_id);

		// parse qualifications
		var $qual_anchor = $("a[id*='qualificationsRequired.tooltip']",$element);
		if($qual_anchor.parent().next().text().trim() === "None") this.tokens.quals = "None";
		else {
			var quals = [];
			$("tr:not(:first-of-type) td:first-of-type",$qual_anchor.closest("table")).each(function() {quals.push($(this).text().collapseWhitespace());});
			this.tokens.quals = quals.join("; ");
		}

		// by default the user has provided no completion time estimate, so this wrapper needs to not be displayed until the user provides that info
		this.tokens.pph_wrapper = "";
	},
	output:function(string) {
		$("div#hermes_export_window textarea#hhe_export_output").show().text(string);
	},
	switch_format:function(string) {
		string = (string || $("#hhe_export_format").val());
		this._format = string;
		this._template = this.get_raw_template(string);
		this._process();
	},
	template_edited:function() {
		this._template = this.get_raw_template(this._format);
		this._process();
	},
	tokens_to_shorten:function() {
		var arr = [];
		$.each(this._template.match(/(\{shortened_[^}]+\})/gi),function(key,val) {
			if(hermes_template.tokens.hasOwnProperty(val.slice(11,-1)) && !hermes_template.tokens.hasOwnProperty(val.slice(1,-1)) && arr.indexOf(val.slice(11,-1)) === -1)
				arr.push(val.slice(11,-1));
		});
		return arr;
	}
};