// "child elements" for the Control class

// the SPECIAL OBJECT control.
// define events, attributes, and methods to handle a "special object"
// extraParams is the JSON-encoded version of the defined array from PHP
// so the structure should be:
// 	- an 2 level deep array, with
// 		first level: assoc array with fieldset names as the key
//		second level : assoc array with conteined objects by each fieldset, and their respective propreties.
//
// when creating new controls, you may want to activate the "undetected foreign attribute" attribute, so you will get a list of any uninterpreted attributes
//		set this.debug_undetectedAttribute=true;
//
//




function _control_specialObject( parent ,extraParams){// constructor for the main class
	if(this.name==null || !this.name )	{
		this.name='_control_specialObject';
	}

	extraParams = (extraParams?extraParams:{});

	// count the instances of this object. we may need thi to keep track of already created objects of this type.
	var instances=Controls.searchParentElements({_objectName:parent.attributes._objectName}).length+1;

	if(extraParams.maxInstances){
		if(instances>extraParams.maxInstances){
			// autodestroy means that we should this cotrold should not exist. the parent will remove us asap
			this.autodestroy=true;
		}
	}

	this.debug=false;
	this.debug_undetectedAttribute=false;

	this.parent=parent;
	this.version='1.0.0.0';
	this._heap=new Array();

	this._heap['functionCache']=new Array();
	this._heap['scheduleInitialization']=new Array();

	this.attributes={};
	this.defaultAttributes={};
	this.controls={};

	this.extraParams=extraParams; //used for re-validating the input data. Should only be used once. after this is parsed, his contents ar reformatted and stored in attributePropreties
	this.attributePropreties={};
	this.defaults=this.getDefaults();

	this.autoApplyUpdateDelay=0.25;
}

// initialize the control with "default" pre-specified values
_control_specialObject.prototype.initialize=function (attributes,attributeTypes){
	if(attributes==null)attributes={};
	var useDefaults=false;
	if(this.parent.attributes.useDefaults){
		useDefaults=true;
	}

	for(i in this.extraParams){
		if(this.extraParams[i].attributes){
			for(j in this.extraParams[i].attributes){
				var attributeName=j;
				var attribute=this.extraParams[i].attributes[j];

				this.attributePropreties[attributeName]=attribute;

				var def=null;

				if(this.attributePropreties[attributeName]){
					def=this.attributePropreties[attributeName]['default'];
					if(Object.isUndefined(def))def=null;
				}

				switch(attribute.type){
					case 'float':
					case 'int':
						if(def==null)def=0;

						this.attributes[attributeName]=(attributes[attributeName]?attributes[attributeName]:def);

						if(	this.attributePropreties[attributeName].validations
							&& this.attributePropreties[attributeName].validations.min!=null
							&& this.attributes[attributeName]<this.attributePropreties[attributeName].validations.min)
							{
							this.attributes[attributeName]=this.attributePropreties[attributeName].validations.min;
						}

						if(	this.attributePropreties[attributeName].validations
							&& this.attributePropreties[attributeName].validations.max!=null
							&& this.attributes[attributeName]>this.attributePropreties[attributeName].validations.max)
							{
							this.attributes[attributeName]=this.attributePropreties[attributeName].validations.max;
						}

						if(attribute.type=='float'){
							this.attributes[attributeName]=(Number(this.attributes[attributeName]).toFixed(3));
						}else{
							this.attributes[attributeName]=(Number(this.attributes[attributeName]).toFixed(0));
						}
					break;

					case 'color':
						if(def==null )def='#000000';
						this.attributes[attributeName]=(attributes[attributeName]?attributes[attributeName]:def);
					break;

					case 'text':
						if(def==null || !useDefaults)def='';
						this.attributes[attributeName]=(attributes[attributeName]?attributes[attributeName]:def);
					break;

					case 'textarea':
						if(def==null || !useDefaults)def='';
						this.attributes[attributeName]=(attributes[attributeName]?attributes[attributeName]:def);

						this.scheduleInitialization('grippie',attributeName);
					break;

					case 'fontFamily':
						if(def==null || !useDefaults)def='';
						this.attributes[attributeName]=(attributes[attributeName]?attributes[attributeName]:def);
					break;

					case 'fontSize':
						if(def==null)def='12';
						this.attributes[attributeName]=(attributes[attributeName]?attributes[attributeName]:def);
					break;

					case 'alignment':
						if(def==null)def='left';
						this.attributes[attributeName]=(attributes[attributeName]?attributes[attributeName]:def);
					break;

					case 'verticalAlignment':
						if(def==null)def='top';
						this.attributes[attributeName]=(attributes[attributeName]?attributes[attributeName]:def);
					break;

					case 'textStyle':
						if(def==null || !useDefaults){
							def=new Array();
						}else{
							def=String(def).split('|');
							def=def.uniq();
						}

						this.attributes[attributeName+'_bold']=(attributes[attributeName] && attributes[attributeName]['bold']?attributes[attributeName]['bold']:			def.indexOf('bold')>-1);
						this.attributes[attributeName+'_italic']=(attributes[attributeName] && attributes[attributeName]['italic']?attributes[attributeName]['italic']:		def.indexOf('italic')>-1);
						this.attributes[attributeName+'_underline']=(attributes[attributeName] && attributes[attributeName]['underline']?attributes[attributeName]['underline']:def.indexOf('underline')>-1);

						// transform this attribute in multiple text styles
						this.attributePropreties[attributeName+'_bold']=new Array();
						this.attributePropreties[attributeName+'_bold']['type']='textStyle';
						this.attributePropreties[attributeName+'_bold']['style']='bold';

						this.attributePropreties[attributeName+'_italic']=new Array();
						this.attributePropreties[attributeName+'_italic']['type']='textStyle';
						this.attributePropreties[attributeName+'_italic']['style']='italic';

						this.attributePropreties[attributeName+'_underline']=new Array();
						this.attributePropreties[attributeName+'_underline']['type']='textStyle';
						this.attributePropreties[attributeName+'_underline']['style']='underline';

					break;

					case 'lineStyle':
					case 'selector':
						if(def==null)def='';

						this.attributes[attributeName]=(attributes[attributeName]?attributes[attributeName]:def);
					break;

					case 'checkbox':

						if(def==null){
							def=new Array();
						}else{
							def=String(def).split('|');
							def=def.uniq();
						}

						for(checkbox in this.attributePropreties[attributeName].list){
							if(!Object.isFunction(this.attributePropreties[attributeName].list[checkbox])){
								var id=attributeName+'_'+checkbox+'_';

								if(useDefaults){
									this.attributes[id]=(attributes[attributeName] && attributes[attributeName][checkbox]?attributes[attributeName][checkbox]:def.indexOf(checkbox)>-1);
								}else{
									this.attributes[id]=(attributes[attributeName] && attributes[attributeName][checkbox]?attributes[attributeName][checkbox]:false);
								}
							}
						}
					break;

					case 'sortable':
					case 'sortable2':
						// init the sortable control
						if(def==null)def="";
						def=String(def).split('|');
						this.attributes[attributeName]=(attributes[attributeName]?attributes[attributeName]:def);

						this.scheduleInitialization('sortable',attributeName);

						// init the checkboxes
						var attribute2Name=null;
						if(this.attributePropreties[attributeName]['extra'] && this.attributePropreties[attributeName]['extra']['name']){
							attribute2Name=this.attributePropreties[attributeName]['extra']['name'];
						}

						if(attribute2Name){
							var def2=null;

							if(this.attributePropreties[attributeName]['extra'] && this.attributePropreties[attributeName]['extra']['default']){
								def2=this.attributePropreties[attributeName]['extra']['default'];
								if(Object.isUndefined(def2))def2=null;
							}
							if(def2==null){
								def2=new Array();
							}else{
								def2=String(def2).split('|');
								def2=def2.uniq();
							}

							for(checkbox in this.attributePropreties[attributeName].list){
								if(!Object.isFunction(this.attributePropreties[attributeName].list[checkbox])){
									var id=attribute2Name+'_'+checkbox+'_';
									if(useDefaults){
										this.attributes[id]=(attributes[attribute2Name] && attributes[attribute2Name][checkbox]?attributes[attribute2Name][checkbox]:def2.indexOf(checkbox)>-1);
									}else{
										this.attributes[id]=(attributes[attribute2Name] && attributes[attribute2Name][checkbox]?attributes[attribute2Name][checkbox]:false);
									}

									// make a "fake-add" of this attribute to the attributes list
									this.attributePropreties[attribute2Name]={type:'sortable2'};
								}
							}
						}
					break;

					case '4borders':
						if(useDefaults){
							this.attributes[attributeName+'_top']=(attributes[attributeName] && attributes[attributeName]['top']?attributes[attributeName]['top']:			def.indexOf('top')>-1);
							this.attributes[attributeName+'_bottom']=(attributes[attributeName] && attributes[attributeName]['bottom']?attributes[attributeName]['bottom']:	def.indexOf('bottom')>-1);
							this.attributes[attributeName+'_left']=(attributes[attributeName] && attributes[attributeName]['left']?attributes[attributeName]['left']:		def.indexOf('left')>-1);
							this.attributes[attributeName+'_right']=(attributes[attributeName] && attributes[attributeName]['right']?attributes[attributeName]['right']:	def.indexOf('right')>-1);
						}else{
							this.attributes[attributeName+'_top']=(attributes[attributeName] && attributes[attributeName]['top']?attributes[attributeName]['top']:			false);
							this.attributes[attributeName+'_bottom']=(attributes[attributeName] && attributes[attributeName]['bottom']?attributes[attributeName]['bottom']:	false);
							this.attributes[attributeName+'_left']=(attributes[attributeName] && attributes[attributeName]['left']?attributes[attributeName]['left']:		false);
							this.attributes[attributeName+'_right']=(attributes[attributeName] && attributes[attributeName]['right']?attributes[attributeName]['right']:	false);
						}

						// transform this attribute in multiple text styles
						this.attributePropreties[attributeName+'_top']=new Array();
						this.attributePropreties[attributeName+'_top']['type']='4borders';
						this.attributePropreties[attributeName+'_top']['style']='top';

						this.attributePropreties[attributeName+'_bottom']=new Array();
						this.attributePropreties[attributeName+'_bottom']['type']='4borders';
						this.attributePropreties[attributeName+'_bottom']['style']='bottom';

						this.attributePropreties[attributeName+'_left']=new Array();
						this.attributePropreties[attributeName+'_left']['type']='4borders';
						this.attributePropreties[attributeName+'_left']['style']='left';

						this.attributePropreties[attributeName+'_right']=new Array();
						this.attributePropreties[attributeName+'_right']['type']='4borders';
						this.attributePropreties[attributeName+'_right']['style']='right';
					break;

					case 'custom':
						// not implemented... should i use some custom object handlng in here?
					break;

					default:
						// this.debug_undetectedAttribute)
					break;
				}
			}
		}else{
			// this.debug_undetectedAttribute)
		}
	}

	var defX=px2mm($('pageContainer').scrollLeft);
	var defY=px2mm($('pageContainer').scrollTop);

	this.attributes.Xmm=(attributes.Xmm?attributes.Xmm:defX);
	this.attributes.Ymm=(attributes.Ymm?attributes.Ymm:defY);
	this.attributes.X=mm2px(this.attributes.Xmm);
	this.attributes.Y=mm2px(this.attributes.Xmm);

	this.attributes.fixed=(attributes.fixed?attributes.fixed:false);
	this.attributes.block=(attributes.block?attributes.block:false);

	this.attributes.layer=(attributes.layer?attributes.layer:'2');
	this.attributes.orderIndex=(attributes.orderIndex?attributes.orderIndex:'-1');

	this.attributes.padding_top=(attributes.padding_top?attributes.padding_top:0);
	this.attributes.padding_bottom=(attributes.padding_bottom?attributes.padding_bottom:0);
	this.attributes.padding_left=(attributes.padding_left?attributes.padding_left:0);
	this.attributes.padding_right=(attributes.padding_right?attributes.padding_right:0);

	// save the default attrbiutes, so they may get re-used
	//  when invalid propreties are met, and we need to do some kind of rollback
	this.defaultAttributes=Object.clone(this.attributes);

	return true;
}

// create the control, the attributeSelector, and attach them to the DOM
_control_specialObject.prototype.create=function (){
	if(this.preview==null)
		this.preview= CloneTemplates['_control_specialObject_'+this.parent.attributes._objectName].createClone(this.parent.page);

	if(this.object==null)
		this.object= $(this.preview).select("[id='object']").first();

	if(this.attributesSelector==null)
		this.attributesSelector = CloneTemplates['_control_specialObject_'+this.parent.attributes._objectName+'_attributes'].createClone(this.parent.attributesContainer);

	// standard initializing
	this.controls=this.getAttributesControls();
	this.fillAttributes();

	// get the dimensions, and save them
	this.getDimensions();
}

// FAST create the control, the attributeSelector, and attach them to the DOM
_control_specialObject.prototype.fastCreate=function (){
	if(this.object==null)
		this.object= $(this.preview).select("[id='object']").first();

	// standard initializing
	this.controls=this.getAttributesControls();
	this.fillAttributes();
	this.parent.attributes.initialized_attributes=true;

	if(this.attributes.fixed){
		disableElement(this.controls.Xmm,true);
		disableElement(this.controls.Ymm,true);

		disableElement(this.controls.layer,true);
	}else{
		disableElement(this.controls.Xmm,false);
		disableElement(this.controls.Ymm,false);

		disableElement(this.controls.layer,false);
	}

	this.parent.status({status:'idle',percent:1});

	// get the dimensions, and save them
	this.getDimensions();
}

_control_specialObject.prototype.destroy=function (){
	this._heap=null;
	this.attributes=null;
	if(this.preview)
		$(this.preview).remove();

	if(this.attributesSelector)
		$(this.attributesSelector).remove();

	this.preview=null;
	this.object=null;
	this.attributesSelector=null;
	this.defaultAttributes=null;
	this.parent=null;
	this.controls=null;
}

_control_specialObject.prototype.getDimensions=function (){
	// calculate the object dimensions. we do a caclulation hare, and another in the onChange event
	this.objectDimensions=Element.getDimensions(this.object);
	return this.objectDimensions;
}

// used by "group" controls, to display a short description of this control
_control_specialObject.prototype.getDescription=function (){
	return "special object '"+this.attributes.Xmm+'x'+this.attributes.Ymm+"'";
}

// - an attribute has changed, reflect this in the internal data, and in the viewed item
// - some attributes are checked separately, when needing to do extra validation,
//     or the input data is non-standard (for example when $F cannot handle it)
_control_specialObject.prototype.onChange=function (event,attribute,disableUpdate){
	if(attribute==null) return;

	var disableUpdate=false;

	var element=this.controls[attribute];
	var initialValue=this.attributes[attribute];

	var forceFillAttributes=false;

	var value;
	try {
		value=$F(element);
	}catch(ex) {
	}

	switch(attribute){
		/////////////// OBJECT POSITION AND DIMENSIONS ////////////////////////////
		case 'Xmm':
			this.attributes.X=mm2px(value);
			this.attributes.Xmm=value;
		break;
		case 'Ymm':
			this.attributes.Y=mm2px(value);
			this.attributes.Ymm=value;

			// add new pages to this document if needed
		    if(this.attributes.Y>=(grid._heap['pageH']*grid._heap['pages'])){
				grid.expandPageRange(this.attributes.X,this.attributes.Y);
		    }
		break;

		case 'Wmm':
			this.attributes.Wmm=value;
			this.attributes.W=mm2px(value);
		break;
		case 'Hmm':
			this.attributes.Hmm=value;
			this.attributes.H=mm2px(value);
		break;

		case 'orderIndex':
			value=parseInt(value);
			if(isNaN(value))value=0;

			var newOrder=smartPlaceElement(this,value,this.attributes.orderIndex,this.parent.staticPage,true);

			if(newOrder!=$F(this.controls['orderIndex']))this.controls['orderIndex'].value=newOrder;	// update the input value if needed.

			this.attributes.orderIndex=newOrder;

			disableUpdate=true;
		break;

		case 'fixed':
			this.attributes.fixed=(value==null?false:true);

			if(this.attributes.fixed){
				this.attributes.Xmm=0;
				this.attributes.Ymm=0;
				this.attributes.X=0;
				this.attributes.Y=0;

				disableElement(this.controls.Xmm,true);
				disableElement(this.controls.Ymm,true);

				this.attributes.layer=2;
				disableElement(this.controls.layer,true);

				this.parent.staticPage.appendChild(this.preview);
			}else{
				disableElement(this.controls.Xmm,false);
				disableElement(this.controls.Ymm,false);

				disableElement(this.controls.layer,false);

				this.parent.page.appendChild(this.preview);
			}

			this.parent._updateAttribtues({	Xmm:this.attributes.Xmm,
											Ymm:this.attributes.Ymm,
											X:this.attributes.X,
											Y:this.attributes.Y,
											layer:this.attributes.layer
										},this.controls);

		break;
		case 'block':
			this.attributes.block=(value==null?false:true);
		break;

		case 'layer':
			this.attributes.layer=value;
		break;


		/////////////// COLORS AND SPACING /////////////////////////////////////////
		case 'padding_top':
		case 'padding_bottom':
		case 'padding_left':
		case 'padding_right':
			this.attributes[attribute]=value;
		break;

		default:
			var key=null;
			var rawAttribute=attribute;
			/* when handling checkboxes, they are "renamed" to have the following name: checkboxName_index_
			 *
			 * but the attributePropreties only contains the checkboxName part..so try to eliminate that part and then check for it's existance
			 */

			if(!this.attributePropreties[attribute]){
				attribute=String(rawAttribute).replace(/_[a-zA-Z0-9\.]+?_$/,'');
				var matches=String(rawAttribute).match(/_([a-zA-Z0-9\.]+?)_$/);

				if(matches){
					key=matches.last();
				}
			}

			if(this.attributePropreties[attribute]){
				switch(this.attributePropreties[attribute].type){
					case 'float':
					case 'int':
						value=Number(value);
						if(isNaN(value)){
							value=0;
							forceFillAttributes=true;
						}

						if(	this.attributePropreties[attribute].validations
							&& this.attributePropreties[attribute].validations.min!=null
							&& value<this.attributePropreties[attribute].validations.min)
							{
							value=this.attributePropreties[attribute].validations.min;

							forceFillAttributes=true;
						}

						if(	this.attributePropreties[attribute].validations
							&& this.attributePropreties[attribute].validations.max!=null
							&& value>this.attributePropreties[attribute].validations.max)
							{
							value=this.attributePropreties[attribute].validations.max;

							forceFillAttributes=true;
						}

						if(attribute.type=='float'){
							value=(Number(value).toFixed(3));
						}else{
							value=(Number(value).toFixed(0));
						}

					break;

					case 'color':
						value=value;
						setPreviewBackgroundColor(this.attributesSelector,attribute,value);
					break;

					case 'lineStyle':
					case 'selector':
						value=value;
					break;

					case 'fontFamily':
						value=value;
					break;

					case 'fontSize':
						value=value;
					break;

					case 'alignment':
						// unselect the last selectesd alignment
						var icon=$$("#"+this.attributesSelector.identify()+" a[id='"+attribute+"_"+this.attributes[attribute]+"']");
						icon[0].removeClassName("icon-align_"+this.attributes[attribute]+"_");
						icon[0].addClassName("icon-align_"+this.attributes[attribute]);

						value=value;

						// select the new alignment
						var icon=$$("#"+this.attributesSelector.identify()+" a[id='"+attribute+"_"+value+"']");
						icon[0].removeClassName("icon-align_"+value);
						icon[0].addClassName("icon-align_"+value+"_");
						icon[0].blur();
					break;

					case 'verticalAlignment':
						// unselect the last selectesd alignment
						var icon=$$("#"+this.attributesSelector.identify()+" a[id='"+attribute+"_"+this.attributes[attribute]+"']");
						icon[0].removeClassName("icon-align_"+this.attributes[attribute]+"_");
						icon[0].addClassName("icon-align_"+this.attributes[attribute]);

						value=value;

						// select the new alignment
						var icon=$$("#"+this.attributesSelector.identify()+" a[id='"+attribute+"_"+value+"']");
						icon[0].removeClassName("icon-align_"+value);
						icon[0].addClassName("icon-align_"+value+"_");
						icon[0].blur();
					break;

					case 'textStyle':
					case 'textStyle_bold':
					case 'textStyle_italic':
					case 'textStyle_underline':

						value=(value==null?false:true);

						var icon=$$("#"+this.attributesSelector.identify()+" a[id='"+attribute+"']");

						var className='icon-style_'+this.attributePropreties[attribute]['style'];

						if(value){
							icon[0].removeClassName(className);
							icon[0].addClassName(className+"_");
						}else{
							icon[0].removeClassName(className+"_");
							icon[0].addClassName(className);
						}

						icon[0].blur();
					break;

					case 'text':
						value=value;
					break;

					case 'textarea':
						value=value;
					break;

					case '4borders':
					case '4borders_top':
					case '4borders_bottom':
					case '4borders_left':
					case '4borders_right':
						value=(value==null?false:true);
					break;

					case 'checkbox':
						value=(value==null?false:true);

						attribute=attribute+'_'+key+'_';
						this.attributes[attribute]=value;

						attribute=null; // so it will not get set again after this switch()
					break;

					case 'sortable':
					case 'sortable2':
						// handle the sortable's checkboxes
						value=(value==null?false:true);

						attribute=attribute+'_'+key+'_';
						this.attributes[attribute]=value;

						attribute=null; // so it will not get set again after this switch()
					break;
				}

				if(attribute) this.attributes[attribute]=value;
			}else{
				/*
				 * Foreign attribute detected
				 * this attribute is not stored in the attributePropreties element, so it will get ignored
				 */
				// this.debug_undetectedAttribute
			}
		break;
	}

	if(!disableUpdate){
		this.updateObject();
		this.parent.reposition({Xmm:this.attributes.Xmm, Ymm:this.attributes.Ymm});
	}

	if(forceFillAttributes){
		this.fillAttributes();
	}

	return true;
}

_control_specialObject.prototype.updateObject=function (realCall,updateGrid){
	if(realCall!=true){
		this.parent.eventScheduler.schedule('updateObject',this.updateObject.bind(this,true,updateGrid),this.autoApplyUpdateDelay);
		return false;
	}

	var style={};
	var previewStyle={};

	if(this.attributes.fixed){
		style.clear="both";
		previewStyle.clear="both";
	}

	style.paddingTop= mm2px(this.attributes.padding_top)+'px';
	style.paddingBottom= mm2px(this.attributes.padding_bottom)+'px';
	style.paddingLeft= mm2px(this.attributes.padding_left)+'px';
	style.paddingRight= mm2px(this.attributes.padding_right)+'px';

	style.display= (this.attributes.fixed?'block':'inline');

	previewStyle.display= (this.attributes.fixed?'block':'inline');
	previewStyle.position=(this.attributes.fixed?'static':'absolute');

	previewStyle.zIndex=parseInt(this.attributes.layer)+100;

	this.object.setStyle(style);
	this.preview.setStyle(previewStyle);

	this.getDimensions();

	if(this.hidden){
		this.preview.hide();
		this.hidden=false;
	}else{
		this.preview.show();
	}
	this.parent.status({status:'idle',percent:1});
}

// return the internal attributes(you should rely on this, instead of the attributes in the attributesSelector!. They are keeped in sync anyway)
_control_specialObject.prototype.getAttributes=function (){
	if(this.object==null) return false;
	return this.attributes;
}

// return a list of attribute controls (the actual inputs). Internally used to sync with the internal attributes list
_control_specialObject.prototype.getAttributesControls=function (){
	// instantiate a caching mechanism.
	// BE CAREFUL IN FUTURE DEVELOPMENTS ABOUT THIS

	/*
	 * the caching mechanism also fixes a bug occuring when having expanded textareas
	 * because ( $(attributesSelector).select('*') will not select the textarea, as it was moved from $(attributesSelector)
	 */

	if(this._heap['functionCache']['getAttributesControls']){
		return this._heap['functionCache']['getAttributesControls'];
	}else{
		var a;
		var elements;
		var subElement;
		var ret={};

		var attributes=this.getAttributes();
		var attributesSelector=this.attributesSelector;

		for(var i in attributes){
			a="[id='"+i+"']";
			elements=$(attributesSelector).select(a);

			if(elements){
				for(var j=0;j<elements.length;j++){
					subElement=elements[j];
					ret[i]=subElement;
				}
			}
		}

		this._heap['functionCache']['getAttributesControls']=ret;

		return this._heap['functionCache']['getAttributesControls'];
	}
}

// startDrag:
_control_specialObject.prototype.startDrag=function (realDragging){
	if(realDragging){
		this.parent.eventScheduler.schedule('child.onDrag',this.onDrag.bind(this),0.1);
	}else{
		if(this._heap['scheduleInitialization']['grippie']){
			var attributesSelector=this.attributesSelector;
			for(var i=0; i<this._heap['scheduleInitialization']['grippie'].length;i++){
				var attribute=this._heap['scheduleInitialization']['grippie'][i];

				var grippie=$(attributesSelector).select("[id='grippie_"+attribute+"_']").first();
				var textarea=$(attributesSelector).select("[id='"+attribute+"']").first();
				var container=$(attributesSelector).select("[id='grippieContainer_"+attribute+"_']").first();

				if(this._heap['grippies']==null)this._heap['grippies']=new Array();
				this._heap['grippies'].push(new Grippie(grippie,textarea,container,false));
			}
		}
		this._heap['scheduleInitialization']['grippie']=null;

		if(this._heap['scheduleInitialization']['sortable']){
			var attributesSelector=this.attributesSelector;
			for(var i=0; i<this._heap['scheduleInitialization']['sortable'].length;i++){
				var attribute=this._heap['scheduleInitialization']['sortable'][i];

				// the container containing all the sortables
				var container=$$("#"+this.attributesSelector.identify()+" div[id='container_"+this.parent.attributes._objectName+"_"+attribute+"']").first();

				if(this._heap['sortables']==null)this._heap['sortables']=new Array();

				// initialize the Sortable list
				/* disabled due to a Scriptaculous bug(or IE6-7 bug..dont really know). Sortables sets element.position:relative, and this causes some scrolling problems in IE

					Sortable.create(id,{tag:'div',hoverclass:'droppable',onChange:Controls.expensiveFunctionCalled.bind(Controls)});

				*/

				// this.atribtues[attribute] is not handled. This means that cloning this attribtue will NOT work
				// re-sort the attributes according to the default specified values
				if(this.parent.attributes.useDefaults){
					var l=Object.values(this.attributePropreties[attribute].list).length;
					var k=Object.keys(this.attributePropreties[attribute].list);
					var def=String(this.attributePropreties[attribute]['extra']['default']).split('|');

					// loop over each key name and check it if needed.
					for(var index=0;index<l;index++){
						var checkbox=k[index]; // get back the key name

						// check the default checkboxes , if this is a sortable2(if it has the 'extra' field
						if(this.attributePropreties[attribute]['extra'] && this.attributePropreties[attribute]['extra']['name']){
							var attribute2Name=this.attributePropreties[attribute]['extra']['name'];
							var checkboxID=attribute2Name+'_'+checkbox+'_';
							var checkboxControl=$(container).down("input[id='"+checkboxID+"']");

							if(checkboxControl){
								if(def.indexOf(checkbox)>-1){
									checkboxControl.checked=true;
								}else{
									checkboxControl.checked=false;
								}
							}// if checkboxControl==null ... this should never happen:0
						}
					}
				}
			}

		}
		this._heap['scheduleInitialization']['sortable']=null;
	}
}

// endDrag :
_control_specialObject.prototype.endDrag=function (realDragging){
	if(!realDragging){
		this.parent.eventScheduler.cancel('child.onDrag');

		// hide maximixed grippies
		if(this._heap && this._heap['grippies']){
			for(var i=0;i<this._heap['grippies'].length;i++){
				if(this._heap['grippies'][i] && this._heap['grippies'][i].maximized){
					this._heap['grippies'][i].restore();
				}
			}
		}

	}else{
		this._heap['onDrag_timed']=null;
		this.parent.eventScheduler.schedule('child.onDrag',this.onDrag.bind(this,'deccelerate'),1);
	}
}

_control_specialObject.prototype.onDrag=function (delay){
	if(this.parent._heap['x']!=null && this.parent._heap['y']!=null){
		if(this.parent._heap['Xmm'] && this.parent._heap['Ymm']){
			this.attributes.Xmm=this.parent._heap['Xmm'];
			this.attributes.Ymm=this.parent._heap['Ymm'];
		}else{
			this.attributes.Xmm=px2mm(this.parent._heap['x'],true,2);
			this.attributes.Ymm=px2mm(this.parent._heap['y'],true,2);
		}

		this.attributes.X=(this.parent._heap['x']);
		this.attributes.Y=(this.parent._heap['y']);

		var fillResponse=this.fillAttributes({Xmm:this.attributes.Xmm,Ymm:this.attributes.Ymm});
	}

	switch(delay){
		case 'deccelerate':
			this._heap['onDrag_timed']=(this._heap['onDrag_timed']?this._heap['onDrag_timed']:1);
			if(fillResponse && fillResponse.changed){
				this._heap['onDrag_timed']=Math.round(this._heap['onDrag_timed']*0.75);
			}else{
				// prevent this event from reoccuring
				break;
			}

			this._heap['onDrag_timed']=Math.max(this._heap['onDrag_timed'],0.25);
			this._heap['onDrag_timed']=Math.min(this._heap['onDrag_timed'],60);
			this.parent.eventScheduler.schedule('child.onDrag',this.onDrag.bind(this,'deccelerate'),this._heap['onDrag_timed']);
		break;
		case 'slow':
			this.parent.eventScheduler.schedule('child.onDrag',this.onDrag.bind(this,'slow'),2.5);
		break;
		default:
			this.parent.eventScheduler.schedule('child.onDrag',this.onDrag.bind(this),0.5);
		break;
	}
}

// sync the attributesSelector with the internal attributes (overwrite them)
_control_specialObject.prototype.fillAttributes=function (attr){
	var controls=this.controls;
	var attributes=(attr?attr:this.attributes);
	var changed=false;

	changed=this.parent._updateAttribtues(attributes,controls);

	return {attributes:attributes,changed:changed};
}

_control_specialObject.prototype.scheduleInitialization=function (group,attributeName){
	if(this._heap['scheduleInitialization']==null)this._heap['scheduleInitialization']=new Array();
	if(this._heap['scheduleInitialization'][group]==null)this._heap['scheduleInitialization'][group]=new Array();

	if(this._heap['scheduleInitialization'][group].indexOf(attributeName)==-1){
		this._heap['scheduleInitialization'][group].push(attributeName);
	}
}

///////////////////////////////////////////////////////////////////////////////////
// standard initalization for this class, performed on pageLoad ///////////////////

///// FOR SPECIALOBJECTS, THE INITIALIZATION IS MADE IN THE INDEX.TPL DIRECTLY ////