Clanheart Alpha Creator Extended

This script changes the drop downs on CH's alpha creator to colours. No more wondering what colour Galaxy or Brick is!

// ==UserScript==
// @name         Clanheart Alpha Creator Extended
// @namespace    fortytwo
// @version      1.34
// @description  This script changes the drop downs on CH's alpha creator to colours. No more wondering what colour Galaxy or Brick is!
// @author       fortytwo
// @match        http://clan.arvixecloud.com/~jakosse/laravel/public/
// @require		 https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
// @grant		 none
// ==/UserScript==

/***
	NOTICE: YOU ARE AGREEING THAT ANY USE OF THE FOLLOWING SCRIPT IS AT
	YOUR OWN RISK. I DO NOT MAKE ANY GUARANTEES THE SCRIPT WILL WORK, NOR 
	WILL I HOLD MYSELF ACCOUNTABLE FOR DAMAGE TO YOUR DEVICE.

	IF THE SCRIPT ISN'T WORKING FOR YOU, FEEL FREE TO SEND ME A MESSAGE: http://games-fortytwo.tumblr.com/
***/
$(document).ready(function(){
//mapping colour names to the appropriate rgb
//http://clanheart.tumblr.com/post/125971903821/eimitanrising-a-polished-collection-of-the
var colours =[
	["None", '255, 255, 255'],
	["Zinnia Red", '232, 5, 5'],
	["Hot Pink", '255, 15, 127'],
	["Neon Purple", '255, 15, 247'],
	["Royal Purple", '179, 15, 225'],
	["Galaxy", '119, 15, 225'],
	["Cobalt", '51, 15, 255'],
	["Azure", '15, 107, 255'],
	["Cerulean", '15, 147, 255'],
	["Aqua", '15, 195, 255'],
	["Teal", '16, 255, 231'],
	["Leaf", '16, 255, 159'],
	["Harlequin", '16, 255, 75'],
	["Chartreuse", '135, 255, 15'],
	["Pear", '195, 255, 15'],
	["Sunlight", '243, 255, 15'],
	["Gold", '255, 199, 15'],
	["Monarch", '255, 139, 15'],
	["Pumpkin", '255, 87, 15'],
	["Coral", '255, 79, 79'],
	["Fuschia", '255, 79, 138'],
	["Magenta", '255, 79, 240'],
	["Electric Purple", '205, 79, 255'],
	["Violet", '144, 79, 255'],
	["Sapphire Blue", '79, 100, 255'],
	["Cornflower", '79, 146, 255'],
	["Sky Blue", '79, 176, 255'],
	["Electric Blue", '79, 232, 255'],
	["Tropical Sea", '79, 255, 214'],
	["Eucalpytis", '79, 255, 141'],
	["Emerald", '88, 255, 79'],
	["Clover", '146, 255, 79'],
	["Key Lime", '188, 255, 79'],
	["Lemon", '237, 255, 79'],
	["Jasmine", '255, 211, 79'],
	["Amber", '255, 167, 79'],
	["Carrot", '255, 129, 79'],
	["Salmon", '255, 153, 153'],
	["Bubblegum", '255, 153, 182'],
	["Carnation", '255, 153, 212'],
	["Heliotrope", '245, 153, 255'],
	["Crocus", '204, 153, 255'],
	["Wisteria", '163, 153, 255'],
	["Powder Blue", '153, 184, 255'],
	["Baby Blue", '153, 219, 255'],
	["Cyan", '153, 255, 250'],
	["Aquamarine", '153, 255, 204'],
	["Mint", '153, 255, 167'],
	["Celery", '182, 255, 153'],
	["Peridot", '228, 255, 153'],
	["Canary", '255, 230, 153'],
	["Peach", '255, 211, 153'],
	["Apricot", '255, 184, 153'],
	["Rose Quartz", '255, 212, 212'],
	["Blush", '255, 212, 229'],
	["Petal Pink", '255, 212, 241'],
	["Lilac", '241, 212, 255'],
	["Lavender", '222, 212, 255'],
	["Periwinkle", '212, 217, 255'],
	["Frost", '212, 233, 255'],
	["Ice", '212, 250, 255'],
	["Spearmint", '212, 255, 244'],
	["Cucumber", '212, 255, 225'],
	["Honeydew", '228, 255, 212'],
	["Ivory", '246, 255, 212'],
	["Cream", '255, 238, 212'],
	["Eggshell", '255, 225, 212'],
	["White", '255, 255, 255'],
	["Ash", '235, 235, 235'],
	["Oyster", '212, 212, 212'],
	["Silver", '181, 181, 181'],
	["Gray", '148, 148, 148'],
	["Overcast", '122, 122, 122'],
	["Smoke", '94, 94, 94'],
	["Coal", '66, 66, 66'],
	["Caviar", '38, 38, 38'],
	["Black", '0, 0, 0'],
	["Garnet", '21, 2, 2'],
	["Burgundy", '51, 2, 41'],
	["Blackberry", '35, 2, 51'],
	["Tanzanite", '9, 2, 51'],
	["Prussian Blue", '2, 31, 51'],
	["Amazon", '2, 51, 50'],
	["Hunter Green", '2, 51, 27'],
	["Moss", '23, 38, 3'],
	["Olive", '43, 43, 2'],
	["Chocolate", '43, 28, 2'],
	["Seal", '43, 12, 2'],
	["Cinnamon", '84, 38, 38'],
	["Brick", '84, 38, 64'],
	["Maroon", '80, 38, 84'],
	["Plum", '56, 38, 84'],
	["Vineyard", '38, 42, 84'],
	["Indigo", '38, 59, 84'],
	["Deep Sea", '38, 80, 84'],
	["Denim", '40, 86, 72'], //CLOSEST MATCH
	["Hunter", '38, 84, 61'], //RENAMED. OFFICIAL 
	["Fern", '56, 84, 38'],
	["Avocado", '83, 84, 38'],
	["Sage", '84, 68, 38'],
	["Sepia", '84, 56, 33'], //CLOSEST MATCH
	["Sienna", '84, 52, 38'], //OFFICIAL
	["Dusty Rose", '140, 91, 116'],
	["Mauve", '136, 91, 140'],
	["Eggplant", '107, 91, 140'],
	["Ube", '91, 97, 140'],
	["Lazuli", '91, 121, 140'],
	["Slate", '91, 130, 135'],//CLOSEST MATCH
	["Juniper", '91, 140, 135'], //OFFICIAL
	["Jade", '91, 140, 106'], //OFFICIAL
	["Asparagus", '104, 140, 91'],
	["Clay", '140, 135, 91'],
	["Beaver", '140, 123, 91'],
	["Umber", '140, 108, 91'],
	["Taupe", '140, 98, 91'],
	["Redwood", '164, 90, 82'], //OFFICIAL
	["Blood", '196, 79, 79'],
	["Mulberry", '196, 79, 145'],
	["Orchid", '173, 79, 196'],
	["Amethyst", '126, 79, 196'],
	["Blueberry", '79, 86, 196'],
	["Hydrangea", '79, 149, 196'],
	["Turqouise", '79, 196, 183'],
	["Grass", '79, 196, 116'],
	["Shamrock", '116, 196, 79'],
	["Citron", '179, 196, 79'],
	["Goldenrod", '196, 183, 79'],
	["Fawn", '196, 147, 79'],
	["Rust", '196, 108, 79'],
	["Poinsetta", '196, 79, 79'] //OFFICIAL
];

/*** STYLING ***/
$('head').append(
"<style>"+
".ace-overlay{position:absolute;top:0px;left:0px;}"+
".custom-dropdown-text{width:100% !important}"+
"</style>");

/***
	CUSTOM DROP DOWN SCRIPT BY FORTYTWO
***/
function dropDown(appendTo, name){
	this.element = $(appendTo);
	this.name = name;
	this.text = this.hidden = this.select = null;
	this.events ={ onchange: [], onmenuclose: [], onmenuopen: [] };

	this._init();
}

dropDown.prototype ={
	/*** INTERNAL FUNCTIONS ***/
	//css stuff and the block
	_firstRun: function(){
		$("head").append($(
		"<style>"+
		".custom-dropdown-text{border-radius:4px;padding:6px 12px;width:100%;border:1px solid #ccc;}"+
		".custom-dropdown-text-active{"+
		"	box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6)}"+
		".custom-dropdown-select{display:none;}"+
		".custom-dropdown-select-active{height:300px; width:300px; overflow-y:scroll;position:absolute;"+
		"	background:#fff;z-index:10510; border: 1px solid #66afe9;display:block; }"+
		".custom-dropdown-option{whitespace:pre;min-height:1.2em;padding:3px 12px;}"+
		".custom-dropdown-option:hover{opacity:0.5}"+
		"#custom-dropdown-overlay{top:0;right:0;left:0;bottom:0;opacity:10;"+
		"	position:fixed;z-index:10500; display:none;}"+
		".custom-dropdown-overlay-active{display:block !important;}"+
		".custom-noselect{-webkit-touch-callout: none; -webkit-user-select: none;"+
		"	-khtml-user-select: none; -moz-user-select: none;"+
		"	user-select: none;}"+
		"</style>"));

		this.block = $("<div />", {id: "custom-dropdown-overlay"});
		this.block.addClass('custom-dropdown-overlay');
		$("body").append(this.block);
	},

	_init: function(){
		var _this = this;
		//create elements and styles
		this.text = $("<input />", {
			type: 'text',
			'class': 'custom-dropdown-text',
			//prevents editing of the content
			readonly: true
		});

		this.hidden = $("<input />", {
			type: 'hidden',
			'class': 'custom-dropdown-hidden',
			name: this.name,
			id: this.name
		});
		
		this.select = $("<div />", { 'class': 'custom-dropdown-select custom-noselect'});
		//disable right click to act like select
		this.select.on('contextmenu', function(){ return false; });
	
		//we use this fixed full-page overlay behind the drop down
		//in order for us to be able to click out of it efficiently
			
		//check we don't have one already
		if(document.getElementById('custom-dropdown-overlay') === null)
			this._firstRun();
		//if we do, use it
		else
			this.block = $('#custom-dropdown-overlay');

		//now we need to install some normal behaviour
		this
			.on('MenuClose', function(){
				_this.select.removeClass("custom-dropdown-select-active");
				_this.block.removeClass("custom-dropdown-overlay-active");
				_this.text.removeClass("custom-dropdown-text-active");
				$('body').removeClass('custom-noselect');
			})
			.on('MenuOpen', function(){
				_this.select.addClass("custom-dropdown-select-active");
				_this.block.addClass("custom-dropdown-overlay-active");
				_this.text.addClass("custom-dropdown-text-active");
				
				//makes it so nothing can be selected just like <select>
				$('body').addClass('custom-noselect');
				
				//ensure our select fits the viewport
				//to do this, we'll utilize positions relative the viewport.
				//this just makes it easier for us.
				var bodyRect	= document.body.getBoundingClientRect(),
					textRect	= _this.text[0].getBoundingClientRect(),
					selectRect	= _this.select[0].getBoundingClientRect(),
					//height of the size we can see, essentially
					viewport	= document.documentElement.clientHeight,
					//distance from top of VP to the element
					textTop		= textRect.top,
					textHeight	= textRect.height,
					menuHeight	= selectRect.height;

				//if there's not enough space below to display our complete, display
				//it above the text box
				if((textTop + textHeight + menuHeight) > viewport){
					_this.select.css('top', -(menuHeight-1));
				}
				else{
					//back to normal
					_this.select.css('top', textHeight-1);
				}
				
				//additionally, also match the dropdown's width to the textbox
				_this.select.css('width', textRect.width);
			})
			.on('Change', function(data){
				//we need to set our hidden's value
				_this.hidden.val(data.value);
				_this.text.val(data.option.html());

				//trigger any onchange handlers so it works much like a select
				_this.hidden.change();
			});

		this.text.on('click', function(){
			_this.trigger("onMenuOpen");
		});

		this.block.on('click', function(){
			_this.trigger("onMenuClose");
		});
		
		this.text.on('resize', function(){
			//_this._adjustDropDownArrow($(this));
		});
		
		//add these elements
		this.element.append(this.text).append(this.hidden).append(this.select);
		//this._adjustDropDownArrow(this.text);

		return this;
	},

	/* for determining where to position the drop down arrow */
	_adjustDropDownArrow: function(textbox){
		var textRect = textbox[0].getBoundingClientRect();
		var position = (textRect.width-13)+"px";
	//	textbox.css('background', "#fff url('"+GM_getResourceURL('arrow')+"') no-repeat "+position+" center");
	},

	/*** PUBLIC FUNCTIONS ***/
	/* triggers an event to occur */
	trigger: function(event, data){
		var eventsToCall = this.events[event.toLowerCase()];
		
		for(i = 0; i < eventsToCall.length; ++i){
			eventsToCall[i](data);
		}
		return this;
	},

	/* allows for event-like functionality */
	on: function(event, callback){
		//use lowercase to ensure people don't use the wrong case
		event = event.toLowerCase();
		this.events['on'+event].push(callback);
		return this;
	},

	addOption: function(data){
		var _this = this;
		
		var option = $("<div />")
			.html(data.text)
			.addClass('custom-dropdown-option');

		if(data.unselectable !== undefined){
			option.attr('data-unselectable', (data.unselectable ? true : false));
		}
		//if it's selectable, then it also has a value.
		else{
			option.attr('data-value', data.value);
		}

		//for when option is selected
		option.on('click', function(){
			if(!$(this).attr('data-unselectable')){
				//call menu close event
				_this.trigger('onMenuClose');
				//trigger the change handler
				_this.trigger('onChange', {
					value: $(this).attr('data-value'),
					option: $(this)
				});
			}	
		});

		this.select.append(option);

		//return it so we can use it outside
		return option;
	}
};

//based on http://stackoverflow.com/questions/8022885/rgb-to-hsv-color-in-javascript
function getV(rgbString){
	var components = rgbString.split(", ");
	var r = components[0] / 255,
        g = components[1] / 255,
        b = components[2] / 255;

     return Math.round((Math.max(r, g, b)) * 100);
}

function customDropDowns(){
	//our selects that we wanna change
	var selects	= $('#bases, #eyes, #dyn, #marking_color'),
		//get all the columns we're gonna change
		columns	= selects.parent();

	for(i = 0; i < columns.length; i++){
		var column		= $(columns[i]),
			//fetch the id so we can replicate the form-like action
			select		= selects.eq(i),
			//make our custom dropper
			dropdown	= new dropDown(column, select.attr('id'));

		//remove previous content
		select.remove();

		//because we want to do some fancy css stuff to the bg
		dropdown.on('Change', function(data){
			var option = data.option;

			//hack because dropdown.text.css({ ... }) doesn't work properly??
			var textbox = option.parent().prev().prev();
			textbox.css({
				"background-color": option.css("background-color"),
				"color": option.css('color')
			});
		});

		//go through and add the colours to our custom
		for(id = 0; id < colours.length; ++id){
			var
				name = colours[id][0],
				rgb = colours[id][1],
				v = getV(rgb);

			titleize(dropdown, name);

			//add our option
			dropdown
				.addOption({
					value: id,
					text: name
				})
				.css({
					'background-color': "rgb("+rgb+")",
					'color': (v < 50 ? "#fff" : "#000")
				});
		}
	}
}

//this code adds headers to the select boxes, so we know what it's classed as
function titleize(dropdown, name){
	switch(name){
		case "Zinnia Red": var text = "BRIGHT COLOURS"; break;
		case "Coral": var text = "DARK PASTEL"; break;
		case "Salmon": var text = "PASTEL COLOURS"; break;
		case "Rose Quartz": var text = "PALE COLOURS"; break;
		case "White": var text = "GREYSCALE COLOURS"; break;
		case "Garnet": var text = "DARK COLOURS"; break;
		case "Cinnamon": var text = "MIDDLE COLOURS"; break;
		case "Dusty Rose": var text = "UNSATURATED COLOURS"; break;
		case "Blood": var text = "RICH COLOURS"; break;
	}

	//if we have a title, put it in
	if(text){
		dropdown.addOption({
			text: text,
			unselectable: true
		});
	}
}

//'nother mac fix...
function overrideBehaviour(){
	//change some styling
	$('#petgencont').css('position', 'relative');
	//hack by removing the default behaviour
	$('#continue input[type="button"]')
		.attr('onclick', "")
		//add our own overriding functionality
		.on('click', function(){
			
			//first, get the pet image
			$('#petgencont').fadeOut(200, function() {
				$.ajax({
					url: 'index.php/create/genpet',
					type: 'GET',
					data: {
						'baseid' :  $('#bases').val(),
						'eyeid' : $('#eyes').val(),
						'speciesid' : $('#species').val(),
						'shading_color' : $('#shading_color').val(),
						'highlight_color' : $('#highlight_color').val(),
						'lineart_color' : $('#lineart_color').val(),
						'markingid' : $('#markings').val(),
						'marking_color' : $('#marking_color').val(),
						'dyn_color' : $('#dyn').val(),
						//CH doesn't understand true/false
						'apply_dyn' : ($('#apply_dyn').prop("checked") ? 1 : 0),
						//CH doesn't use this, but we might as well keep it in 
						//for future-proofing
						'gender': $('input[name=gender]:checked').val()
					},
					success: function(msg) {
						//replace the content
						$('#petgencont').html(msg.replace("index.php/", "")).delay(500);
						$('#petgencont').fadeIn(200);

						//now add any overlays. line by line
						if($('#ace-overlays').val().length > 0){
						var overlays = $('#ace-overlays').val().split("\n");
						for(i = 0; i < overlays.length; ++i){
							var overlayURL = overlays[i];
							$("<img src='"+overlayURL+"' class='ace-overlay' />").appendTo($('#petgencont'));
						}
						}
					}
				});
			});
			

		});
}

function addOverlaysBox(){
	var row = $("<div class='align-center'>Add things (such as accessories or dyes) here. It must be an image URL. Separate each URL by a new line. Make sure each one is the same dimensions as the pet image.<br /></div>").insertBefore($('#continue'));
	var textarea = $("<textarea id='ace-overlays' cols='50' rows='5'></textarea>").appendTo(row);
}


//run app
customDropDowns();
addOverlaysBox();
overrideBehaviour();
});