/*	Script: element.dimensions.js
Extends the <Element> object.

Dependancies:
	 mootools - <Moo.js>, <String.js>, <Array.js>, <Function.js>, <Element.js>, <Dom.js>

Author:
	Aaron Newton, <aaron [dot] newton [at] cnet [dot] com>
	
Class: Element
		This extends the <Element> prototype.
	*/
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((dim.width == 0 || $type(dim.width) != 'number')||(dim.height == 0 || $type(dim.height) != 'number')){
			var holder = new Element('div').setStyles({
				'position':'absolute',
				'top':'-1000px',
				'left':'-1000px',
				'display':'block'
			}).injectAfter(this);
			var clone = this.clone().injectInside(holder).setStyle('display','block');
			dim = getSize(clone, options);
			holder.remove();
		}
		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);
	}
});
/* do not edit below this line */   
/* Section: Change Log 

$Source: /cvs/main/flatfile/html/rb/js/global/cnet.global.framework/mootools.extended/Native/element.dimensions.js,v $
$Log: element.dimensions.js,v $
Revision 1.4  2007/08/08 18:22:07  newtona
fixed a bug with Element.getDimensions (which affected Fx.SmoothMove, Fx.SmoothShow, Element.setPosition, and the bazillion other things that use it). would only show up under certain CSS layout situations.

Revision 1.3  2007/05/30 20:32:33  newtona
doc updates

Revision 1.2  2007/05/29 22:01:53  newtona
Split element.cnet.js into seperate files; updated docs in files to note this
Changed element.visible to element.isVisible (left old namespace for legacy support)
Fixed Element.empty in prototype.compatibility.js
Removed as many dependencies in common code to element.*.js as possible (espeically element.shortcuts.js)

Revision 1.1  2007/05/29 21:25:31  newtona
splitting up element.cnet.js (which had grown to be too unweildy); moving things into sub-directories


*/
/*	Script: element.position.js
Extends the <Element> object.

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

		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,
			overflown: [] //dom elements
		}, options);
		if(options.position == "center" && !options.edge) options.edge == "center";
		//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.position = fixValue(options.position);
		if(!options.edge) {
			if(options.position.x == 'center' && options.position.y == 'center')
				options.edge = 'center';
			else options.edge = 'upperLeft'
		}
		
		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});
		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){
			options.edge = fixValue(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)?pos.x:0).toInt()+'px',
			top: ((pos.y >= 0)?pos.y:0).toInt()+'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 */   
/* Section: Change Log 

$Source: /cvs/main/flatfile/html/rb/js/global/cnet.global.framework/mootools.extended/Native/element.position.js,v $
$Log: element.position.js,v $
Revision 1.7  2007/07/28 00:03:29  newtona
removed a dbug line

Revision 1.6  2007/07/17 20:38:44  newtona
Fx.SmoothShow - refactored the exploration of the element dimensions when hidden so that it isn't visible to the user
element.position - refactored to allow for more than just the previous 5 positions, now supports nine: all corners, all mid-points between those corners, and the center
string.cnet.js - fixed up the query string logic to decode values

Revision 1.5  2007/06/27 18:58:15  newtona
checking in built versions of cat libraries; i have not compressed these or anything to prevent accidental publishing
element.position.js: fixed an error with overflown items in Element.setPosition; this fix depends on Mootools 1.11, see: http://forum.mootools.net/viewtopic.php?pid=20391#p20391
default.accordion.nav.js: changed window.onDomReady (which is deprecated) to window.addEvent('domready'...
checking in the build file for redball.global.framework.dev.js - this will build a copy of redball.global using the copy of Mootools in the dev directory (currently, 1.11)

Revision 1.4  2007/06/27 18:30:33  newtona
debugger.footer.js: added a delay for IE to avoid the dreaded "operation aborted" bug
element.position.js: deprecated the smoothMove option in Element.setPosition
Fx.SmoothMove: moving this effect out of Element.setPosition and making it a stand alone Fx class
bat file (build file) changes: adding Fx.SmoothMove

Revision 1.3  2007/05/30 20:32:33  newtona
doc updates

Revision 1.2  2007/05/29 22:01:53  newtona
Split element.cnet.js into seperate files; updated docs in files to note this
Changed element.visible to element.isVisible (left old namespace for legacy support)
Fixed Element.empty in prototype.compatibility.js
Removed as many dependencies in common code to element.*.js as possible (espeically element.shortcuts.js)

Revision 1.1  2007/05/29 21:25:31  newtona
splitting up element.cnet.js (which had grown to be too unweildy); moving things into sub-directories


*/
/*	Script: element.pin.js
		Extends the Mootools <Window> and <Element> classes to allow fixed positioning for an element.

		Dependencies:
		mootools = <Element.js>, <Window.js> and all their dependencies
	
		Class: window
		This extends the <window> class from the <http://mootools.net> library.
	*/
window.extend({
/*	Property: supportsPositionFixed
		Returns true if the browser supports fixed positioning; must be called after DomReady (or it returns null);
	*/
	supportsPositionFixed: function(){
		if(!window.loaded) return null;
		var test = new Element('div').setStyles({
			position: 'fixed',
			top: '0px',
			right: '0px'
		}).injectInside(document.body);
		var supported = (test.offsetTop === 0);
		test.remove();
		return supported;
	}
});

/*	Class: Element
		Extends the <Element> class from the <http://mootools.net> library.
	*/
Element.extend({
/*	Property: pin
		Affixes an element at its current position, even if the window is scrolled.
		
		Arguments: 
		pin - (boolean) true: pin, false: release pin. See also <Element.unpin>.
	*/
	pin: function(enable){
		var p = this.getPosition();
		if(enable!==false) {
			if(!this.pinned) {
				var pos = {
					top: (p.y - window.getScrollTop())+'px',
					left: (p.x - window.getScrollLeft())+'px'
				};
				if(window.supportsPositionFixed()) {
					this.setStyle('position','fixed').setStyles(pos);
				} else {
					this.setStyles({
						position: 'absolute',
						top: p.y+'px',
						left: p.x+'px'
					});
					window.addEvent('scroll', function(){
						var to = {
							top: (pos.top.toInt() + window.getScrollTop())+'px',
							left: (pos.left.toInt() + window.getScrollLeft())+'px'
						};
						this.setStyles(to);
					}.bind(this));
				}
				this.pinned = true;
			}
		} else {
			this.pinned = false;
			this.setStyles({
				top: (p.y+window.getScrollTop())+'px',
				left: (p.x+window.getScrollLeft())+'px',
				position: 'absolute'
			});
		}
		return this;
	},
/*	Property: unpin
		Un-pins an element at its current position (see <Element.pin>).
	*/
	unpin: function(){
		return this.pin(false);
	},
/*	Property: togglepin
		Toggles the pin state of the element.
	*/
	togglepin: function(){
		this.pin(!this.pinned);
	}
});

/* do not edit below this line */   
/* Section: Change Log 

$Source: /cvs/main/flatfile/html/rb/js/global/cnet.global.framework/mootools.extended/Native/element.pin.js,v $
$Log: element.pin.js,v $
Revision 1.2  2007/05/30 20:32:33  newtona
doc updates

Revision 1.1  2007/05/29 21:25:31  newtona
splitting up element.cnet.js (which had grown to be too unweildy); moving things into sub-directories

Revision 1.7  2007/05/04 16:40:50  newtona
fixed a trailing comma typo

Revision 1.6  2007/05/04 01:22:38  newtona
added togglepin

Revision 1.5  2007/05/04 01:06:54  newtona
*sigh* ok, last typo

Revision 1.4  2007/05/04 01:04:35  newtona
woops, missing a "+"

Revision 1.3  2007/05/04 01:03:01  newtona
fixed a bug with unpin

Revision 1.2  2007/05/04 01:01:45  newtona
.pin only pins if the element isn't already pinned.

Revision 1.1  2007/05/04 00:36:19  newtona
*** empty log message ***


*/
/*	Script: element.shortcuts.js
Extends the <Element> object with some basic shortcuts (like .hide and .show).

Dependancies:
	 mootools - <Moo.js>, <String.js>, <Array.js>, <Function.js>, <Element.js>, <Dom.js>

Author:
	Aaron Newton, <aaron [dot] newton [at] cnet [dot] com>
	
Class: Element
		This extends the <Element> prototype.
	*/
Element.extend({

/*	Property: isVisible
		Returns a boolean; true = visible, false = not visible.
		
		Example:
		>$(id).isVisible()
		> > true | false	*/
	isVisible: function() {
		return this.getStyle('display') != 'none';
	},
/*	Property: toggle
		Toggles the state of an element from hidden (display = none) to 
		visible (display = what it was previously or else display = block)
		
		Example:
		> $(id).toggle()
	*/
	toggle: function() {
		return this[this.isVisible() ? 'hide' : 'show']();
	},
/*	Property: hide
		Hides an element (display = none)
		
		Example:
		> $(id).hide()
		*/
	hide: function() {
		this.originalDisplay = this.getStyle('display'); 
		this.setStyle('display','none');
		return this;
	},
/*	Property: show
		Shows an element (display = what it was previously or else display = block)
		
		Example:
		>$(id).show() */
	show: function(display) {
		this.originalDisplay = (this.originalDisplay=="none")?'block':this.originalDisplay;
		this.setStyle('display',(display || this.originalDisplay || 'block'));
		return this;
	},
/*	Property: tidy
		Uses <String.tidy> to clean up common special characters with their ASCII counterparts (smart quotes, elipse characters, stuff from MS Word, etc.).
	*/
	tidy: function(){
		try {	
			if(this.getValue().tidy())this.value = this.getValue().tidy();
		}catch(e){dbug.log('element.tidy error: %o', e);}
	},
	//DO NOT USE THIS METHOD
	//it is temporary, as Mootools 1.1 will negate its requirement
	fxOpacityOk: function(){
		if (!window.ie6)return true;
		var isColor = false;
		try {
			if (new Color(this.getStyle('backgroundColor'))) isColor = true;
		}catch(e){}
		return isColor;
	}
});
//legacy namespace
Element.visible = Element.isVisible;


if(!Element.empty) {
	Element.extend({
		/*
		Property: empty
			Empties an element of all its children (overridden by Mootools 1.1 <Element.empty> if present).
	
		Example:
			>$('myDiv').empty() // empties the Div and returns it
	
		Returns:
			The element.
		*/
		empty: function(){
			//Garbage.trash(this.getElementsByTagName('*'));
			return this.setHTML('');
		}
	});
}
/*	legacy support for $S	*/
var $S = $$;
/* do not edit below this line */   
/* Section: Change Log 

$Source: /cvs/main/flatfile/html/rb/js/global/cnet.global.framework/mootools.extended/Native/element.shortcuts.js,v $
$Log: element.shortcuts.js,v $
Revision 1.3  2007/05/30 20:32:33  newtona
doc updates

Revision 1.2  2007/05/29 22:01:53  newtona
Split element.cnet.js into seperate files; updated docs in files to note this
Changed element.visible to element.isVisible (left old namespace for legacy support)
Fixed Element.empty in prototype.compatibility.js
Removed as many dependencies in common code to element.*.js as possible (espeically element.shortcuts.js)

Revision 1.1  2007/05/29 21:25:31  newtona
splitting up element.cnet.js (which had grown to be too unweildy); moving things into sub-directories

Revision 1.29  2007/05/17 19:45:57  newtona
updated element.empty for mootools 1.1

Revision 1.28  2007/05/16 20:09:42  newtona
adding new js files to redball.common.full
product.picker.js now has no picklets; these are in the implementations/picklets directory
ProductPicker now detects if there is no doctyp and, if not, sets the position of the picker to be fixed (no IE6 support)
small docs update in element.cnet.js
added new picklet: CNETProductPicker_PricePath
added new picklet: NewsStoryPicker_Path
new file: clipboard.js (allows you to insert text into the OS clipboard)
new file: html.table.js (automates building html tables)
new file: element.forms.js (for managing text inputs - get selected text information, insert content around selection, etc.)

Revision 1.27  2007/05/05 01:01:26  newtona
stickywinHTML: tweaked the options for buttons
element.cnet: tweaked smoothshow/hide css handling

Revision 1.26  2007/04/09 21:35:22  newtona
removing garbage collection from Element.empty (will have to wait for Mootools 1.1)

Revision 1.25  2007/04/02 18:04:48  newtona
syntax fix

Revision 1.24  2007/03/30 21:42:43  newtona
adding Element.isEmpty

Revision 1.23  2007/03/30 19:27:58  newtona
moved .empty to .flush

Revision 1.22  2007/03/29 22:36:42  newtona
Element.fxOpacityOk now only checks bgcolor for ie6
Added Element.flush

Revision 1.21  2007/03/28 23:22:54  newtona
Element.smoothShow/smoothHide: added Element.fxOpacityOk to deal with the IE bug where text gets blurry when you fade an element in and out without a bgcolor set

Revision 1.20  2007/03/28 18:07:21  newtona
added Element.fxOpacityOk to deal with the IE bug where text gets blurry when you fade an element in and out without a bgcolor set

Revision 1.19  2007/03/26 18:30:12  newtona
iframeShim: fixed reference to options (should be this.options)
element.cnet: removed some dbug lines

Revision 1.18  2007/03/23 20:13:38  newtona
getDimensions: added support for getComputedSize
getComputedSize: added
setPosition: added edge option, uses getComputedSize
smoothHide: uses getComputedSize
smoothShow: uses getComputedSize
sumObj: removed function; no longer needed

Revision 1.17  2007/03/16 00:23:24  newtona
added string.tidy and element.tidy

Revision 1.16  2007/03/08 23:32:14  newtona
strict javascript warnings cleaned up

Revision 1.15  2007/03/01 00:50:35  newtona
type.isNumber now returns false for NaN
element.smoothshow/hide now works (in IE specifically) when there are no values for border

Revision 1.14  2007/02/27 19:37:56  newtona
element.show now enforces that the original display was not 'none'

Revision 1.13  2007/02/22 21:05:35  newtona
smoothHide now checks that the element is not already hidden

Revision 1.12  2007/02/21 00:21:22  newtona
added legacy support for $S

Revision 1.11  2007/02/08 22:14:04  newtona
added border widths to smoothshow/hide

Revision 1.10  2007/02/08 01:30:58  newtona
tweaking element.setPosition, now can use effects

Revision 1.9  2007/02/07 20:52:34  newtona
added Element.position

Revision 1.8  2007/02/06 18:13:13  newtona
added element.smoothShow and smoothHide; depends on latest svn of mootools

Revision 1.7  2007/02/03 01:40:05  newtona
fixed a typo bug

Revision 1.6  2007/01/26 06:06:13  newtona
element.replace now takes a 2nd argument to eval scripts or not
element.getDimensions now returns w & h for hidden elements

Revision 1.5  2007/01/05 19:45:48  newtona
made getDimensions capable of discovering dimensions of hidden elements

Revision 1.4  2006/12/06 20:14:59  newtona
carousel - improved performance, changed some syntax, actually deployed into usage and tested
cnet.nav.accordion - improved css selectors for time
multiple accordion - fixed a typo
dbug.js - added load timers
element.cnet.js - changed syntax to utilize mootools more effectively
function.cnet.js - equated $set to $pick in preparation for mootools v1

Revision 1.3  2006/11/27 17:59:32  newtona
small change to replace and the way it uses timeouts

Revision 1.2  2006/11/02 21:34:00  newtona
Added cvs footer


*/
/*	Script: simple.template.parser.js
		Provides functionality for very simple template parsing; for more complex template parsing, use TrimPath's excellent Javascript Templates (JST): http://trimpath.com/project/wiki/JavaScriptTemplates.

		Dependencies:
		Moo - <Moo.js>, <Utility.js>, <Function.js>, <String.js>
	
		Author:
		Aaron Newton (aaron [dot] newton [at] cnet [dot] com)
		
		Object: simpleTemplateParser
		This object provides functionality for very simple template parsing; for more complex template parsing, use TrimPath's excellent Javascript Templates (JST): http://trimpath.com/project/wiki/JavaScriptTemplates. It can be used on its own or implemented into a class.
	*/

var simpleTemplateParser = {
		STP: {},
/*	Property: parseTemplate
		Parses a template with the values of an object, substituting those values for all instances of the keys in the object found within the template.

		Arguments: 
		template - a string to parse
		object - the object with your key/value pairs
		regexOptions - the options for the regex replace; defaults to 'ig' (ignore case, global replace)
		wrappers - an object with the before and after strings that are on either side of your keys (see example);
			defaults to {before: "%", after: "%"}

		Example:
(start code)
<textarea id="myTemplate">
	<p>This is some html that lets me subsitute things.</p>
	<ul>
		<li>%firstThing%</li>
		<li>%secondThing%</li>
		<li>%thirdThing%</li>
	</ul>
</textarea>
<script>
	var myTemplate = $('myTemplate').innerHTML;
	var myObject = {
		firstThing: 'hi there',
		secondThing: 'howzit goin?',
		thirdThing: 'really? me too!'
	}
	var parsed = simpleTemplateParser.parseTemplate(myTemplate, myObject);
</script>(end)
	*/
		parseTemplate: function(template, object, regexOptions, wrappers) {
			var STP = this.STP;
			STP.template = template;
			STP.object = object;
			STP.regexOptions = $pick(regexOptions, 'ig');
			STP.wrappers = $pick(wrappers, {before:'%', after:'%'});
			return STP.result = this.runParser(STP.object, STP.template, STP.regexOptions);
		},
		runParser: function(object, string, regexOptions){
			for(value in object){
				switch($type(object[value])){
					case 'string':
						string = this.tmplSubst(value, object[value], string, regexOptions);
						break;
					case 'number':
						string = this.tmplSubst(value, object[value], string, regexOptions);
						break;
					case 'object':
						string = this.runParser(object[value]);
						break;
					case 'array':
						string = this.tmplSubst(value, object[value].toString(), string, regexOptions);
						break;
				}
			}
			return string;
		},
		tmplSubst: function(key, value, string, regexOptions){
			return string.replace(new RegExp(this.STP.wrappers.before+key+this.STP.wrappers.after, 'gi'), value);
		}
	};
/* do not edit below this line */   
/* Section: Change Log 

$Source: /cvs/main/flatfile/html/rb/js/global/cnet.global.framework/common/utilities/simple.template.parser.js,v $
$Log: simple.template.parser.js,v $
Revision 1.4  2007/06/07 18:43:37  newtona
added CSS to autocompleter.js
removed string.cnet.js dependencies from template parser and stickyWin.default.layout.js

Revision 1.3  2007/03/02 01:32:52  newtona
swapped out string.replace with string.replaceAll

Revision 1.2  2007/01/26 05:56:03  newtona
syntax update for mootools 1.0
docs update

Revision 1.1  2007/01/09 02:39:35  newtona
renamed addons directory to "common" directory

Revision 1.2  2007/01/09 01:25:47  newtona
docs syntax fix

Revision 1.1  2007/01/05 18:55:02  newtona
first check in


*/
/*	Script: jsonp.js
		Creates a Json request using a script tag include and handles the callbacks for you.
		
		Dependencies:
		Mootools - <Moo.js>, <Array.js>, <String.js>, <Function.js>, <Utility.js>, <Element.js>, <Common.js>
		
		Author:
		Aaron Newton <aaron [dot] newton [at] cnet [dot] com>
		
		Class: JsonP
		Creates a Json request using a script tag include and handles the callbacks for you.
		
		Arguments:
		url - the url to get the json data
		options - an object with key/value options
		
		Options:
		onComplete - (optional) function to execute when the data returns; it will be passed the data
		callBackKey - (string) the key in the url that the server uses to wrap the Json results. 
				So, for example, if you used "callBackKey: 'callback'" then the server is expecting
				something like http://..../?q=search+term&callback=myFunction
				defaults to "callback". This must be defined correctly.
		queryString - (string, optional) additional query string values to append to the url
		data - (object, optional) additional key/value data to append to the url
		
		Example:
(start code)
new JsonP('http://api.cnet.com/restApi/v1.0/techProductSearch', {
	data: {
		partTag: 'mtvo',
		iod: 'hlPrice',
		iewType: 'json',
		results: '100',
		query: 'ipod'
	},
	onComplete: myFunction.bind(someObject)
}).request();
(end)

		The above example would generate this url:
(start code) http://api.cnet.com/restApi/v1.0/techProductSearch?partTag=mtvo&iod=hlPrice&viewType=json&results=100&query=ipod&callback=JsonP.requestors[0].handleResults&
(end)

		It would embed this script tag (in the head of the document) and, when it loaded, execute the "myFunction"
		callback defined.
	*/
var JsonP = new Class({
	options: {
		onComplete: Class.empty,
		callBackKey: "callback",
		queryString: "",
		data: {}
	},
	initialize: function(url, options){
		this.setOptions(options);
		this.url = this.makeUrl(url).url;
		this.fired = false;
		this.scripts = [];
	},
/*	Property: request
		Executes the Json request.
	*/
	request: function(url){
		if(url) this.url = this.makeUrl(url).url;
		dbug.log('retrieving by json script method: %s', this.url);
		var dl = (window.ie)?50:0; //for some reason, IE needs a moment here...
		(function(){
			var script = new Asset.javascript(this.url, {id: 'jsonp_'+u.index});
			this.fired = true;
			this.addEvent('onComplete', function(){
				try {script.remove();}catch(e){}
			}.bind(this));
		}.bind(this)).delay(dl);
	},
	makeUrl: function(url){
		var index = (JsonP.requestors.contains(this))?
								JsonP.requestors.indexOf(this):
								JsonP.requestors.push(this) - 1;
		var separator = (url.test('\\?'))?'&':'?';
		var jurl = url + separator + this.options.callBackKey + "=JsonP.requestors[" +
				index+"].handleResults";
		if(this.options.queryString) jurl += "&"+this.options.queryString;
		jurl += "&"+Object.toQueryString(this.options.data);
		return {url: jurl, index: index};
	},
	handleResults: function(data){
		this.fireEvent('onComplete', data);
	}
});
JsonP.requestors = [];
JsonP.implement(new Options);
JsonP.implement(new Events);

/* do not edit below this line */   
/* Section: Change Log 

$Source: /cvs/main/flatfile/html/rb/js/global/cnet.global.framework/common/utilities/jsonp.js,v $
$Log: jsonp.js,v $
Revision 1.7  2007/08/17 17:24:28  newtona
fixed a bug in jsonp; url is no longer a required argument for the request method

Revision 1.6  2007/08/15 01:03:32  newtona
Added more event info for Autocompleter.js
Slimbox no longer adds css to the page if there aren't any images found for the instance
Iframeshim now exits quietly if you try and position it before the dom is ready
jsonp now handles having more than one request open at a time
removed a console.log statement from window.cnet.js (shame on me for leaving it there)

Revision 1.5  2007/06/21 17:44:04  newtona
fixed a typo; same line was duplicated and I removed the errant one.

Revision 1.4  2007/03/05 19:30:46  newtona
added a short (50ms) delay for IE

Revision 1.3  2007/02/27 21:46:43  newtona
docs update; fixing references

Revision 1.2  2007/02/22 23:58:33  newtona
fixed a bug with the queryString option

Revision 1.1  2007/02/21 00:30:59  newtona
first commit


*/
/*	Script: Autocompleter.js
		3rd party script for managing autocomplete functionality.
		
		Details:
		Author - Harald Kirschner <mail [at] digitarald.de>
		Refactored by - Aaron Newton <aaron [dot] newton [at] cnet [dot] com>
		License - MIT-style license
		Version - 1.0rc3
		Source - http://digitarald.de/project/autocompleter/
		
		Dependencies:
		Mootools 1.1 - <Class.Extras>, <Element.Event>, <Element.Selectors>, <Element.Form>, <Element.Dimensions>, <Fx.Style>, <Ajax>, <Json>
		Autocompleter - <Observer.js>
		
		Namespace: Autocompleter
		All functions are part of the <Autocompleter> namespace.
	*/
var Autocompleter = {};
/*	Class: Autocompleter.Base
		Base class for the Autocompleter classes.
		
		Arguments:
		el - (DOM element or id) element to observe.
		options - (object) key/value set of options.
		
		Options:
		minLength - (integer, default 1) Minimum length to start auto compliter
		useSelection - (boolean, default true) Select completed text part (works only for appended strings)
		markQuery - (boolean, default true) Mark queried string with <span class="autocompleter-queried">*</span>
		inheritWidth - (boolean, default true) Inherit width for the autocompleter overlay from the input field
		maxChoices - (integer, default 10). Maximum of suggested fields.
		injectChoice - (function, optional). Callback for injecting the list element with the arguments (itemValue, itemIndex), take a look at updateChoices for default behaviour.
		onSelect - Event Function. Fires when when an item gets selected; passed the input and the value selected.
		onShow - Event Function. Fires when autocompleter list shows.
		onHide - Event Function. Fires when autocompleter list hides.
		customTarget - (element, optional). Allows to override the autocompleter list element with your own list element.
		className - (string, default 'autocompleter-choices'). Class name for the list element.
		zIndex - (integer, default 42). z-Index for the list element.
		observerOptions - optional Options Object. For the Observer class.
		fxOptions - optional Options Object. For the Fx.Style on the list element.
		allowMulti - (boolean, defaults to false) allow more than one value, seperated by a delimeter
		delimeter - (string) default delimter between multi values (defaults to ", ")
		autotrim - (boolean) trim the delimeter on blur
		allowDupes - (boolean, defaults to false) if multi, prevent duplicate entries
		baseHref - (string) the base url where the css and image assets are located (defaults to cnet's servers you should change)

		Note:
		If you're not cnet, you should download these assets to your own local:
		http://www.cnet.com/html/rb/assets/global/autocompleter/Autocompleter.css
		http://www.cnet.com/html/rb/assets/global/autocompleter/spinner.gif
		
		Then either change this script or pass in the local when you instantiate the class.
		
		Example:
		This base class is not used directly (but rather its inheritants are such as <Autocompleter.Ajax>)
		so there is no example here.
	*/
Autocompleter.Base = new Class({

	options: {
		minLength: 1,
		useSelection: true,
		markQuery: true,
		inheritWidth: true,
		dropDownWidth: 100,
		prefetchOnFocus: false,
		maxChoices: 10,
		injectChoice: null,
		onSelect: Class.empty,
		onShow: Class.empty,
		onHide: Class.empty,
		customTarget: null,
		className: 'autocompleter-choices',
		zIndex: 42,
		observerOptions: {},
		fxOptions: {},
		multi: false,
		delimeter: ', ',
		autotrim: true,
		allowDupes: false,
		/*	if you're not cnet, you should download these assets to your own local:
				http://www.cnet.com/html/rb/assets/global/autocompleter/Autocompleter.css
				http://www.cnet.com/html/rb/assets/global/autocompleter/spinner.gif
			*/
		baseHref: 'http://www.cnet.com/html/rb/assets/global/autocompleter/'
	},

	initialize: function(el, options) {
		this.setOptions(options);
		if(!$('AutocompleterCss')) window.addEvent('domready', function(){
			new Asset.css(this.options.baseHref+'Autocompleter.css', {id: 'AutocompleterCss'});
		}.bind(this));
		this.element = $(el);
		this.build();
		this.observer = new Observer(this.element, this.prefetch.bind(this), $merge({
			delay: 400
		}, this.options.observerOptions));
		this.value = this.observer.value;
		this.queryValue = null;
		this.element.addEvent('blur', function(e){
			this.autoTrim.delay(50, this, e);
		}.bind(this));
		this.addEvent('onSelect', function(){
			this.element.focus();
			this.userChose = true;
			(function(){
				this.userChose = false;
			}).delay(100, this);
		}.bind(this));
	},

/*	Property: build
		Builds the html structure for choices and appends the events to the element.
		Override this function to modify the html generation.	*/

	build: function() {
		if ($(this.options.customTarget)) this.choices = this.options.customTarget;
		else {
			this.choices = new Element('ul', {
				'class': this.options.className,
				'styles': {zIndex: this.options.zIndex}
				}).injectInside(document.body);
			this.fix = new OverlayFix(this.choices);
		}
		this.fx = this.choices.effect('opacity', $merge({wait: false, duration: 200}, this.options.fxOptions))
			.addEvent('onStart', function() {
				if (this.fx.now) return;
				this.choices.setStyle('display', '');
				this.fix.show();
			}.bind(this))
			.addEvent('onComplete', function() {
				if (this.fx.now) return;
				this.choices.setStyle('display', 'none');
				this.fix.hide();
			}.bind(this)).set(0);
		this.element.setProperty('autocomplete', 'off')
			.addEvent(window.ie ? 'keydown' : 'keypress', this.onCommand.bindWithEvent(this))
			.addEvent('mousedown', this.onCommand.bindWithEvent(this, [true]))
			.addEvent('focus', this.toggleFocus.bind(this, [true]))
			.addEvent('blur', this.toggleFocus.bind(this, [false]))
			.addEvent('trash', this.destroy.bind(this));
	},
	
	autoTrim: function(e){
		if(this.userChose) return this.userChose = false;
		var del = this.options.delimeter;
		var val = this.element.getValue();
		if(this.options.autotrim && val.test(del+"$")){
			e = new Event(e);
			this.observer.value = this.element.value = val.substring(0, val.length-del.length);
		}
	},
/*	Property: getQueryValue
		Returns the user's input to use to match against the full list. When options.multi == true, this value is the last entered string after the last index of the delimeter.
		
		Arguments:
		value - (string) optional, the value to clean; defaults to this.observer.value

		Examples:
(start code)
user input: blue
getQueryValue() returns "blue"

user input: blue, green, yellow
options.multi = true
options.delimter = ", "
getQueryValue() returns "yellow"

user input: blue, green, yellow, 
options.multi = true
options.delimter = ", "
getQueryValue() returns ""
(end)
	*/
	getQueryValue: function(value){
		value = $pick(value, this.observer.value);
		return (this.options.multi)?value.lastElement(this.options.delimeter):value||'';
	},
	
/*	Property: destroy
		Remove the autocomplete functionality from the input.
	*/
	destroy: function() {
		this.choices.remove();
	},

	toggleFocus: function(state) {
		this.focussed = state;
		if (!state) this.hideChoices();
	},

	onCommand: function(e, mouse) {
		var val = this.getQueryValue();
		if ( (mouse && this.focussed) || this.options.prefetchOnFocus) this.prefetch();
		if (e.key) switch (e.key) {
			case 'enter':
				if (this.selected && this.visible) {
					this.choiceSelect(this.selected);
					e.stop();
				} return;
			case 'up': case 'down':
				if (this.getQueryValue() != (val || this.queryValue)) this.prefetch();
				else if (this.queryValue === null) break;
				else if (!this.visible) this.showChoices();
				else {
					this.choiceOver((e.key == 'up')
						? this.selected.getPrevious() || this.choices.getLast()
						: this.selected.getNext() || this.choices.getFirst() );
					this.setSelection();
				}
				e.stop(); return;
			case 'esc': case 'tab': 
				this.hideChoices(); 
				if (this.options.multi) this.element.value = this.element.getValue().trimLastElement();
				return;
		}
		this.value = false;
	},

	setSelection: function() {
		if (!this.options.useSelection) return;
		var del = this.options.delimeter;
		var qVal = this.getQueryValue(this.queryValue);
		var elVal = this.getQueryValue(this.element.getValue());
		var startLength;
		if(this.options.multi)	{
			var index = this.queryValue.lastIndexOf(del);
			var delLength = (index<0)?0:del.length;
			startLength = qVal.length+(index<0?0:index)+delLength;
		} else startLength = qVal.length;

		if (elVal.indexOf(qVal) != 0) return;
		var insert = this.selected.inputValue.substr(startLength);
		if (window.ie) {
			var sel = document.selection.createRange();
			sel.text = insert;
			sel.move("character", - insert.length);
			sel.findText(insert);
			sel.select();
		} else {
			var offset = (this.options.multi && this.element.value.test(del))?
				this.element.getValue().length-elVal.length+qVal.length
				:this.queryValue.length;
			this.element.value = this.element.value.substring(0, offset) + insert;
			this.element.selectionStart = offset;
			this.element.selectionEnd = this.element.value.length;
		}
		this.value = this.observer.value = this.element.value;
	},
/*	Property: hideChoices
		Hides the choices from the user.
	*/
	hideChoices: function() {
		if (!this.visible) return;
		this.visible = this.value = false;
		this.observer.clear();
		this.fx.start(0);
		this.fireEvent('onHide', [this.element, this.choices]);
	},

/*	Property: showChoices
		Shows the choices to the user.
	*/
	showChoices: function() {
		if (this.visible || !this.choices.getFirst()) return;
		this.visible = true;
		var pos = this.element.getCoordinates(this.options.overflown);
		this.choices.setStyles({'left': pos.left, 'top': pos.bottom});
		this.choices.setStyle('width', (this.options.inheritWidth)?pos.width:this.options.dropDownWidth);
		this.fx.start(1);
		this.choiceOver(this.choices.getFirst());
		this.fireEvent('onShow', [this.element, this.choices]);
	},

	prefetch: function() {
		var val = this.getQueryValue(this.element.getValue());
		if (val.length < this.options.minLength) this.hideChoices();
		else if (val == this.queryValue) this.showChoices();
		else this.query();
	},

	updateChoices: function(choices) {
		this.choices.empty();
		this.selected = null;
		if (!choices || !choices.length) return;
		if (this.options.maxChoices < choices.length) choices.length = this.options.maxChoices;
		choices.each(this.options.injectChoice || function(choice, i){
			var el = new Element('li').setHTML(this.markQueryValue(choice));
			el.inputValue = choice;
			this.addChoiceEvents(el).injectInside(this.choices);
		}, this);
		this.showChoices();
	},

	choiceOver: function(el) {
		if (this.selected) this.selected.removeClass('autocompleter-selected');
		this.selected = el.addClass('autocompleter-selected');
	},

	choiceSelect: function(el) {
		if(this.options.multi) {
			var del = this.options.delimeter;
			var value = (this.element.value.trimLastElement(del) + el.inputValue).split(del);
			var fin = [];
			if (!this.options.allowDupes) {
				value.each(function(item){
					if(fin.contains(item))fin.remove(item); //move it to the end
					fin.include(item);
				})
			} else fin = value;
			this.observer.value = this.element.value = fin.join(del)+del;
		} else this.observer.value = this.element.value = el.inputValue;
		
		
		this.hideChoices();
		this.fireEvent('onSelect', [this.element, el.inputValue], 20);
	},

/*	Property: markQueryValue
		Marks the queried word in the given string with <span class="autocompleter-queried">*</span>
		Call this i.e. from your custom parseChoices, same for addChoiceEvents
		
		Arguments:
		txt - (string) the string to mark
	 */
	markQueryValue: function(txt) {
		var val = (this.options.mult)?this.lastQueryElementValue:this.queryValue;
		return (this.options.markQuery && val) ? txt.replace(new RegExp('^(' + val.escapeRegExp() + ')', 'i'), '<span class="autocompleter-queried">$1</span>') : txt;
	},

/*	Property: addChoiceEvents
		Appends the needed event handlers for a choice-entry to the given element.
		
		Arguments:
		el - (DOM element or id) the element to add
*/
	addChoiceEvents: function(el) {
		return el.addEvents({
			'mouseover': this.choiceOver.bind(this, [el]),
			'mousedown': this.choiceSelect.bind(this, [el])
		});
	},
	query: Class.empty
});

Autocompleter.Base.implement(new Events);
Autocompleter.Base.implement(new Options);

/*	Class: OverlayFix
		Private class used by <Autocompleter> - basically an <IframeShim>.
	*/
var OverlayFix = new Class({

	initialize: function(el) {
		this.element = $(el);
		if (window.ie){
			this.element.addEvent('trash', this.destroy.bind(this));
			this.fix = new Element('iframe', {
					'properties': {'frameborder': '0', 'scrolling': 'no', 'src': 'javascript:false;'},
					'styles': {'position': 'absolute', 'border': 'none', 'display': 'none', 'filter': 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)'}})
				.injectAfter(this.element);
		}
	},

	show: function() {
		if (this.fix) this.fix.setStyles($extend(
			this.element.getCoordinates(), {'display': '', 'zIndex': (this.element.getStyle('zIndex') || 1) - 1}));
		return this;
	},

	hide: function() {
		if (this.fix) this.fix.setStyle('display', 'none');
		return this;
	},

	destroy: function() {
		this.fix.remove();
	}

});

String.extend({
	lastElement: function(separator){
		separator = separator || ' ';
		var txt = this; //(separator.test(' $'))?this:this.trim();
		var index = txt.lastIndexOf(separator);
		var result = (index == -1)? txt: txt.substr(index + separator.length, txt.length);
		return result;
	},
 
 
	trimLastElement: function(separator){
		separator = separator || ' ';
		var txt = this; //(separator.test(' $'))?this:this.trim();
		var index = this.lastIndexOf(separator);
		return (index == -1)? "": txt.substr(0, index + separator.length);
	}
});

/* do not edit below this line */   
/* Section: Change Log 

$Source: /cvs/main/flatfile/html/rb/js/global/cnet.global.framework/common/3rdParty/Autocomplete/Autocompleter.js,v $
$Log: Autocompleter.js,v $
Revision 1.4  2007/08/15 01:03:30  newtona
Added more event info for Autocompleter.js
Slimbox no longer adds css to the page if there aren't any images found for the instance
Iframeshim now exits quietly if you try and position it before the dom is ready
jsonp now handles having more than one request open at a time
removed a console.log statement from window.cnet.js (shame on me for leaving it there)

Revision 1.3  2007/06/12 20:26:52  newtona
*** empty log message ***

Revision 1.2  2007/06/07 18:43:35  newtona
added CSS to autocompleter.js
removed string.cnet.js dependencies from template parser and stickyWin.default.layout.js

Revision 1.1  2007/06/02 01:35:17  newtona
*** empty log message ***


*/
/*	Script: Autocompleter.JsonP.js
		Implements <JsonP> support for the <Autocompleter> class.
		
		Dependencies:
		Mootools 1.1 - <Class.Extras>, <Element.Event>, <Element.Selectors>, <Element.Form>, <Element.Dimensions>, <Fx.Style>, <Ajax>, <Json>
		Autocompleter - <Autocompleter.js>, <Observer.js>
		CNET - <jsonp.js>
		
		Author:
		Aaron Newton (aaron [dot] newton [at] cnet [dot] com)
		
		Class: Autocompleter.JsonP
		Implements <JsonP> support for the <Autocompleter> class.
		
		Arguments:
		el - (DOM element or id) element to observe.
		url - (string) the url to query for values
		options - (object) key/value set of options.

		Options:
		postVar - (string) the key to which the user's entry is mapped - passed to the server as postVar=userEntry (see example below)
		jsonpOptions - (object) options passed along to the <JsonP> class.
		onRequest - (callback function) fired when the request is sent
		onComplete - (callback function) fired when the request is complete
		minLength - (integer) Overrides minLength (defaults to 1).
		filterResponse - Function, optional. Allows to override default filterResponse method
		
		Example:
		Let's say the user is typing into an input to search for ipods and you need to take what they've typed ("ipo" so far) and send it to a server to get back filtered results like so:

http://server.com/handler.jsp?query=ipo
		
		Then the postVar option would be "query" so that the user's input is mapped to this key in the query string.
		
(start code)
var myCompleter = new Autocomplete.JsonP($('myinput'), 'http://server.com/handler.jsp', {
	postVar: 'query'
	...
});
(end)
	
		You're not really done though, because you need to handle the results that come back using the functionality in the base <Autocompleter> class. Here's an example that will work with the cnet API:

(start code)
new Autocompleter.JsonP($('jsonp'), 'http://api.cnet.com/restApi/v1.0/techProductSearch',
{
	jsonpOptions: {
		//this data gets added to the query string using JsonP's options
		data: {
			viewType: 'json',
			partKey: '19926949750937665684988687810562', //this is my code, user your own!
			iod:'none',
			start:0,
			results:10
		}
	},
	//require at least a key stroke from the user
	minLength: 1,
	//this function filters the results based on the input
	filterResponse: function(resp) {
		//test it
		if(!choices || choices.length == 0) return [];
		//filter it and return it
		var regex = new RegExp('^' + (this.queryValue || '').escapeRegExp(), 'i');
		return choices.filter(function(choice){
			return (regex.test(choice.Name.$) || regex.test(choice['@id']));
		});
	},
	useSelection: false,
	//because the data returned has a unique structure, we must manage the parsing ourselves
	filterResponse: function(resp) {
		try {
			//this structure is unique to the CNET API
			choices = resp.CNETResponse.TechProducts.TechProduct;
			//test it
			if(!choices || choices.length == 0) return [];
			//filter it and return it
			return choices.filter(function(choice){
				return (choice.Name.$.test(this.getQueryValue(), 'i') || choice['@id'].test(this.getQueryValue()), 'i');
			}.bind(this));
		} catch(e){'filterResponse error: ', dbug.log(e)}
	},
	injectChoice: function(choice) {
		//again, the structure of these items is unique to the CNET API
		if(! choice.Name.$)return;
		var el = new Element('li')
			.setHTML(this.markQueryValue(choice.Name.$))
			.adopt(new Element('span', {'class': 'example-info'}).setHTML(this.markQueryValue(choice['@id'])));
		el.inputValue = choice.Name.$+' ('+choice['@id']+')';
		this.addChoiceEvents(el).injectInside(this.choices);
	}
});
(end)
	*/

Autocompleter.JsonP = Autocompleter.Base.extend({

	options: {
		postVar: 'query',
		jsonpOptions: {},
		onRequest: Class.empty,
		onComplete: Class.empty,
		minLength: 1, 
		filterResponse: null
	},

	initialize: function(el, url, options) {
		this.url = url;
		this.parent(el, options);
		if (this.options.filterResponse) this.filterResponse = this.options.filterResponse.bind(this);
	},

	query: function(){
		var multi = this.options.multi;
		var data = $extend({}, this.options.jsonpOptions.data);
		if(multi) this.lastQueryElementValue = this.element.value.lastElement(this.options.delimeter);
		data[this.options.postVar] = (multi)?this.lastQueryElementValue:this.element.value;

		this.jsonp = new JsonP(this.url, $merge(
			{
				data: data
			},
			this.options.jsonpOptions
		));
		this.jsonp.addEvent('onComplete', this.queryResponse.bind(this));

		this.fireEvent('onRequest', [this.element, this.jsonp]);
		this.jsonp.request();
	},
	
/*	Property: queryResponse
		Inherated classes have to extend this function and use this.parent(resp)
		
		Arguments:
		resp - (String) the response from the ajax query.
*/
	queryResponse: function(resp) {
		try {
			this.value = this.queryValue = this.element.value;
			var choices = this.filterResponse(resp);
			this.selected = false;
			this.hideChoices();
		} catch(e) {
			try { dbug.log('jsonp request error: ', e); } catch(e) {}
		}
		this.fireEvent(choices ? 'onComplete' : 'onFailure', [this.element, choices], 20);
		if (!choices || !choices.length) return;
		this.updateChoices(choices);
	},

	filterResponse: function(resp) {
		var regex = new RegExp('^' + this.queryValue.escapeRegExp(), 'i');
		return this.tokens.filter(function(token) {
			return regex.test(token);
		});
	}

});
/* do not edit below this line */   
/* Section: Change Log 

$Source: /cvs/main/flatfile/html/rb/js/global/cnet.global.framework/common/3rdParty/Autocomplete/Autocompleter.JsonP.js,v $
$Log: Autocompleter.JsonP.js,v $
Revision 1.1  2007/06/12 20:26:52  newtona
*** empty log message ***


*/
/*	Script: Autocompleter.Local.js
		Extends the <Autocompleter.Base> class to add support for a pre-defined object.
		
		Class: Autocompleter.Local
		Extends the <Autocompleter.Base> class to add support for a pre-defined object.
		
		Arguments:
		el - (DOM element or id) element to observe.
		tokens - (Array) an array of values
		options - (object) key/value set of options.
		
		Options:
		All values passed to <Autocompleter.Base>
		
		minLength - Overrides minLength to 0.
		filterTokens - Function, optional. Allows to override default filterTokens method

		Example:
(start code)
//this object's structure is arbitrary
var tokens = [
	['Apple', 'Red'],
	['Lemon', 'Yellow'],
	['Grape', 'Purple']	
];

new Autocompleter.Local($('myInput'), tokens, {
	delay: 100,
	//this is a custom filter because our object has a unique structure
	filterTokens: function() {
		var regex = new RegExp('^' + (this.queryValue || '').escapeRegExp(), 'i');
		var filtered = this.tokens.filter(function(token){
			return (regex.test(token[0]) || regex.test(token[1]));
		});
		return filtered;
	},
	//again, because our data structure is unique, we must handle the results ourselves
	injectChoice: function(choice) {
		var el = new Element('li')
			.setHTML(this.markQueryValue(choice[0]))
			.adopt(new Element('span', {'class': 'example-info'}).setHTML(this.markQueryValue(choice[1])));
		el.inputValue = choice[0];
		this.addChoiceEvents(el).injectInside(this.choices);
	}
});
(end)
	*/

Autocompleter.Local = Autocompleter.Base.extend({
	options: {
		minLength: 0,
		filterTokens : null
	},
	initialize: function(el, tokens, options) {
		this.parent(el, options);
		this.tokens = tokens;
		if (this.options.filterTokens) this.filterTokens = this.options.filterTokens.bind(this);
	},
	query: function() {
		this.hideChoices();
		this.queryValue = (this.options.multi)?
				this.element.value.lastElement(this.options.delimeter).trim()
				:this.element.value;
		this.updateChoices(this.filterTokens());
	},
	filterTokens: function(token) {
		var regex = new RegExp('^' + this.queryValue.escapeRegExp(), 'i');
		return this.tokens.filter(function(token) {
			return regex.test(token);
		});
	}
});
/* do not edit below this line */   
/* Section: Change Log 

$Source: /cvs/main/flatfile/html/rb/js/global/cnet.global.framework/common/3rdParty/Autocomplete/Autocompleter.Local.js,v $
$Log: Autocompleter.Local.js,v $
Revision 1.1  2007/06/12 20:26:52  newtona
*** empty log message ***


*/
/*	Script: Autocompleter.Remote.js
		Contains the classes for the Remote methods for <Autocompleter>.

		Namespace: Autocompleter.Ajax
		Contains the classes for the Remote methods for <Autocompleter>
		
		Details:
		Author - Harald Kirschner <mail [at] digitarald.de>
		Refactored by - Aaron Newton <aaron [dot] newton [at] cnet [dot] com>
		License - MIT-style license
		Version - 1.0rc3
		Source - http://digitarald.de/project/autocompleter/
		
		Dependencies:
		Mootools 1.1 - <Class.Extras>, <Element.Event>, <Element.Selectors>, <Element.Form>, <Element.Dimensions>, <Fx.Style>, <Ajax>, <Json>
		Autocompleter - <Autocompleter.js>, <Observer.js>
	*/
Autocompleter.Ajax = {};
/*	Class: Autocompleter.Ajax.Base
		The base functionality for all <Autocompleter.Ajax> functionality.
		
		Arguments:
		el - (DOM element or id) element to observe.
		url - (string) the url to query for values
		options - (object) key/value set of options.
		
		Options:
		postVar - String, default ‘value’. Post variable name for the query string
		postData - Object, optional. Additional request data
		ajaxOptions - optional Options Object. For the Ajax instance
		onRequest - Event Function.
		onComplete - Event Function.
		
		Example:
		The <Autocompleter.Ajax.Base> class is not used directly but rather its inhertants are (see 
		<Autocompleter.Ajax.Json> and <Autocompleter.Ajax.Xhtml>) so there is no example here.
	*/
Autocompleter.Ajax.Base = Autocompleter.Base.extend({

	options: {
		postVar: 'value',
		postDataEval: "",
		postData: {},
		ajaxOptions: {},
		onRequest: Class.empty,
		onComplete: Class.empty
	},

	initialize: function(el, url, options) {
		this.parent(el, options);
		this.ajax = new Ajax(url, $merge({
			autoCancel: true
		}, this.options.ajaxOptions));
		this.ajax.addEvent('onComplete', this.queryResponse.bind(this));
		this.ajax.addEvent('onFailure', this.queryResponse.bind(this, [false]));
	},

	query: function(){
		if(this.options.postDataEval)	this.options.postData = eval(this.options.postDataEval);
		var data = $extend({}, this.options.postData);
		data[this.options.postVar] = this.element.value;
		this.fireEvent('onRequest', [this.element, this.ajax]);
		this.ajax.request(data);
	},

	/**
	 * queryResponse - abstract
	 *
	 * Inherated classes have to extend this function and use this.parent(resp)
	 *
	 * @param		{String} Response
	 */
	queryResponse: function(resp) {
		this.value = this.queryValue = this.element.value;
		this.selected = false;
		this.hideChoices();
		this.fireEvent(resp ? 'onComplete' : 'onFailure', [this.element, this.ajax], 20);
	}

});

/*	Class: Autocompleter.Ajax.Json
		Extends <Autocompleter.Ajax.Base> to include Json support.
		
		Arguments:
		All those specified in <Autocompleter.Ajax.Base> and <Autocompleter.Base>.
		
		Example:
new Autocompleter.Ajax.Json($('ajaxJson'), 'server/auto.php' {
	postVar: 'query'
});
	*/
Autocompleter.Ajax.Json = Autocompleter.Ajax.Base.extend({

	queryResponse: function(resp) {
		this.parent(resp);
		var choices = Json.evaluate(resp || false);
		if (!choices || !choices.length) return;
		this.updateChoices(choices);
	}

});

/*	Class: Autocompleter.Ajax.Xhtml
		Extends <Autocompleter.Ajax.Base> to include Xhtml support.

		Arguments:
		All those specified in <Autocompleter.Ajax.Base> and <Autocompleter.Base>.

		Example:		
(start code)
new Autocompleter.Ajax.Xhtml($('ajaxXhtml'), 'server/auto.php', {
	postData: {html: 1}, //some data to go along with the request
	//handle the data returned
	parseChoices: function(el) {
		var value = el.getFirst().innerHTML;
		el.inputValue = value;
		this.addChoiceEvents(el).getFirst().setHTML(this.markQueryValue(value));
	}
});
(end)
	*/
Autocompleter.Ajax.Xhtml = Autocompleter.Ajax.Base.extend({

	options: {
		parseChoices: null
	},

	queryResponse: function(resp) {
		this.parent(resp);
		if (!resp) return;
		this.choices.setHTML(resp).getChildren().each(this.options.parseChoices || this.parseChoices, this);
		this.showChoices();
	},

	parseChoices: function(el) {
		var value = el.innerHTML;
		el.inputValue = value;
		el.setHTML(this.markQueryValue(value));
	}

});

/* do not edit below this line */   
/* Section: Change Log 

$Source: /cvs/main/flatfile/html/rb/js/global/cnet.global.framework/common/3rdParty/Autocomplete/Autocompleter.Remote.js,v $
$Log: Autocompleter.Remote.js,v $
Revision 1.1  2007/06/12 20:26:52  newtona
*** empty log message ***


*/
/*	Script: Observer.js
		Observes formelements for changes; part of the <Autocomplete> 3rd party package.
		
		Details:
		Author - Harald Kirschner <mail [at] digitarald.de>
		License - MIT-style license
		Version - 1.0rc1
		Source - http://digitarald.de/project/autocompleter/
		
		Dependencies:
		Mootools 1.1 - <Class>, <Class.Extras>, <Element.Event>
		
		Class: Observer
		Observes form elements for changes; part of the <Autocomplete> 3rd party package.
		
		Arguments:
		el - (DOM element or id) element to observe
		onFired - (function) event to execute periodically and/or on keyup
		options - (object) a set of key/value options
		
		Options:
		periodical - (boolean) update and fire the onFired event regularly; defaults to false
		delay - (integer) how often to update using periodical if (periodical is true); defaults to 1000
	*/
var Observer = new Class({

	options: {
		periodical: false,
		delay: 1000
	},

	initialize: function(el, onFired, options){
		this.setOptions(options);
		this.addEvent('onFired', onFired);
		this.element = $(el);
		this.listener = this.fired.bind(this);
		this.value = this.element.getValue();
		if (this.options.periodical) this.timer = this.listener.periodical(this.options.periodical);
		else this.element.addEvent('keyup', this.listener);
	},

	fired: function() {
		var value = this.element.getValue();
		if (this.value == value) return;
		this.clear();
		this.value = value;
		this.timeout = this.fireEvent.delay(this.options.delay, this, ['onFired', [value]]);
	},

	clear: function() {
		$clear(this.timeout);
		return this;
	}
});

Observer.implement(new Options);
Observer.implement(new Events);

/* do not edit below this line */   
/* Section: Change Log 

$Source: /cvs/main/flatfile/html/rb/js/global/cnet.global.framework/common/3rdParty/Autocomplete/Observer.js,v $
$Log: Observer.js,v $
Revision 1.2  2007/06/12 20:26:52  newtona
*** empty log message ***

Revision 1.1  2007/06/02 01:35:17  newtona
*** empty log message ***


*/

var OverlayFix = new Class({

	initialize: function(el) {
		this.element = $(el);
		if (window.ie){
			this.element.addEvent('trash', this.destroy.bind(this));
			this.fix = new Element('iframe', {
				properties: {
					frameborder: '0',
					scrolling: 'no',
					src: 'javascript:false;'
				},
				styles: {
					position: 'absolute',
					border: 'none',
					display: 'none',
					filter: 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)'
				}
			}).injectAfter(this.element);
		}
	},

	show: function() {
		if (this.fix) this.fix.setStyles($extend(
			this.element.getCoordinates(), {
				display: '',
				zIndex: (this.element.getStyle('zIndex') || 1) - 1
			}));
		return this;
	},

	hide: function() {
		if (this.fix) this.fix.setStyle('display', 'none');
		return this;
	},

	destroy: function() {
		this.fix.remove();
	}

});
