/*
Script: IframeShim.js
Iframe shim class for hiding elements below a floating DOM element.

Dependancies:
         mootools - <Moo.js>, <Utility.js>, <Common.js>, <String.js>, <Array.js>, <Function.js>, <Element.js>, <Dom.js>

Author:
        Aaron Newton, <aaron [dot] newton [at] cnet [dot] com>


Class: IframeShim
                There are two types of elements that (sometimes) prohibit you from
                positioning a DOM element over them: some form elements and some
                flash elements. The two options you have are:
                        - to hide these elements when your dom is going to be over them;
                        this works if you know your DOM element is going to completely
                        obscure that element
                       
                        - an iframe shim - where you put an iframe below your element but
                        ABOVE the form/flash element. more details here:
                        http://www.macridesweb.com/oltest/IframeShim.html
                       
                The IframeShim class handles a lot of the dirty work for you.

                        Arguments:
                        element -  (required; DOM element or its id) the element you want to put this shim under
                        display -  (boolean; optional) display the shim on instantiation; defaults to false
                        name -  (string; optional) the id you want to give the new DOM element of the iframe shim; gets "_shim" added to it
                        zindex -  (integer; optional) the index of the shim; optional, default is 1 less than the element
                        margin -  (integer; optional) make the iframe smaller than the element to give a buffer (for
                                                        things like shadows)
                        offset -  (object: {x:#, y:#}; optional) move the iframe up/down, left/right relative to
                                                        the element
                        className - (string; optional) className for the shim; defaults to "iframeShim"
                        browsers - (boolean; optional) allows you to specify the browsers that the iframe should show up for;
                                                        defaults to ie6 or gecko on a mac (window.ie6 || (window.gecko && navigator.userAgent.test('mac', 'i'))).
                                                        Example usage: *browsers: window.ie6 || window.khtml* //will show for safari, konqueror, and ie6

                        Events:
                        onInject - (callback) function executed when the iframe is added to the DOM (which waits until window.onload)
               
                then, when you make your floating DOM show up you just execute .hide() or .show()
                to make the shim do its magic. You can also call .position() if the element the
                shim is supposed to be under happens to move.
               
                example:
               
                > <div id="myFloatingDiv">stuff</div>
                > <script>
                >       var myFloatingDivShim = new IframeShim({
                >               element: 'myFloatingDiv',
                >               display: false,
                >               name: 'myFloatingDivShimId'
                >       });
                >       function showMyFloatingDiv(){
                >               $('myFloatingDiv').show();
                >               myFloatingDivShim.show();
                >       }
                > </script>
               
                See also <hide>, <show>, <position>
        */
       
var IframeShim = new Class({
        options: {
                element: false,
                name: '',
                className:'iframeShim',
                display:false,
                name: '',
                zindex: false,
                margin: 0,
                offset: {
                        x: 0,
                        y: 0
                },
                browsers: (window.ie6 || (window.gecko && navigator.userAgent.test('mac', 'i')))
        },
        initialize: function (options){
                this.setOptions(options);
                //legacy
                if(this.options.offset && this.options.offset.top) this.options.offset.y = this.options.offset.top;
                if(this.options.offset && this.options.offset.left) this.options.offset.x = this.options.offset.left;
                this.element = $(this.options.element);
                if(!this.element) return;
                else this.makeShim();
                return;
        },
        makeShim: function(){
                this.shim = new Element('iframe');
                this.id = (this.options.name || new Date().getTime()) + "_shim";
                if(this.element.getStyle('z-Index').toInt()<1 || isNaN(this.element.getStyle('z-Index').toInt()))
                        this.element.setStyle('z-Index',5);
                var z = this.element.getStyle('z-Index')-1;
               
                if($chk(this.options.zindex) &&
                         this.element.getStyle('z-Index').toInt() > this.options.zindex)
                         z = this.options.zindex;
                this.shim.setStyles({
                        'position': 'relative',
                        'zIndex': z,
                        'border': 'none',
                        'filter': 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)'
                }).setProperties({
                        'src':'javascript:\'<html></html>\'',
                        'frameborder':'0',
                        'scrolling':'no',
                        'id':this.id
                }).addClass(this.options.className);
               
                var inject = function(){
                        //this.shim.injectInside(document.body);
						this.shim.injectAfter(this.element);
                        if(this.options.display) this.show();
                        else this.hide();
                        this.fireEvent('onInject');
                };
                if(this.options.browsers){
                        if(window.ie && !IframeShim.ready) {
                                window.addEvent('load', inject.bind(this));
                        } else {
                                inject.bind(this)();
                        }
                }
        },

/*      
                Property: position
                This will reposition the iframe element. Call this when you move or resize
                the iframe element.
        */
        position: function(shim){
                if(!this.options.browsers || !IframeShim.ready) return;
                var wasVis = this.element.getStyle('display')!='none';
                if(!wasVis) this.element.setStyle('display','block');
                var size = this.element.getSize().size;
                var pos = this.element.getPosition();
                if(!wasVis) this.element.setStyle('display','none');
                if($type(this.options.margin)){
                        size.x = size.x-(this.options.margin*2);
                        size.y = size.y-(this.options.margin*2);
                        this.options.offset.x += this.options.margin;
                        this.options.offset.y += this.options.margin;
                }
                //offset.x+=100;// ******* This is my change ********

                this.shim.setStyles({
                        'width': size.x + 'px',
                        'height': size.y + 'px'
                }).setPosition({
                        relativeTo: this.element,
                        offset: this.options.offset
                });
        },
/*      
                Property: hide
                This will hide the IframeShim object. If you don't call this when you
                hide the element that's over the flash or select list, then that thing
                will still be hidden.
        */
        hide: function(){
                if(!this.options.browsers) return;
                this.shim.setStyle('display','none');
        },

/*      
                Property: show
                This will obscure any form elements or flash elements below the iframe
                shim element. Call this when you show your floating element.
        */
        show: function(){
                if(!this.options.browsers) return;
				this.shim.setStyle('display','block');				
                this.position();
        },
/*      
                Property: remove
                This will remove the iframe from the DOM.
        */
        remove: function(){
                if(!this.options.browsers) return;
                this.shim.remove();
        }
});
IframeShim.implement(new Options);
IframeShim.implement(new Events);
//legacy namespace
var iframeShim = IframeShim;
window.addEvent('load', function(){
        IframeShim.ready = true;
});
/* do not edit below this line */  

/*	Script: element.position.js
Extends the <Element> object with Element.setPosition; Sets the location of an element relative to another (defaults to the document body).

Dependancies:
	 mootools - <Moo.js>, <String.js>, <Array.js>, <Function.js>, <Element.js>, <Dom.js>
	 cnet - <element.dimensions.js>

Author:
	Aaron Newton, <aaron [dot] newton [at] cnet [dot] com>
	
Class: Element
		This extends the <Element> prototype.
	*/
Element.extend({
/*	Property: setPosition
		Sets the location of an element relative to another (defaults to the document body).
		
		Note:
		The element must be absolutely positioned (if it isn't, this method will set it to be);
		
		Arguments:
		options - a key/value object with options
		
		Options:
		relativeTo - (element) the element relative to which to position this one; defaults to document.body.
		position - (string OR object) the aspect of the relativeTo element that this element should be positioned. See position section below.
		edge - (string OR object; optional) the edge of the element to set relative to the relative elements corner; this way you can specify to position this element's upper right corner to the bottom left corner of the relative element. this is optional; the default behavior positions the element's upper left corner to the relative element unless position == center, in which case it positions the center of the element to the center of the relative element. See position section below.
		offset - (object) x/y coordinates for the offset (i.e. {x: 10, y:100} will move it down 100 and to the right 10). Negative values are allowed.
		returnPos - (boolean) don't move the element, but instead just return the position object ({top: '#', left: '#'}); defaults to false
		overflown - (collection) optional, an array of nested scrolling containers for scroll offset calculation, use this if your element is inside any element containing scrollbars
		relFixedPosition - (boolean) if true, adds the scroll position of the window to the location to account for a fixed position relativeTo item; defaults to false
		ignoreMargins - (boolean) you can have the position calculate the offsets added margins if you like; defaults to false. If true, the corner of the element will be used EXCLUDING the margin.

		Position & Edge Options:
		There are two ways to specify the position: strings and objects. The strings are combinations of "left", "right", and "center" with "top" (or "upper"), "bottom", and "center". These are case insensitive. These translate to:

    - upperLeft, topLeft (same thing) - or upperleft, leftupper, LEFTUPPER whatever.
    - bottomLeft
    - centerLeft
    - upperRight, topRight (same thing)
    - bottomRight
    - centerRight
    - centerTop
    - centerBottom
    - center

		Alternatively, you can be a little more expicit by using an object with x and y values. Acceptable values for the x axis are "left", "right", and "center", and for y you can use "top", "bottom" and "center".

    - {x: 'left', y: 'top'} � same as "upperLeft" or "topLeft"
    - {x: 'left', y: 'bottom'} � same as "bottomLeft"
    - etc.

		Using these options you can specify a position for each corner of the relativeTo object as well as the points between those corners (center left, top, right, bottom and the center of the entire object). 

	*/
	setPosition: function(options){
		options = $merge({
			relativeTo: document.body,
			position: {
				x: 'center', //left, center, right
				y: 'center' //top, center, bottom
			},
			edge: false,
			offset: {x:0,y:0},
			returnPos: false,
			relFixedPosition: false,
			ignoreMargins: false,
			overflown: [] //dom elements
		}, options);
		//compute the offset of the parent positioned element if this element is in one
		var parentOffset = {x: 0, y: 0};
		var parentPositioned = false;
		if(this.getParent() != document.body) {
			var parent = this.getParent();
			while(parent != document.body && parent.getStyle('position') == "static") {
				parent = parent.getParent();
			}
			if(parent != document.body) {
				parentOffset = parent.getPosition();
				parentPositioned = true;
			}
			options.offset.x = options.offset.x - parentOffset.x;
			options.offset.y = options.offset.y - parentOffset.y;
		}
		//upperRight, bottomRight, centerRight, upperLeft, bottomLeft, centerLeft
		//topRight, topLeft, centerTop, centerBottom, center
		function fixValue(option) {
			if($type(option) != "string") return option;
			option = option.toLowerCase();
			var val = {};
			if(option.test('left')) val.x = 'left';
			else if(option.test('right')) val.x = 'right';
			else val.x = 'center';

			if(option.test('upper')||option.test('top')) val.y = 'top';
			else if (option.test('bottom')) val.y = 'bottom';
			else val.y = 'center';
			return val;
		};
		options.edge = fixValue(options.edge);
		options.position = fixValue(options.position);
		if(!options.edge) {
			if(options.position.x == 'center' && options.position.y == 'center') options.edge = {x:'center',y:'center'};
			else options.edge = {x:'left',y:'top'};
		}
		
		this.setStyle('position', 'absolute');
		var rel = $(options.relativeTo) || document.body;
		if (window.opera) {
      var top = (rel == document.body)?window.getScrollTop():rel.getTop();
      var left = (rel == document.body)?window.getScrollLeft():rel.getLeft();
    } else {
      var top = (rel == document.body)?window.getScrollTop():rel.getTop(options.overflown);
      var left = (rel == document.body)?window.getScrollLeft():rel.getLeft(options.overflown);
    }
		
		if (top < 0) top = 0;
    if (left < 0) left = 0;
		var dim = this.getDimensions({computeSize: true, styles:['padding', 'border','margin']});
		if (options.ignoreMargins) {
			options.offset.x += ((options.edge && options.edge.x == "right")?dim['margin-right']:-dim['margin-left']);
			options.offset.y += ((options.edge && options.edge.y == "bottom")?dim['margin-bottom']:-dim['margin-top']);
		}
		var pos = {};
		var prefY = options.offset.y.toInt();
		var prefX = options.offset.x.toInt();
		switch(options.position.x) {
			case 'left':
				pos.x = left + prefX;
				break;
			case 'right':
				pos.x = left + prefX + rel.offsetWidth;
				break;
			default: //center
				pos.x = left + (((rel == document.body)?window.getWidth():rel.offsetWidth)/2) + prefX;
				break;
		};		
		switch(options.position.y) {
			case 'top':
				pos.y = top + prefY;
				break;
			case 'bottom':
				pos.y = top + prefY + rel.offsetHeight;
				break;
			default: //center
				pos.y = top + (((rel == document.body)?window.getHeight():rel.offsetHeight)/2) + prefY;
				break;
		};
		
		if(options.edge){
			var edgeOffset = {};
			
			switch(options.edge.x) {
				case 'left':
					edgeOffset.x = 0;
					break;
				case 'right':
					edgeOffset.x = -dim.x-dim.computedRight-dim.computedLeft;
					break;
				default: //center
					edgeOffset.x = -(dim.x/2);
					break;
			};
			switch(options.edge.y) {
				case 'top':
					edgeOffset.y = 0;
					break;
				case 'bottom':
					edgeOffset.y = -dim.y-dim.computedTop-dim.computedBottom;
					break;
				default: //center
					edgeOffset.y = -(dim.y/2);
					break;
			};
			pos.x = pos.x+edgeOffset.x;
			pos.y = pos.y+edgeOffset.y;
		}
		pos = {
			left: ((pos.x >= 0 || parentPositioned)?pos.x:0).toInt()+'px',
			top: ((pos.y >= 0 || parentPositioned)?pos.y:0).toInt()+'px'
		};
		if(rel.getStyle('position') == "fixed"||options.relFixedPosition) {
			pos.top = pos.top.toInt() + window.getScrollTop()+'px';
			pos.left = pos.left.toInt() + window.getScrollLeft()+'px';
		}

		if(options.returnPos) return pos;
		if(options.smoothMove) new Fx.SmoothMove(this, options).start(); //deprecated; use Fx.SmoothMove instead
		else this.setStyles(pos);
		return this;
	}
});
/* do not edit below this line */   

Element.extend({
/*	Property: getDimensions
		Returns width and height for element; if element is not visible the element is
		cloned off screen, shown, measured, and then removed.
		
		Arguments:
		options - a key/value set of options
		
		Options:
		computeSize - (boolean; optional) use <Element.getComputedSize> or not; defaults to false
		styles - (array; optional) see <Element.getComputedSize>
		plains - (array; optional) see <Element.getComputedSize>
		
		Returns:
		An object with .width and .height defined as integers. If options.computeSize is true, returns
		all the values that <Element.getComputedSize> returns.
		
		Example:
		>$(id).getDimensions()
		> > {width: #, height: #}
	*/
	getDimensions: function(options) {
		options = $merge({computeSize: false},options);
		var dim = {};
		function getSize(el, options){
			if(options.computeSize) dim = el.getComputedSize(options);
			else {
				dim.width = el.getSize().size.x;
				dim.height = el.getSize().size.y;
			}
			return dim;
		}
		try { //safari sometimes crashes here, so catch it
			dim = getSize(this, options);
		}catch(e){}
		if(this.getStyle('display') == 'none'){
			var before = {};
			//use this method instead of getStyles 
			['visibility', 'display', 'position'].each(function(style){
				before[style] = this.style[style]||'';
			}, this);
			//this.getStyles('visibility', 'display', 'position');
			this.setStyles({
				visibility: 'hidden',
				display: 'block',
				position:'absolute'
			});
			dim = getSize(this, options); //works now, because the display isn't none
			this.setStyles(before); //put it back where it was
		}
		return $merge(dim, {x: dim.width, y: dim.height});
	},
/*	Property: getComputedSize
		Calculates the size of an element including the width, border, padding, etc.
		
		Arguments:
		options - an object with key/value options
		
		Options:
		styles - (array) the styles to include in the calculation; defaults to ['padding','border']	
		plains - (object) an object with height and width properties, each of which is an 
							array including the edges to include in that plain. 
							defaults to {height: ['top','bottom'], width: ['left','right']}
		mode - (string; optional) limit the plain to 'vertical' or 'horizontal'; defaults to 'both'
		
		Returns:
		size - an object that contans dimension values (integers); see list below
		
		
		Dimension Values Returned:
		width - the actual width of the object (not including borders or padding)
		height - the actual height of the object (not including borders or padding)
		border-*-width - (where * is top, right, bottom, and left) the width of the border on that edge
		padding-* - (where * is top, right, bottom, and left) the width of the padding on that edge
		computed* - (where * is Top, Right, Bottom, and Left; e.g. computedRight) the width of all the 
			styles on that edge computed (so if options.styles is left to the default padding and border,
			computedRight is the sum of border-right-width and padding-right)
		totalHeight - the total sum of the height plus all the computed styles on the top or bottom. by
			default this is just padding and border, but if you were to specify in the styles option
			margin, for instance, the totalHeight calculated would include the margin.
		totalWidth - same as totalHeight, only using width, left, and right

		Example:
(start code)
$(el).getComputedSize();
returns:
{
	padding-top:0,
	border-top-width:1,
	padding-bottom:0,
	border-bottom-width:1,
	padding-left:0,
	border-left-width:1,
	padding-right:0,
	border-right-width:1,
	width:100,
	height:100,
	totalHeight:102,
	computedTop:1,
	computedBottom:1,
	totalWidth:102,
	computedLeft:1,
	computedRight:1
}

(end)		
	*/
	getComputedSize: function(options){
		options = $merge({
			styles: ['padding','border'],
			plains: {height: ['top','bottom'], width: ['left','right']},
			mode: 'both'
		}, options);
		var size = {width: 0,height: 0};
		switch (options.mode){
			case 'vertical':
				delete size.width;
				delete options.plains.width;
				break;
			case 'horizontal':
				delete size.height;
				delete options.plains.height;
				break;
		}
		var getStyles = [];
		//this function might be useful in other places; perhaps it should be outside this function?
		$each(options.plains, function(plain, key){
			plain.each(function(edge){
				options.styles.each(function(style){
					getStyles.push((style=="border")?style+'-'+edge+'-'+'width':style+'-'+edge);
				});
			});
		});
		var styles = this.getStyles.apply(this, getStyles);
		var subtracted = [];
		$each(options.plains, function(plain, key){ //keys: width, height, plains: ['left','right'], ['top','bottom']
			size['total'+key.capitalize()] = 0;
			size['computed'+key.capitalize()] = 0;
			plain.each(function(edge){ //top, left, right, bottom
				size['computed'+edge.capitalize()] = 0;
				getStyles.each(function(style,i){ //padding, border, etc.
					//'padding-left'.test('left') size['totalWidth'] = size['width']+[padding-left]
					if(style.test(edge)) {
						styles[style] = styles[style].toInt(); //styles['padding-left'] = 5;
						if(isNaN(styles[style]))styles[style]=0;
						size['total'+key.capitalize()] = size['total'+key.capitalize()]+styles[style];
						size['computed'+edge.capitalize()] = size['computed'+edge.capitalize()]+styles[style];
					}
					//if width != width (so, padding-left, for instance), then subtract that from the total
					if(style.test(edge) && key!=style && 
						(style.test('border') || style.test('padding')) && !subtracted.test(style)) {
						subtracted.push(style);
						size['computed'+key.capitalize()] = size['computed'+key.capitalize()]-styles[style];
					}
				});
			});
		});
		if($chk(size.width)) {
			size.width = size.width+this.offsetWidth+size.computedWidth;
			size.totalWidth = size.width + size.totalWidth;
			delete size.computedWidth;
		}
		if($chk(size.height)) {
			size.height = size.height+this.offsetHeight+size.computedHeight;
			size.totalHeight = size.height + size.totalHeight;
			delete size.computedHeight;
		}
		return $merge(styles, size);
	}
});


