// fix an IE problem on toQueryString when a <select> is involved:
$A = function(iterable, start, length){
	if (Browser.Engine.trident){ // jpl && $type(iterable) == 'collection'){
		start = start || 0;
		if (start < 0) start = iterable.length + start;
		length = length || (iterable.length - start);
		var array = [];
		for (var i = 0; i < length; i++) array[i] = iterable[start++];
		return array;
	}
	start = (start || 0) + ((start < 0) ? iterable.length : 0);
	var end = ((!$chk(length)) ? iterable.length : length) + start;
	return Array.prototype.slice.call(iterable, start, end);
};


/****************************************************************************
//	SECTION::Hash
*/

// restore Hash.remove
Hash.alias({erase: 'remove'});

/*
//	End SECTION::Hash
****************************************************************************/


/****************************************************************************
//	SECTION::AJAX Script Handling
	
	Let's make the execution of scripts delay a little as a general rule:
*/
	
Request.implement({
	
	processScripts: function(text){
	//	dbug.log('lag version processing of scripts!');
		
		if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text);
		
		var scriptText = '';
		var scriptFreeText = text.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){
			scriptText += arguments[1] + '\n';
			return '';
		});
		
		if(this.options.evalScripts)
			$exec.delay(this.options.scriptDelay || 50, null, scriptText);
		
		return scriptFreeText;
	}

});
/*
//	End SECTION::AJAX Script Handling
****************************************************************************/



/****************************************************************************
//	SECTION::Element.Form functionality
*/
Element.implement({

	/*
	Property: getValue
		Returns the value of the Element, if its tag is textarea, select or input. getValue called on a multiple select will return an array.
	*/

	getValue: function(){
		switch(this.getTag()){
			case 'select':
				var values = [];
				$each(this.options, function(option){
					if (option.selected) values.push($pick(option.value, option.text));
				});
				return (this.multiple) ? values : values[0];
			case 'input': if (!(this.checked && ['checkbox', 'radio'].contains(this.type)) && !['hidden', 'text', 'password'].contains(this.type)) break;
			case 'textarea': return this.value;
		}
		return false;
	},

	getFormElements: function(){
		return $$(this.getElementsByTagName('input'), this.getElementsByTagName('select'), this.getElementsByTagName('textarea'));
	}
});
/*
//	End SECTION::Element.Form functionality
****************************************************************************/



/****************************************************************************
//	SECTION::OuterClick Event
*/
(function() {
	var events;
	var check = function(e) {
		var target = $(e.target);
		var parents = target.getParents();
		events.each(function(item) {
			var element = item.element;
			if(element != target  &&  !parents.contains(element))
				item.fn.call(element, e);
		});
	};
	Element.Events.outerClick = {
		onAdd: function(fn) {
			if(!events) {
				document.addEvent('click', check);
				events = [];
			}
			events.push({element: this, fn: fn});
		},
		onRemove: function(fn) {
			events = events.filter(function(item) {
				return item.element != this  ||  item.fn != fn;
			}, this);
			if(!events.length) {
				document.removeEvent('click', check);
				events = null;
			}
		}
	};
})();

/*
//	End SECTION::OuterClick Event
****************************************************************************/

	
/****************************************************************************
//	SECTION::Number Formatting
*/
Number.extend({

	/*
	Property: numberFormat
		Format a number with grouped thousands.

	Arguments:
		decimals, optional - integer, number of decimal percision; default, 2
		dec_point, optional - string, decimal point notation; default, '.'
		thousands_sep, optional - string, grouped thousands notation; default, ','

	Returns:
		a formatted version of number.

	Example:
		>(36432.556).numberFormat()  // returns 36,432.56
		>(36432.556).numberFormat(2, '.', ',')  // returns 36,432.56
	*/

	numberFormat : function(decimals, dec_point, thousands_sep) {
		decimals = Math.abs(decimals) + 1 ? decimals : 2;
		dec_point = dec_point || '.';
		thousands_sep = thousands_sep || ',';
	
		var matches = /(-)?(\d+)(\.\d+)?/.exec((isNaN(this) ? 0 : this) + ''); // returns matches[1] as sign, matches[2] as numbers and matches[3] as decimals
		var remainder = matches[2].length > 3 ? matches[2].length % 3 : 0;
		return (matches[1] ? matches[1] : '') + (remainder ? matches[2].substr(0, remainder) + thousands_sep : '') + matches[2].substr(remainder).replace(/(\d{3})(?=\d)/g, "$1" + thousands_sep) + 
				(decimals ? dec_point + (+matches[3] || 0).toFixed(decimals).substr(2) : '');
	}

/*
//	End SECTION::Number Formatting
****************************************************************************/
});




/****************************************************************************
//	SECTION::Element Effect Shortcuts
*/
Element.extend({
	appear: function() {
		var currentOpacity = this.getStyle('opacity');
		var currentDisplay = this.getStyle('display');
		var currentVisibility= this.getStyle('visibility');
		if(currentOpacity == 1  && 
		   currentDisplay != 'none'  &&
		   currentVisibility == 'visible')
			return; // is already appeared!
		
		if(currentDisplay == 'none'  ||  currentVisibility == 'hidden') {
			this.setStyle('opacity', 0);
			currentOpacity = 0;
			this.setStyle('display', $pick(this.originalDisplay, this.tagName.toLowerCase()=='span' ? 'inline' : 'block'));
			this.setStyle('visibility', 'visible');
		}
		
		// Now we're ready to show:
		this.effect('opacity').start(currentOpacity, 1);
	},
	
	
	fade: function() {
		var currentOpacity = this.getStyle('opacity');
		var currentDisplay = this.getStyle('display');
		var currentVisibility= this.getStyle('visibility');
		this.originalDisplay = currentDisplay; // save to preserve on re-appear!
		if(currentOpacity == 0  || 
		   currentDisplay == 'none'  ||
		   currentVisibility == 'hidden')
			return; // is already invisible!
		
		// Now we're ready to fade:
		this.effect('opacity').start(currentOpacity, 0);
	}
});
/*
//	End SECTION::Element Effect Shortcuts
****************************************************************************/




/****************************************************************************
//	SECTION::Element Spatial Computation Shortcuts
*/
Element.extend({
	getNonContentHeight: function() {
		return 0 +
			parseInt(this.getStyle('border-top-width')) +
			parseInt(this.getStyle('border-bottom-width')) +
			parseInt(this.getStyle('margin-top')) +
			parseInt(this.getStyle('margin-bottom')) +
			parseInt(this.getStyle('padding-top')) +
			parseInt(this.getStyle('padding-bottom'));
	}
	
});
/*
//	End SECTION::Element Spatial Computation Shortcuts
****************************************************************************/





/****************************************************************************
//	SECTION::Drag that can handle % in addition to px in position styling
*/

Drag.implement({
	stop: function(event){
		this.document.removeEvent(this.selection, this.bound.eventStop);
		this.document.removeEvent('mousemove', this.bound.drag);
		this.document.removeEvent('mouseup', this.bound.stop);
		if (event) this.fireEvent('complete', [this.element, event]);  // jpl: pass the event
	}
});

Drag.Move.implement({

	start: function(event){
		// We're overriding bits in Drag.start, but that won't work directly since it
		// needs to impact Drag.Move.start (which calls the parent Drag.start()),
		// so we take all all of Drag.Move's start() and unroll the parent function:
		
		// jpl move beforeStart event up here (it may alter the spatial properties of
		// the dragging object in a way that impacts the container constraints!)
		this.fireEvent('beforeStart', this.element);
		
		if (this.container){
			var el = this.element, cont = this.container, ccoo = cont.getCoordinates(el.offsetParent), cps = {}, ems = {};

			['top', 'right', 'bottom', 'left'].each(function(pad){
				cps[pad] = cont.getStyle('padding-' + pad).toInt();
				ems[pad] = el.getStyle('margin-' + pad).toInt();
			}, this);

			var width = el.offsetWidth + ems.left + ems.right, height = el.offsetHeight + ems.top + ems.bottom;
			var x = [ccoo.left + cps.left, ccoo.right - cps.right - width];
			var y = [ccoo.top + cps.top, ccoo.bottom - cps.bottom - height];

			this.options.limit = {x: x, y: y};
		}
		
		//===========================================
		// Parent function starts here:
		
		if (this.options.preventDefault) event.preventDefault();
		this.mouse.start = event.page;
		// jpl added
		this.modifierUnits = {x: 'px', y: 'px'};  // px by default, we'll see what's actually used
		
		var limit = this.options.limit;
		this.limit = {'x': [], 'y': []};
		for (var z in this.options.modifiers){
			if (!this.options.modifiers[z]) continue;
			
	// jpl change to:
			this.modifierUnits[z] = this.getStyleUnit(this.element.getStyle(this.options.modifiers[z]));
			this.value.now[z] = this.getElementModifierStyleAsPixels(this.options.modifiers[z]);
	///		dbug.log('this.modifierUnits[z]=' + this.modifierUnits[z] + ', this.value.now[z]=' + this.value.now[z]);
	// Was:
	//		if (this.options.style) this.value.now[z] = this.element.getStyle(this.options.modifiers[z]).toInt();
	//		else this.value.now[z] = this.element[this.options.modifiers[z]];
			if (this.options.invert) this.value.now[z] *= -1;
			this.mouse.pos[z] = event.page[z] - this.value.now[z];
			if (limit && limit[z]){
				for (var i = 2; i--; i){
					if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])();
				}
			}
		}
		if ($type(this.options.grid) == 'number') this.options.grid = {'x': this.options.grid, 'y': this.options.grid};
		this.document.addEvents({mousemove: this.bound.check, mouseup: this.bound.cancel});
		this.document.addEvent(this.selection, this.bound.eventStop);
	},

	
	
	// jpl:  handles either % or px units
	getElementModifierStyleAsPixels: function(modifier) {
		var theStyle = this.element.getStyle(modifier) + '';
		if(theStyle.substring(theStyle.length-1) == '%') {
			// a percentage of what?
			var APCoordinates = this.element.getOffsetParent().getCoordinates();
			if(['height', 'top'].contains(modifier)) 			// the parent height:
				return (theStyle.toInt() * APCoordinates.height/100).round();
			
			else if(['width', 'left'].contains(modifier))		// the parent width:
				return (theStyle.toInt() * APCoordinates.width/100).round();
			
		}
		else
			return theStyle.toInt();
	},
	
	
	// jpl:  handles either % or px units
	getElementStyleFromPixelsToUnit: function(modifier, pixelValue, unit) {
		if(unit == '%') {
			// a percentage of what?
			var APCoordinates = this.element.getOffsetParent().getCoordinates();
			if(['height', 'top'].contains(modifier))			// the parent height:
				return (pixelValue * 100/APCoordinates.height).toFixed(2) + '%';
			
			else if(['width', 'left'].contains(modifier))		// the parent width:
				return (pixelValue * 100/APCoordinates.width).toFixed(2) + '%';
		}
		else // cheap but highly legit assumption
			return pixelValue + 'px';
	},
	
	// jpl:  extract the unit from a given style value:
	getStyleUnit: function(theStyle) {
		if(theStyle.substring(theStyle.length-1) == '%')
			return '%';
		if(theStyle.substring(theStyle.length-2) == 'px')
			return 'px';
		if(theStyle.substring(theStyle.length-2) == 'em')
			return 'em';
		if(theStyle.substring(theStyle.length-2) == 'ex')
			return 'ex';
		if(theStyle.substring(theStyle.length-2) == 'pt')
			return 'pt';
		
		return 'px'; // reasonable default
		
	}
	
});


/*
//	End SECTION::Drag that can handle % in addition to px in position styling
****************************************************************************/
