/************************************************/
/* IPB3 Javascript								*/
/* -------------------------------------------- */
/* ips.skin_gen.js - VSE						*/
/* (c) IPS, Inc 2011							*/
/* -------------------------------------------- */
/* Author: Rikki Tissier, Matt Mecham			*/
/************************************************/

var defaultTemplates = {
	css_wrapper: new Template("#{selector} {\n#{contents}\n}"),
	bg_color: new Template("\t\nbackground: #{value} !important;\tbackground-color: #{value} !important;\n"),
	bgcolonly_color: new Template("\t\n\tbackground-color: #{value} !important;\n"),
	font_color: new Template("\tcolor: #{value};\n"),
	border_color: new Template("\tborder-color: #{value} !important;\n"),
	box_shadow_color: new Template("\tbox-shadow: #{extra} #{value} !important;\n\t-moz-box-shadow: #{extra} #{value} !important;\n\n\t-webkit-box-shadow: #{extra} #{value} !important;")
}

var skingen = {
	
	currentClass: null,
	showingLocations: false,
	classList: [],
	classStored: {},
	updatedImages: $H(),
	classBackup: null,
	pickers: {},
	
	//------------------------------------------
	// Initialize
	//------------------------------------------
	boot: function(){
		if( !this.checkDB() ){
			alert("Необходимо чтобы ваш браузер поддерживал localStorage");
			return;
		}
		
		/* Fix image */
		$('logo').down('img').writeAttribute('src', ipb.vars['img_url'] + "/logo_transparent.png" );
		
		this.dropper._parent = this.xray._parent = this.db._parent = this.cssManipulator._parent = this.imageEditor._parent = this.hueChanger._parent = this;
		
		this.loadStoredValues();
		
		if( this.db.load("storedImages") ){
			this.loadStoredImages();
		}
		
		this.buildEditor();
		this.buildMasterClassList();
		
		this.cssManipulator.createInjectedStylesheet();
	},
	
	//-----------------------------------------
	// Get hex from selector value
	//-----------------------------------------
	getHexFromSelector: function( val, type )
	{
		if ( type == 'background-color' )
		{
			type = 'background';
		}
		
		Debug.write( val + '-' + type );
		
		if ( ! Object.isUndefined( val ) )
		{
			if ( typeof( val ) == 'string' )
			{
				val = this.skinClasses.get( val )[3][ type ];
				
				if ( Object.isUndefined( val ) || Object.isUndefined( val[0] ) )
				{
					return false;
				}
				else
				{
					Debug.write( "Got " + val[0] + ' - ' + val[1] );
				}
			}
			
			if ( ! Object.isUndefined( val[0] ) )
			{
				return this._cleanHex( val[0] );
			}
		}
		
		return false;
	},
	
	//------------------------------------------
	// Builds the panel
	//------------------------------------------
	buildEditor: function(){
		if( $('skingen_editor') ){ return; }
		pushLater = new Array();
		
		this.body = $$('body')[0]; // quick reference to body
		// Build list
		var list = [];
		if( this.skinClasses.size() && this.skinGroups.size() ){
			this.skinGroups.each( function( group ){
				var items = [];
				if( group.value[1].length ){
					$H(group.value[1]).each( function(item){
						item = item[1];
						if( this.skinClasses.get( item ) )
						{
							var bg = this.getHexFromSelector( item, 'background' );
							var fc = this.getHexFromSelector( item, 'color' );
							
							items.push( this.templates.sectionItem.evaluate( { id: item, name: this.skinClasses.get( item )[1], style: 'background: ' + ( bg ? bg : fc ) } ) );
						}
					}.bind(this));
					
					if ( group.value[0].match( /^app: /i ) )
					{
						pushLater.push( this.templates.sectionGroup.evaluate( { id: group.key, title: group.value[0], content: items.join('') } ) );
					}
					else
					{
						list.push( this.templates.sectionGroup.evaluate( { id: group.key, title: group.value[0], content: items.join('') } ) );
					}
				}
			}.bind(this));
		}
		
		// Build HTML
		$(this.body).insert({bottom: this.templates.editor.evaluate( { sections: list.join('') + pushLater.join('') } )});
		
		// Existing coords?
		if( this.db.load('editorPos') ){
			var newPos     = this.db.load('editorPos');
			var screendims = document.viewport.getDimensions();
			var wrapSize   = $('skingen_editor').getDimensions();
			
			if ( newPos.left > ( screendims.width - wrapSize.width ) )
			{
				newPos.left = ( ( screendims.width - wrapSize.width ) / 2 );
			}
			
			$('skingen_editor').setStyle('left: ' + newPos.left + 'px; top: ' + newPos.top + 'px');
		}
		
		if( this.db.load('editorState') && this.db.load('editorState') == 'hide' ){
			$('skingen_content').hide();
		}
		
		// Any pane loaded?
		if( this.db.load('editorPane') ){
			var current = this.db.load('editorPane');
		} else {
			var current = 'body';
		}
		
		// Build the first pane
		this.showPane( current );
		
		// Make draggable
		new Draggable( 'skingen_editor', { handle: $('skingen_editor').down('h1'), onEnd: this.updateDragPos.bind(this) } );
		
		// Add Events
		this.setUpEditorEvents();
	},
	
	//------------------------------------------
	// Builds a simple array of classnames from our
	// customizable list. Used for matching.
	//------------------------------------------
	buildMasterClassList: function(){
		this.classList = [];
		this.skinClasses.each( function(item){
			this.classList.push( item.value[0] );
		}.bind(this));
	},
	
	
	//-----------------------------------------
	// Get the template for CSS we'll need
	//-----------------------------------------
	getClassTemplate: function( classArray )
	{
		template = defaultTemplates.css_wrapper;
		name     = classArray[0];
		contents = '';
		
		if ( ! Object.isUndefined( classArray[3] ) )
		{
			$H(classArray[3]).each( function( dec )
			{
				switch( dec.key )
				{
					case 'background':
						if ( classArray[1] == 'Search Button' )
						{
							contents += defaultTemplates.bgcolonly_color.evaluate( { value: dec.value[0], extra: dec.value[1] } );
						}
						else
						{
							contents += defaultTemplates.bg_color.evaluate( { value: dec.value[0], extra: dec.value[1] } );
						}
					break;
					case 'color':
						contents += defaultTemplates.font_color.evaluate( { value: dec.value[0], extra: dec.value[1] } );
					break;
					case 'border':
						contents += defaultTemplates.border_color.evaluate( { value: dec.value[0], extra: dec.value[1] } );
					break;
					case 'boxshadow':
						contents += defaultTemplates.box_shadow_color.evaluate( { value: dec.value[0], extra: dec.value[1] } );
					break;
				}
			} );
		}
		
		return template.evaluate( { 'selector': name, 'contents': contents } );
	},
	
	
	//------------------------------------------
	// Events that make the editor work
	//------------------------------------------
	setUpEditorEvents: function(){
		$('skingen_locate').on("click", this.showLocations.bind(this));
		$('skingen_select').on("click", this.selectByClick.bind(this));
		$('skingen_editor').on("dblclick", 'h1', this.toggleEditor.bind(this));
		$('skingen_sections').on('click', 'li', this.showPaneEv.bind(this));
		$('skingen_save').on('click', this.buildSkin.bind(this));
		$('skingen_revert').on('click', this.revertSkin.bind(this));
		$('skingen_sections').on('click', 'h3', this.toggleGroup.bind(this));
		$('skingen_colorize').on('click', this.toggleColorizer.bind(this));
		$('skingen_settings').on('click', this.toggleSettings.bind(this));
		//$('skingen_images').on('click', this.imageEditor.toggleImageEditor.bind( this.imageEditor ));
		
		ipb.delegate.register(".sg_dropper", this.showDropper.bind(this) );
	},
	
	//------------------------------------------
	// This would build the skin CSS,
	// but for now just we just pretend 
	//------------------------------------------
	buildSkin: function(e){
		
		Event.stop(e);
		
		if ( ! $('skingen_buildskin_editor') )
		{
			this.body.insert( { bottom: this.templates.buildSkin.evaluate() } );
		
			new Draggable( 'skingen_buildskin_editor', { handle: $("skingen_buildskin_editor").down('h1') } );
			
			$('skingen_buildskin_editor').hide();
		}
		
		if ( $('skingen_buildskin_editor').visible() )
		{
			new Effect.Appear( $('skingen_editor'), { duration: 0.3 } );
			new Effect.Fade( $('skingen_buildskin_editor'), { duration: 0.2 } );
			
			/* Click handlers */
			$('skingen_buildskin_cancel').stopObserving('click');
			$('skingen_buildskin_save').stopObserving('click');
		}
		else
		{
			/* Position correctly */
			$('skingen_buildskin_editor').setStyle( { 'top': $('skingen_editor').getStyle('top'), 'left': $('skingen_editor').getStyle('left') } );
		
			new Effect.Appear( $('skingen_buildskin_editor'), { duration: 0.3 } );
			new Effect.Fade( $('skingen_editor'), { duration: 0.2 } );
			
			/* Click handlers */
			$('skingen_buildskin_cancel').on( 'click', this.buildSkin.bindAsEventListener(this) );
			$('skingen_buildskin_save').on( 'click', this.buildSkinProcess.bindAsEventListener(this) );
		}

	},
	
	//------------------------------------------
	// Throw everything back to PHP
	//------------------------------------------
	buildSkinProcess: function(e)
	{
		/* Fix form */
		$('skingen_buildskin_save').writeAttribute('disabled', 'true' );
		$('skingen_buildskin_save').update( 'Сохранение...' );
		
		var css = [];
		
		this.skinClasses.each( function(item){
			css.push( this.buildRenderedRule( item.key ) );
		}.bind(this) )
		
		output = css.join("\n\n");
		
		var settings = this.db.load("storedSettings");
		var width    = ( Object.isUndefined( settings['width_value'] ) ) ? '87' : settings['width_value'];
		var px_pc    = ( Object.isUndefined( settings['width_type'] ) )  ? '%' : settings['width_type'];
		
		output += '#content, .main_width { width: ' + width + px_pc + ' !important; }';
		
		output += ".topic_buttons li.non_button a {\
		background: transparent !important;\
		background-color: transparent !important;\
		border: 0 !important;\
		box-shadow: none !important;\
		-moz-box-shadow: none !important;\
		-webkit-box-shadow: none !important;\
		text-shadow: none !important;\
		min-width: 0px;\
		color: #777777;\
		font-weight: normal;\
		}";

		Debug.write( $H( this.db.load("storedClasses") ).toJSON() );
		var params = { 'storedClasses' : JSON.stringify( this.db.load("storedClasses") ),
					   'storedSettings': JSON.stringify( this.db.load("storedSettings") ),
					   'css': output };
					   
		var url    = ipb.vars['base_url'] + 'app=core&section=skingen&module=ajax&do=save&md5check=' + ipb.vars['secure_hash'];
		Debug.write( url );
		new Ajax.Request( url,
							{
								method: 'post',
								parameters: params,
								onSuccess: function(t)
								{
									if ( t.responseJSON['status'] == 'ok' )
									{
										new Effect.Fade( $('skingen_buildskin_editor'), { duration: 0.1 } );
										
										/* Clear storage */
										this.dropStoredValues();
										
										/* Reload yo */
										document.location.reload();
									}
									else
									{
										alert( ipb.lang['action_failed'] );
										return;
									}
								}.bind(this)
							});
	},
	
	//------------------------------------------
	// Reverts a skin to the default values
	// and drops the database
	//------------------------------------------
	revertSkin: function(e){
		if( confirm("Вы действительно хотите отменить все внесенные изменения?") ){
			this.dropStoredValues();
			this.dropStoredImages();
			document.location.reload();
		}		
	},
	
	//------------------------------------------
	// Toggles a style group
	//------------------------------------------
	toggleGroup: function(e, elem){
		var group = $(elem).readAttribute('data-group');
		
		if( elem.hasClassName('open') && $('skingen_group_' + group).visible() ){
			new Effect.BlindUp( $('skingen_group_' + group), { duration: 0.3, afterFinish: function(){
				$(elem).removeClassName('open').addClassName('closed');
			} } );
		} else {
			new Effect.BlindDown( $('skingen_group_' + group), { duration: 0.3, afterFinish: function(){
				$(elem).addClassName('open').removeClassName('closed');
			} } );
		}
	},
	
	//------------------------------------------
	// Toggle the settings panel
	//------------------------------------------
	toggleSettings: function(e){
		Event.stop(e);
		
		if( !$('skingen_settings_editor') ){
			this.buildSettings();
		}
		
		if ( $('skingen_settings_editor').visible() )
		{
			new Effect.Appear( $('skingen_editor'), { duration: 0.3 } );
			new Effect.Fade( $('skingen_settings_editor'), { duration: 0.2 } );
		}
		else
		{
			new Effect.Appear( $('skingen_settings_editor'), { duration: 0.3 } );
			new Effect.Fade( $('skingen_editor'), { duration: 0.2 } );
		}
	},
	
	//------------------------------------------
	// Build the settings panel
	//------------------------------------------
	buildSettings: function(e){
		settings = this.db.load("storedSettings");
		
		var width = ( Object.isUndefined( settings['width_value'] ) ) ? '87' : settings['width_value'];
		var px_pc = ( Object.isUndefined( settings['width_type'] ) )  ? '%' : settings['width_type'];
		
		this.body.insert( { bottom: this.templates.settingsEditor.evaluate( { 'width': width } ) } );
		
		/* Set drop down up correctly */
		if ( px_pc == 'px' )
		{
			$('skingen_setting_widthunit').options[0].selected = true;
		}
		else
		{
			$('skingen_setting_widthunit').options[1].selected = true;
		}
		
		/* Position correctly */
		$('skingen_settings_editor').setStyle( { 'top': $('skingen_editor').getStyle('top'), 'left': $('skingen_editor').getStyle('left') } );
		
		/* Click handlers */
		$('skingen_settings_cancel').on( 'click', this.toggleSettings.bindAsEventListener(this) );
		$('skingen_settings_save').on( 'click', this.saveSettings.bindAsEventListener(this) );
		
		$('skingen_settings_editor').hide();
		
		new Draggable( 'skingen_settings_editor', { handle: $("skingen_settings_editor").down('h1') } );
	},
	
	//-----------------------------------------
	// Save settings
	//-----------------------------------------
	saveSettings: function(e)
	{
		Event.stop(e);
		
		var width = $F('skingen_setting_width');
		var px_pc = ( $F('skingen_setting_widthunit') == 'percent' ) ? '%' : 'px';
		
		if ( px_pc == 'px' && parseInt( width ) < 900 )
		{
			width = 900;
		}
		else if ( px_pc == 'pc' && parseInt( width ) < 60 )
		{
			width = 60;
		}
		
		/* write */
		this.db.write("storedSettings", { 'width_value': width, 'width_type': px_pc } );
		
		/* update stylesheet */
		this.cssManipulator.updateWidth();
		

		this.toggleSettings(e);
	},
	
	//------------------------------------------
	// Shows the colorizer
	//------------------------------------------
	toggleColorizer: function(e){
		Event.stop(e);
		
		if( !$('skingen_colorize_editor') ){
			this.buildColorizer();			
		}
		
		if( $('skingen_colorize_editor').visible() ){
			new Effect.Appear( $('skingen_editor'), { duration: 0.3 } );
			new Effect.Fade( $('skingen_colorize_editor'), { duration: 0.2 } );
			$('skingen_colorize').removeClassName('active');
			this.db.write("currentWindow", "main");
			return;
		} else {
			// Back up our class values temporarily, so that
			// we can cancel the change if necessary
			var backup = {};
			
			this.skinClasses.each( function(item){
				backup[ item.key ] = item.value[3];
			}.bind(this));
			
			this.db.write("classBackup", backup);
			
			/* Position correctly */
			$('skingen_colorize_editor').setStyle( { 'top': $('skingen_editor').getStyle('top'), 'left': $('skingen_editor').getStyle('left') } );
			
			// Fade main window/show colorizer
			new Effect.Fade( $('skingen_editor'), { duration: 0.3 } );
			new Effect.Appear( $('skingen_colorize_editor'), { duration: 0.2 } );
			$('skingen_colorize').addClassName('active');
			this.db.write("currentWindow", "colorize");
			return;
		}
	},
	
	//-----------------------------------------
	// Returns BG color of colourizer group
	//-----------------------------------------
	getFirstBackgroundColorOfColorizerGroup: function( group )
	{
		var hex 	   = this.getHexFromSelector( 'maintitle', 'background' );
		var background = false;
		var text	   = false;
		
		if ( ! Object.isUndefined(this.colorizeGroups.get( group )) )
		{
			this.colorizeGroups.get( group ).each( function(item)
			{
				if ( $H( this.skinClasses ).get( item ) )
				{
					$H(this.skinClasses.get( item )[3] ).each( function(property)
					{ 
						if ( background === false && property.key == 'background' )
						{
							background = this.getHexFromSelector( item, 'background' );
						}
						
						if ( text === false && property.key == 'color' )
						{
							text = this.getHexFromSelector( item, 'color' );
						}
						
					}.bind(this) );
				}
			}.bind(this) );
		}
		
		/* Failsafe */
		return ( background ) ? background : ( text ? text : hex );
	},
	
	//------------------------------------------
	// Builds the colorizer window
	//------------------------------------------
	buildColorizer: function(){
		this.body.insert( { bottom: this.templates.colorizeEditor.evaluate() } );
		$('skingen_colorize_editor').hide();
		new Draggable( 'skingen_colorize_editor', { handle: $("skingen_colorize_editor").down('h1') } );
		
		// Set up pickers
		['base', 'secondary', 'tertiary', 'text'].each( function(item){
			new colorPicker( 'skingen_colorize_' + item, {
					color: this.getFirstBackgroundColorOfColorizerGroup( item ),
	 				livePreview: true,
	 				previewElement: 'skingen_colorize_' + item,
	 				onHide: this.updateColorizer.bind(this, item),
					onSubmit: this.updateColorizer.bind(this, item)
			});
		}.bind(this));
		
		$('skingen_colorize_cancel').on('click', this.cancelColorizer.bind(this));
		$('skingen_colorize_save').on('click', this.saveColorizer.bind(this));
		return;
	},
	
	//------------------------------------------
	// Event handler for colorizer picker
	// Updates all of our classes with a new color
	//------------------------------------------
	updateColorizer: function( type, picker ){
		var newHue = picker.color['h'];
		var newSat = picker.color['s'];
		
		if ( ! Object.isUndefined( this.colorizeGroups.get( type ) ) )
		{
			this.colorizeGroups.get( type ).each( function(item){
				if( this.skinClasses.get( item )){
					$H(this.skinClasses.get( item )[3]).each( function(property){
						if( property.key == 'background' || property.key == 'color' || property.key == 'border'  || property.key == 'boxshadow'  ){
							val = this.getHexFromSelector( item, property.key );
							if ( val )
							{
								this.skinClasses.get( item )[3][ property.key ][0] = '#' + this.hueChanger.convertHex( val, newHue, newSat );
							}
						}
					}.bind(this));
					// Now manipulate the CSS for the full preview
					this.cssManipulator.updateSelector( item );			
				}
			}.bind(this));
		}
	},
	
	//------------------------------------------
	// Cancels a colorize change without saving
	//------------------------------------------
	cancelColorizer: function(e){
		var backup = $H( this.db.load('classBackup') );

		backup.each( function(item){
			this.skinClasses.get( item.key )[3] = item.value;
		}.bind(this));
		
		this.skinClasses.each( function(item){
			this.cssManipulator.updateSelector( item.key );
		}.bind(this));
		
		this.db.write('classBackup', false);
		this.toggleColorizer( e );
	},
	
	//------------------------------------------
	// Save the changes that have been made
	//------------------------------------------
	saveColorizer: function(e){
		this.updateStoredValues();
		this.resetTextboxValues();
		this.db.write('classBackup', false);
		this.toggleColorizer( e );
	},
	
	//------------------------------------------
	// Updates all of the textbox pickers with current values
	//------------------------------------------
	resetTextboxValues: function( id ){
		this.skinClasses.each( function(item){
			if( Object.isUndefined( this.pickers[ item.key ] ) ){
				return;
			}
			 
			$H( item.value[3] ).each( function(style){
				if( !Object.isUndefined( this.pickers[ item.key ][ style.key ] ) ){
					var val = this.getHexFromSelector( style.value, style.key );
					
					this.pickers[ item.key ][ style.key ].setColor( val.replace('#', '') );
					this.pickers[ item.key ][ style.key ].el.value = val.replace('#', '');
					$( this.pickers[ item.key ][ style.key ].options.previewElement ).setStyle( { 'background-color': val } );
				}
			}.bind(this));
		}.bind(this));			
	},
	
	//------------------------------------------
	// Shows an editor pane
	//------------------------------------------
	showPaneEv: function(e, elem){
		this.showPane( elem.id.replace("section_", '') );
	},	
	showPane: function( id, otherClasses ){		
		$$("#skingen_editor .skingen_pane").invoke("hide");
		
		if( $('skingen_pane_' + id ) ){
			$('skingen_pane_' + id ).show();
		} else {
			this.buildPane( id );
		}
		
		if ( ! Object.isUndefined( otherClasses ) )
		{
			$('skingen_pane_' + id + '_others').stopObserving('click');
			$('skingen_pane_' + id + '_others').update( this.buildOtherClassesForPane( otherClasses ) ).show();
			$('skingen_pane_' + id + '_others').on('click', 'li', this.showPaneEv.bind(this));
		}
		else
		{
			$('skingen_pane_' + id + '_others').hide();
		}
		
		this.currentClass = id;
		this.db.write("editorPane", id);
	},
	//-----------------------------------------
	// Show others
	//-----------------------------------------
	buildOtherClassesForPane: function( otherClasses )
	{
		var items = [];
		
		$H(otherClasses).each( function(item){
			item = item[1];
			
			if( this.skinClasses.get( item ) )
			{
				var bg = this.getHexFromSelector( item, 'background' );
				var fc = this.getHexFromSelector( item, 'color' );
				
				items.push( this.templates.sectionItem.evaluate( { id: item, name: this.skinClasses.get( item )[1], style: 'background: ' + ( bg ? bg : fc ) } ) );
			}
		}.bind(this));
		
		return this.templates.otherClassesGroup.evaluate( { content: items.join('') } );
	},
	
	//------------------------------------------
	// Builds an editor pane
	//------------------------------------------
	buildPane: function( id ){
		var content = [];
		var colors = [];
		var borders = [];
		var classInfo = this.skinClasses.get( id );
	
		$H(classInfo[3]).each( function(item){
			switch( item.key ){
				case 'background':
					colors.push( this.templates.widgetBackground.evaluate({ id: id, current: item.key }) );
				break;
				case 'color':
					colors.push( this.templates.widgetForeground.evaluate({ id: id, current: item.key }) );
				break;
				case 'border':
					borders.push( this.templates.widgetBorder.evaluate({ id: id, current: item.key }) );
				break;
				case 'boxshadow':
					borders.push( this.templates.widgetBoxShadow.evaluate({ id: id, current: item.key }) );
				break;
			}
		}.bind(this));
		
		if( colors.length ){
			content.push( this.templates.sectionColors.evaluate({ id: id, content: colors.join('') }) );
		}
		
		if( borders.length ){
			content.push( this.templates.sectionBorders.evaluate({ id: id, content: borders.join('') }) );
		}
		
		var pane = this.templates.paneWrap.evaluate({ id: id, selector: classInfo[0], name: classInfo[1], description: classInfo[2], content: content.join('') });
		$('skingen_panes').insert({bottom: pane});

		// Set up color pickers
		[ 'background', 'color', 'border', 'boxshadow' ].each( function(item){
			if( $( item + '_' + id ) ){
				if( !this.pickers[ id ] ){
					this.pickers[ id ] = {};
				}

				this.pickers[ id ][ item ] = this.activatePicker( id, item );
			}
		}.bind(this) );
	},
	
	//------------------------------------------
	// Creates a new color picker object
	//------------------------------------------
	activatePicker: function( id, type ){
		var val = this.getHexFromSelector( id, type );
		
		return new colorPicker( type + '_' + id, {
				color: val,
 				livePreview: true,
 				previewElement: type + '_preview_' + id,
 				onHide: this.updateColor.bind(this),
				onSubmit: this.updateColor.bind(this),
				onLoad: function(picker){
					picker.el.value = val.replace('#', '')
				}.bind(this)
		});		
	},
	//-----------------------------------------
	// Update picker from external source
	//-----------------------------------------
	updatePicker: function( id, type, color )
	{
		this.pickers[ id ][ type ].setColor( color );
		
		$( type + '_' + id ).value = color;
		
		this.updateColor( this.pickers[ id ][ type ] ); 
	},
	
	//------------------------------------------
	// Event handler for the picker
	//------------------------------------------
	updateColor: function(picker){
		// Get the element
		var elem = picker.el;
		if( !elem.id.match( this.currentClass ) ){ return; }
		
		// Type
		var type = $(elem).readAttribute('data-type');
		var obj = {};
		
		if( type == 'background' || type == 'color' || type == 'border' || type == 'boxshadow' ){
			elemValue = '#' + $(elem).value;
		} else {
			elemValue = $(elem).value;
		}
		
		obj[ type ] = elemValue;
		
		this.updateClassList( this.currentClass, obj );
		
		this.updatePreview( this.currentClass, obj );
		
		// Now manipulate the CSS for the full preview
		this.cssManipulator.updateSelector( this.currentClass );
		
		return true;
	},
	
	//------------------------------------------
	// Updates the preview boxes
	//------------------------------------------
	updatePreview: function( selector, values ){		
		values = $H(values);
		
		if( values.size() ){
			values.each( function(item){
				$('section_' + selector).down('span').setStyle( { 'backgroundColor' : item.value + ' !important' } );
			}.bind(this));
		}
	},
	
	//------------------------------------------
	// Updates the master class list with new values
	//------------------------------------------
	updateClassList: function( selector, values ){		
		values = $H(values);
		if( values.size() ){
			values.each( function(item){
				this.skinClasses.get( selector )[3][ item.key ][0] = item.value;
			}.bind(this));
		}
		
		this.updateStoredValues();				
	},
	
	//------------------------------------------
	// Builds an object we can write to the database
	// for cross-page styling
	//------------------------------------------
	updateStoredValues: function(){
		this.classStored = {};
		
		this.skinClasses.each( function(item){
			this.classStored[ item.key ] = item.value[3];
		}.bind(this));
		
		this.db.write("storedClasses", this.classStored);		
	},
	
	loadStoredValues: function(){
		var storedValues   = this.db.load("storedClasses");
		var storedSettings = this.db.load("storedSettings");
		
		if ( ! storedValues && typeof(IPS_SKIN_GEN_SAVED_DATA) != 'undefined' )
		{
			if ( ! Object.isUndefined( IPS_SKIN_GEN_SAVED_DATA['storedClasses'] ) )
			{
				storedValues = $H( IPS_SKIN_GEN_SAVED_DATA['storedClasses'] );
			}
		}
		
		if ( ! storedSettings && typeof(IPS_SKIN_GEN_SAVED_DATA) != 'undefined' )
		{
			if ( ! Object.isUndefined( IPS_SKIN_GEN_SAVED_DATA['storedSettings'] ) )
			{
				this.db.write("storedSettings", $H( IPS_SKIN_GEN_SAVED_DATA['storedSettings'] ) );
			}
		}
		
		$H(storedValues).each( function(item){
			this.skinClasses.get( item.key )[3] = item.value;
		}.bind(this));
	},
	
	dropStoredValues: function(){
		this.db.write("storedClasses", false);
		this.db.write("storedSettings", false );
	},
	
	updateStoredImages: function(){
		this.db.write("storedImages", this.updatedImages);
	},
	
	loadStoredImages: function(){
		/*var storedImages = this.db.load("storedImages");
		
		$H( storedImages ).each( function(item){
			this.imageEditor.updateImageReferences( item.value, item.key );
		}.bind(this));*/
	},
	
	dropStoredImages: function(){
		this.db.write("storedImages", false);
	},
	
	//------------------------------------------
	// Start the XRay
	//------------------------------------------
	selectByClick: function(e){
		Event.stop(e);
		this.xray.start();
	},
	
	//-----------------------------------------
	// Do the dropper background_dropper_ setColor 
	//-----------------------------------------
	showDropper: function( e, elem )
	{
		Event.stop(e);
		
		var item = elem.id.replace( /^(.+?)_.*$/, "$1" );
		var id   = elem.id.replace( /^(?:[^_]+?)_(?:[^_]+?)_(.+?)$/, "$1" );
		
		this.dropper.start( item, id );
	},
	
	//------------------------------------------
	// Highlight the current elements
	//------------------------------------------
	showLocations: function(e){
		Event.stop(e);
		
		if( this.showingLocations ){
			$('skingen_locate').removeClassName('active');
			$$('.skingen_highlight').invoke("remove");
			this.showingLocations = false;
			return;
		}
		
		// Find elements matching the selector
		var elements = $$( this.skinClasses.get( this.currentClass )[0] );
		
		if( !elements.size() ){
			alert("Данный элемент отсутсвует на этой странице! Попробуйте поискать его на другой странице");
			return;
		} 
		
		$('skingen_locate').addClassName('active');
		
		elements.each( function(item){
			var offset = item.cumulativeOffset();
			var dims = item.getLayout();
			var newElem = new Element('div').addClassName('skingen_highlight').setStyle({
							width: (dims.get('padding-box-width') + 2) + "px",
							height: (dims.get('padding-box-height') + 2) + "px",
							top: (offset.top - 2) + "px",
							left: (offset.left - 2) + "px"
						});
						
			this.body.insert({bottom: newElem});			
		}.bind(this));
		
		this.showingLocations = true;
	},
	
	//------------------------------------------
	// Check whether localStorage is supported
	//------------------------------------------
	checkDB: function(){		
		try {
			return'localStorage' in window && window['localStorage'] !== null;
		} catch (e) {
			return false;
		}
	},
	
	//------------------------------------------
	// Updates the position of the editor pane in the DB
	//------------------------------------------
	updateDragPos: function(e){
		var pos = $('skingen_editor').cumulativeOffset();
		this.db.write("editorPos", pos);
	},
	
	//------------------------------------------
	// Shows/hides the editor
	//------------------------------------------
	toggleEditor: function(e){
		if( $('skingen_content').visible() ){
			new Effect.BlindUp( $('skingen_content'), { duration: 0.3 } );
			this.db.write("editorState", 'hide');
		} else {
			new Effect.BlindDown( $('skingen_content'), { duration: 0.3 } );
			this.db.write("editorState", 'show');
		}		
	},
	
	//------------------------------------------
	// Builds a fully-formed CSS rule
	//------------------------------------------
	buildRenderedRule: function( selector ){
		return this.getClassTemplate( this.skinClasses.get( selector ) );
	},
	
	//-----------------------------------------
	// Clean up hex
	//-----------------------------------------
	_cleanHex: function( hex )
	{
		if ( hex.length == 4 )
		{
			hex = hex.replace( /#/, '' );
			rbg = hex.split('');
			
			hex = '#' + rbg[0] + rbg[0] + rbg[1] + rbg[1] + rbg[2] + rbg[2];
		}
		
		return hex;
	},
	
	//============================================================
	// HUE CHANGER
	// Changes the hue of a class
	//============================================================
	hueChanger: {
		_parent: null,
		
		//------------------------------------------
		// Main function for changing the hue of a hex
		//------------------------------------------
		convertHex: function( hex, toHue, toSat ){
			// Remove #
			hex = hex.replace(/#/, '');
			
			// Check for shorthand hex
			if( hex.length == 3 ){
				hex = hex.slice(0,1) + hex.slice(0,1) + hex.slice(1,2) + hex.slice(1,2) + hex.slice(2,3) + hex.slice(2,3);
			}
			
			if( hex.length != 6 ){
				Debug.write("Not a valid hex");
				return;
			}
			
			Debug.write("Converting " + hex + " to hue " + toHue );
			// Split the hex into pieces, convert to RGB, and create fraction
			var r = ( this.hexToRGB( hex.slice(0,2) ) / 255 );
			var g = ( this.hexToRGB( hex.slice(2,4) ) / 255 );
			var b = ( this.hexToRGB( hex.slice(4,6) ) / 255 );
			
			// Convert to HSL
			var hsl = this.RGBtoHSL( r, g, b );
			
			Debug.dir( hsl );
			
			// Change our hue to a fraction
			hsl[0] = (1 / 360) * toHue;			
			hsl[1] = (1 / 100) * toSat;
			// Back to RGB
			var rgb = this.HSLtoRGB( hsl[0], hsl[1], hsl[2] );
			
			return this.RGBtoHex( rgb[0], rgb[1], rgb[2] );
		},
		
		//------------------------------------------
		// Converts an HSL value to RGB
		//------------------------------------------
		HSLtoRGB: function( h, s, l ){			
			var red = 0;
			var green = 0;
			var blue = 0;
			
			if( s == 0 ){
				red = l * 255;
				green = l * 255;
				blue = l * 255;
			} else {
				if( l < 0.5 ){
					var v2 = l * ( 1 + s );
				} else {
					var v2 = ( l + s ) - ( s * l );
				}
				
				var v1 = 2 * l - v2;
				
				red = 255 * this.HueToRGB( v1, v2, (h + ( 1 / 3 ) ) );
				green = 255 * this.HueToRGB( v1, v2, h );
				blue = 255 * this.HueToRGB( v1, v2, (h - ( 1 / 3 ) ) );
			}
			
			return [ Math.round( red ), Math.round( green ), Math.round( blue ) ];
		},
		
		//------------------------------------------
		// Converts a hue to an RGB decimal value
		//------------------------------------------
		HueToRGB: function( v1, v2, h ){
			if( h < 0 ){ h += 1; }
			if( h > 1 ){ h -= 1; }
			
			if( ( 6 * h ) < 1 ){
				return ( v1 + ( v2 - v1 ) * 6 * h );
			}			
			if( ( 2 * h ) < 1 ){
				return v2;
			}			
			if( ( 3 * h ) < 2 ){
				return ( v1 + ( v2 - v1 ) * ( ( 2 / 3 ) - h ) * 6 );
			}	
			
			return v1;
		},
		
		//------------------------------------------
		// Converts an RGB to a HSL value
		//------------------------------------------
		RGBtoHSL: function( r, g, b ){			
			var lightness, hue, saturation = 0;
			
			var min = [ r, g, b ].min();
			var max = [ r, g, b ].max();
			var delta = max - min;
			
			lightness = ( max + min ) / 2;
			
			if( delta == 0 ){ 	// Grey
				hue = 0;
				saturation = 0;
			} else {
				if( lightness < 0.5 ){
					saturation = delta / ( max + min );
				} else {
					saturation = delta / ( 2 - max - min );
				}
				
				var delta_r = ( ( ( max - r ) / 6 ) + ( delta / 2 ) ) / delta;
				var delta_g = ( ( ( max - g ) / 6 ) + ( delta / 2 ) ) / delta;
				var delta_b = ( ( ( max - b ) / 6 ) + ( delta / 2 ) ) / delta;
				
				if( r == max ){
					hue = delta_b - delta_g;
				} else if( g == max ){
					hue = ( 1 / 3 ) + delta_r - delta_b;
				} else if( b == max ){
					hue = ( 2 / 3 ) + delta_g - delta_r;
				}
				
				if( hue < 0 ){
					hue += 1;
				}
				
				if( hue > 1 ){
					hue -= 1;
				}
			}
			
			return [ hue, saturation, lightness ];
		},
		
		//------------------------------------------
		// Converts a hex to an RGB value
		//------------------------------------------
		hexToRGB: function( hex ){
			return parseInt(hex,16);
		},
		
		RGBtoHex: function( r, g, b ){
			var hex = [ r.toString(16), g.toString(16), b.toString(16) ];
			hex.each(function(val,nr) {
				if(val.length == 1){
					hex[nr] = '0' + val;
				}
			});
			return hex.join('');
		}
	},
	
	//============================================================
	// IMAGE EDITOR
	// Accepts uploads of images
	//============================================================
	imageEditor: {
		_parent: null,
		
		//------------------------------------------
		// Shows/hides the image editor
		//------------------------------------------
		toggleImageEditor: function(e){
			if( typeof FileReader === 'undefined' ){
				alert("Ваш браузер не поддерживает работу с редактированием изображений");
				return;
			}
			
			if( !$('skingen_images_editor') ){
				this.buildImageEditor();
			}		
		},
		
		//------------------------------------------
		// Builds the image editor window
		//------------------------------------------
		buildImageEditor: function(e){
			var content = [];
			
			// Build image entries
			this._parent.imageReplacements.each( function(item){
				content.push( this._parent.templates.imageEditorItem.evaluate( { 
									id: item.key,
									img: this.buildImage( item ),
									upload: this.buildUploadControl( item ) } ) );				
			}.bind(this));
			
			var imageEditor = this._parent.templates.imageEditor.evaluate( { content: content.join('') } );
			$('skingen_editor').insert( { bottom: imageEditor } );
			
			$('skingen_images_editor').on('click', 'input[type=button][data-img]', this.uploadImage.bind(this));
			
			// Make draggable
			new Draggable( 'skingen_images_editor', { handle: "#skingen_images_editor h1" } );
		},
		
		//------------------------------------------
		// Event handler for uploading an image
		//------------------------------------------
		uploadImage: function( e, elem ){
			// Find upload control
			var img = elem.readAttribute('data-img');
			var thumb = $('img_' + img);
			var uploader = $('skingen_images_content').select("input[type=file][data-img=" + img + "]")[0];
			
			if( !thumb || !uploader || !uploader.files[0] ){ return; }
			
			var file = uploader.files[0];
			
			if( !(/image/i).test( file.type ) ){
				alert("Этот файл не является файлом изображения, поробуйте другой!");
				return;
			}
			
			var reader = new FileReader();
			reader.onload = function(e) { 
				this.updateImageReferences( e.target.result, img );
			}.bind(this);
			
			reader.readAsDataURL( file );
		},
		
		//------------------------------------------
		// Updates all instances of an image on the page
		// to use the data url returned by FileReader
		//------------------------------------------
		updateImageReferences: function( dataURL, img ){
			// Update preview image first
			if( $('img_' + img) ){
				$('img_' + img).src = dataURL;
			}
			
			var itemObj = this._parent.imageReplacements.get( img );
			
			// find all places the image is used
			$$( itemObj[1] + " img" ).each( function( item ){
				if( item.src.match( itemObj[0] ) || item.readAttribute('data-img') == img ){
					item.src = dataURL;
					item.writeAttribute('data-img', img);
				}
			});
			
			// Update customized image in the object
			this._parent.updatedImages.set( img, dataURL );
			this._parent.updateStoredImages();
		},
		
		//------------------------------------------
		// Returns an image tag
		//------------------------------------------
		buildImage: function( item ){
			return "<img src='" + ipb.vars['img_url'] + item.value[2] + item.value[0] + "' id='img_" + item.key + "' />";
		},
		
		//------------------------------------------
		// Returns an upload control
		//------------------------------------------
		buildUploadControl: function(item){
			return "<input type='file' id='upload_img_" + item.key + "' data-img='" + item.key + "'  /> <input type='button' id='upload_img_btn_" + item.key + "' data-img='" + item.key + "' value='Save' />";			
		}
	},
	
	//============================================================
	// CSS MANIPULATOR
	// Manipulates CSS in the stylesheet in order to preview styles
	//============================================================
	cssManipulator: {
		_parent: null,
		styleSheet: null,
		
		//------------------------------------------
		// Update one selector in the injected stylesheet
		//------------------------------------------
		updateSelector: function( selector ){
			if( !$('injected_stylesheet') ){
				this.createInjectedStylesheet();
				return;
			}
			
			var getRules = '';
			var styleSheet = this.fetchStyleSheet();
			
			// Build our rule
			var newRule      = this._parent.buildRenderedRule( selector );
			var realSelector = this._unifyRule( this._parent.skinClasses.get( selector )[0] );
			
			Debug.write( "Updating - " + selector + ' - :' + realSelector + ':' );
			
			// Compatibility issue		
			getRules = ( styleSheet['cssRules'] ) ? 'cssRules' : 'rules';			

			// Loop through rules
			for ( rules = 0; rules < styleSheet[ getRules ].length; rules++ )
			{
				Debug.write( "Looking at... :" + styleSheet[ getRules ][ rules ].selectorText + ':' );
				
				if ( this._unifyRule( styleSheet[ getRules ][ rules ].selectorText ) == realSelector )
				{
					Debug.write( "Found it... :" + newRule  + ':');
					
					// Remove rule completely then readd it
					styleSheet.deleteRule( rules );
					styleSheet.insertRule( newRule, rules );
				}
			}
		},
		
		updateWidth: function( selector ){
			if( !$('injected_stylesheet') ){
				this.createInjectedStylesheet();
				return;
			}
			
			var getRules = '';
			var styleSheet = this.fetchStyleSheet();
			
			/* figure out width, etc */
			var settings = this._parent.db.load("storedSettings");
			var width    = ( Object.isUndefined( settings['width_value'] ) ) ? '87' : settings['width_value'];
			var px_pc    = ( Object.isUndefined( settings['width_type'] ) )  ? '%' : settings['width_type'];
			
			// Build our rule
			var newRule = '#content, .main_width { width: ' + width + px_pc + ' !important; }';
			var realSelector = '#content, .main_width';
			
			// Compatibility issue		
			getRules = ( styleSheet['cssRules'] ) ? 'cssRules' : 'rules';			

			// Loop through rules
			for( rules = 0; rules < styleSheet[ getRules ].length; rules++ ){
				if( styleSheet[ getRules ][ rules ].selectorText == realSelector ){
					// Remove rule completely then readd it
					styleSheet.deleteRule( rules );
					styleSheet.insertRule( newRule, rules );
				}
			}
		},
		
		//------------------------------------------
		// Finds and returns the injected stylesheet
		//------------------------------------------
		fetchStyleSheet: function(){
			if( this.styleSheet ){
				return this.styleSheet;
			}
			
			var styleSheets = document.styleSheets;
			
			for( sheet = 0; sheet < styleSheets.length; sheet++ ){
				if( styleSheets[ sheet ].ownerNode.id == 'injected_stylesheet' ){
					this.styleSheet = styleSheets[ sheet ];
					return this.styleSheet;
				}
			}
			
			return false;
		},
		
		//------------------------------------------
		// Build an injected stylesheet from scratch
		//------------------------------------------
		createInjectedStylesheet: function(){
			var output = '';
			
			$H( this._parent.skinClasses ).each( function(item){
				output += this._parent.getClassTemplate( this._parent.skinClasses.get( item.key ) );
			}.bind(this));
			
			/* figure out width, etc */
			var settings = this._parent.db.load("storedSettings");
			var width    = ( Object.isUndefined( settings['width_value'] ) ) ? '87' : settings['width_value'];
			var px_pc    = ( Object.isUndefined( settings['width_type'] ) )  ? '%' : settings['width_type'];
			
			output += '#content, .main_width { width: ' + width + px_pc + ' !important; }';
			
			output += ".topic_buttons li.non_button a {\
		background: transparent !important;\
		background-color: transparent !important;\
		border: 0 !important;\
		box-shadow: none !important;\
		-moz-box-shadow: none !important;\
		-webkit-box-shadow: none !important;\
		text-shadow: none !important;\
		min-width: 0px;\
		color: #777777;\
		font-weight: normal;\
	}";
	
			Debug.write(output);
			var stylesheet = new Element("style", { id: 'injected_stylesheet' }).update( output );
			
						
			
			$$('head')[0].insert( { bottom: stylesheet } );
			
		},
		//-----------------------------------------
		// Cleans rule to make sure spacing isn't an issue
		//-----------------------------------------
		_unifyRule: function( rule )
		{
			rule = rule.replace( /,\s{1,}/g, ',' );
			
			return rule;
		}
	},
	
	//============================================================
	// DB OBJECT
	// Simple DB wrapper with a few standard methods
	//============================================================
	db: {
		_parent: null,
		
		write: function( saveTo, saveValue ){			
			localStorage.setItem("skingen." + saveTo, JSON.stringify( saveValue ) );
		},
		
		load: function( loadKey ){
			if( localStorage.getItem("skingen." + loadKey) ){
				return JSON.parse( localStorage.getItem("skingen." + loadKey) );
			}
			
			return false;
		}
	},
	/* ! xray */
	//============================================================
	// XRAY INSPECTOR
	// Click to select elements, finds IP.Board classes that apply
	//============================================================
	xray: {
		_parent: null,
		matched: null,
		e: {},
		
		//------------------------------------------
		// Find eligible elements, and set events
		//------------------------------------------
		start: function(){
			// All elements except the skingen_editor
			this.exclude = $$("#skingen_editor, #skingen_editor *");
			
			$('skingen_select').addClassName('active');
			
			this.e['md'] = $(document).on('mousedown', this.doXray.bind(this));
			this.e['mm'] = $(document).on('mousemove', this.doFalse.bind(this));
			this.e['mo'] = $(document).on('mouseover', this.doFalse.bind(this));
		},
		
		//------------------------------------------
		// Unset events
		//------------------------------------------
		finish: function(){
			$('skingen_select').removeClassName('active');
			this.exclude = null;
			this.e['md'].stop();
			this.e['mm'].stop();
			this.e['mo'].stop();
			this._parent.body.removeClassName('selecting');
		},
		
		//------------------------------------------
		// mousedown handler; shows the xray flash box
		//------------------------------------------
		doXray: function(e){
			Event.stop(e);
			
			var item = Event.findElement(e);
			
			if( this.exclude.include( item ) ){
				return;
			}
						
			var offset = item.cumulativeOffset();
			var dims = item.getLayout();
			var newElem = new Element('div', { id: 'skingen_xray' }).addClassName('skingen_xray').setStyle({
							width: dims.get('padding-box-width') + "px",
							height: dims.get('padding-box-height') + "px",
							top: (offset.top ) + "px",
							left: (offset.left) + "px"
						}).writeAttribute('elem', id);
						
			this._parent.body.insert({bottom: newElem});
			new Effect.Fade( newElem, { duration: 0.7, afterFinish: function(){ newElem.remove() } } );
			
			this.finish();
			this.findMatchingSelectors( item );
		},
		
		//------------------------------------------
		// Simply return false and set the default cursor
		//------------------------------------------
		doFalse: function(e){
			this._parent.body.addClassName('selecting');
			Event.stop(e);
		},
		
		//------------------------------------------
		// Finds the nearest customizable selector
		// to the clicked element
		//------------------------------------------
		findMatchingSelectors: function( item ){
			// Get ancestors and add item itself to it
			var ancestors = $(item).ancestors();
			ancestors.unshift( item );
			
			var matchedClass = false;
			var className	 = false;
			var others       = new Array();
			
			// Double loop - go through ancestors, checking each class for a match
			var matching = ancestors.find( function(n){
				return this._parent.classList.find( function(m){
					matchedClass = m;
					return $(n).match( m );
				}.bind(this));
			}.bind(this));
			
			if ( ! matchedClass ) {
				alert("Не найдено ни одного стиля удовлетворяющего элементу.");
			}
			
			/* Look for any parents */
			var matching = ancestors.find( function(n)
			{
				var gotIt = this._parent.classList.find( function(m)
				{
					className = m;
					
					if ( matchedClass == className )
					{
						return false;
					}
					else
					{
						return $(n).match( m );
					}
				}.bind(this) );
				
				if ( gotIt && className )
				{
					this._parent.skinClasses.find( function(c)
					{
						if ( c.value[0] == className )
						{
							others.push( c.key );
						}
					}.bind(this) );
				}
			}.bind(this));
			
			// So we know the real classname
			// Now loop through our hash of customizable values
			// and find the one that matches the classname
			this._parent.skinClasses.find( function(c){
				if( c.value[0] == matchedClass ){
					this._parent.showPane( c.key, others );
					return true;
				} else {
					return false;
				}
			}.bind(this));
		}	
	},
	/* ! dropper */
	//============================================================
	// DROPPER
	// Selects a hex from mouse position
	//============================================================
	dropper: {
		_parent: null,
		e: {},
		item: null,
		id: null,
		
		//------------------------------------------
		// Find eligible elements, and set events
		//------------------------------------------
		start: function( item, id ){
			// All elements except the skingen_editor
			this.exclude = $$("#skingen_editor, #skingen_editor *");
			this.item = item;
			this.id   = id;
			
			this.e['md'] = $(document).on('mousedown', this.doDropper.bind(this));
			this.e['mm'] = $(document).on('mousemove', this.doFalse.bind(this));
			this.e['mo'] = $(document).on('mouseover', this.doFalse.bind(this));
		},
		
		//------------------------------------------
		// Unset events
		//------------------------------------------
		finish: function(){
			this.exclude = null;
			this.e['md'].stop();
			this.e['mm'].stop();
			this.e['mo'].stop();
			this._parent.body.removeClassName('selecting');
		},
		
		//------------------------------------------
		// mousedown handler; shows the xray flash box
		//------------------------------------------
		doDropper: function(e){
			Event.stop(e);
			
			var item = Event.findElement(e);
			
			if( this.exclude.include( item ) ){
				return;
			}
						
			var offset = item.cumulativeOffset();
			var dims = item.getLayout();
			var newElem = new Element('div', { id: 'skingen_dropper' }).addClassName('skingen_xray').setStyle({
							width: dims.get('padding-box-width') + "px",
							height: dims.get('padding-box-height') + "px",
							top: (offset.top ) + "px",
							left: (offset.left) + "px"
						}).writeAttribute('elem', id);
						
			this._parent.body.insert({bottom: newElem});
			new Effect.Fade( newElem, { duration: 0.3, afterFinish: function(){ newElem.remove() } } );
			
			this.finish();
			this.setColorPickerFromKnownSelectors( item );
		},
		
		//------------------------------------------
		// Simply return false and set the default cursor
		//------------------------------------------
		doFalse: function(e){
			this._parent.body.addClassName('selecting');
			Event.stop(e);
		},
		
		//------------------------------------------
		// Sets the color picker
		//------------------------------------------
		setColorPickerFromKnownSelectors: function( item ){
			// Get ancestors and add item itself to it
			var ancestors = $(item).ancestors();
			ancestors.unshift( item );
			
			var matchedClass = '';
			
			// Double loop - go through ancestors, checking each class for a match
			var matching = ancestors.find( function(n){ 
				return this._parent.classList.find( function(m){
					matchedClass = m;
					return $(n).match( m );
				}.bind(this));
			}.bind(this));
			
			if( !matchedClass ){
				alert("Не найдено ни одного стиля удовлетворяющего элементу.");
			} 
			
			// So we know the real classname
			// Now loop through our hash of customizable values
			// and find the one that matches the classname
			this._parent.skinClasses.find( function(c){
				if( c.value[0] == matchedClass ){
					try
					{
						var bg = this._parent.getHexFromSelector( c.key, 'background' );
						var fc = this._parent.getHexFromSelector( c.key, 'color' );
						var hex = ( bg ) ? bg : fc;
						
						if ( hex !== false )
						{
							hex = hex.replace( '#', '' );
							
							this._parent.updatePicker( this.id, this.item, hex );
						}
					}
					catch( e ) { }
					
					return true;
				} else {
					return false;
				}
			}.bind(this));
		}	
	},

	
	//============================================================
	// TEMPLATES
	// Various templates for the editor
	//============================================================
	templates: {
		editor: new Template("<div id='skingen_editor' class='skingen_editor'>								\
								<h1>Визуальный редактор стиля</h1>													\
								<div id='skingen_content' class='skingen_content'>							\
									<div id='skingen_toolbar'>												\
										<!--<a href='#' id='skingen_images'>Редактирование изображений</a>&nbsp;&nbsp;-->	\
										<a href='#' id='skingen_settings'><img src='" + skingen_imgs + "/settings.png' />&nbsp; Настройки стиля</a>&nbsp;&nbsp;		\
										<a href='#' id='skingen_colorize'><img src='" + skingen_imgs + "/colorize.png' />&nbsp; Колорайзер</a>&nbsp;&nbsp; \
										<a href='#' id='skingen_revert'><img src='" + skingen_imgs + "/delete.png' />&nbsp; Сброс</a>&nbsp;&nbsp;		\
										<a href='#' id='skingen_save'><img src='" + skingen_imgs + "/build.png' />&nbsp; Создать стиль</a>&nbsp;&nbsp;			\
										<a href='#' id='skingen_locate' class='right' title='Найти элементы, использующие выбранный стиль на этой странице'><img src='" + skingen_imgs + "/show_locations.png' /></a>&nbsp;&nbsp;\
										<a href='#' id='skingen_select' class='right' title='Выбор стиля, путем выбора элемента на странице'><img src='" + skingen_imgs + "/select_element.png' /></a>\
									</div>																	\
									<div id='skingen_sections' class='skingen_sections'>#{sections}</div>							\
									<div id='skingen_panes'></div>											\
								</div>																		\
							</div>"),
		otherClassesGroup: new Template("<h3 class='open'>Связанные классы</h3><ul>#{content}</ul>"),
		sectionGroup: new Template("<h3 class='open' data-group='#{id}'><a href='#' class='skingen_toggle_group'>&nbsp;</a> #{title}</h3><ul id='skingen_group_#{id}' data-group='#{id}'>#{content}</ul>"),
		sectionItem: new Template("<li id='section_#{id}' class='clearfix'><span style='#{style}' class='color'></span>#{name}</li>"),
		paneWrap: new Template("<div id='skingen_pane_#{id}' class='skingen_pane'><p class='right skingen_selector' title='Данный селектор используется для стилизации элемента в CSS'><span>#{selector}</span></p><h2>#{name}</h2><p>#{description}</p><div class='skingen_pane_contents'>#{content}</div><div id='skingen_pane_#{id}_others' class='skingen_sections otherClasses' style='display:none'></div></div>"),
		sectionColors: new Template("<fieldset class='colors'><span class='legend'>Цвета</span>#{content}</fieldset>"),
		sectionBorders: new Template("<fieldset class='border'><span class='legend'>Рамка</span>#{content}</fieldset>"),
		widgetBackground: new Template("<span class='control' title='Цвет фона'><img src='" + skingen_imgs + "/background.png' /> <input type='text' class='' id='background_#{id}' data-type='background' /><span id='background_preview_#{id}' class='skingen_preview'></span> <a href='#' class='sg_dropper' title='Пипетка' id='background_dropper_#{id}'><img src='" + skingen_imgs + "/color-picker.png' /></a></span>"),
		widgetForeground: new Template("<span class='control' title='Цвет текста'><img src='" + skingen_imgs + "/color.png' /> <input type='text' class='' id='color_#{id}' data-type='color' /><span id='color_preview_#{id}' class='skingen_preview'></span> <a href='#' class='sg_dropper' title='Пипетка' id='color_dropper_#{id}'><img src='" + skingen_imgs + "/color-picker.png' /></a></span>"),
		widgetBorder: new Template("<span class='control' title='Цвет рамки'><img src='" + skingen_imgs + "/border-color.png' /> <input type='text' class='' id='border_#{id}' data-type='border' /><span id='border_preview_#{id}' class='skingen_preview'></span> <a href='#' class='sg_dropper' title='Пипетка' id='border_dropper_#{id}'><img src='" + skingen_imgs + "/color-picker.png' /></a></span>"),
		widgetBoxShadow: new Template("<span class='control' title='Цвет тени'><img src='" + skingen_imgs + "/box-shadow.png' /> <input type='text' class='' id='boxshadow_#{id}' data-type='boxshadow' /><span id='boxshadow_preview_#{id}' class='skingen_preview'></span> <a href='#' class='sg_dropper' title='Пипетка' id='boxshadow_dropper_#{id}'><img src='" + skingen_imgs + "/color-picker.png' /></a></span>"),
		settingsEditor: new Template("<div id='skingen_settings_editor' class='skingen_editor'>				\
										<h1>Настройки стиля</h1>												\
										<div id='skingen_settings_content' class='skingen_content'>			\
											<p class='skingen_desc'>Основные настройки вашего стиля.</p>	\
												<br />														\
												<table style='margin: 10px auto; width: 80%'>				\
												<tr>														\
													<td>Ширина</td>										\
													<td><input type='text' id='skingen_setting_width' value='#{width}' /> <select id='skingen_setting_widthunit'><option value='px'>pixels</option><option value='percent'>%</option></select>										\
													</td>													\
												</tr>														\
											</table>														\
											<br />																				\
											<p class='skingen_buttons'>															\
												<a href='#' id='skingen_settings_cancel'>Отменить</a>&nbsp;&nbsp;&nbsp;<a href='#' id='skingen_settings_save'>Сохранить</a>\
											</p>																				\
										</div>																					\
									</div>"),
		buildSkin: new Template("<div id='skingen_buildskin_editor' class='skingen_editor'>				\
										<h1>Создание стиля</h1>												\
										<div id='skingen_buildskin_content' class='skingen_content'>			\
											<p class='skingen_desc'>Сохранение изменений и создание стиля.</p>	\
												<br />														\
												<table style='margin: 10px auto; width: 80%'>				\
												<tr>														\
													<td><p>Все изменения будут внесены в стиль. Редактор будет закрыт.</p>	</td>													\
												</tr>															\
											</table>														\
											<br />																				\
											<p class='skingen_buttons'>															\
												<a href='#' id='skingen_buildskin_cancel'>Отмена</a>&nbsp;&nbsp;&nbsp;<a href='#' id='skingen_buildskin_save'>Создать</a>\
											</p>																				\
										</div>																					\
									</div>"),
		colorizeEditor: new Template("<div id='skingen_colorize_editor' class='skingen_editor' style='display: none'>			\
										<h1>Раскраска стиля</h1>																\
										<div id='skingen_colorize_content' class='skingen_content'>								\
											<p class='skingen_desc'>Используйте инструмент для изменения оттенка (цвета) всех соответствующих стилей в один прием. Затем можно редактировать отдельные стили, используя главный редактор. ВНИМАНИЕ: использование инструмента приведет к перезаписи любых изменений в стилях, которые вы уже настрили.</p>																							\
											<table style='margin: 10px 0'>													\
												<tr>																			\
													<td style='width: 50%'><p id='skingen_colorize_base' class='color' data-type='base'></p><br />Первичный цвет</td>\
													<td style='width: 50%'><p id='skingen_colorize_secondary' class='color' data-type='secondary'></p><br />Вторичный цвет</td>\
												</tr>																			\
												<tr>																			\
													<td><p id='skingen_colorize_tertiary' class='color' data-type='tertiary'></p><br />Третичный Color</td>\
													<td><p id='skingen_colorize_text' class='color' data-type='text'></p><br />Цвет текста</td>\
												</tr>																			\
											</table>																			\
											<br />																				\
											<p class='skingen_buttons'>															\
												<a href='#' id='skingen_colorize_cancel'>Отменить</a>&nbsp;&nbsp;&nbsp;<a href='#' id='skingen_colorize_save'>Применить</a>																													\
											</p>																				\
										</div>																					\
									</div>"),				
		imageEditor: new Template("<div id='skingen_images_editor' class='skingen_editor'><h1>Редактор изображений</h1><div id='skingen_images_content' class='skingen_content'>#{content}</div></div>"),
		imageEditorItem: new Template("<div id='image_#{id}' class='skingen_image_item clearfix'>#{img}<p class='right'>#{upload}</p></div>")
	},
	
	//============================================================
	// SKIN CLASSES
	// Our master hash of the classes that can be customized
	//============================================================
	skinClasses: $H( IPS_SKIN_GEN_DATA['classes'] ),
	
	//============================================================
	// SKIN GROUPS
	// This object groups the classes in the main display
	//============================================================
	skinGroups: $H( IPS_SKIN_GEN_DATA['skinGroups'] ),	
	
	//============================================================
	// SKIN GROUPS
	// This object defines which classes are altered when one of the colorize selectors is altered
	//============================================================
	colorizeGroups: $H( IPS_SKIN_GEN_DATA['colorizeGroups'] ),
	
	imageReplacements: $H({
		logo: 	[	'logo.png',												// Filename
					'#logo',												// Scope to find image in (default: *)
					'/',													// Path to find this image in the skin folder
					'Логотип сообщества'								// Description
				],
				
		defaultPhoto: [ 	'default_large.png',
							'',
							'/profile/',
							'Фотография по умолчанию'
					]
	})
};

document.observe("dom:loaded", function(){
	skingen.boot();
});