/*<![CDATA[*//* #### LAF CONCRETE #### */

/**
 * Look And Feel concrete module. Contains or imports the implementation of all "look and feel"
 * components needed to the interaction logic to works properly
 * @author Roberto Maggi 
 * @file LAF_h.js
 * @requires LAF_h
*/
include("const.js");
include("laf/LAF_h.js");
include("laf/dragAndDrop.js");
include("laf/dialogBox.js");
include("laf/dialogTemplates.js");
include("laf/MooRoundedCorners.js");
include("laf/mooSplitter.js");
include("laf/mapFactory.js");

/**
 * @class BLookAndFeelFactory
*/
var BLookAndFeelFactory = new Class({
	Extends: AbstractBLookAndFeelFactory,
	initialize: function( skinLib, langLib, imagesLib, mapLib ){
		// initialize all global pres var
		this.skin = new BSkin(skinLib);
		this.lang = new BLang(langLib);
		this.images = new BImages(imagesLib, this.lang);
		//this.mapFactory = new BMapFactory(mapLib, this);
	},
	// # MANIPULATORS #
	createDraggable: function(el, strategy, options, ns){
		return new BDraggable(el, strategy, this.skin.get("draggable", ns),options);
	},
	createMultipleDraggable: function(elms, strategy, storage, options, ns){
		return new BMultiDragContainer( elms, strategy, storage, this.skin.get("multiDragContainer", ns), options);
	},
	createDroppable: function(el, options, ns){
		return new BDroppable(el, this.skin.get("droppable",ns), options);
	},
	createEditableInputBox: function(elm, strategy, ns ){
		return new BEditableInputBox(elm, strategy, this, this.skin.get("inputText",ns));
	},
	createEditableRadioButton: function(elm, strategy, ns ){
		return new BEditableRadioButton(elm, strategy, this, this.skin.get("inputText",ns));
	},
	createEditableSelectBox: function(elm, strategy, ns ){
		return new BEditableSelectBox(elm, strategy, this, this.skin.get("inputText",ns));
	},
	createEditableCheckBox: function(elm, strategy, ns ){
		return new BEditableCheckBox(elm, strategy, this, this.skin.get("inputText",ns));
	},
	createEnumeratekBox: function(elm, strategy, ns ){
		return new BEnumerateBox(elm, strategy, this, this.skin.get("inputText",ns));
	},
	createEditableTextBox: function(elm, strategy, ns ){
		return new BEditableTextBox(elm, strategy, this, this.skin.get("editableTextBox",ns));
	},
	createEditableTextarea: function(elm, strategy, ns){
		return new BEditableTextarea(elm, strategy, this, this.skin.get("editableTextarea",ns));
	},
	createSelectable : function(el, ns){
		return new BSelectable(el, this.skin.get("selectable", ns));
	},
	createEditToolbar: function(textarea, parentElement){
		return new BMediaWikiEditToolbar(textarea, parentElement);
	},
	
	
	// # PAGE MANIPULATORS #
	createSplitter: function(options){
		return new Splitter(options);
	},
	createTabManager: function(){},
	createExpander: function(expandable, activator, ns, options){
		return new BExpander(expandable, activator, this.lang, this.skin.get("expander", ns), options);
	},
	createAddElement: function(expandable, activator, ns, options){
		return new BAddElement(expandable, activator, this.lang, this.skin.get("addRemove", ns), options);
	},
	createAddPanel: function(expandable, activator, ns, options){
		return new BAddPanel(expandable, activator, this.lang, this.skin.get("addRemove", ns), options);
	},
	createTextualTreeItemExpander: function(treeItem, ns, options){
		return new BTextualTreeItemExpander(treeItem, this.lang, this.skin.get("textualTreeItemExpander", ns), options);
	},
	createAccordion: function(){},
	
	createDialogBox: function(title, focusManager, options, ns){
		return new BDialogBox( title, focusManager, this, this.skin.get("dialogBox", ns), options);
	},
	createCommandsChoice: function(useButton, ns){
		var style = $merge(this.skin.get("commandChoice", ns),{ useButton: useButton})
		return new BCommandsChoice( style );
	},
	createPrompt: function(){},
	createConfirm: function( intro, optYesLabel, optNoLabel, optCancelLabel, callback, ns){
		return new BConfirm( intro, optYesLabel, optNoLabel, optCancelLabel, callback, this.skin.get("confirm", ns));
	},
	createOperationLogger: function(mode, ns){
		return new BOperationLogger(mode, this.skin.get("operationLogger", ns), {
			startLogo : this.images.get("performingOperation"),
			failed : this.images.get("operationFailed"),
			success : this.images.get("operationSuccess"),
			warn : this.images.get("operationWarn")
		});
	},
	// # Create a list <ul><li/></ul> to show error
	createList: function(el,ns){
		return new BListError(el,this.skin.get("inputText",ns));
	},
	
	// # VISUAL BEHAVIOURS #
	createRoundedCorners: function(el, options){
		return new RoundedCorners(el, options);
	},
	createShadowing: function(){},
	
	// # COMPLEX VIEW ELEMENT #
	createTreeViewElement: function(type, options, ns){
		return new BTreeViewElement(type, this, this.skin.get("treeViewElement", ns), options );
	},
	createMenuBar: function(focusManager, parentMenu, ns){
		return new BMenuBar(focusManager , parentMenu, this.skin.get("actionsMenu",ns));
	},
	
	// # GUIDE PARTS #
	createGuidePartTab : function(type, options, ns){
		return new BGuidePartTab(type, this.skin.get("guidePartTab",ns), options);
	},
	
	// # SPECIFIC ENVIRONMENT FACTORY #
	getMapFactory: function(){
		return this.mapFactory;
	}
});


/**
 * EditableInputBox manipulator.
 * Trigger events: fieldChanged, fieldFocus
 * @class BEditableInputBox
*/
var BEditableInputBox = Class({
	Extends: AbstractBEditableBox,
	_error: null,
	 
	initialize: function(elm, strategy , factory, style){
		this.parent(elm, strategy , factory, style);
		this.status = {
			isEnable : false
		};
		this._input = new Element('input');
		this._input.addClass(this.style.baseClass);
		this._input.addClass(this.style.text);
		this._input.inject(this.element);

        this._input.addEvent('blur', this.fieldChanged.bind(this));
        this._input.addEvent('focus', this.fieldFocus.bind(this));
	},
	fieldFocus : function(event){
        	this.fireEvent('fieldFocus', this);
	},
	fieldChanged : function(event){
        	this.fireEvent('fieldChanged', this);
	},
	/**
	 * Returns the name of input form
	*/
	getName : function(){
		return this._input.getProperty('name').trim();
	},
	/**
	 * Sets the name of the input form
	*/
	setName : function(value){
		return this._input.setProperty('name', value);
	},
	/**
	 * Returns the current value of the subject
	*/
	getValue : function(){
		return this._input.getProperty('value').trim();
	},
	/**
	 * Sets the value of the subject
	*/
	setValue : function(value){
		return this._input.setProperty('value', value);
	},
	addError: function(){
		//if(this._error == null){
			this.addClass("error");
	//		this._error = "error";
			/*this._error = new Element('div');
			this._error.addClass(this.style.errorDialog);
			var pos = this._input.getCoordinates(this.element);
			this._error.setStyles({
				'left': pos.left,
				'top': pos.bottom,
				'width': pos.width,
			});
			this._error.inject(this.element);*/
	//	}
		//this._error.set('html',text);
	},
	removeError: function(){
		//if(this._error != null){
			this.removeClass("error");
			//this._error.dispose();
	//		this._error == null;
		//}
	}
});

/**
 * BEditableRadioButton manipulator.
 * Trigger events: fieldChanged
 * @class BEditableRadioButton
*/
var BEditableRadioButton = Class({
	Extends: AbstractBEditableTextBox,
	_name : null,
	_input : null,
	_value : null,
	_lang : null,
	_n : null,
	 
	initialize: function(elm, strategy , factory, style){
		this.parent(elm, strategy , factory, style);
		this._n = 0;
		this._lang = this.laf.getLang();
		
		this.status = {
			isEnable : false
		};
		this._input = new Element('ul');
		this._input.addClass(this.style.radioButton);
		this._input.inject(this.element);
		
       // this._input.addEvent('blur', this.fieldChanged.bind(this));
	},
	fieldChanged : function(){
        this.fireEvent('fieldChanged', this);
	},
	fieldAddChanged : function(){
        this.fireEvent('fieldAddChanged', this._n);
	},
	/**
	 * Returns the name of input form
	*/
	getName : function(){
		return this._input.getProperty('name').trim();
	},
	/**
	 * Sets the name of the input form
	*/
	setName : function(value){
		this._name = value;
		return this._input.setProperty('name', value);
	},
	/**
	 * Returns the current value of the subject
	*/
	getValue : function(){
		return this._value;
	},
	/**
	 * Sets the value of the subject
	*/
	setValue : function(el,value){
		el.setProperty('value', value.toLowerCase());
		el.set('text', value);
	},
	/**
	 * Add tag option
	*/
	addOptionTag: function(value,checked){
		this._n++;
		var li = new Element("li",{"text": value});
		var el = new Element("input",{'type':'radio', 
				'name' : this._name,
				'value' : value.toLowerCase()});
		if(checked){
			el.set("checked", "checked");
		}
		el.addEvent('change', (function(){
			 this._value = el.get("value");
			 this.fieldChanged();
		}).bind(this));
		
		el.inject(li,'top');
		li.inject(this._input);
	},
	addInputTag: function(){
		var li = new Element("li", {"text" : this._lang.get('other')});
		var el = new Element("input",{
			'type':'radio',
			'name' : this._name,
			'value' : ''
		});
		
		var field = new Element("input",{
			'type':'text'
		});
		field.addClass(this.style.baseClass);
		field.addClass(this.style.choiceText);
		
		el.addEvent('change', (function(){
			el.set('value',field.get("value"));
			this._value = el.get("value");
			this.fieldAddChanged();
		}).bind(this));
		
		field.addEvent('blur', (function(){
			 el.set("checked", "checked");
			 el.set('value',field.get("value"));
			 this._value = field.get("value");
			 this.fieldAddChanged();
		}).bind(this));
		el.inject(li,'top');
		field.inject(li,'bottom');
		li.inject(this._input);
		
		return li;
	}
});

/**
 * BEditableSelectBox manipulator.
 * Trigger events: fieldChanged
 * @class BEditableSelectBox
*/
var BEditableSelectBox = Class({
	Extends: AbstractBEditableTextBox,
	_input : null,
	_inputEvent : null,
	 
	initialize: function(elm, strategy , factory, style){
		this.parent(elm, strategy , factory, style);
		this._input = null;
		this.status = {
			isEnable : false
		};
		this._input = new Element('select');
		this._input.addClass( this.style.baseClass);
		this._input.inject(this.element);
		(new Element("option")).inject(this._input);
		
        this._input.addEvent('change', this.fieldChanged.bind(this));
	},

	fieldChanged : function(event){
        this.fireEvent('fieldChanged', this);
	},
	/**
	 * Returns the name of input form
	*/
	getName : function(){
		return this._input.getProperty('name').trim();
	},
	/**
	 * Sets the name of the input form
	*/
	setName : function(value){
		return this._input.setProperty('name', value);
	},
	/**
	 * Returns the current value of the subject
	*/
	getValue : function(){
		return this._input.getProperty('value').trim();
	},
	/**
	 * Sets the value of the subject
	*/
	setValue : function(el,value){
		el.setProperty('value', value.toLowerCase());
		el.set('text', value);
	},
	/**
	 * Add tag option
	*/
	addOptionTag: function(value,selected){
		var el = new Element("option");
		el.setProperty('value', value.toLowerCase());
		if(selected){
			el.set("selected", "selected");
		}
		el.set('text', value);
		el.inject(this._input);
	}
});

/**
 * BEditableCheckBox manipulator.
 * Trigger events: fieldChanged
 * @class BEditableCheckBox
*/
var BEditableCheckBox = Class({
	Extends: AbstractBEditableTextBox,
	_name : null,
	_input : null,
	_lang : null,
	_el : [],
	_n : null,
	 
	initialize: function(elm, strategy , factory, style){
		this.parent(elm, strategy , factory, style);
		this._n = 0;
		this._lang = this.laf.getLang();
		this.status = {
			isEnable : false
		};
		this._input = new Element('ul');
		this._input.addClass(this.style.radioButton);
		this._input.inject(this.element);
		
	},
	fieldChanged : function(pos){
		pos--;
        this.fireEvent('fieldChanged', pos);
	},
	fieldAddChanged : function(pos){
		pos--;
        this.fireEvent('fieldAddChanged', pos);
	},
	/**
	 * Returns the name of input form
	*/
	getName : function(){
		return this._input.getProperty('name').trim();
	},
	/**
	 * Sets the name of the input form
	*/
	setName : function(value){
		this._name = value;
		return this._input.setProperty('name', value);
	},
	/**
	 * Returns the current value of the subject
	*/
	getValue : function(pos){
		return this._el[pos].get('value');
		//return this._value;
	},
	/**
	 * Sets the value of the subject
	*/
	setValue : function(el,value){
		el.setProperty('value', value.toLowerCase());
		el.set('text', value);
	},
	/**
	 * Return the number of element
	 * witout the textField
	 */
	getSizeDefaultValue : function(){
		return this._n;
	},
	/**
	 * Add tag option
	*/
	addOptionTag: function(value,checked){
		this._n++;
		var li = new Element("li",{"text": value});
		this._el.include( new Element("input",{'type':'checkbox', 
				'name' : this._name,
				'value' : value.toLowerCase()}) );
				
		var pos = this._el.length;
		if(checked){
			this._el[pos-1].set("checked", "checked");
		}
				
		this._el[pos-1].addEvent('change', (function(){
			 this.fieldChanged(pos);
		}).bind(this));
		
		this._el[pos-1].inject(li,'top');
		li.inject(this._input);
	},
	addInputTag: function(){
		
		var li = new Element("li",{"text" : this._lang.get('other')});
		this._el.include( new Element("input",{
			'type':'checkbox',
			'name' : this._name
		}) );
		
		var pos = this._el.length;
		
		var field = new Element("input",{
			'type':'text'
			//'disabled' : 'disabled'
		});
		field.addClass(this.style.baseClass);
		field.addClass(this.style.choiceText);
		
		this._el[pos-1].addEvent('change', (function(){
			/*if($chk(field.get('disabled'))){
				field.removeProperty('disabled');
			}else{
				field.set('disabled','disabled');
			}*/
			this._el[pos-1].set('value', field.get('value'));
			this.fieldChanged(pos);
		}).bind(this));
		
		field.addEvent('blur', (function(){
			if(this._el[pos-1].get("checked")){
				this._el[pos-1].set('value', field.get('value'));
				this.fieldAddChanged(pos);
			}
		}).bind(this));
		
		this._el[pos-1].inject(li,'top');
		field.inject(li,'bottom');
		li.inject(this._input);
		
		return li;
	}
	
});


var BEnumerateBox = Class({
	Extends: AbstractBEditableTextBox,
	_size : 16.8,
	_input : null,
	_lang : null,

	 
	initialize: function(elm, strategy , factory, style){
		this.parent(elm, strategy , factory, style);
		this._n = 0;
		this._lang = this.laf.getLang();
		this.status = {
			isEnable : false
		};
		this._input = new Element('div');
		this._input.addClass(this.style.enum_container_inner);
		
		this._enum = new Element('div');
		this._enum.addClass(this.style.enumerator);
		
		this._enum_curr = new Element('span',{'style':'width:0%'});
		this._enum_curr.addClass(this.style.enum_curr);
		this._enum_curr.inject(this._enum);
		
		
		this._enum_value = new Element('div');
		this._enum_value.addClass(this.style.enum_val);
		
		this._input.adopt([this._enum, this._enum_value]);
		
		this._input.inject(this.element);
		
	},
	setImage : function(cl){
		this._enum.addClass(this.style[cl]);
		this._enum_curr.addClass(this.style[cl]);
	},
	
	setWidth : function(w){
		this._enum.set('style','width:'+w+'px');
		var x = this._value * this._size;
		this._enum_curr.set('style','width:'+x+'px');
	},
	
	getEnum : function(){
		return this._enum;
	},
	getValue : function(){
		return this._value;
	},
	
	setValue : function(v){
		this._value = v;
		this._enum_value.set('text',v);
	}

});


/**
 * EditableTextBox manipulator.
 * Trigger events: toEdit, toView
 * @class BEditableTextBox
*/
var BEditableTextBox = Class({
	Extends: AbstractBEditableTextBox,
	_input : null,
	_button : null,
	initialize: function(elm, strategy , factory, style){
		this.parent(elm, strategy , factory, style);
		this._input = null;
		this._button = null;
		this.status = {
			isEditMode : false
		};
		this.element.addEvent('click', this.toEdit.bind(this));
	},
	/**
	 * Turn the subject Element to edit mode
	 * Trigger the "toEdit" event.
	*/
	toEdit : function(event){
		if(!this.status.isEditMode && this.strategy.canBeTurnedToEdit(this, event) ){
			if( !this._input ){
				this._input = Element('input', {
				  'type' : 'text'
				});
			}
            this._input.setProperty('value',this.element.get('text'));
			
//            input.setControllerObject(this);
            this._input.inject(this.element, 'before');
            
            this.element.dispose();
//			this.titleBox = input;
//			input.setProperty('id','titleBox-on');
            this._input.addEvent('focus', (function(){
                this._input.addClass( this.style.inputInserting);
            }).bind(this));
            
            // Setup and inject the confirm button
            if(!this._button){
                this._button = Element( 'input' , {
		            'type':'button',
		            'value': this.laf.getLang().get('ok').toUpperCase()
		        });
            }
//            this._button.setControllerObject(this);
            this._button.inject(this._input,'after');
            this._button.addEvent('click', this.toView.bind(this));
            this._input.addEvents({
                'keydown' : function(e){
                    e = new Event(e);
                    if ( e.key == 'enter'){
                        e.stop();
                        if( this.getNext() )
                        	this.getNext().fireEvent('click');
                    }
                },
                'click': function(e){(new Event(e)).stopPropagation();},
                'mousedown': function(e){(new Event(e)).stopPropagation();}
            });
            this._input.focus();
//          this.getContextManager().disableSubmit();
            // remember that I'm in edit mode
            this.status.isEditMode = true;            
            this.fireEvent('toEdit', this);
        }
	},
	/**
	 * Turn the subject Element to view mode
	 * Trigger the "toView" event.
	*/
	toView : function(event){
		if( this.status.isEditMode && this.strategy.canBeTurnedToView(this, event)){
            this.element.set('text', this._input.getProperty('value').trim());
//            this.titleBox.addEvent('click', this._bind.toEdit );
            this.element.inject( this._input ,'before');
            // remove the button
            this._input.getNext().dispose();
            // remove the input box
            this._input.dispose();
            // remember that I'm in view mode
            this.status.isEditMode = false;
            // signal the change
            this.fireEvent('toView', this);
//            ctxMng.enableSubmit();            
        }
	},
	/**
	 * Returns the current value of the subject
	*/
	getValue : function(){
		if( $(this.element) ){
			return this.element.get('text');
		}else{
			return this._input.getProperty('value').trim();
		}
	},
	/**
	 * Sets the value of the subject
	*/
	setValue : function(value){
		if( $(this.element) ){
			return this.element.set('text', value);
		}else{
			return this._input.setProperty('value', value);
		}
	}
});



/**
 * BEditableTextarea manipulator. 
 * Trigger events: toEdit, toView
 * @class BEditableTextarea
 * 
*/
var BEditableTextarea = Class({
	Implements: Event,
//	ImplementsInterface: Manipulator,
	Extends: AbstractBEditableTextarea,
	_textarea : null,
	_button : null,
	_editToolbar: null,
	/**
	 * Initialize the object
	 * @constructor {public} initialize
	 * @param {Element} elm - The subject Element 
	 * @param {EditableTextareaStrategy} strategy - The manipulation strategy 
	 * @param {LookAndFeelFactory} factory - The look and feel factory, needed to create view Elements.
	 * @param {Object} style - An object with the skin classes
	*/
	initialize: function(elm, strategy , factory, style){
		this.parent(elm, strategy , factory, style);
		this._textarea = null;
		this._button = null;
		this.status = {
			isEditMode : false
		};
		this.element.addEvent('click', this.toEdit.bind(this));
	},
	/**
	 * Turn the subject Element to edit mode
	 * Trigger the onToEdit event.
	*/
	toEdit : function(event){
		if(!this.status.isEditMode && this.strategy.canBeTurnedToEdit(this, event) ){
	         this.fireEvent("toEdit", this);

//            var ctxMng = this.getContextManager()
//            ctxMng.disableSubmit();
//            this.expand();

            this._textarea = new Element('textarea', {
                'class' : this.style.textareaBase,
                'rows':'10'
            });
//            this._textarea.setControllerObject(this);
//            var textvalue = '';
/*            if ( ! this.titleBox.hasClass('empty') ){ 
              // try to get content not rendered ( wikitext) form data source collection
              textvalue = this.getContent().getText();
            }
 */
            this._textarea.setProperty('value', this.element.get("html"));
            this._textarea.addClass( this.style.inserting );
            this._textarea.inject(this.element,'before');
                  
            // Setup edit toolbar
            this._toolbar = new Element('div', {'class': this.style.editToolbar });
            this._toolbar.inject( this._textarea , 'before');
            this.laf.createEditToolbar( this._textarea, this._toolbar );
            
            this._textarea.addEvent('click', function(e){
               /*
                Stop propagation of click if it is performed on the textarea
                (situation: after a blur event due of a click on the edit toolbar )
                this.getPrevious is the edit toolbar
               */
               if( this.getPrevious().getChildren().length > 0){
                   e = new Event(e);
                   e.stop();
                   e.stopPropagation();
               }
            });
            // remove the title box div
            this.element.dispose();
//            this.titleBox = textarea;
            
            // Setup and inject the ok button
            this._button = new Element( 'input' , {
                'type':'button',
                'value': this.laf.getLang().get('ok').toUpperCase()
            });
//            this._button.setControllerObject(this);
            this._button.inject(this._textarea,'after');
            this._button.addEvent('click', this.toView.bind(this));
            // set the focus
            this._textarea.focus();
            
            // remember that I'm in edit mode
            this.status.isEditMode = true;
        }
	},
	/**
	 * Turn the subject Element to view mode
	 * Trigger the onToView event.
	*/
	toView : function(event){
		if( this.status.isEditMode && this.strategy.canBeTurnedToView(this, event)){
            // remove and destory the edit toolbar
            if( this._toolbar.hasClass( this.style.editToolbar) ){ // sanity check
                this._toolbar.destroy();
            }
            // remove the button
            this._button.dispose();
            
            // create the new titleBox 
//            this.titleBox.setControllerObject(this);
            this.element.injectBefore(this._textarea);
			this.element.set("text", this._textarea.getProperty("value"));
            this._textarea.dispose();
            // remember that I'm in view mode
            this.status.isEditMode = false;
		this.fireEvent("toView",this);
 		}
	},
	/**
	 * Returns the current value of the subject
	*/
	getValue : function(){
		if( $(this.element) ){
			return this.element.get('text');
		}else{
			return this._input.getProperty('value').trim();
		}
	},
	/**
	 * Set the text of the managed Element
	*/
	setValue : function(text){
		if( $(this.element) ){
			return this.element.set('text', text);
		}else{
			return this._textarea.set('text', text);
		}	
	}
});



/**
 * Concrete class for creating a popup menu.
 * Triggers: 
 * - "execute" event when a menuItem (with a command associated to) is clicked.
 * - "open" event when the menu is opened
 * - "close" event when the menu is closed
 * @class {abstract} BMenuBar
*/
var BMenuBar = new Class({
	Extends: AbstractBMenuBar,
	/**
	 * @constructor initialize
	 * @param {BFocusManager} focusManager - Focus manager needed for closing the menu
	 * @param {optional BMenuBar} parentMenu - A reference to the BMenuBar that incorporate
	 * this menu (if any)
	*/
	initialize: function( focusManager , parentMenu, style){
		this.parent(focusManager, parentMenu, style);
		this.element = new Element('div', {'class': this.style.menu});
	},
	/**
	 * Add an item to the MenuBar
	 * @function {public} addItem
	 * @param {String} text - The text to show
	 * @param {Boolean} asHtml - Indicates to treat the text as html
	 * @param {Command} command - The command to execute when user clicks the label.
	 * @param {optional String} tooltip - A tooltip to show when mouse is over the label.
	*/
	addItem: function(text, asHtml, command, tooltip ){
		var menuItem = new Element('a',{'style':'clear:right'});
		menuItem.adopt( new Element('span') );
		menuItem.getFirst().set( (asHtml)?"html":"text" , text);
		menuItem.addEvent('click', (function(){
			this.menuBar.fireEvent('execute');
			this.command.execute();
		}).bind({
			menuBar : this,
			command : command
		}));
		this.element.adopt(menuItem);
	},
	/**
	 * Add an item to the MenuBar
	 * @function {public} addItemWithMenuBar
	 * @param {Boolean} asHtml - Indicates to treat the text as html
	 * @param {String} text - The text to show
	 * @param {BMenuBar} nestedMenuBar - The BMenuBar to open when the item is overed.
	*/
	addItemWithMenuBar: function(text, asHtml, nestedMenuBar ){
		var menuItem = new Element('a',{'style':'clear:right'});
		menuItem.adopt(  new Element('span',{
							'class': this.style.subMenuArrow ,
							'style':'float:right'
						 }),
						 new Element('span'));
		menuItem.getLast().set( (asHtml)?"html":"text" , text);
		
		menuItem.addEvent('mouseover', (function(){
			this.nestedMenu.show();
		}).bind({
			menuBar: this,
			nestedMenu: nestedMenuBar
		}));
	
		menuItem.addEvent('mouseleave', (function(){
			this.nestedMenu.hide();
		}).bind({
			menuBar: this,
			nestedMenu: nestedMenuBar
		}));
			
	},
	addSeparator: function(){
		this.element.adopt( new Element("hr"));
	},
	open: function(where, container, hidden, position ){
		// if already opened quit
/*		if($(this.element))
			return;
		*/	
		// open the menu hanged to some Element
		if( $type(where) == 'element'){
			this.element.setStyles({
				'visibility':'hidden'
			});
			var whereCoords = where.getCoordinates(container);
//			var whereCoords = where.getCoordinates();
			var wherePos = where.getPosition();
			var whereSize = where.getSize();

			this.element.inject( (container)? container : $(window.document.body),'top');
			position = position || "bottom";
			if( position == 'bottom'){
				var elmPos = this.element.getPosition($("content"));
				var baseTopPos = whereCoords.bottom;// - this.element.getCoordinates(container).top;
//				var baseTopPos = wherePos.y;// - $(container).getPosition().y;
//				var baseLeftPos = whereCoords.left;// - ( this.element.getSize().x);

//				var baseLeftPos = whereCoords.left;// - ( this.element.getSize(container).x - whereSize.x);
//				var scrollOffset = (container) ? container.getScroll().y : 0;
				this.element.setStyles({ 
					'position':'relative',
					'display' : 'inline',
					'visibility':(!hidden)? 'visible' : 'hidden',
					'float':'right', 
					'top':  (baseTopPos).toInt()+"px", 
					'left':  0,//(baseLeftPos).toInt()+"px",  
					'margin-right': 0,
					'margin-bottom': '-'+ this.element.getSize().y+'px',
					'z-index': '150'
				});
				
				//workaround to place the menu in the correct position
				//FIXME This works only with Monobook -> check on the other MediaWiki Skins
				var elmPos = this.element.getPosition($("content"));
				var elmCoords = this.element.getCoordinates($("content"));
				var off_x = 4; // FIXME elmCoords.right - where.getCoordinates($("content")).right ;
				var elmSize = this.element.getSize();
				
				this.element.setStyles({
					'position':'absolute',
					'left' : (elmPos.x - off_x).toInt() +"px",
					'top' : (elmPos.y ).toInt() +"px"
				});
				
				
			}else{
				//TODO horizontal orientation
			}
			
		}else{
			// open the menu on mouse pointer
			this.element.setStyles({
				'position':'absolute',
				'top' : where.y.toInt()+"px",
				'left': where.x.toInt()+"px"
			});
			this.element.inject( (container)? container : $(window.document.body),'inside');
		}
		
		this.element.addEvent('mousedown', (function(){
			this.preventClosing();
		}).bind(this));
		
		this.fireEvent('open', this);
	},
	hide: function(){
		this.element.setStyle('display','none');
	},
	show: function(){
		this.element.setStyle('display','block');
	},
	close: function(){
		if(this.element){
			for(var i=0; i != this.nestedMenus.length; ++i){
				this.nestedMenus[i].getClearerCommand().execute();
			}
			this.element = this.element.destroy();
			this.fireEvent('close', this);
		}
	},
	empty: function(){
		if( this.element )
			this.element.empty();
	}
});


/**
 * @class BExpander
 * @extends AbstractBExpander
*/
var BExpander = new Class({
	Extends : AbstractBExpander,
	initialize: function( expandable, activator , lang ,  style, options){
		this.parent( expandable, activator , lang ,  style);
		this.activator.addClass(this.style.baseClass);
		this.activator.addClass(this.style.compress);
		this.activator.addEvent('click', this.expand.bind(this));
		options = options || {};
		if(options.expandOnBoot){
			this.expand();
		}
		else{
			this.status.isExpanded = true;
			this.compress();
		}
	},

	/**
	 * Expand the Element showing its content
	 * @function {public} expand
	*/
	expand : function(){
		if(! this.status.isExpanded ){
			this.expandable.setStyle('display','block');
			this.activator.setProperties({
				//'src' : bImages.get('cmd_compress','src'),
				//'alt': bImages.get('cmd_compress','alt'),
				'title': this.lang.get('cmd_compress')
			});
			this.activator.removeClass(this.style.expand);
			this.activator.addClass(this.style.compress);
			this.activator.removeEvents('click');
			this.activator.addEvent('click', this.compress.bind(this));
			this.status.isExpanded = true;
			this.fireEvent("expand", this);
		}
	},
	/**
	 * Compress the Element hiding its content
	 * @function {public} compress
	*/
	compress: function(){
		if(this.status.isExpanded ){
			this.expandable.setStyle('display','none');
			this.activator.setProperties({
				//'src' : bImages.get('cmd_compress','src'),
				//'alt': bImages.get('cmd_compress','alt'),
				'title': this.lang.get('cmd_expand')
			});
			this.activator.removeClass(this.style.compress);
			this.activator.addClass(this.style.expand);
			this.activator.removeEvents('click');
			this.activator.addEvent('click', this.expand.bind(this));
			this.status.isExpanded = false;
			this.fireEvent("compress", this);
		}
	}
	
});

/**
 * @class BAddElement
 * @extends AbstractBAddRemove
*/
var BAddElement = new Class({
	Extends : AbstractBAddRemove,
	_element : null,
	initialize: function( container, laf , lang ,  style, options){
		this.parent( container, laf , lang ,  style);
		options = options || {};
		switch (options.event) {
			case 'add':
					this._element = new Element('a', {
		        		'class': this.laf.getSkin().get("treeViewElement", 'base').expander,
		       			'href': B_VOID_FN,
		       			'text':'_',
		        		'title' : this.laf.getImageManager().get('cmd_add', 'title')
		    		}) ;
		    		this._element.addClass(this.style.baseClass);
		    		this._element.addClass(this.style.add);
		    		this._element.inject(this.container);
		    		this._element.addEvent('click', this.add.bind(this));
				break;
			case 'remove':
					this._element = new Element('a', {
		        		'class': this.laf.getSkin().get("treeViewElement", 'base').expander,
		       			'href': B_VOID_FN,
		       			'text': '_',
		        		'title' : this.laf.getImageManager().get('cmd_remove', 'title')
		    		}) ;
		    		this._element.addClass(this.style.baseClass);
		    		this._element.addClass(this.style.remove);
		    		this._element.inject(this.container);
		    		this._element.addEvent('click', this.remove.bind(this));
				break;
			case 'null':
					this._element = new Element('span', {
		        		'class': this.laf.getSkin().get("treeViewElement", 'base').expander,
		        		'style': 'cursor: default;'
		    		}) ;
		    		this._element.inject(this.container);
				break;
		}
	},

	/**
	 * @function {public} add
	*/
	add : function(){
		this.fireEvent('add', this);
	},
	/**
	 * @function {public} remove
	*/
	remove: function(){
		this.fireEvent('remove', this);
	}
});

/**
 * @class BAddPanel
 * @extends AbstractBExpander
*/
var BAddPanel = new Class({
	Extends : AbstractBExpander,
	initialize: function( expandable, activator , lang ,  style, options){
		this.parent( expandable, activator , lang ,  style);
		this.text = this.activator.getText();
		this.activator.addClass(this.style.baseClass);
		this.activator.addEvent('click', this.expand.bind(this));
		options = options || {};
		if(options.expandOnBoot){
			this.expand();
		}else{
			this.status.isExpanded = true;
			this.compress();
		}
	},

	/**
	 * Expand the Element showing its content
	 * @function {public} expand
	*/
	expand : function(){
		if(! this.status.isExpanded ){
			this.expandable.setStyle('display','block');
			this.expandable.getAllNext().each(function(item, index){
				item.setStyle('display','none');
			});
			this.activator.setProperties({
				'title': this.lang.get('cmd_hide')
			});
			var text = this.activator.getText();
			text.replace(this.lang.get('hide_panel'),'');
			this.activator.set('html',  this.lang.get('hide_panel')+" "+this.text);
			this.activator.removeEvents('click');
			this.activator.addEvent('click', this.compress.bind(this));
			this.status.isExpanded = true;
			this.fireEvent("expand", this);
		}
	},
	/**
	 * Compress the Element hiding its content
	 * @function {public} compress
	*/
	compress: function(){
		if(this.status.isExpanded ){
			this.expandable.setStyle('display','none');
			this.expandable.getAllNext().each(function(item, index){
				item.setStyle('display','block');
			});
			
			this.activator.setProperties({
				'title': this.lang.get('cmd_show')
			});
			var text = this.activator.getText();
			text.replace(this.lang.get('show_panel'),'');
			this.activator.set('html', this.lang.get('show_panel')+" "+this.text);
			this.activator.removeEvents('click');
			this.activator.addEvent('click', this.expand.bind(this));
			this.status.isExpanded = false;
			this.fireEvent("compress", this);
		}
	}
	
});

/**
 * @class BTextualTreeItemExpander
 * @extends AbstractBTextualTreeItemExpander
*/
var BTextualTreeItemExpander = new Class({
	Extends : AbstractBTextualTreeItemExpander,
	initialize: function( treeItem , lang ,  style, options){
		this.parent( treeItem , lang ,  style, options);
		//note: superclass initialize the attribute
		if( this.activator) {
			this.activator.addClass(this.style.baseClass);
			this.activator.addClass(this.style.compress);
			this.activator.addEvent('click', this.expand.bind(this));
			options = options || {};
			if(options.expandOnBoot)
				this.expand();
			else{
				this.status.isExpanded = true;
				this.compress();
			}
		}
		
	},

	/**
	 * Expand the Element showing its content
	 * @function {public} expand
	*/
	expand : function(){
		if(! this.status.isExpanded ){
			var btnSize = 0;
			var imgLogoSize = 0;
		    this.expandable.removeClass(this.style.compressedContent);
		    this.expandable.addClass(this.style.expandedContent);
			if( this.treeItem.getActionsButton())
				btnSize = this.treeItem.getActionsButton().getSize().x
			if( this.treeItem.getImgLogo()){
			    this.treeItem.getImgLogo().setStyle('float','left');
				imgLogoSize = this.treeItem.getImgLogo().getSize().x
			}
		    this.expandable.setStyles({
		       /* Set the correct with respect the current box width (5px of gap for sanity)*/
	//	        'max-width': (this.treeItem.getData().getSize().x - btnSize - imgLogoSize - 5)+'px',
		        'height' : 'auto'
		    });
			this.activator.setProperties({
				'title': this.lang.get('cmd_compress')
			});
			this.activator.removeClass(this.style.expand);
			this.activator.addClass(this.style.compress);
			this.activator.removeEvents('click');
			this.activator.addEvent('click', this.compress.bind(this));
			this.status.isExpanded = true;
			this.fireEvent("expand", this);
		}
	},
	/**
	 * Compress the BTextualTreeItem::titleBox. Note: a textual part needs a
	 * css definition of its height when compressed. This function only works
	 * on the width of the expandable element mantaing it correct respect to
	 * the image logo and the actions button (if they're showed).
	 * @function {public} compress
	*/
	compress: function(){
		if(this.status.isExpanded ){
		    this.expandable.removeClass(this.style.expandedContent);
		    this.expandable.addClass(this.style.compressedContent);
			var btnSize = 0;
			var imgLogoSize = 0;
			if( this.treeItem.getActionsButton())
				btnSize = this.treeItem.getActionsButton().getSize().x
			if( this.treeItem.getImgLogo()){
			    this.treeItem.getImgLogo().setStyle('float','left');
				imgLogoSize = this.treeItem.getImgLogo().getSize().x
			}
			this.expandable.removeProperty("style"); // for sanity
		    this.expandable.setStyles({
		       /* Set the correct with respect the current box width (5px of gap for sanity)*/
		//        'max-width': (this.treeItem.getData().getSize().x - btnSize - imgLogoSize - 5)+'px',
		    });
			this.activator.setProperties({
				//'src' : bImages.get('cmd_compress','src'),
				//'alt': bImages.get('cmd_compress','alt'),
				'title': this.lang.get('cmd_expand')
			});
			this.activator.removeClass(this.style.compress);
			this.activator.addClass(this.style.expand);
			this.activator.removeEvents('click');
			this.activator.addEvent('click', this.expand.bind(this));
			this.status.isExpanded = false;
			this.fireEvent("compress", this);
		}
	}
	
});

/**
 * Concrete implementation of AbstractBSelectable
 * @class BSelectable
 * @extends AbstractBSelectable
*/
var BSelectable = new Class({
	Extends: AbstractBSelectable,
	initialize: function(el, style){
		this.parent(el, style)
		this.element.addEvent('shiftmousedown', (function(event){
			this.fireEvent('shiftKeySelection', [this, event]);
		}).bind(this));
		this.element.addEvent('ctrlmousedown', (function(event){
			this.fireEvent('controlKeySelection', [this, event]);
		}).bind(this));
		this.element.addEvent('singleselection', (function(event){
			this.fireEvent('singleSelection', [this, event]);
		}).bind(this));
	},
	/**
     * Transform a Selectable element to looks like it is selected
     * @function {public} transformToSelected
    */
    transformToSelected:function(){
    	this.element.addClass(this.style.selected);
    },
    /**
     * Transform a Selectable element to looks like it is unselected
     * @function {public} transformToUnselected
    */
    transformToUnselected: function(){
    	this.element.removeClass(this.style.selected);
    }
});

/**
 * BList to create a list of <ul><li/></ul> of error 
 * @class BList
*/
var BListError = new Class({
	_skin:null,
	_root:null,
	_element:null,
	initialize: function(root,skin){
		this._skin = skin;
		this._root = root;
		this._element = new Element('ul');
		this._element.addClass(this._skin.listError);
		this._element.inject(this._root);
	},
	addError: function(text){
		var li = new Element('li',{
			'class' : this._skin.errorDialog,
			'html' : text
			});
		li.inject(this._element);
	},
	addWarning: function(text){
		var li = new Element('li',{
			'class' : this._skin.warningDialog,
			'html' : text
			});
		li.inject(this._element);
	},
	getElement : function(){
		return this._element;
	}
});


/**	 * @param {Element} container - Container Element where to inject the menu (useful for focus scoping)

 * Provide a class for the creation of a tree view representation of something.
 * It creates the structure depending of the input parameters and provide getters
 * for the access of all the structure parts that allow client to set the value
 * of every view Elements.
 * @class {abstract} BTreeViewElement
*/
var BTreeViewElement = new Class({
	Extends: AbstractBTreeViewElement,
	/**
	 * Initialize the class
	 * @constructor {public} initialize
	 * @param {String} treeViewItemType - A string with the tree view item type.
	 *	Possible values : 
	 * <ul>
	 * <li>"normal" (default) : a particular structured element,with possibility
	 *  to set an expander, an icon, a title box, a content and a menuButton
	 * (right-top corner)</li> 
	 * <li>"textPart" : a variation of the normal type for simple textual elements 
	 *	that doesn't have nothing but textual content (eg. text notes, guide's
	 * specific components, ..)</li>
	 * <li>"formPart" : a variation of the normal type to create a form, the 
	 *	handle div is splitted in two sides: a label and an input element.</li>
	 * <li>"empty" : an empty container without any other sub component
	 *  (something like an html "li" element ). If you pass this type all other 
	 *  parameters are ingnored (use it if you need to directly setup the
	 *	internal BTreeViewElement structure).</li> 
	 * </ul>
	 * @param {LookAndFeelFactory} laf - The LookAndFeelFactory object needed to
	 *	 access to lang and to others visual widget
	 * @param {Object} style - The style classes for this BTreeViewElement
	 * @param {Object} options - Stucture with options
	 * @... {optional Boolean} isRoot - Indicates if this is the root node of a 
	 *		subtree logically autonomous respect to the others (eg. the content 
	 *		subtree of a guide), default false. If true no other options is 
	 *		cosidered because a root node is associated with a container div.
	 * @... {optional Boolean} anchoredTitle - Indicates if the titleBox must be
	 *		 a link to the resource's URI, default false.
	 * @... {optional Object} anchoredTitleProperties - An object with the 
	 *		properties of the anchored title:
	 *			<ul>
	 *			   <li> String src - The uri that anchor must point to</li>
	 *			   <li> String title - A text to show as the title properties 
	 *								of the anchor</li>
	 *			</ul>
	 * 			Works only if anchoredTitle is true.
	 * @... {optional Boolean} imgLogo - Indicates to create or not the imgLogo,
	 *		default true.
	 * @... {optional Object} imgLogoProperties - An object with properties of the logo.
	 *			<ul>
	 *			   <li> String src - The uri that anchor must point to</li>
	 *			   <li> String title - A text to show as the title properties of the anchor</li>
	 *			   <li> String alt - A text to show as alternative when browser could not display the image</li>
	 *			</ul>
	 * @... {optional Boolean} expander - Indicates to create or not the expander logo, default true.
	 * @... {optional Boolean} expandOnBoot - Indicates to expand the tree element on statup
	 * @... {optional Boolean} addElement - Indicates to set "add" or "remove" event
	 * @... {optional Boolean} actionsButton - Indicates to create or not the actionsMenuButton, default false.
	 *	
	*/

	initialize: function( treeViewItemType , laf, style, options){
		this.style = $merge( this.style, style);
		this.laf = laf;
		
		options = options || {};
		options.textPartFullView = ( options.textPartFullView === false)? false : true;
		
		if( options.isRoot){
			this.element = new Element('div', {'class' : this.style.baseTreeRoot });
			this.content = this.element;
			return this;
		}
		
		// if not the root and not the empty type build up every part
		this.element = new Element('div',{'class' : this.style.baseTreeItem });
		if( treeViewItemType != 'empty'){
			this.handle = new Element('div', {'class' : this.style.handle});
			this.data = new Element('div', { 'class' : this.style.data});
			this.extras = new Element('div', {'class':this.style.extras});
			this.content = new Element('div', {'class':this.style.content} );
		}else{
			// the content is created (for compatibility with the composite structure) but not injected
			this.content = new Element('div', {'class':this.style.content} );
			options.expander = false;
		}
		
		
		if( options.anchoredTitle ){
			this.titleBox = new Element( 'a',{
				'class': this.style.titleBox ,
				'href' : (options.anchoredTitleProperties) ? options.anchoredTitleProperties.src : '#',
				'title': (options.anchoredTitleProperties) ? options.anchoredTitleProperties.title : ''
			});
		}else{
			this.titleBox = new Element( 'div',{
				'class': this.style.titleBox 
			});
		}
		
		// create the optional components
		var customParts = {
		    actionsButton : ($defined(options.actionsButton) ? options.actionsButton : false),
		    imgLogo : ($defined(options.imgLogo) ? options.imgLogo : true),
		    expander : ($defined(options.expander) ? options.expander : true)
		};
		if( customParts.actionsButton ){
		    this.actionsButton = new Element('a' ,{
		        'class': this.style.actionsButton,
		        'href': B_VOID_FN,
		        //'src': bImages.get('cmd_openActionsMenu', 'src'),
		        //'alt':bImages.get('cmd_openActionsMenu', 'alt'),
		        'title' : this.laf.getImageManager().get('cmd_openActionsMenu', 'title')
		    });
		    this.actionsButton.addEvent('click',(function(e){
					e.preventDefault();
					this.fireEvent('menuRequest',this);
				}).bind(this));
		} else {
			this.actionsButton = null;
			this.element.addEvent('rightclick',(function(e){
					e.preventDefault();
					this.fireEvent('menuRequest',this);
			}).bind(this));
		}
		if( customParts.imgLogo ){
		    this.imgLogo = new Element('img',{'class': this.style.imgLogo}); // other properties will be defined later
		    if(options.imgLogoProperties){
		    	this.imgLogo.setProperties({
						'src': options.imgLogoProperties.src,
						'alt': options.imgLogoProperties.alt,
						'title' : options.imgLogoProperties.title
					});
		    }else{
		    	throw "Cannot set image properties because no values has been passed in the options."
		    	return;
		    }
		} else {
			this.imgLogo = null;
		}
		if( customParts.expander ){
		    this.expander = new Element('a', {
		        'class':this.style.expander,
		        'href': B_VOID_FN,
		        //'src': bImages.get('cmd_expand', 'src'),
		        //'alt':bImages.get('cmd_expand', 'alt'),
		        'text' : '_',
		        'title' : this.laf.getImageManager().get('cmd_expand', 'title')
		    }) ;
		}else{ 
			this.expander = null;
			this.titleBox.setProperty("style","display:inline");
		}

		// build the appropriate structure
		if( treeViewItemType == 'empty'){
			this.element.adopt(this.imgLogo);
		}else if( treeViewItemType == 'textPart'){
		
			this.element.addClass( this.style.baseText);
		
			this.element.adopt([ this.handle, this.data ]);
			this.data.adopt( [ this.actionsButton , this.imgLogo ,this.titleBox , this.content ,  this.extras ]);
			this.handle.adopt( this.expander );
			// for text we declare a RoundedCorner with no rounded corners, just to have the same html structure of others elements
			this.laf.createRoundedCorners(this.element, { corners:[]});
	
			if(this.expander){
				this._expandableObject = this.laf.createTextualTreeItemExpander(this, "tree");
				this._expandableObject.addEvents({
					'expand' : (function(){
						this.fireEvent('expand', this);
					}).bind(this),
					'compress' : (function(){
						this.fireEvent('compress',this);
					}).bind(this)
				});
			}
		}else{
			// normal type of output
		    this.element.adopt([ this.handle, this.data ]);
		    if(treeViewItemType == 'formPart'){
		    	// specific form type elements
		    	this.leftHandle = new Element('div', {'class' : this.style.leftHandle});
		    	this.rightHandle = new Element('div', {'class' : this.style.rightHandle});
		    	
		    	this.leftHandle.adopt([ this.expander, this.imgLogo, this.titleBox]);
		    	var s = new Element('span',{'class' : this.style.rightSpan});
		    	//, {'style' : 'margin: 0px 0px 2px; padding: 0px; clear: both; display: block;'});
		    	this.handle.adopt([ this.leftHandle, this.rightHandle, s]);
		    } else {
		    	 this.handle.adopt([ this.expander, this.imgLogo, this.titleBox]);
		    }
		    this.data.adopt([ this.content, this.extras]);
		    this.laf.createRoundedCorners( this.element );
		    /* inject the menu activator before the rounded corner top div
		     (this is combined with css to have the correct layout)
		     FIXME it's not a good approach cause it strictly depends on css
		     */
	    	if( this.actionsButton )
	        	this.actionsButton.inject(this.element, 'top'); 
	        	
			if(this.expander){
				this._expandableObject = this.laf.createExpander( this.content, this.expander, "tree" ,{expandOnBoot : options.openStartUp});
				this._expandableObject.addEvents({
					'expand' : (function(){
						this.fireEvent('expand');
					}).bind(this),
					'compress' : (function(){
						this.fireEvent('compress');
					}).bind(this)
				});
			}
	    }
	},
	/**
	 * @var {readonly} element - The Element container representing the ModelComponent tree view
	*/
	element : null,
	/**
	 * Returns the content Element.
	 * @function {public} getContent
	*/
	getContent : function(){ return this.content; },
	/**
	 * Returns the data wrapper Element.
	 * @function {public} getData
	*/
	getData: function(){ return this.data;},
	/**
	 * Returns the "handle" part, that is the main Element where the user drag this TreeViewElement
	 * @function {public} getHandle
	*/
	getHandle: function(){ return this.handle;},
	/**
	 * Returns the extra Element.
	 * @function {public} getExtra
	*/
	getExtra : function(){return this.extras;},
	/**
	 * Returns all the "handles" parts as an array, that is the Elements where the user can drag this TreeViewElement.
	 * This function is provided in the case one sublcass needs more than one handles.
	 * @function {public} getAllDraggableHandles
	*/
	getAllDraggableHandles: function(){
		return [this.handles, this.element.getFirst()];
	},
	/**
	 * Returns the image logo Element (if any)
	 * @function {public Element} getImgLogo
	*/
	getImgLogo : function(){ return this.imgLogo; },
	
	/**
	 * Set the image logo (if displayed) of this Element to the one passed
	 * @function {public} setImgLogo 
	*/
	setImgLogo : function(img){
		if(img && img.src){
			this.imgLogo.setProperties({
				src : img.src,
				title : img.title,
				alt : img.alt
			}); 
		}
	},
	
	/**
	 * Returns the expandr Element.
	 * @function {public} getExpander
	*/
	getExpander : function(){ return this.expander; },
	/**
	 * Returns the title box Element.
	 * @function {public} getTitleBox
	*/
	getTitleBox : function(){ return  this.titleBox; },
	/**
	 * Returns the actions button Element.
	 * @function {public} getActionsButton
	*/
	getActionsButton : function(){return this.actionsButton},

		
	/** @var {BExpander} _expandableObject */
	_expandableObject : null,
	expand: function(){
		if(this.expander){
			this._expandableObject.expand();
		}
	},
	compress: function(){
		if(this.expander){
			this._expandableObject.compress();
		}
	},
	isExpanded: function(){
		return (this._expandableObject)? this._expandableObject.isExpanded() : true;
	},
	
	/**
	 * Show an action menu MenuBar object associating it with this element.
	 * @function {public} showActionsMenu
	 * @param {MenuBar} actionsMenu - The actions menu to show
	 * @param {Element} container - Container Element where to inject the menu (useful for focus scoping)
	 * @param {optional Object} pos - An {x,y} object representing the coordinates where to show the actions menu
	 * when the actions button has not been created. If not passed, the Event.page ones saved at the previously rightmousedown
	 * will be used.
	 TODO capire se serve seriamente sta funzione o si può semplicemente generare
	*/
	showActionsMenu: function(actionsMenu, container , pos){
		if( this.actionsButton ){
			actionsMenu.open(this.actionsButton, container );
			this.element.addEvent("mousedown",(function(){
			
			}))
		}else{
			actionsMenu.open( null, container, false , pos || this._mousePosition );
		}
	},
	/**
	 * Cache the Event.page coordinates if there's a rightclick for using it when showing an action menu.
	 * @var {private} _mousePosition 
	*/
	_mousePosition:{x:0,y:0},
	

	
	/**
	* Return the number of TreeViewElement contained in this one. Useful to check 
	* if a TreeViewElement looks empty.
	* @function {Int} getContainedTreeViewElementsCount
	* @return The number of contained TreeViewElementsCount
	*/
	getContainedTreeViewElementsCount: function(){
		return this.getContent().getElements( '.'+this.style.baseTreeItem.trim()).length;
	},
	
	getFormBox : function(){return this.rightHandle;}
});



/*
#############################################################################
#							SKIN - LANG - IMAGES  							#
*/


/**
* Skin library manager. Provides a unified interfaces to get the css classes
* used in the look and feel management.
* @class BSkin
*/
var BSkin = new Class({
	_skin : null,
	initialize: function( skinLib){
        this._skin = skinLib || bSkinLib;
    },
    /**
     * Return an object with the styles of a particular element.
     * @function {public} get
     * @param {string} key - The key of the images
     * @param {optional string} ns - The namespace of the key. 
     *  This parameters allow you to do two things:<ol><li>
     *	Allows you to define particular namespace for reffering to particular kind
     *	of skin properties to maintains your code clearer. For example, a namespace
     *	"id" could be used to refers to the id value of visual component.</li>
     *  <li>Allows you to define different style objects to be used in the same skin library 
     *	on the same kind of Elements but in different representation context. For
     *  example an editable textarea of a textual node could looks different from 
     *	the one of the comments to an article, but their two styles object
     *	are included in the same skin library. This parameter allows the interaction
     *	logic to specify at runtime what style your Element should use.</li>
     * </ol>
     * @return an object with the styles of a particular element
    */
    get: function(key,  ns){
    	ns = ns || "base";
        return this._skin[ns+":"+key];
    }
});

/**
 * Language manager for i18n support.
 * @class BLang
*/
var BLang = new Class({
    _lang: null,
    _langCode: '',    
    initialize: function( langLibObject ){
        this._lang = langLibObject || bLangLib;
        this._langCode = this._lang.B_LANG_CODE;
    },
    /**
     * Return the string message of a key
     * @function {public String} get
     * @param {string} key - The key of the message
     * @param {optional array} par - An array with the paremeter to insert in the message.
     *                      <b>note :</b> this function doesn't check the order so pass them correctly.
     * @param {optional string} ns - The namespace of the key (for future use)
    */
    get: function(key, par,  ns){
        var msg = this._lang[key];
        if( par && msg ){
        	if($type(par) != 'array')
        		par = [par];
            for( var i = 0; i != par.length ; ++i){
                msg = msg.replace( '$_'+ (i+1).toInt() , par[i]);
            }
        }
        return msg;
    },
    getLangCode: function(){
    	return this._langCode;
    }
});

/**
 * Images manager. It's a adapter for images lib object, it provide an abstraction
 * to get an image's path or property to insert it in the html dom.
 * TODO We should perform a preload of the needed images.
 * @class BImages 
*/
var BImages = new Class({
    _images: null,
    _lang : null,
    /**
     * @constructor initialize
     * @param {Object} imagesLib - Byog images library object
     * @param {BLang} lang - A BLang object to get the "alt" and "title" images' properties at boot
    */
    initialize: function( imagesLib , lang ){
        this._images = new Hash(imagesLib);
        this._lang = lang;
        this._applyLang();
    },
    /**
     * Return an image's properties objects form its key.
     * @function {public} get
     * @param {string} key - The key of the images
     * @param {optional string} par - The string key of the property to retrieve in 'src','alt','title'.
     * @param {optional string} ns - The namespace of the key (for future use)
     * @return An object with src, alt and title properties for the image.
    */
    get: function(key, par,  ns){
        var img = this._images[key];
        if( par )
            return img[par];
        else
            return img;
    },
    /**
     * @function {private} _applyLang
    */
 	_applyLang: function(){
    	var translate = function(str, bLang){ 
    	//FIXME this function works only with one lang keyword for string
    		var key_b, key_e;
    		if( (key_b = str.indexOf("{{")) >= 0){
    			key = str.substr(key_b+2);
    			key_e = key.indexOf("}}");
    			key = key.substr(0, key_e);
    			return bLang.get(key);
    		}
    		return str;
    	}
    	this._images.each(function(image , key, images){
			images.set(key, {
				alt :  translate( image.alt, this._lang),
				src : image.src,
				title : translate(image.title, this._lang)
			});
    	},this);
    }
});





/*
#							SKIN - LANG - IMAGES						      #
###############################################################################
*/





/**
 * Class for instanciating an edit toolbar, useful for textual input when user
 * is supposed to insert also wikitext.
 * The methods are an overload by copy/paste of the Mediawiki's default functions
 * for creating the edit toolbar of their default editor (the textarea).
 * <b>Note</b> Probably Mediawiki's team will make some update to their functions
 * so we should upate ours if something doesn't work.
 * @class BMediaWikiEditToolbar 
*/
var BMediaWikiEditToolbar = new Class({
    /**
     * Create the edit toolbar. Is the overloading (by copy/paste) of the
     * Mediawiki mwSetupToolbar function included in Common.js .
     * It uses the global mediawiki variables:
     * - mwEditButtons
     * - mwCustomEditButtons
     * @function initialize
    */
    initialize: function ( textarea, parent ) {
        // # BEGIN of byog modified code
        if (!textarea) {
            return false;
        }
        parent = (parent)? parent : textarea.parentNode;
        // # END of byog modified code
	
        // Don't generate buttons for browsers which don't fully
        // support it.
        if (!(document.selection && document.selection.createRange)){
        	try{
			if( textarea.selectionStart === null) {
	        	        return false;
                	}
        	}catch(e){}
        }
        
        for (var i = 0; i < mwEditButtons.length; i++) {
            // # BEGIN of byog modified code
            this._insertEditButton( mwEditButtons[i], parent, textarea );
            // # END of byog modified code
	}
        for (var i = 0; i < mwCustomEditButtons.length; i++) {
            // # BEGIN of byog modified code
            this._insertEditButton( mwCustomEditButtons[i], parent, textarea );
            // # END of byog modified code
        }
        return true;
    },
    /**
     * Insert an edit button in the toolbar Element.
     * It's an overloading (by copy/paste) of Mediawiki's mvInsertButton function
     * @function {private} _insertEditButton
    */
    _insertEditButton: function ( item, parent, textarea, options) {
		var image = document.createElement("img");
		if( options){
		            image.width = options.width;
		            image.height =options.height;
		}
		    image.width = image.width ? image.width : 23;
		    image.height = image.width ? image.width : 22;
		    image.className = "mw-toolbar-editbutton";
		if (item.imageId) image.id = item.imageId;
		image.src = item.imageFile;
		image.border = 0;
		image.alt = item.speedTip;
		image.title = item.speedTip;
		image.style.cursor = "pointer";
		    
		    // # BEGIN of byog modified code
		image.onclick = (function(e) {
		        this.insertTags(item.tagOpen, item.tagClose, item.sampleText, textarea );
		        (new Event(e)).stopPropagation();
		        return false;
		}).bind(this);
		    // # END of byog modifed code
		    
		parent.appendChild(image);
		return true;
    },
    /**
     * Overloading of the Mediawiki's mwInsertTags function. It's the handler
     * of click event on toolbars' buttons.
     * Mediawiki's doc is : "Apply tagOpen/tagClose to selection in textarea, use sampleText instead
     * of selection if there is none" .
     * @function {public} insertTags
    */
    insertTags: function (tagOpen, tagClose, sampleText, targetTextarea) {
		// # BEGIN of byog modified code
		var txtarea = targetTextarea;
		    // # END of byog modified code
	
		    var selText, isSample = false;

		if (document.selection  && document.selection.createRange) { // IE/Opera

			//save window scroll position
			if (document.documentElement && document.documentElement.scrollTop)
				var winScroll = document.documentElement.scrollTop
			else if (document.body)
				var winScroll = document.body.scrollTop;
			//get current selection  
			txtarea.focus();
			var range = document.selection.createRange();
			selText = range.text;
			//insert tags
			checkSelectedText();
			range.text = tagOpen + selText + tagClose;
			//mark sample text as selected
			if (isSample && range.moveStart) {
				if (window.opera)
					tagClose = tagClose.replace(/\n/g,'');
				range.moveStart('character', - tagClose.length - selText.length); 
				range.moveEnd('character', - tagClose.length); 
			}
			range.select();   
			//restore window scroll position
			if (document.documentElement && document.documentElement.scrollTop)
				document.documentElement.scrollTop = winScroll
			else if (document.body)
				document.body.scrollTop = winScroll;

		} else if (txtarea.selectionStart || txtarea.selectionStart == '0') { // Mozilla

			//save textarea scroll position
			var textScroll = txtarea.scrollTop;
			//get current selection
			txtarea.focus();
			var startPos = txtarea.selectionStart;
			var endPos = txtarea.selectionEnd;
			selText = txtarea.value.substring(startPos, endPos);
			//insert tags
			checkSelectedText();
			txtarea.value = txtarea.value.substring(0, startPos)
				+ tagOpen + selText + tagClose
				+ txtarea.value.substring(endPos, txtarea.value.length);
			//set new selection
			if (isSample) {
				txtarea.selectionStart = startPos + tagOpen.length;
				txtarea.selectionEnd = startPos + tagOpen.length + selText.length;
			} else {
				txtarea.selectionStart = startPos + tagOpen.length + selText.length + tagClose.length;
				txtarea.selectionEnd = txtarea.selectionStart;
			}
			//restore textarea scroll position
			txtarea.scrollTop = textScroll;
		} 

		function checkSelectedText(){
			if (!selText) {
				selText = sampleText;
				isSample = true;
			} else if (selText.charAt(selText.length - 1) == ' ') { //exclude ending space char
				selText = selText.substring(0, selText.length - 1);
				tagClose += ' '
			} 
		}

    }
});


//FIXME
var BGuidePartTab = new Class({
	element: null,
	options: {
		head: false,
		activator: false,
		activatorProperties:{
			exclusive : false,
			choices : null
		}
	},
	style: {
		head: null,
		content: null
	},
	initialize: function( type, style, options ){
		this.style = style;
		this.element = new Element("div",{"class": this.style.baseClass});
		
		options = options || {};
		options = {
			head: options.head || false,
			activator: options.activator || false
		};

		
		
		
		if(type=="free"){
		
		}else if(type=="mediating"){
			
		}
		
	},
	toEdit: function(){
	},
	toView: function(){}
	
});


/** #################################
 *   Exposing module component names
*/

this.BLookAndFeelFactory = BLookAndFeelFactory;
this.BEditableTextBox = BEditableTextBox;
this.BEditableTextarea = BEditableTextarea;
this.BMenuBar = BMenuBar;
this.BExpander = BExpander;
this.BSelectable = BSelectable;

this.BSkin = BSkin;
this.BImages = BImages;
this.BLang = BLang;

/** ################################# */
/*]]>*/
