/**
 * @classDescription Vivieb.ButtonsManager
 * this class allows to give one or more <img> tags a common button behavior
 * through the creation and the management of related "button" objects.
 * All the managed buttons share the same behaviors and the same event handlers.
 * 
 * Requires: class.js, jquery.js, jquery.vivieb.js, j2browse.js, iepngfix.htc
 * 
 * Button object
 * -------------
 * A button object has the following properties:
 * 
 * element {DOM Element}
 * 	the DOM Element object related to the img tag that implements the button
 * 
 * image {integer}
 * 	identifier in its buttons manager for the basic file name of the images 
 * 	used to render the button
 * 
 * selected {boolean}
 * 	the current state of the button. Only defined for "switch" and "radio" 
 * 	button
 * 
 * disabled {boolean}
 * 	true, if the button is currently disabled
 * 
 * group {integer}
 * 	identifier in its buttons manager for the group that owns the button.
 * 	Only defined for "radio" button.
 * 
 * params {object}
 * 	data object linked to the button
 * 
 * 
 * Buttons Manager Callbacks
 * -------------------------
 * 
 * The context (this) of all callbacks is set to the button manager instance 
 * that owns the button that originates the event. The callbacks are:
 * 
 * onClick, onMouseOver, onMouseOut, onMouseDown, onMouseUp {function}:
 * 	invoked after the corresponding event on the img that implements the button.
 * 	The parameters to this callback are (in order):
 * 		button {object}
 * 			the button object that originates the event
 * 		event {object}
 * 			the jQuery event object. For the onClick callback, this parameter
 * 			is undefined if the callback is invoked due to a "click" method 
 * 			call. 
 * 		
 * onBeforeSelect {function}:
 * 	invoked before the selection of a "radio" button. The function can return
 * 	false to cancel the selection. 
 * 		button {object}: 
 * 			the button object being selected
 *		unselecting {object}: 
 *			the button object being unselected, if any. Otherwise, null.
 * 
 * onUnselect {function}
 * 	invoked after a "radio" button has been unselected (after selecting 
 * 	another one).
 * 	The parameter to this callback is:
 * 		button {object}
 * 			the button object that has been unselected
 * 
 * 
*/

(function($) {

Vivieb._ButtonsManager = {
	
	/**
	 * @constructor Vivieb.ButtonsManager
	 *
	 * @param options {Object}
	 *	object that specifies the options for the class behavior through its properties.
	 *	These are:
	 *
	 * (Required)
	 *
	 * id {string}:
	 * 	Unique identifier for the button manager object. It's used as
	 * 	the common prefix to name the img tags that implement the managed buttons.
	 * 	Default: "btnmng"
	 *
	 * path {string}:
	 * 	the path from which to search for the resources used to render the
	 * 	buttons managed.
	 *
	 * type {string}:
	 * 	the type of button managed by the instance. Possible values are:
	 * 		"icon"	:	static image as button.
	 * 		"switch":	two state (unselected/selected) button changing
	 * 					on every click.
	 * 		"button":	button with press effect.
	 * 		"radio"	:	two state (unselected/selected) button owned by
	 * 					a group. Only one button per group can be selected.
	 * 	Default: "icon"
	 *
	 * (Optional)
	 *
	 * disabled {boolean}:
	 * 	if true, every new button is added in the disabled state
	 *
	 * rollover {boolean}:
	 * 	if true, the rollover effect is enabled on all the managed buttons
	 *
	 * srcs {string, array of string}:
	 * 	the basic file name(s) of the images to preload
	 *
	 * ext {string}:
	 * 	the default file extension for the images. Default: "png".
	 *
	 * selected {boolean}:
	 * 	the initial state of every new added button. This option is valid only 
	 * 	for the "switch" type.
	 *
	 * style {object}:
	 * 	object that specifies through its properties the CSS properties
	 * 	for every img tag that implements a new added button.
	 *
	 * */
	initialize: function(options){
		var instance = this;
		instance.impl = Vivieb._ButtonsManagerImpl;
		instance.srcs = [];
		instance.btns = [];
		instance.nId = 0;
		var impl = instance.impl;
		instance._click = instance._dblclick = impl._getClickHandler(instance);
		instance._mouseover = impl._getMouseoverHandler(instance);
		instance._mouseout = impl._getMouseoutHandler(instance);
		instance._mousedown = impl._getMousedownHandler(instance);
		instance._mouseup = impl._getMouseupHandler(instance);
		impl._setOptions(instance, options);
		$(window).unload(impl._destroy(instance));
	},
	
	/**
	 * @method $
	 * 	searches for the button objects based on the search criteria specified
	 * 	with the key and searchField parameters
	 * 
	 * @param {Object} key
	 * 	the data to search for
	 * 
	 * @param {Object} searchField
	 * 	where to search for key. If not defined, the search is performed directly
	 * 	on the params property of every button object managed by this instance.
	 * 	If defined, the method assumes that the params property	of the button 
	 * 	objects references to an object. In that case, the searchField must indicate 
	 * 	the name of the object property which to perform the search on.
	 * 
	 *  @return {object, array of object}
	 *  an array of all the button objects matching the search criteria, or a single
	 *  button object if there is only one match. null, if no button is found.
	 */
	findKey: function(key, searchField) {
	 	var btns = this.btns;
	 	var rslt = [];
	 	for(var i=0, ln=btns.length; i<ln; i++) {
	 		var btn = btns[i];
	 		if(searchField) {
				if(btn.params[searchField] == key) rslt.push(btn);
			}
	 		else if(btn.params == key) rslt.push(btn);
	 	}
	 	var ln = rslt.length;
	 	if(ln == 0) return null;
	 	if(ln == 1) return rslt[0];
	 	return rslt;
	},
	
	/**
	 * @method build
	 * create new managed button objects without adding them in the DOM tree.
	 *
	 * @param options {Object}
	 *	object that specifies the options for the creation of the new buttons
	 *	These are:
	 *
	 * (Required)
	 *
	 * src {string}:
	 * 	the basic file name (the common part without postfix and extension)
	 * 	that indicates the images used to render the buttons. See the
	 * 	"_getPostfixes" method to know the required extensions for the file names.
	 *
	 *
	 * (Optional)
	 *
	 * attributes {object}:
	 * object that specifies through its properties the attributes
	 * that will be assigned to the img tag that implements the button.
	 *
	 * disabled {boolean}:
	 * 	if true, the button will be created in the disabled state. This option
	 * 	ovveride the "disabled" instance option.
	 *
	 * ext {string}:
	 * 	the file extension for the images required to render the button.
	 * 	If not specified, the "ext" instance option will be used.
	 *
	 * group {string}:
	 * 	the name of the group that owns the button. This option is valid
	 * 	only for the "radio" type. If not specified, the button will be
	 * 	assigned to the default group.
	 *
	 * dom {DOM Element, string}:
	 * 	if specified, indicates the existing DOM img element to use to
	 * 	render the buttons. It can be a DOM Element reference or
	 * 	a valid jQuery selector. A button object will be created for
	 * 	every img tags found and stored in the manager. If no img tag 
	 * 	will be found or if this property is not defined, only one button 
	 * 	object will be created and a new DOM element will be created to 
	 * 	render the button.
	 *
	 * params {object}:
	 * 	it can be any kind of data. This value will be assigned to the
	 * 	params property of every new button object created
	 *
	 * path {string}:
	 * 	the path from which to search for the images used to render the
	 * 	managed buttons. If not specified, the "path" instance option will
	 * 	be used.
	 *
	 * selected {string}:
	 * 	the initial state of the created button. This option is valid only for
	 *  the "switch" type. This option ovveride the "selected" instance option.
	 *  If not specified, the "selected" instance option  will be used. 
	 *
	 * style {object}:
	 * 	object that specifies through its properties the CSS properties for the
	 * 	img tag that implements the created button. If specified, they will be
	 * 	added to those ones specified in the manager instance.
	 *
	 * @return {array of object}
	 * an array containing the button objects that acts for every button created.
	 * */
	build: function(options){
		if (!(options && options.src)) 
			return null;
		
		var instance = this;
		var o = instance.options;
		var btns = [];
		
		var elem;
		if (options.dom) 
			elem = $(options.dom).filter(function() {
				return this.nodeName.toLowerCase() == 'img';
			});
		if (!elem || elem.length == 0) 
			elem = $('<img>');
		elem.each(function(){
			this.id = o.id + '_' + instance.nId++;
			var btn = {
				element: this,
				image: instance.impl._preload(instance, options.src, options.path, options.ext),
				disabled: options.disabled != undefined ? options.disabled : o.disabled
			};
			if (o.selected != undefined) 
				btn.selected = typeof options.selected == 'boolean' ? 
					options.selected : o.selected;
			if (options.params) 
				btn.params = options.params;
			if (o.type == 'radio') 
				if (!options.group) 
					btn.group = 0;
				else {
					var groupPos = $.vv_indexOfKey(instance.grps, 'name', options.group);
					if (groupPos != -1) 
						btn.group = groupPos;
					else {
						instance.grps.push({
							name: options.group
						});
						btn.group = instance.grps.length - 1;
					}
				}
			btns.push(btn);
			instance.btns.push(btn);
		})
		.css($.extend({
				border: '0px',
				verticalAlign: 'middle'
			}, o.style ||{}, options.style || {}, {
				cursor: options.disabled ? '' : 'pointer'
			}
		))
		.attr(options.attributes || {})
		.bind('click.' + o.id, instance._click)
		.bind('dblclick.' + o.id, instance._click)
		.bind('mouseover.' + o.id, instance._mouseover)
		.bind('mouseout.' + o.id, instance._mouseout)
		.bind('mousedown.' + o.id, instance._mousedown)
		.bind('mouseup.' + o.id, instance._mouseup);
		if (o.ext == 'png' && $.browser.msie && $.browser.version.major == 6) {
			var path = o.path || '';
			if(!$.vv_endWith(path, '/'))
			path += '/';
			elem.css('behavior', 'url("' + path + 'iepngfix.htc")');
		}
		return btns;
	},
	
	/**
	 * @method clear
	 * 	remove all the button objects from the manager and all the related
	 * 	img tags from the DOM tree. The image cache is cleaned based on the
	 * 	clearCache parameter's value.
	 * 
	 */
	clear: function(clearCache){
		var instance = this;			
 		instance.remove(instance.btns);
 		instance.nId = 0;
		if(clearCache)
		 	instance.srcs = [];
	},
	
	/**
	 * @method click
	 * 	this method acts like a mouse click has occured on the img tag 
	 * 	with the id attribute equal to btnId parameter. The effects differs
	 *  depending on the button type:
	 * 		"icon" e "button":
	 * 			the onClick callback is invoked, if defined
	 * 		"switch":
	 * 			the selected state of the button is inverted. The onClick 
	 * 			callback is invoked, if defined.
	 * 		"radio":
	 * 			all the following procedure occurs only if the button is not
	 * 			already selected:
	 * 			- the onBeforeSelect callback is invoked, if defined. 
	 * 			- if the callback function doesn't cancel the selection: 
	 * 				- the button is selected
	 * 				- the potential already selected button	into the same group
	 * 				  is unselected
	 * 				- if such a button exists, the onUnselect callback is 
	 * 				  invoked, if defined. 
	 *	 			- the onClick callback is invoked, if defined.
	 * 			 
	 * @param {string} btnId
	 *  the id attribute value of the img tag pretending to be clicked
	 *  Obviously, the DOM element must already be managed by this manager.
	 *   
	 */
	
	click: function(btnId){
		var instance = this;
		var btn = instance.impl._$(instance.btns, btnId);
		if(!btn || btn.disabled) 
			return;
		var style = btn.element.style;
		var cursor = style.cursor; 
 		style.cursor = 'wait';
 		try {
 			switch(instance.options.type) {
 				case 'switch':
 					btn.selected = !btn.selected;
 					var ro = instance.options.rollover;
 					instance.impl._render(instance, btn, btn.selected ? (ro ? 5: 3) : (ro ? 2: 1));
					if(typeof instance.onClick == 'function')
						instance.onClick.call(instance, btn, arguments[1]);
 					break;
 				case 'radio':
 					if (!btn.selected) {
						var group = instance.grps[btn.group];
						var unselecting = group.selected;
						var trigger = true;
						if (typeof instance.onBeforeSelect == 'function') 
							trigger = instance.onBeforeSelect.call(instance, btn, unselecting || null) != false;
						if (trigger) {
							if (unselecting) {
								unselecting.selected = false;
								instance.impl._render(instance, unselecting, 1);
								if (typeof instance.onUnselect == 'function') 
									instance.onUnselect.call(instance, unselecting);
							}
							btn.selected = true;
							group.selected = btn;
							var ro = instance.options.rollover;
							instance.impl._render(instance, btn, btn.selected ? (ro ? 5 : 3) : (ro ? 2 : 1));
							if (typeof instance.onClick == 'function') 
								instance.onClick.call(instance, btn, arguments[1]);
						}
					}
 					break;
 				default:
					if(typeof instance.onClick == 'function')
						instance.onClick.call(instance, btn, arguments[1]);
 			}
 		}
 		catch(e) {throw e;}
 		finally {
 			style.cursor = cursor;
 		}
 	},
	
	/**
	 * @method disable
	 * 	disable the buttons identified by the who and target parameters
	 * 
	 * @param {Object, Array of Object, string} who
	 * specify the button objects (or a single button object) to disable.
	 * If not defined, it specifies all the managed buttons.
	 * The button set to disable can also be identified through one of the
	 * following string:
	 * 	"all"	: all the managed buttons (like undefined).
	 * 	"image"	: all the buttons with a specified basic file name. The name 
	 * 			  is given by the "target" parameter. 
	 * 	"group"	: all the "radio" buttons owned by a specified group. The 
	 * 			  group name is given by the "target" parameter. To disable
	 * 			  the default group, don't specify the "target" parameter. 
	 * 	
	 * @param {string} target
	 * 	the meaning of this parameter depends on the "who" parameter's value.
	 * 	Specify it only when required.
	 */
	disable: function(who, target){
		var instance = this;
		instance.impl._enable(instance, who, target, instance.impl._disableButton);
	},
	
	/**
	 * @method enable
	 * 	enables the buttons identified by the who and target parameters
	 * 
	 * @param {Object, Array of Object, string} who
	 * 	see the disable method.
	 * @param {string} target
	 * 	see the disable method.
	 */
	enable: function(who, target){
		var instance = this;
		instance.impl._enable(instance, who, target, instance.impl._enableButton);
	},
	
	/**
	 * @method getGroup
	 * 	returns the group object specified by the parameter id.
	 * 	
	 * @param {integer} id
	 *  the group identifier. It can be given by the group property of the 
	 *  button object.
	 *  
	 * @return {object}
	 *  the group object corresponding to the id parameter or undefined if
	 *  the group doesn't exist. The method returns null if the button type managed
	 *  is not "radio".
	 *  A group object has the following properties:
	 *  	"name" {string}: 
	 *  		the name of the group. This property is not defined for the
	 *  		default group (id=0)
	 *  	"selected" {integer}:
	 *  		the internal identifier of the potential button object currently 
	 *  		selected for the group. The selected button object can be 
	 *  		obtained through:	instance.btns[selected].
	 * 
	 */
	getGroup: function(id){
		var instance = this;
		if(instance.options.type != 'radio') return null;
 		return instance.grps[id];
	},
	
	/**
	 * @method getSrc
	 * 	obtains the basic file name used to render the given object button.
	 * 
	 * @param {Object} btn
	 * 	tho object button which to get the information from. 
	 */
	getSrc: function(btn){
		if(!btn) return null;
		var instance = this;
		return instance.srcs[btn.image].name;
	},
	
	/**
	 * @method insert
	 * insert one or more managed buttons in the DOM tree. The managed buttons
	 * can already exist or can be created depending on the options passed
	 * as the argument.
	 *
	 * @param options {Object}
	 * object that specifies (through its properties) the options for the
	 * (potential) creation of the buttons and theirs insertion in the DOM
	 * tree. These are:
	 *
	 * buttons {array of object,object}:
	 * 	the array of button objects (or a single button object) resulting
	 * 	from a previous creation with the "build" or "insert" methods.
	 * 	This option is used only to insert already existing buttons. If
	 * 	the button is already inserted in the DOM tree, it will be moved 
	 * 	to its new position.
	 *
	 * As an alternative, if the button doesn't exist, the "options" argument
	 * must specify all the options expected by the "build" method for
	 * the button's creation.
	 * In both cases, the "options" argument must specify the options that
	 * serve the purpose to position the buttons in the DOM tree, that are:
	 *
	 * target {DOM object, string}:
	 * 	the DOM element that will be refered to insert the button.
	 * 	It can be a DOM object reference or a valid jQuery selector.
	 * 	If more than one target are found with a jQuery selector, only
	 * 	the first one will be the target.
	 *
	 * position {string}:
	 * 	the position where to insert the button in respect to the
	 * 	"target" element. It can be one of these values:
	 * 		"prepend":	the button will be inserted before the content
	 * 					of "target".
	 * 		"append":	the button will be inserted after the content
	 * 					of "target".
	 * 		"before":	the button will be inserted before "target".
	 * 		"after":	the button will be inserted after "target".
	 * 	Default: "append"
	 *
	 * These options can be omitted if all the img tags that implement
	 * the buttons are already inserted. In tath case, the insert method
	 * only renders the indicated buttons in their current position.
	 *
	 * @return {array of object}
	 * an array containing the button objects that acts for every button inserted.
	 *
	 * */
	insert: function(options){
		if (!options) 
			return null;
		var instance = this;
		var btns = options.buttons;
		if (btns) {
			if (btns.length == undefined) 
				btns = [btns];
		}
		else 
			btns = this.build(options);
		if (options.target) 
			try {
				instance.toJQuery(btns).vv_insert(options.target, options.position);
			} 
			catch (e) {
				$.vv_exception(instance, e);
			}
		for (var i = 0, ln = btns.length; i < ln; i++) {
			var btn = btns[i];
			if (!btn.element.parentNode) 
				$.vv_exception(instance, {name: 'ParentNodeMissing'}, btn.element.id);
			instance.impl._render(instance, btn, btn.selected ? 
				(instance.options.rollover ? 4 : 3) : 1);
		}
		return btns;
	},
	
	/**
	 * @method remove
	 * 	removes the given button objects from the manager. The related
	 * 	img tags are also removed from the DOM tree.
	 * 
	 * @param {Object, Array of Object} btns
	 * 	the button objects (or a single object) to remove.
	 */
	remove: function(btns){
		if(!btns) return null;
		var instance = this;
		if(isNaN(btns.length)) btns = [btns];
		for(var i=btns.length-1; i>=0; i--) {
 			var btn = btns[i];
 			if(instance.options.type=='radio') {
 				var group = instance.grps[btn.group];
 				if(group.selected == btn) group.selected = null;
 			}
 			$(btn.element).remove();
 			btn.element = null;
 			btn.params = null;
 			$.vv_removeItem(instance.btns, btn);
 		}
	},
	
	/**
	 * @method setSelected
	 * 	allows to set the selected state of the specified buttons.
	 * 	Only for "switch" buttons.
	 * 
	 * @param {Object, Array of Object} btns
	 * 	the button objects which to set the selected state to
	 *  
	 * @param {boolean} selected
	 * 	the selected state to set to the specified buttons.
	 * 	If not specified, the button's selected state is inverted.
	 */
	setSelected: function(btns, selected){
		if(!btns) return;
		var instance = this;
		if(instance.options.type != "switch")
			return;
		if(isNaN(btns.length)) btns = [btns];
		for(var i=0, ln=btns.length; i<ln; i++) {		
 			var btn = btns[i];
 			if(selected == undefined) selected = !btn.selected;
 			btn.selected = selected;
			instance.impl._render(instance, btn,
 				btn.selected ? (instance.options.rollover ? 4: 3) : 1);
 		}
	},
	
	/**
	 * @method toJQuery
	 * 	obtains a jQuery wrapped set containing the img elements used to render
	 * 	the given managed buttons.
	 * 
	 * @param {Object, Array of Object} btns
	 * 	the button objects (or a single object) which to obtain the wrapped
	 * 	set from. If not specified, all the managed buttons will be considered.
	 */
	toJQuery: function(btns){
		var instance = this;
		if (btns == undefined) btns = instance.btns;
		else if(isNaN(btns.length)) btns = [btns];
		return $($.map(btns, function(btn){
			return btn.element;
		}));
	},
	
	toString: function(){
		var instance = this;
		return 'ButtonsManager (id: ' + instance.options.id + ')';
	}
};

// Buttons Manager private implementation:
Vivieb._ButtonsManagerImpl = {
	
	exception: {
		ParentNodeMissing: 'Parent node missing inserting button \'{0}\'!'
	},
	
	_$:function(btns, btnId) {
		for(var i=0, ln=btns.length; i<ln; i++)
			if(btns[i].element.id == btnId) return btns[i];
	},
	
	_destroy: function(instance) {
		return function(){
			instance.clear(true);
			instance._click = null;
			instance._dblclick = null;
			instance._mouseover = null;
			instance._mouseout = null;
			instance._mousedown = null;
			instance._mouseup = null;
		}
	},
	
	_disableButton: function(instance, btn){
		if(btn.disabled) return;
		btn.disabled = true;
		instance.impl._render(instance, btn);
		btn.element.style.cursor = '';
	},
	
	_enable: function(instance, who, target, enableMethod){
		if(!who) who = 'all';
		var btns = instance.btns;	
		switch(who) {
			case 'all':
				for(var i=0, ln=btns.length; i<ln; i++) 
					enableMethod(instance, btns[i]);
				break;
			case 'image':
				if(!target) return;
				var image = $.vv_indexOfKey(instance.srcs, 'name', target);
				if(image == -1) return;
				for(var i=0, ln=btns.length; i<ln; i++) {
					var btn = btns[i];
					if(btn.image == image) enableMethod(instance, btn);
				}
				break;
			case 'group':
				if(instance.options.type != 'radio') return;
				var group = 0;
				if(target) {
					group = $.vv_indexOfKey(instance.grps, 'name', target);
					if(group == -1) return;
				}
				for(var i=0, ln=btns.length; i<ln; i++) {
					var btn = btns[i];
					if(btn.group == group) enableMethod(instance, btn);
				}
				break;
			default:
				if(isNaN(who.length)) who = [who];
				for(var i=0, ln=who.length; i<ln; i++)
					enableMethod(instance, who[i]);
		}
	},

	_enableButton: function(instance, btn){
		if(!btn.disabled) return;
		delete btn.disabled;
		instance.impl._render(instance, btn, 
			btn.selected == undefined ? 1 : (instance.options.rollover ? 4 : 3));
		btn.element.style.cursor = 'pointer';
	},

	_getClickHandler: function(instance) {
		return function(event) {
			if(event.target != this)
				return;
			instance.click(event.target.id, event);
			event.stopPropagation();
		}
	},
	
	_getMousedownHandler: function(instance) {
		return function(event){
			if(event.target != this)
				return;
			var btn = instance.impl._$(instance.btns, event.target.id);
			if(!btn || btn.disabled) 
				return;
			if(instance.options.type == 'button')
				instance.impl._render(instance, btn, instance.options.rollover ? 3 : 2);
			if(typeof instance.onMouseDown == 'function')
				instance.onMouseDown.call(instance, btn, event);
			event.stopPropagation();
		}
	},

	_getMouseoutHandler: function(instance) {
		return function(event){
			if(event.target != this)
				return;
			var btn = instance.impl._$(instance.btns, event.target.id);
			if(!btn || btn.disabled) 
				return;
			if(instance.options.rollover || instance.options.type == 'button')
				instance.impl._render(instance, btn, btn.selected ? 4 : 1);
			if(typeof instance.onMouseOut == 'function')
				instance.onMouseOut.call(instance, btn, event);
			event.stopPropagation();
		}
	},
	
	_getMouseoverHandler: function(instance) {
		return function(event){
			if(event.target != this)
				return;
			var btn = instance.impl._$(instance.btns, event.target.id);
			if(!btn || btn.disabled) 
				return;
			if(instance.options.rollover)
				instance.impl._render(instance, btn, btn.selected ? 5 : 2);
			if(typeof instance.onMouseOver == 'function')
				instance.onMouseOver.call(instance, btn, event);
			event.stopPropagation();
		}
	},
	
	_getMouseupHandler: function(instance) {
		return function(event){
			if(event.target != this)
				return;
			var btn = instance.impl._$(instance.btns, event.target.id);
			if(!btn || btn.disabled) 
				return;
			if(instance.options.type == 'button')
				instance.impl._render(instance, btn, 1);
			if(typeof instance.onMouseUp == 'function')
				instance.onMouseUp.call(instance, btn, event);
			event.stopPropagation();
		}
	},
	
	_getPostfixes: function(type, rollover) {
		switch(type) {
			case 'icon':
				return rollover ? ['_fx', '_f', '_fh'] : ['_fx', '_f'];
			case 'switch':; case 'radio':
				return rollover ? 
					['_fx', '_f', '_fh', '_ox', '_o', '_oh'] : ['_fx', '_f', '_ox', '_o'];
			case 'button': 
				return rollover ? ['_fx', '_f', '_fh', '_fd'] : ['_fx', '_f', '_fd'];
		}
	},

	_preload: function(instance, basicName, path, ext) {
		var idx = $.vv_indexOfKey(instance.srcs, 'name', basicName);
		if(idx!=-1) 
			return idx;
		var src = {name: basicName, imgs: []};
		var o = instance.options;
		var postfixes = instance.impl._getPostfixes(o.type, o.rollover);
		path = path || o.path;
		if(!$.vv_endWith(path, '/'))
			path += '/';
		ext = ext || o.ext;
		basicName = path + basicName;
		for(var i=0, ln=postfixes.length; i<ln; i++) {
			var img = new Image();
			img.src = basicName + postfixes[i] + '.' + ext;
			src.imgs.push(img);
		}
		instance.srcs.push(src);
		return instance.srcs.length -1;
	},
	
	_render: function(instance, btn, nState) {
		if(btn.disabled) 
			if(!btn.selected) nState = 0;
			else if(btn.selected) nState = instance.options.rollover ? 3: 2;
		var element = btn.element;
		var cached = instance.srcs[btn.image].imgs[nState];
		element.src = cached.src;
		//element.width= cached.width;
		//element.height= cached.height;
	},
	
	_setOptions: function(instance, options) {
		var o = instance.options = $.extend(true, {
			id		: 'btnmng',
			type	: 'icon',
			ext		: 'png',
			disabled: false
		}, options || {});
		
		var srcs = o.srcs;
		if(srcs) {
			if(typeof srcs == "string")
				srcs = [srcs];
			for(var i=0, ln=srcs.length; i<ln; i++)
				instance.impl._preload(instance, srcs[i]);
			delete o.srcs;
		} 
		if(o.type == 'switch') {
			if(! (typeof o.selected == 'boolean'))
				o.selected = false;			
		} 
		else if(o.type == 'radio') {
			instance.grps = [{name: null}];
			o.selected = false; 
		}
		else delete o.selected;
	}
};

Vivieb.ButtonsManager = new Class(Vivieb._ButtonsManager);

})(jQuery);
