mTurkThemes

This script inserts custom CSS for mturk.com

2014-10-10 या दिनांकाला. सर्वात नवीन आवृत्ती पाहा.

// ==UserScript==
// @name       mTurkThemes
// @namespace  http://ericfraze.com
// @version    0.9
// @description  This script inserts custom CSS for mturk.com
// @match      https://www.mturk.com/*
// @resource cssfile https://dl.dropboxusercontent.com/u/8371875/mTurk%20Theme/Output/style.css?version=6456789123464332623456789123456789
// @resource colpickcssfile https://dl.dropboxusercontent.com/u/8371875/mTurk%20Theme/Output/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";

// List of all themes installed
var themeNames;

// 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", ""],
        ["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", ""],
    ];

    // 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 (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);

    // Return the value
    return groups[2];
}

// 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('mturk-theme-data-' + themeName, JSON.stringify(variables));
}

function loadTheme(themeName) {
    // Check for an unsaved theme
    if (getSavedVariables(unsavedTheme) !== -1){
        console.log("Loading unsaved theme changes");
        variables = getSavedVariables(unsavedTheme);
    }else{
        // Checck 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");

    // 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) {
        variableName = variables[i][0];
        
        color = variables[i][1];
        
        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);
        variables[i][1] = color;
        
        if (color in colorGroups) {
            colorGroups[color].push(i);
        }else{
            colorGroups[color] = [];
            colorGroups[color][0] = i;
        }
    }
}