mTurkThemes

This script provides a UI that allows you to make and share themes for mTurk.com

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name            mTurkThemes
// @namespace       http://ericfraze.com
// @version         1.2
// @description     This script provides a UI that allows you to make and share themes for mTurk.com
// @match           https://www.mturk.com/*
// @resource        cssfile https://magnilucent.github.io/mTurk-Themes/css/style.css?version=71234123
// @resource        colpickcssfile https://magnilucent.github.io/mTurk-Themes/css/colpick.css?version=17
// @grant           GM_addStyle
// @grant           GM_getResourceText
// @grant           GM_setClipboard
// @grant           GM_getValue
// @grant           GM_setValue
// @grant           GM_deleteValue
// @copyright       2014+, Eric Fraze
// @require         http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js
// @require         https://greasyfork.org/scripts/1562-colpick-color-picker/code/colpick%20Color%20Picker.js?version=3858
// ==/UserScript==

// Add color picker CSS
GM_addStyle(GM_getResourceText("colpickcssfile"));

// The CSS used to apply the themes
var CSSText;

// Holds all the current color states
var variables;

// Groups the variables by color value
var colorGroups;

// Name of current theme
var currentTheme;

// Hiddden "theme" that holds the unsaved changes of a theme
// TODO: Make sure no one can name a name 'unsavedTheme'.
var unsavedTheme = "unsavedTheme";

// The version of the script on last save
var savedVariableVersion;

// The version of the script on last save
var currentVariableVersion = 5;

// List of all themes installed
var themeNames;


// Get last save script version
savedVariableVersion = GM_getValue('savedVariableVersion', -1);

// Check to see if there is a current theme. If not, this is probably the first run.
if (GM_getValue('mturk-current-theme', -1) === -1){
	// Set to default theme if there is no current theme.
	currentTheme = "Default";
}else{
	// Get the current theme name.
	currentTheme = GM_getValue('mturk-current-theme');
}

// Load theme names
if (GM_getValue('mturk-theme-names', -1) === -1){
	themeNames = ['Default'];
}else{
	themeNames = JSON.parse(GM_getValue("mturk-theme-names"));
}

/*****************************************
* Load current theme
******************************************/
// Set variables to defaults
resetVariables();

// Apply the theme
applyTheme(currentTheme);

// Wait for DOM to load
$(document).ready(function() {
	// Add the text used to open the panel
	$("#user_name_field").after(' | <span id="mturkthemeoptions" class="header_links">Theme Options</span>');

	// Add the mTurkTheme panel
	$("body").append('<div id="mturktheme" class=""></div>');

	// Add the toolbar (import/save/undo buttons, ect)
	addToolbar();

	// Add the wrapper for the color list
	$("#mturktheme").append('<div class="color-list-wrapper"></div>');

	// Make the color picker plugin apply on "DOMNodeInserted"
	bindColorPicker();

	// Refresh the color list
	refreshList();
});

// Toolbar has the buttons and theme select
function addToolbar(){
	$("#mturktheme").append('<div class="toolbar"></div>');
	$("#mturktheme .toolbar").append('<div title="New theme" class="icon add"></div>');
	$("#mturktheme .toolbar").append('<select class="theme-select"></select>');
	$("#mturktheme .toolbar").append('<div title="Import theme" class="icon import"></div>');
	$("#mturktheme .toolbar").append('<div title="Export theme" class="icon export"></div>');
	$("#mturktheme .toolbar").append('<div title="Hide" class="icon close"></div>');
	$("#mturktheme .toolbar").append('<div title="Save theme" class="icon save"></div>');
	$("#mturktheme .toolbar").append('<div title="Revert to last save" class="icon revert"></div>');

	 updateThemeList();
}

// Update the theme list in the toolbar
function updateThemeList() {
	var unsavedFlag = "";

	$("#mturktheme .toolbar .theme-select").empty();

	for (var i in themeNames){
		if ( (themeNames[i] == currentTheme) && (GM_getValue('mturk-theme-data-' + unsavedTheme, -1) !== -1) )
			unsavedFlag = "*";
		else
			unsavedFlag = "";

		$("#mturktheme .toolbar .theme-select").append('<option value="' + themeNames[i] + '">' + unsavedFlag + themeNames[i] + unsavedFlag +'</option>');
	}

	$("#mturktheme .toolbar .theme-select").val(currentTheme);
}

// Binds the color picker to each new color list element
function bindColorPicker() {
	// Dynamically binds the color picker to each new color list item
	$("#mturktheme .color-list-wrapper").bind('DOMNodeInserted', function() {
		// Binds the colorpicker to a list item
		$("#mturktheme .variable-group-wrapper .color").colpick({
			// Sets the color of the color picker to the list item's color
			onBeforeShow:function() {
				var oldColor;

				// Returns an RBG version of the background
				oldColor = $(this).css('background-color');

				// Single out the three colors
				oldColor = oldColor.replace("rgb(", "").replace(")","");

				// Make an array of the colors
				oldColor = oldColor.split(", ");

				// This function requires an object to be passed, so send one over.
				$(this).colpickSetColor( { 'r':oldColor[0], 'g':oldColor[1], 'b':oldColor[2] } );
			},
			// Changes the color in the CSS and applies the changes
			onSubmit:function(hsb,hex,rgb,el) {
				var oldColor, variableName;

				// Hides the color picker
				$(el).colpickHide();

				// Using the browser to make the old and new colors the same text value, then comparing them
				$("#mturkthemeoptions").css("border-color", "#" + hex);
				if ( $(el).css('background-color') != $("#mturkthemeoptions").css("border-color") ) {
					// Check if the list item is a single item or a group item
					if ($(el).parent('.variable-color').length) {
						// Single items have the variable name as their name
						variableName = $(el).attr('name');

						// Need to get the color dynamically
						oldColor = variables[getPropertyIndex(variableName)][1];

						// Change the CSS
						setProperty(variableName, oldColor, "#" + hex);

						// Old code that updated the list item. May use this again when RefreshList is updated.
						//$(this).css('background-color', newColor);
					}else{
						// Group items have the color value as their name
						oldColor = $(el).attr('name');


						// Changes the CSS of the entire group
						setGroupProperty(oldColor, "#" + hex);
						
						// Old code that updated the list item. May use this again when RefreshList is updated.
						//$('#mturktheme .color[style="background-color: ' + color + ';"]').css('background-color', newColor);
						//$(this).parent().children(".header").text(newColor);
					}

						// Adds the CSS to the page
						applyCSS();

						// Refreshes the color list
						refreshList();

						// Saves unsaved changes
						// Seems ironic but they need to be saved so they persist through page loads.
						saveTheme(unsavedTheme);

						// Update the theme list to add the unsaved marker
						updateThemeList();
				}
			}
		});
	});
}

$("#mturktheme .variable-color").live('mouseenter', function () {
	var color = $(this).find(".color");
	// Single items have the variable name as their name
	variableName = $(color).attr('name');

	// Need to get the color dynamically
	oldColor = variables[getPropertyIndex(variableName)][1];

	// Change the CSS
	setProperty(variableName, null, "red");

	// Adds the CSS to the page
	applyCSS();
});

$("#mturktheme .variable-color").live('mouseleave', function () {
	var color = $(this).find(".color");
	// Single items have the variable name as their name
	variableName = $(color).attr('name');

	// Need to get the color dynamically
	oldColor = variables[getPropertyIndex(variableName)][1];

	// Change the CSS
	setProperty(variableName, null, oldColor);

	// Adds the CSS to the page
	applyCSS();
});

// Clicking the text makes mTurkTheme slide
$("#mturkthemeoptions").live('click', function () {
	$("#mturktheme").addClass("active");
});

// Expand group list items
$("#mturktheme .variable-group .expand").live('click', function () {
	$(this).parents(".variable-group-wrapper").toggleClass("active");
});

// Add a theme
$("#mturktheme .toolbar .add").live('click', function () {
	var themeName = prompt("Enter the theme name:");
	addTheme(themeName);
});

// Import a theme
$("#mturktheme .toolbar .import").live('click', function () {
	// Get the stringified variables
	var newTheme = prompt("Paste in the theme!");

	// Make sure the user didn't cancel.
	if (newTheme != null) {
		// Reset variables to defaults
		resetVariables();

		// Load variables from stringified variables
		variables = JSON.parse(newTheme);

		// Load changes in unsaved theme so they can be reverted
		saveTheme(unsavedTheme);

		// Apply the changes
		// applyTheme will detect the unsaved changes and load theme
		applyTheme(currentTheme);

		// Refresh the color list
		refreshList();

		// Update the theme list to add the unsaved marker
		updateThemeList();
	}
});

// Switch themes when they are selected in the dropdown box
$("#mturktheme .toolbar .theme-select").live('change', function () {
	// Delete unsaved changes
	deleteTheme(unsavedTheme);

	// Get the theme name
	currentTheme = $("#mturktheme .toolbar .theme-select :selected").attr('value');

	// Set the current theme
	GM_setValue('mturk-current-theme', currentTheme);

	// Apply the theme
	applyTheme(currentTheme);

	// Refresh color list
	refreshList();

	// Update the theme list to add the unsaved marker
	updateThemeList();
});

// Export a theme
$("#mturktheme .toolbar .export").live('click', function () {
	// Set the stringified variables to the clipboard
	GM_setClipboard(JSON.stringify(variables));
	alert("Theme copied to clipboard. Pastebin and share!");
});

// Revert to last save
$("#mturktheme .toolbar .revert").live('click', function () {
	// Delete unsaved changes
	deleteTheme(unsavedTheme);

	// Apply last save
	applyTheme(currentTheme)

	// Refresh color list
	refreshList();

	// Update the theme list to add the unsaved marker
	updateThemeList();
});

// Save theme
$("#mturktheme .toolbar .save").live('click', function () {
	saveTheme(currentTheme);
	deleteTheme(unsavedTheme);

	// Update the theme list to add the unsaved marker
	updateThemeList();
});

// Close the panel
$("#mturktheme .toolbar .close").live('click', function () {
	$("#mturktheme").removeClass("active");
});

function resetVariables() {
	// All of the color variables
	variables = [
		["hit-capsule-title-color", ""],
		["hit-capsule-title-hover-color", ""],
		["hit-capsule-title-visited-color", ""],
		["hit-capsule-link-right-color", ""],
		["hit-capsule-link-right-hover-color", ""],
		["hit-capsule-link-right-visited-color", ""],
		["hit-capsule-field-title-color", ""],
		["hit-capsule-field-text-color", ""],
		["header-link-color", ""],
		["subtab-text-color", ""],
		["separator-text-color", ""],
		["searchbar-text-color", ""],
		["whatis-link-color", ""],
		["dashboard-and-workerID-text-color", ""],
		["if-you-re-not-text-color", ""],
		["link-default-color", ""],
		["link-default-hover-color", ""],
		["link-default-visited-color", ""],
		["show-earnings-details-text-color", ""],
		["button-background-color", ""],
		["button-border-color", ""],
		["button-text-color", ""],
		["tab-text-color", ""],
		["tab-background-color-inactive", ""],
		["tab-border-color-inactive", ""],
		["tab-background-color-active", ""],
		["tab-border-color-active", ""],
		["page-background-color", ""],
		["tab-text-color-active", ""],
		["page-header-background-color", ""],
		["page-header-border-color", ""],
		["default-text-color", ""],
		["search-go-button-background-color", ""],
		["search-go-button-border-color", ""],
		["search-go-button-text-color", ""],
		["table-body-background-color", ""],
		["table-header-background-color", ""],
		["table-header-text-color", ""],
		["table-body-border-color", ""],
		["table-list-text-color", ""],
		["table-list-header-background-color", ""],
		["table-list-header-text-color", ""],
		["table-list-row-even-background-color", ""],
		["table-list-row-border-color", ""],
		["table-list-row-odd-background-color", ""],
		["table-link-color", ""],
		["sort-button-text-color", ""],
		["hit-border-color", ""],
		["hit-header-unqualified-background-color", ""],
		["hit-body-unqualified-background-color", ""],
		["hit-header-qualified-background-color", ""],
		["hit-body-qualified-background-color", ""],
		["page-footer-background-color", ""],
		["footer-text-color", ""],
		["footer-link-text-color", ""],
		["details-link-text-color", ""],
	];

	// Used to dynamically group the list items
	colorGroups = [];
}

// Returns the style element that holds the theme
function getStyle() {
	// Find the correct style element by looking for my comment
	return $( "style:contains('/* mTurk Theme */')" );
}

// Applies CSSText to the style element
function applyCSS() {
	// Replace the text of the style element
	getStyle().text(CSSText);
}

// Makes the regex string used to find and change properties in the CSS
function findPropertyRegex(variableName, flags) {
	// Fancy thing lets flags be optiional.
	flags = typeof flags !== 'undefined' ? flags : 'g';

	// Ugh. Regex. I need to escape the escapes here. Crazy.
	// Select the CSS property by the comment in the line above it.
	// NOTE: This relies on the property you want to change being the FIRST value
	// "border: 1px blue solid ;" will change "1px", not "blue"
	// "border: blue 1px solid;" will change "blue"
	// Group 1: comment & property
	// Group 2: property you want to change (I hope!)
	// Group 3: values after the property
	// Group 4: the semicolon
	var regstring = "(.*\\/\\*\\s" + variableName + "\\s\\*\\/\\s+.*:\\s+)(#*[A-Za-z0-9]+)(.*)(;.*)";

	// Create the RegEx object
	return new RegExp(regstring, flags);
}

// Edits the CSS to set a property
function replaceProperty(regex, str, value) {
	return str.replace(regex, "$1" + value + "$3$4");
}

// Finds the index of a property in variables
function getPropertyIndex (variableName) {
	for (var i in variables) {
		if (variables[i] != null) {
			if (variableName == variables[i][0]) {
				return i;
			}
		}
	}

	return -1;
}


// Gets a property from the CSS
// TODO: I think this function may be pretty unnessary and is slowing down the script
// The "variables" variable has the values too as long as its been applied
function getProperty(variableName) {
	// Get the CSS property regex
	regex = findPropertyRegex(variableName, '');
	// Get the groups
	groups = regex.exec(CSSText);
	if (groups != null) {
		// Return the value
		return groups[2];
	}else{
		console.log("Couldn't find " + variableName);
		var i = getPropertyIndex(variableName);
		delete variables[i];
		return -1;
	}
}

// Set an individual property to a color
function setProperty(variableName, oldColor, newColor) {
	// Get the CSS property regex
	regex = findPropertyRegex(variableName);

	// Replace the color with the one we want.
	CSSText = replaceProperty(regex, CSSText, newColor);

	// Update the color grouping if needed
	if (oldColor !== null) {
		var index = getPropertyIndex(variableName);

		variables[index][1] = newColor;
		
		if (newColor in colorGroups) {
			colorGroups[newColor].push(index);
		}else{
			colorGroups[newColor] = [];
			colorGroups[newColor][0] = index;
		}
		
		var groupIndex = colorGroups[oldColor].indexOf(index);
		delete colorGroups[oldColor][groupIndex];
	}
}

// Set a group of colors to another color
function setGroupProperty (oldColor, newColor) {
	// Update the color grouping
	for (var i in colorGroups[oldColor]) {
		index = colorGroups[oldColor][i];
		variableName = variables[index][0];
		setProperty(variableName, oldColor, newColor);
	}

	delete colorGroups[oldColor];
}

// Refreshes the color lust
// TODO: Make this only update what is needed instead of closing all color groups.
function refreshList () {
	var savedVariables = getSavedVariables(currentTheme);
	// Remove the old color pickers
	$(".colpick").remove();

	// Empty the list
	$("#mturktheme .color-list-wrapper").empty();

	// Refill the list
	for (var color in colorGroups) {
		var colorGroup = colorGroups[color];
		var appendString = "";
		var appendColorString = "";
		var unsavedChanges = false;
		

		for (var ii in colorGroup) {
			var index = colorGroup[ii];
			var variableName = variables[index][0];
			var unsavedText = "";
			if (savedVariables != -1) {
				if (color != savedVariables[index][1]){
					unsavedChanges = true;
					unsavedText = " *";
					console.log("Unsaved color");
				}else{
					unsavedText = "";
				}
			}
			
			appendColorString += '<div class="variable-color"><div class="content">' + variableName + unsavedText + '</div>'
			appendColorString += '<div name = "' + variableName + '" class="color" style="background-color: ' + color + ';"></div></div>';
		}

		if (unsavedChanges) {
			unsavedText = " *";
		}else{
			unsavedText = "";
		}

		appendString = '<div class = "variable-group-wrapper"><div class="variable-group">';
		appendString += '<div class="content">' + color + unsavedText + '</div><div name = "' + color + '" class="color" style="background-color: ' + color + ';"></div>';
		appendString += '<div class="expand"></div></div>';
		appendString += appendColorString;
		appendString += '</div>';
		$("#mturktheme .color-list-wrapper").append(appendString);
	}
}

// Add a theme
// TODO: Make sure the theme doesn't exist.
// TODO: Set the current theme to the new theme
function addTheme(themeName){
	// Add the theme name to the array
	themeNames.push(themeName);
	// Update the theme list to show the added theme
	updateThemeList();
	// Save the name names in local storage
	GM_setValue('mturk-theme-names', JSON.stringify( themeNames ));
	// Save the theme (uses current variables)
	saveTheme(themeName);
}

function deleteTheme(themeName) {
	GM_deleteValue('mturk-theme-data-' + themeName);
}

function saveTheme(themeName) {
	GM_setValue('savedVariableVersion', currentVariableVersion);
	GM_setValue('mturk-theme-data-' + themeName, JSON.stringify(variables));
}

function loadTheme(themeName, overrideUnsaved) {
	if (overrideUnsaved == null)
		overrideUnsaved = false;
	// Check for an unsaved theme
	if ( ( getSavedVariables(unsavedTheme) !== -1 ) && !overrideUnsaved ) {
		console.log("Loading unsaved theme changes");
		variables = getSavedVariables(unsavedTheme);
	}else{
		// Check for an existing theme
		if (getSavedVariables(themeName) !== -1){
				console.log("Loading saved theme");
				variables = getSavedVariables(themeName);
		}else{
			// Load the default CSS if all else fails.
			console.log("Loading default CSS");
			CSSToVariables();
		}
	}
}

function getSavedVariables(themeName) {
	return JSON.parse(GM_getValue('mturk-theme-data-' + themeName, -1));
}

// Apply a theme
function applyTheme(themeName) {
	resetVariables();

	currentTheme = themeName;

	// Get original CSS text
	CSSText = GM_getResourceText("cssfile");
	if (savedVariableVersion != currentVariableVersion){
		console.log("New version!");

		if (getSavedVariables(themeName) !== -1){
			loadTheme(currentTheme, true);
			VariablesToCSS();
			CSSToVariables();
			saveTheme(themeName);
		}

		if (getSavedVariables(unsavedTheme) !== -1){
			loadTheme(currentTheme);
			VariablesToCSS();
			CSSToVariables();
			saveTheme(unsavedTheme);
		}
	}else{
		// Load theme's variables
		loadTheme(currentTheme);

		// Applies the variables to the CSS
		VariablesToCSS();
	}

	// Add the CSS to the page
	if (getStyle().length){
		applyCSS();
	}else{
		GM_addStyle(CSSText);
	}
}

// Applies the variables to the CSS
function VariablesToCSS() {
	var variableName;
	var color;
	for (var i in variables) {
		if (variables[i]) {
			variableName = variables[i][0];
			
			color = variables[i][1];
			if (color == "") {
				color = getProperty(variableName);
			}

			setProperty(variableName, null, color);
			
			if (color in colorGroups) {
				colorGroups[color].push(i);
			}else{
				colorGroups[color] = [];
				colorGroups[color][0] = i;
			}
		}
	}
}

// Converts a CSS string to a variable list.
function CSSToVariables() {
	resetVariables();
	// Load all the color values.
	var variableName;
	var color;
	for (var i in variables) {
		variableName = variables[i][0];
		color = getProperty(variableName);
		console.log(variableName + " | " + color);
		if (color != -1){
			variables[i][1] = color;
			
			if (color in colorGroups) {
				colorGroups[color].push(i);
			}else{
				colorGroups[color] = [];
				colorGroups[color][0] = i;
			}
		}
	}
}