/***************************************************************/
// Bicycle JLS (Javascript Library Collection) 1.1b            //
// Collected & Hacked by Marco Solazzi - DWJ '07               //
// dwj [at] buzzurri [dot] net                                 //
// http://www.buzzurri.net/index.php?id=52                     //
//                                                             //
// This Collection Features the following libraries:           //
// - liberty - Basic JavaScript Library 0.1                    //
//   by Andreas Kalsch, Trier                                  //
//   licensed under GPL, look at gnu.org/licenses/gpl.html     //
//                                                             //
// - part of ff Javascript Library                             //
//   by Sven Helmberger                                        // 
//   Copyright (c) 2006, Sven Helmberger All rights reserved.  //
//                                                             //
// Snippets, functions, prototypes, inspirations by:           //
//	- Public Domain                                        //
//	- Embimedia                                            //
//	- Svend Tofte                                          //
//	- Ben Nadel                                            //
//	- Mootools 1.0                                         //
//	- Nomadic Functions                                    //
//	- Mega69                                               //
//       - Robert Nyman                                     //
//	- http://programming.arantius.com/dollar-e             //
//	- http://www.thescripts.com/forum/thread596105.html    //
// 	(see code for details)                                 //
//                                                             //
// Some Native Functions and Shortcuts by Marco Solazzi '07    //
// licensed under CC-GNU GPL                                   //
// http://creativecommons.org/licenses/GPL/2.0/                //
/***************************************************************/
$bicycle = {
	version : "1.1b"
}



/////////////////////////////////////////////////////////////////////
// liberty - Basic JavaScript Library 0.1 by Andreas Kalsch, Trier //
// Tutorial and API: http://aka-fotos.de/web?javascript/liberty /////
/////////////////////////////////////////////////////////////////////

try {
	var t = undefined;
}
catch(e) {
	undefined=null;
}

// From: Prototype JavaScript framework, version 1.4.0
// (c) 2005 Sam Stephenson <sam@conio.net>
if (!Array.prototype.push)
	Array.prototype.push = function() {
		var startLength = this.length;
		for (var i = 0; i < arguments.length; i++)
			this[startLength + i] = arguments[i];
		return this.length;
	}

function $getEls() {
	var elements = new Array();
	
	for (var i = 0; i < arguments.length; i++) {
		var element = arguments[i];
		if (typeof element == 'string')
			element = document.getElementById(element);
	
		if (arguments.length == 1)
			return element;
	
		elements.push(element);
	}
	
	return elements;
}


extendObject = function(destination, source) {
	for (var property in source)
		destination[property] = source[property];
	return destination;
}

if (!Array.prototype.indexOf)
	Array.prototype.indexOf = function(object) {
		for (var i = 0; i < this.length; i++)
			if (this[i] == object) 
				return i;
		return -1;
	}

getDimension = function(element) {
  // hacked by DWJ  - border hack from ExtendDOM (CSStyle) by Mega69 --- http://www.sitomega.net/projects/extendDOM/main.php
var el = typeof(element) == 'string' ? $getEls(element) : element;
		var border=(function() {
							if($browser.ie) { //IE
									return !getStyle(el,"border-size") ? 0 : getStyle(el,"border-size").toNumber();
							}
							else { //W3C browsers
									return 0; 
							}
					})();
	if (getStyle(el, 'display') != 'none')
		return {width: el.offsetWidth-border, height: el.offsetHeight-border};
	
	var els = el.style;
	var originalVisibility = els.visibility;
	var originalPosition = els.position;
	els.visibility = 'hidden';
	els.position = 'absolute';
	els.display = '';
	var obj = {width: el.clientWidth-border, height: el.clientHeight-border};
	els.display = 'none';
	els.position = originalPosition;
	els.visibility = originalVisibility;
	return obj;
}
//

uniqueId = function(prefix) {
	if (prefix == undefined)
		prefix = 'id';
		
	for (var i = 0; $getEls(prefix+i); i++);
	return prefix+i;
}

isArray = function(object) {
	if (!window.Array || object == null)
		return false;
	return object.constructor == window.Array;
}

getLength = function(object) {
	var i = 0;
	for (var key in object)
		i++;
	return i;
}

extendObject(String.prototype, {
	escapeXML: function() {
		var value = this;
		
		value = value.replace(/&/g, '&amp;');
		value = value.replace(/</g, '&lt;');
		value = value.replace(/>/g, '&gt;');
		value = value.replace(/\"/g, '&quot;');
		value = value.replace(/\'/g, '&apos;');
		
		return value;
	},
	
	unescapeXML: function() {
		var value = this;
		
		value = value.replace(/&lt;/gi, '<');
		value = value.replace(/&gt;/gi, '>');
		value = value.replace(/&quot;/gi, '"');
		value = value.replace(/&apos;/gi, '\'');
		value = value.replace(/&amp;/gi, '&');
		
		return value;
	},
// from Mootools 1.0
// by Valerio Proietti, <http://mad4milk.net>
	test: function(r, p){
		return ((typeof r == 'string') ? new RegExp(r, p) : r).test(this);
	},
// string conversion to number prototype from ExtendDOM (String.js)
// by Mega69 
// http://www.sitomega.net/projects/extendDOM/main.php
	toNumber: function () {
	var str=this.replace(/[^0-9\.\-]/g, "");
	if(str.match(".")) { return parseFloat(str) }
		return parseInt(str);
}
});

Number.prototype.setInRange = function(from, to) {
	if (this < from)
		return from;
	if (this > to)
		return to;
	return this;
}

getStyle = function(element, st) {
  // hacked by DWJ
var el = typeof(element) == 'string' ? $getEls(element) : element;
	if (typeof el == 'object' && typeof st == 'string') {
  // hacked by DWJ
var style = camelCase(st);
		if (el != null) {
			var value = el.style[style];
			if (value != '')
				return value;
			try {
				return el.currentStyle[style];
			} catch(e) {}
			try {
				return window.getComputedStyle(el, null)[style];
			} catch(e) {}
		}
	}
	
	return '';
}

addHandler = function(elements, types, handler) {
	if (!isArray(elements))
		elements = [elements];
	if (!isArray(types))
		types = [types];
	if (typeof handler == 'function') {
		for (var i = 0; i < elements.length; i++) {
			var el = $getEls(elements[i]);
			for (var j = 0; j < types.length; j++) {
				var type = types[j].toLowerCase();
				if (type.substr(0,2) == 'on')
					type = type.substr(2);
				var ontype = 'on'+type; 
					
				for (var k = 0; typeof el[type+k] == 'function'; k++);
				if (k == 0) {
					if (el[ontype]) {
						el[type+k] = el[ontype];
						el[ontype] = null;
						k++;
					}
				}
				el[type+k] = handler;
				if (!el[ontype])
					el[ontype] = function(e) {
						if (!e)
							e = window.event;

						for (var i = 0; typeof this[e.type+i] == 'function'; i++) {
							this[e.type+i](e);
						}
					}
			}
		}
		
		return true;
	}
	
	return false;
}

removeHandler = function(elements, types, handler) {
	if (!isArray(elements))
		elements = [elements];
	if (!isArray(types))
		types = [types];
		
	var ok = false;
	for (var i = 0; i < elements.length; i++) {
		var el = $getEls(elements[i]);
		
		for (var j = 0; j < types.length; j++) {
			var type = types[j].toLowerCase();
			if (type.substr(0,2) == 'on')
				type = type.substr(2);
			var ontype = 'on'+type; 
				
			if (typeof handler == 'function') {
				for (var k = 0; typeof el[type+k] != 'undefined'; k++) {
					if (el[type+k] == handler) {
						el[type+k] = function(e){};
						ok = true;
					}	
				}
			}
			else {
				el[ontype] = null;
				ok = true;
			}
		}
	}
	
	return ok;
}

getPosition = function(element, top, scrolling) {
  // hacked by DWJ
var el = typeof(element) == 'string' ? $getEls(element) : element;
	
	if (top == undefined)
		top = document.body;
	else
		top = $getEls(top);
	if (scrolling != undefined)
		scrolling = Math.round(Number(scrolling).setInRange(0, 1));
	else
		scrolling = 1;

	for (var lx = 0, ly = 0; el != top; el = el.offsetParent) {
		var par = el.offsetParent;
		var offsetLeft = el.offsetLeft;
		var offsetTop = el.offsetTop;
			
		if (scrolling) {
			offsetLeft -= par.scrollLeft;
			offsetTop -= par.scrollTop;
		}
		
		lx += offsetLeft;
		ly += offsetTop;
	}

	return {x: lx, y: ly};
}

getMousePosition = function(e, element) {
	if (!e)
		e = window.event;
	if (!e.target)
		e.target = e.srcElement;
	
	if (element == undefined)
		return {x: e.clientX, y: e.clientY};
	
	var x, y;
	if (e.layerX) {
		x = e.layerX;
		y = e.layerY;
	}
	else {
		x = e.offsetX;
		y = e.offsetY;
	}
	
	var el = $getEls(element);
	if (el != e.target) {
		var pos = getPosition(e.target, el);
		x += pos.x;
		y += pos.y;
	}
	
	return {x: x, y: y};
}

changeDisplay = function(element) {
// hacked by DWJ
	var el = typeof(element) == 'string' ? $getEls(element) : element; 
	var display = getStyle(el, 'display');
	if (typeof el._display == 'undefined')
		el._display = '';
	$S(el).display = (display == 'none') ? el._display : 'none';	
	el._display = display;	
	return $S(el).display;
}
/**
 ff js API (c) 2005 by Sven Helmberger ( sven dot helmberger at gmx dot de )
 project homepage: http://fforw.de/post/ff_javascript_library/
 available under a revised BSD-like library
 
 @projectDescription ff javascript library
 @author Sven Helmberger ( sven dot helmberger at gmx dot de )
 @version 1.11
 @namespace ff
 */
var ff= {
/**
 * @type {String} ffjs info field. Modified by IE compat and ff.dom
 */
dom:
    {
    /**
     * Removes all children from the given DOMElement
     * @param {DOMElement} elem DOM element
     */        
    clear:
      function ( elem)
      {
        while (elem.hasChildNodes())
          elem.removeChild(elem.firstChild);    
      },  
    /**
     * Replaces the current children of the DOM element with the given child(ren)
     * @param {DOMElement} elem DOM element
     * @param {DOMElement,Array} The new child/children
     */
    replace:
    	function(elem, newChildren)
    	{
    		ff.dom.clear(elem);
    		ff.dom.append(elem, newChildren);
    	},
    /**
     * Appends the given child(ren) to the children of the given DOM element
     * @param {DOMElement} e DOM element
     * @param {DOMElement,Array} c child/children to append
     */
    append:
    	function(e, c)
    	{					
    		if (c.length && isArray(c))
    		{
    			while(c.length)
    			{
    				e.appendChild(c[0]);
    			}
    		} else if (typeof c == "string"){
				var current = e.innerHTML;
				e.innerHTML = current+c;
				} else {
    			e.appendChild(c);
    		}
    	},
    /**
     * Creates a DOM creation function for every tag type
     * @param {Object} domBase base object which the DOM creation functions are properties of. 
     */
    /**
     * Sets the given attribute on the given DOM element to the given value.
     * @param {DOMElement} e DOM element 
     * @param {String} a attribute name
     * @param {String} v value
     */
    setAttribute:
// hacked by DWJ
        function(e,a,v)    {
var el = typeof e == "string" ? $getEls(e) : e;

              var a2=this._ar[a];
    if (a2)
        el.setAttribute(a2,v);
    else if (a == "style")
        el.style.cssText=v;
    else if (a.substring(0,2) == "on") 
        el[a] = new Function(v);
    else
            el.setAttribute(a,v);       
        },
    /**
     * Returns the value of the given attribute of the given DOM element
     * @param {DOMElement} e DOM element 
     * @param {String} a attribute name
     */
    getAttribute:
// hacked by DWJ    

        function(e,a)    {
var el = typeof e == "string" ? $getEls(e) : e;

                var a2=this._ar[a];
    if (a2)
        return el.getAttribute(a2);
    else        
        return el.getAttribute(a);
        }
    }
    };
ff.dom._ar=
{
    "class": "className",
    "checked": "defaultChecked",
    "usemap": "useMap",
    "for": "htmlFor"
};
/***************************************************************************/
// insertAfter
// on public domain & Sammy
// http://www.dustindiaz.com/top-ten-javascript/
function insertAfter(parent, node, referenceNode){
if(referenceNode.nextSibling){
parent.insertBefore(node, referenceNode.nextSibling);
} else {
parent.appendChild(node);
}
}
extendObject(Array.prototype,{
// inArray Prototype Array object
// by EmbiMedia
// http://code.mikebrittain.com/2006/01/inarray-method-for-javascript-and-actionscript/
	inArray : function (value) {
		var i;
		for (i=0; i < this.length; i++) {
		if (this[i] === value) {
			return true;
		}
		}
		return false;
	},
// Prototypes & functions by Svend Tofte
// http://www.svendtofte.com/
	filter : function(fnc) {
    		var a = [];
    		for (var i = 0; i < this.length; i++) {
       			if (fnc(this[i])) {
           			a.push(this[i]);
        			}
		}
    		return a;
	},
// hacked by DWJ
	map : function(fnc) {
    		var a = new Array(this.length);
    		for (var i = 0; i < this.length; i++) {
        		a[i] = fnc(this[i],i);
    		}
    	return a;
	},
// checkAgainst Prototype Array object
// Bicyle native
	checkAgainst : function (value) {
         if (this.inArray(value) != true) {
            this.push(value);
         }
         return this;
	}
});
window.getInnerWidth = function() {
    if (window.innerWidth) {
        return window.innerWidth;
    } else if (document.body.clientWidth) {
        return document.body.clientWidth;
    } else if (document.documentElement.clientWidth) {
        return document.documentElement.clientWidth;
    }
} 

window.getInnerHeight = function() {
    if (window.innerHeight) {
        return window.innerHeight;
    } else if (document.body.clientHeight) {
        return document.body.clientHeight;
    } else if (document.documentElement.clientHeight) {
        return document.documentElement.clientHeight;
    }
}
// renamed functions from
// Ben Nadel
// http://www.bennadel.com/snippets/categories/4-Javascript-Library-snippets.htm

function getNext( objNode ){

// Travel down the sibling chain looking for a non-text node.
for (
objNode = objNode.nextSibling ;
(
objNode &&
objNode.nodeType == 3
) ;
objNode = objNode.nextSibling
){
// Nothing has to happen here. The FOR loop itself is taking care
// of the node traversing. We don't have to really worry about any
// error checking as we can't really make it outside of HTML DOM
// elements without hitting a structured node, or coming up with NULL.
}

// Return the sibling node.
return( objNode );

}
function getPrev( objNode ){
 
// Travel up the sibling chain looking for a non-text node.
for ( 
objNode = objNode.previousSibling ;
(
objNode && 
objNode.nodeType == 3
) ;
objNode = objNode.previousSibling
){
// Nothing has to happen here. The FOR loop itself is taking care
// of the node traversing. We don't have to really worry about any
// error checking as we can't really make it outside of HTML DOM
// elements without hitting a structured node, or coming up with NULL.
}

// Return the sibling node.
return( objNode );

}
// ritorna il testo all'interno di un nodo senza tag etc
// from http://www.thescripts.com/forum/thread596105.html
function getText (el) {

   if (el.textContent) {return el.textContent;}
   if (el.innerText) {return el.innerText;}
   if (typeof el.innerHTML == 'string') {return el.innerHTML.replace(/<[^<>]+>/g,'');}
}
// from Mootools 1.0
// by Valerio Proietti, <http://mad4milk.net>
function camelCase (string) {
		return string.replace(/-\D/g, function(match){
			return match.charAt(1).toUpperCase();
		});
	}

/**
 * document.createElement convenience wrapper
 *
 * The data parameter is an object that must have the "tag" key, containing
 * a string with the tagname of the element to create.  It can optionally have
 * a "children" key which can be: a string, "data" object, or an array of "data"
 * objects to append to this element as children.  Any other key is taken as an
 * attribute to be applied to this tag.
 *
 * Release homepage:
 * http://programming.arantius.com/dollar-e
 *
 * Available under an MIT license:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * @param {Object} data The data representing the element to create
 * @return {Element} The element created.
 Examples
 var element=$E({
    tag:'div',
    className:'toolGroup',
    id:'toolGroup_1',
    children:{
        tag:'div',
        className:'roundBarTop',
        children:[{
            tag:'div',
            className:'leftEdge'
        },{
            tag:'div',
            className:'rightEdge'
        },{
            tag:'div',
            className:'heading',
            children:[{
                tag:'a',
                className:'collapser'
            },
                'Group Heading'
            ]
        }]
    }
});
 */
function $E(data) {
   var el;
   if ('string'==typeof data) {
       el=document.createTextNode(data);
   } else {
       //create the element
       el=document.createElement(data.tag);
       delete(data.tag);

       //append the children
       if ('undefined'!=typeof data.children) {
           if ('string'==typeof data.children ||
               'undefined'==typeof data.children.length
           ) {
               //strings and single elements
               el.appendChild($E(data.children));
           } else {
               //arrays of elements
               for (var i=0, child=null; 'undefined'!=typeof (child=data.children[i]); i++) {
                   el.appendChild($E(child));
               }
           }
           delete(data.children);
       }

       //any other data is attributes
       for (attr in data) {
         // hacked by DWJ
attr == "style" ? el.style.cssText = data[attr] : el[attr]=data[attr];
       }
   }

   return el;
}


//mine functions and shortcuts

/**
 *  getElements: collects elements with options
 * @author Marco Solazzi, inspired by Liberty getElements
 * @param {String} tagName Tag name to collect or '*' for all tags
 * @param {Object} [opt] attributes filter, specify html attribute and value as string or regular expression. Use '*' to just check attribute presence;
 *                       You can pass a parent element by adding property 'parent' : DOM element    
 * @return {Array} Returns an array of matched object
 * @version 1.5 revision 01/17/2007 
 */

function getElements (tagName, opt) {

	var options = (opt || {});
	var parent = (options.parent || document);
        if (options.parent) {
			delete options.parent;
		}
	
        var children;
	if (tagName == '*') {
		children = parent.getElementsByTagName('*');
		if (children.length == 0) {
				children = document.all;
			}
	}
	else {
		children = parent.getElementsByTagName(tagName);
	}
	var elements = [];
	var optsL = getLength(options);
        for (var i = 0; i < children.length; i++) {
             var child = children[i];
			if (optsL != 0) {
			 var points = 0;
             for (var opt in options) {
             var optTrans = ff.dom._ar[opt] != undefined ? ff.dom._ar[opt] : opt;
			 /**
			  * todo: fix the href absolute bug;
			  */
		if (options[opt] == "*" && child[optTrans]) {
			points++;
		} else if (opt == "class" && getElementsByClassName(options[opt],tagName,parent).inArray(child)){
			points++;
		}
		else if (options[opt] != "*" && (typeof(options[opt]) == "string" ? child[optTrans] == options[opt] : child[optTrans].test(options[opt]))) {
              points++;       
             }
           }
		   	if (points == optsL) {
		   		elements.checkAgainst(child);
		   	} 
           } else {
		   		elements.checkAgainst(child);
		   }

 	}

        return elements;
}

// shortcut to the first or the last element of an "elementsByTagName" array
function $TN(v,i,p) {
var elArray = (p || document).getElementsByTagName(v);
return elArray[(i == "last" ? (elArray.length-1) : 0)];
}
// shortcut to the first or the last element of an "elementsByName" array
function $EN(v,i,parentEl) {
var elArray = (parentEl || document).getElementsByName(v);
return elArray[(i == "last" ? (elArray.length-1) : 0)];
}
// shortcut to style
// inspired from http://www.nofunc.com/
function $S(v) { return typeof v == 'string' ? $getEls(v).style : v.style;}
// shortcut to generate a DIV element with id and optional text and append it to an element (div as string or other)
function addDIV (whichID,whereEl,whichTEXT) {
(typeof whereEl == 'string' ? $getEls(whereEl) : whereEl).appendChild($E({
tag:'div',id:whichID,children:(whichTEXT ? whichTEXT : '')
}));
}
// browser sniffing from ExtendDOM (utility.js) 
// by Mega69 
// http://www.sitomega.net/projects/extendDOM/main.php
$browser=(function() {
	var browser=navigator.userAgent;
	return { 
		'ie':browser.test(/MSIE/i),
		'ie6':this.ie||!window.XMLHttpRequest,
		'ie7':this.ie||window.XMLHttpRequest,
		'ff':browser.test(/Firefox/),
		'moz':browser.test(/Mozilla/),
		'ns':browser.test(/Netscape/),
		'op':Boolean(window.opera),
		'saf':browser.test(/WebKit/i),
		'kq':browser.test(/Khtml/i)
	}
})();


/*
	Developed by Robert Nyman, http://www.robertnyman.com
	Code/licensing: http://code.google.com/p/getelementsbyclassname/
*/
var getElementsByClassName = function (className, tag, elm){
	if (document.getElementsByClassName) {
		getElementsByClassName = function (className, tag, elm) {
			elm = elm || document;
			var elements = elm.getElementsByClassName(className),
				nodeName = (tag)? new RegExp("\\b" + tag + "\\b", "i") : null,
				returnElements = [],
				current;
			for(var i=0, il=elements.length; i<il; i+=1){
				current = elements[i];
				if(!nodeName || nodeName.test(current.nodeName)) {
					returnElements.push(current);
				}
			}
			return returnElements;
		};
	}
	else if (document.evaluate) {
		getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
				classesToCheck = "",
				xhtmlNamespace = "http://www.w3.org/1999/xhtml",
				namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace)? xhtmlNamespace : null,
				returnElements = [],
				elements,
				node;
			for(var j=0, jl=classes.length; j<jl; j+=1){
				classesToCheck += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]";
			}
			try	{
				elements = document.evaluate(".//" + tag + classesToCheck, elm, namespaceResolver, 0, null);
			}
			catch (e) {
				elements = document.evaluate(".//" + tag + classesToCheck, elm, null, 0, null);
			}
			while ((node = elements.iterateNext())) {
				returnElements.push(node);
			}
			return returnElements;
		};
	}
	else {
		getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
				classesToCheck = [],
				elements = (tag === "*" && elm.all)? elm.all : elm.getElementsByTagName(tag),
				current,
				returnElements = [],
				match;
			for(var k=0, kl=classes.length; k<kl; k+=1){
				classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)"));
			}
			for(var l=0, ll=elements.length; l<ll; l+=1){
				current = elements[l];
				match = false;
				for(var m=0, ml=classesToCheck.length; m<ml; m+=1){
					match = classesToCheck[m].test(current.className);
					if (!match) {
						break;
					}
				}
				if (match) {
					returnElements.push(current);
				}
			}
			return returnElements;
		};
	}
	return getElementsByClassName(className, tag, elm);
};/**
 * Bicyle Chain
 * @projectDescription Adds chainability to the Bicycle Javascript Library Collection
 * @author             Marco "DWJ" Solazzi
 * @license            CC-GNU GPL (where applicable)
 * @version            0.1b revision 23/05/2008
 * @namespace          $b
 */
(function() {
  // private constructor
  /**
   * Collect elements by various means
   * @param {Object} [...] Comma separated arguments can be a CSS simple string to get single class,id,tag or a DOM element. If no arguments are passed collects document 
   */
  function _$(els) {
	this.elements = [];
	for (var i=0; i<els.length; i++) {
		var element = els[i];
		if (typeof element == 'string') {
			if (element.charAt(0) == '.') {
				var el = getElementsByClassName(element.substr(1));
				this.elements.merge(el);	
			} else if (element.charAt(0) == '#') {
				var el = document.getElementById(element.substr(1));
				if (el != null) {
					this.elements.checkAgainst(el);
				}
			} else {
				console.log(getElements(element));
				this.elements.merge(getElements(element));
			}
		} else if (isArray(element)){
			this.elements.merge(element);
		} else if ($type(element) == "collection") {
			for (var i=0; i<element.length; i++) {
				this.elements.checkAgainst(element[i]);
				}
		} else {
			this.elements.checkAgainst(element);
		}
	}
	if (els.length == 0) { this.elements.push(document); }    
	return this;
  }

  _$.prototype = {
  	/**
  	 * Get elements contained  by current matched elements
  	 * replace previously matched elements
  	 * @param {String} tag search a specific tag or * for all tags
  	 * @param {Object} [opts] Object containing pairs of tag attribute : attribute value
  	 * @return {Object} this
  	 */
	get : function (tag,opts) {
		var els = [];
		this.each(function (el) {
			var options = {parent : el};
			extendObject(options,(opts || {}));
			var e = getElements(tag,options);
			els.merge(e);		
		});
		this.elements = els;
		return this;
	},
	/**
	 * Add elements to the elements stack
	 * @param {String} ... mixed arguments.
	 * @see #_$
	 * @return {Object} this
	 */
	add : function () {
		var els = _$(arguments);
		this.elements.merge(els.elements);
		return this;
	},
	/**
	 * Count elements contained in stack
	 * @return {Integer} elements stack count
	 */
	length : function () {
		return this.elements.length;
	},
	/**
	 * Extract a specific item from the stack
	 * @param {Number,String} i can be the index of the element or "last"t extract the last element in the stack
	 * @return {Object} this or element if grab is true (default: false)
	 */
	item : function (i,grab) {
		var el = [];
		var index = i == "last" ? this.elements.length-1 : i;
		el.push(this.elements[index]);
		this.elements = el;
		return (grab || false) ?  this.elements[0] : this;
	},
	/**
	 * Shortcut to get elements by classname
	 * @param {String} c the searched class name
	 * @param {String} [tag] optional tag name
	 * @see #get
	 * @return {Object} this
	 */
	byClass : function (c,tag) {
		this.get( (tag || '*') , {'class' : c});	
		return this;
	},
	/**
	 * Iterate a function on elements stack
	 * @param {Function} fn The function to iterate
	 * @return {Object} this
	 */
    each: function(fn) {
      this.elements.map(fn);
      return this;
    },
	/**
	 * Single CSS style manipulation
	 * @param {String} prop The CSS property
	 * @param {String} [val] New value for the CSS property
	 * @return {Object,String} fi val is not defined returns the CSS property value, else returns this
	 */
    Styles: function(prop, val) {
      if ($type(val)) {
		  this.each(function(el) {
	        el.style[prop.camelize()] = val;
	      });
	      return this;
	  } else {
	  	return getStyle(this.elements[0],prop);
	  }
    },
	/**
	 * Multiple CSS manipulation
	 * @param {Object,String} o Object with CSS property : CSS value or a CSS property string
	 * @param {String} [val] if o is a string, the new CSS value
	 * @see #Styles
	 * @return {Object,String} if o is a string and value is not defined returns the CSS propoerty value, else returns this
	 */
    css: function(o,val) {
      var that = this;
	  if ($type(o) == "object") {
	  	this.each(function(el){
	  		for (var prop in o) {
	  			that.Styles(prop, o[prop]);
	  		}
	  	});
	  	return this;
	  } else {
	  	return that.Styles(o,val);
	  }
    },
	/**
	 * Fire a function on a specific event on the elements stack
	 * @param {String,Array} type Type of event
	 * @param {Function} fn The function to fire
	 * @return {Object} this 
	 */
    on: function(type, fn) {
		if (!isArray(type) && $b.events[type]) {
			$b.events[type].apply(this,[this.elements,fn]);
		} else {
			addHandler(this.elements, type, fn);
		}
    	return this;
    },
	/**
	 * Remove a registered event handler from elements stack
	 * @param {String,Array} e Event(s) to remove 
	 * @param {Function} h Specific handler to remove
	 * @return {Object} this
	 */
	removeEvent : function (e,h) {
		removeHandler(this.elements,e,h);
		return this;
	},
	/**
	 * Removes all handlers registered for the specified event(s)
	 * @param {String,Array} e The event(s) handler to clear
	 * @return {Object} this
	 */
	removeEvents : function (e) {
		removeHandler(this.elements,e);
		return this;
	},
	/**
	 * Get or Set elements attribute
	 * @param {Object} a
	 * @param {Object} [v] value of the attribute
	 * @return {Object,String} if v is not defined returns this, else returns attribute value 
	 */
	attr : function (a,v) {
		if (v) {
			var that = this;
			this.each(function (el) {
				ff.dom.setAttribute(el,a,v);
			});
			return this;
		 } else {
			return ff.dom.getAttribute(this.elements[0],a);
		}  
	},
	/**
	 * Return text of first element in the stack stripping all childs html tags
	 * @return {String} contained text
	 */
	txt : function () { return getText(this.elements[0]).replace(/^\s+|\s+$/g, ''); 
	},
	/**
	 * Get next simbling element of the first element in the stack
	 * @return {Object} this
	 */
	getNext : function () {
		this.elements = new Array();
		this.elements.push(getNext(this.elements[0]));
		return this;
	},
	/**
	 * Get previous simbling element of the first element in the stack
	 * @return {Object} this
	 */ 
	getPrev : function () {
		this.elements = new Array();
		this.elements.push(getPrev(this.elements[0]));
		return this;
	},
	/**
	 * Returns the dimensions in px of the first element in the stack
	 * @param {String} [d] return a specific dimension: "w" for width, "h" for heigth
	 * @return {Object,String} if no specific dimension is requested returns an object {width:value,height:value} 
	 */
	dimension : function (d) {
		if (this.elements[0] == window) {
			var dims = {
				width: window.getInnerWidth(),
				height: window.getInnerWidth()
			};
		} else {
			var dims = getDimension(this.elements[0]);
		}
		if (d && d=="h") {
			return dims.height;
		} else if (d && d=="w") {
			return dims.width;
		} else if (!d) {
			return dims;
		}
	},
	/**
	 * Returns the position in px of the first element in the stack
	 * @param {Object} [d] Specific position to get "x" or "y"
	 * @param {Object,String} [t] The parent element to start measures from, if is a string than referes to a ID, defaults to body
	 * @param {Bool} [s] Scrolling inside of elements should be subtracted (default: true - better for absolute positioning).
	 * @return {Object,String} if no specific dimension is requested returns an object {x:value,y:value} 
	 */
	position : function (d,t, s) {
		var pos = getPosition(this.elements[0],(t || document.body),(s || true));
		if (d && d=="x") {
			return pos.x;
		} else if (d && d=="y") {
			return pos.y;
		} else if (!d) {
			return pos;
		}
	},
	/**
	 * An object containing dimensions and position of the first element in the stack
	 * @see #position
	 * @see #dimension
	 * @return {Object} Object with dimensions and position
	 */
	space : function () {
		var pos = getPosition(this.elements[0]);
		return extendObject(pos,this.dimension());
	},
	/**
	 * get the current mouse position relative to the first element in the stack
	 * @param {Object} e The event attached to the mouse action
	 * @return {Object} x,y position of the mouse
	 */
	mousePosition: function(e){
		return getMousePosition(e, this.elements[0]);
	},
	/**
	 * Toggle visibility of stack elements by CSS
	 * @return {Object} this 
	 */
	toggle : function () {
		this.each(function (el) {
			changeDisplay(el);
			});
		return this;
	},
	/**
	 * Removes all child elements of the stack elements.
	 * @return {Object} this 
	 */
	clear : function () {
		this.each(function (el) {
			ff.dom.clear(el);
		});
		return this;
	},
	/**
	 * Append the given child(ren) element(s) to each element in the stack
	 * @param {Object,Array} c Child(ren), can be either a DOM element or an array of DOM elements.
	 * @return {Object} this 
	 */
	append : function (c) {
		this.each(function(el) {
			ff.dom.append(el,c);
		});
		return this;
	},
	/**
	 * Replaces the current children elements of each element in the stack with the passed in element(s). 
	 * @param {Object,Array} e New child(ren) element(s). Can be either a DOM element or an array of DOM elements.
	 * @return {Object} this 
	 */
	replace : function (e) {
		this.each(function(el){
			ff.dom.replace(el,e);
		});
		return this;
	},
	/**
	 * Inserts stack elements inside a parent node and after a reference node.
	 * @param {Object} [ref] Reference node. If None is found just appends the node element inside the parent.
	 * @param {Object,String} [p] Parent element, can be a DOM element or a string that refer to a ID, defaults to document
	 * @return {Object} this  
	 */
	queueTo : function (ref,p) {
		var p = p || document;
		var r = typeof(ref) == "string" ? $getEls(ref) : ref;
		this.each(function(el){
			insertAfter(p,el,r);
		});
		return this;
	}
	
  };
  
  /**
   * Init the $b class
   */
  window.$b = function() {
    return new _$(arguments);
  };
  /**
   * Method to extend Bicycle Chain
   * @param {Object} obj An object cointaining functions to add to Bicycle 
   */
  window.$b.extend = function (obj) {
		for (var property in obj) {
			_$.prototype[property] = obj[property];
		}
	};
})();
extendObject(window.$b,{
	components : {
		source : $bicycle.version,
		chain : "0.1b"
	},
	events : {}
});
/**
 * Extend String Object by add camelize function
 * @see $b#Styles
 */
extendObject(String.prototype,{
	camelize : function () {
		return camelCase(this);
	}
});
/**
 * Merge two arrays filtering duplicates
 * @param {Array} array The array to merge with
 * @return {Array} the merged array
 */
extendObject(Array.prototype,{
	merge : function (array) {
		for (var i = 0; i < array.length; i++) {
		        if ( !this.inArray(array[i])) {
            			this.push(array[i]);
         		}	
		}
         return this;
	}
});/**
 * @author dwj
 */
$b.extend({
	/**
	 * Get specific(s) element(s) from the stack, or returns elements stack array
	 * @param {Number} [n] start index for extraction (can be negative)
	 * @param {Number} [o] number of items to extract (defaults to 0, extract 1 element)
	 * @return {Object,Array} if n is specified and grab is false (default) returns this, else returns all elements in stack as an array
	 */
	items : function (n,o,grab) {
		var howmany = n || false;
		if (howmany) {
			var offset = (o || 0);
			var endSlice = howmany+offset;
			if (howmany.toString().charAt(0) == "-") {
				offset = howmany-(o || 0);
				endSlice = this.elements.length-(o || 0);
			}
			this.elements = this.elements.slice(offset,endSlice);
			return (grab || false) ?  this.elements : this;
		} else {
			return this.elements; 
		}
	},

	/** 
	 * Test if the first element in the stack has passed in class name
	 * @author Robert Nyman/DOMAssistant team: http://code.google.com/p/domassistant/
	 * @param {String} className The class name to test
	 * @return {Bool}
	 */
	hasClass : function (className) {
			return new RegExp(("(^|\\s)" + className + "(\\s|$)"), "i").test((this.elements[0]).className);
	},
	
	/**
	 * Add a className to each element of the stack
	 * @author Robert Nyman/DOMAssistant team: http://code.google.com/p/domassistant/
	 * @param {String} className The class to add
	 * @return {Object} this
	 */
	addClass : function (className) {
			this.each(function (el){
				var currentClass = el.className;
				if (!new RegExp(("(^|\\s)" + className + "(\\s|$)"), "i").test(currentClass)) {
					el.className = currentClass + (currentClass.length ? " " : "") + className;
				}
			});
			return this;
	},
	
	/**
	 * Removes a class from each element of the stack
	 * @author Robert Nyman/DOMAssistant team: http://code.google.com/p/domassistant/
	 * @param {String} className The class to remove
	 * @return {Object} this
	 */
	removeClass : function (className) {
			var classToRemove = new RegExp(("(^|\\s)" + className + "(\\s|$)"), "i");
			this.each(function (el){
				el.className = el.className.replace(classToRemove, function (match) {
				var retVal = "";
				if (new RegExp("^\\s+.*\\s+$").test(match)) {
					retVal = match.replace(/(\s+).+/, "$1");
				}
				return retVal;
				}).replace(/^\s+|\s+$/g, "");
			});

			return this;
		},
	/**
	 * Event delegation shortcut
	 * @param {String,Array} eventType Type of event
	 * @param {Object} rules Object with Element class Name : function to fire 
	 * @param {Bool} [prevent] prevent the default element behaviour, defaults to true 
	 * @see #on
	 * @return {Object} this
	 */
	delegate : function (eventType,rules,prevent) {
		return this.on(eventType, function(e) {
			var target = $b.getTarget(e);
			var prevent = prevent || true;
			for (var selector in rules) {
				if ($b(target).hasClass(selector)) {
					if (prevent) {$b.preventDefault(e); }
					return rules[selector].apply(target, arguments);
				}
			}
		});
	},
	/**
	 * Get or Set HTML for the first element in the stack
	 * @param {String} [c] HTML String inject in the element
	 * @return {Object,String} if c is defined returns this else returns the content on the element as a string 
	 */
	html : function (c) {
		var el = this.elements[0];
		if (v) {
			el.innerHTML = c;
			return this;
		} else {
			return el.innerHTML;
		}
	},
	/**
	 * Removes all the elements in the stack
	 * @return {Object} this
	 */
	remove : function () {
		this.elements.map(function (el) {
			el.parentNode.removeChild(el);
		});
		return this;
	},
	/**
	 * Load via AJAX content of a given file inside stack elements
	 * @param {String} url Url of the filename which holds the content
	 * @param {Object} [fn] A callbacl function
	 */
	ajaxload : function (url,fn) {
		var ajax = new ajaxObject(url);
		var cb = fn || function () {};
		var that = this;
		ajax.callback = function (responseText) {
			that.replace(responseText);
			cb();
		};
		ajax.update();
		return this;
	}	
});
/**
 * Add custom events to the .on method
 * @see #on
 */
extendObject(window.$b.events,{
	/**
	 * Add DOM loaded handler
	 * @param {Array} elements Stack elements
	 * @param {Function} fn Function to run 
	 */
	"domready" : function (elements,fn) {
		if (elements.length == 1 && elements[0] == document && typeof(DOMAssistant) != "undefined") {
			DOMAssistant.DOMReady(fn);
		}
	},
	/**
	 * Fix mouseover event for elements with children
	 * @param {Array} elements Stack elements
	 * @param {Function} fn Function to run 
	 */
	"mouseenter" : function (elements,fn) {
		this.on("mouseover",function (e) {
			var callback = fn;
			if ($b.isMouseLeaveOrEnter(e,this)) {
				callback();
			}
		});
	},
	/**
	 * Fix mouseout event for elements with children
	 * @param {Array} elements Stack elements
	 * @param {Function} fn Function to run 
	 */
	"mouseleave" : function (elements,fn) {
		this.on("mouseout",function (e) {
			var callback = fn;
			if ($b.isMouseLeaveOrEnter(e,this)) {
				callback();
			}
		});
	},
	/**
	 * Fix MouseWheel scrolling event handling
	 * @author	Andrea Giammarchi		[http://www.devpro.it/]
	 * @license	MIT 				[http://www.opensource.org/licenses/mit-license.php]
	 * @credits	Adomas Paltanavicius 		[http://adomas.org/javascript-mouse-wheel/]
	 * @param {Array} elements Elements stack
	 * @param {Function} callback A callback function to launch. takes delta as argument
	 */
	"mousewheel": function onmousewheel(elements, callback) {
		var element = elements[0];		
		function __onwheel(event) {
			var	delta = 0;
			if(event.wheelDelta) {
				delta = event.wheelDelta / 120;
				if(window.opera)
					delta = -delta;
			}
			else if(event.detail)
				delta = -event.detail / 3;
			if(delta)
				callback.call(element, delta);
			$b.preventDefault(event);
		};
		
		if(element.addEventListener && !window.opera)
			element.addEventListener("DOMMouseScroll", __onwheel, false);
		else
			element.onmousewheel = (function(base){return function(evt){
				if(!evt) evt = window.event;
				if(base) base.call(element, evt);
				return __onwheel(evt);
			}})(element.onmousewheel);
	}
});

/**
 * Crossbrowser event management and DOMready shortcut
 */
extendObject(window.$b,{
	/**
	 * Prevent the default behaviour to be launched
	 * @param {Object} evt event
	 */
	preventDefault: function(evt){
		if (evt && evt.preventDefault) {
			evt.preventDefault();
		}
		else {
			window.event.returnValue = false;
		}
	},
	/**
	 * Prevent the propagation of the event
	 * @param {Object} evt event
	 */
	cancelBubble : function (evt) {
			if (evt && evt.stopPropagation) {
				evt.stopPropagation();
			}
			else {
				window.event.cancelBubble = true;
			}
	},
	/**
	 * Get a reference to the element which triggered the event 
	 * @param {Object} evt event
	 * @return {Object} the target reference. You can work on it using $b() wrapper
	 */
	getTarget: function(evt){
		var event = evt || window.event;
        return event.target || event.srcElement;
	},
	/**
	 * Fix for mouseover and mouseout event handlers 
	 * @author http://www.dynamic-tools.net/toolbox/isMouseLeaveOrEnter/
	 * @param {Object} e The event
	 * @param {Object} handler The object which handles the event 
	 */
	isMouseLeaveOrEnter : function (e, handler) { 
		if (e.type != 'mouseout' && e.type != 'mouseover') {
			return false; 
		}
		var reltg = e.relatedTarget ? e.relatedTarget : e.type == 'mouseout' ? e.toElement : e.fromElement;
		while (reltg && reltg != handler) {
			reltg = reltg.parentNode; 
		}
		return (reltg != handler); 
	},
	/**
	 * Shortcut for $b(document).on("domready",fn);
	 * @see #on
	 * @param {Function} fn The function to fire on DOM ready
	 * @return {Object} this
	 */
	ready : function (fn) {
		return $b(document).on("domready",fn);
	}
});




// Developed by Robert Nyman/DOMAssistant team, code/licensing: http://code.google.com/p/domassistant/, documentation: http://www.domassistant.com/documentation. Module inspiration by Dean Edwards, Matthias Miller, and John Resig: http://dean.edwards.name/weblog/2006/06/again/
/*extern DOMAssistant */
if (typeof DOMAssistant == "undefined") {
		DOMAssistant = {};
}
DOMAssistant.DOMLoad = function () {
	var DOMLoaded = false;
	var DOMLoadTimer = null;
	var functionsToCall = [];
	var addedStrings = {};
	var errorHandling = null;
	var execFunctions = function () {
		for (var i=0, il=functionsToCall.length; i<il; i++) {
			try {
				functionsToCall[i]();
			}
			catch (e) {
				if (errorHandling && typeof errorHandling === "function") {
					errorHandling(e);
				}
			}
		}
		functionsToCall = [];
	};
	var DOMHasLoaded = function () {
		if (DOMLoaded) {
			return;
		}
		DOMLoaded = true;
		execFunctions();
	};
	/* Internet Explorer */
	/*@cc_on
	@if (@_win32 || @_win64)
		if (document.getElementById) {
			document.write("<script id=\"ieScriptLoad\" defer src=\"//:\"><\/script>");
			document.getElementById("ieScriptLoad").onreadystatechange = function() {
				if (this.readyState === "complete") {
					DOMHasLoaded();
				}
			};
		}
	@end @*/
	/* Mozilla/Opera 9 */
	if (document.addEventListener) {
		document.addEventListener("DOMContentLoaded", DOMHasLoaded, false);
	}
	/* Safari, iCab, Konqueror */
	if (/KHTML|WebKit|iCab/i.test(navigator.userAgent)) {
		DOMLoadTimer = setInterval(function () {
			if (/loaded|complete/i.test(document.readyState)) {
				DOMHasLoaded();
				clearInterval(DOMLoadTimer);
			}
		}, 10);
	}
	/* Other web browsers */
	window.onload = DOMHasLoaded;
	
	return {
		DOMReady : function () {
			for (var i=0, il=arguments.length, funcRef; i<il; i++) {
				funcRef = arguments[i];
				if (!funcRef.DOMReady && !addedStrings[funcRef]) {
					if (typeof funcRef === "string") {
						addedStrings[funcRef] = true;
						funcRef = new Function(funcRef);
					}
					funcRef.DOMReady = true;
					functionsToCall.push(funcRef);
				}
			}
			if (DOMLoaded) {
				execFunctions();
			}
		},
		
		setErrorHandling : function (funcRef) {
			errorHandling = funcRef;
		}
	};
}();
DOMAssistant.DOMReady = DOMAssistant.DOMLoad.DOMReady;


/**
 * @projectDescription Ultimate AJAX Object : http://www.hunlock.com/blogs/The_Ultimate_Ajax_Object
 * @author Patrick Hunlock
 * @copyright Patrick Hunlock 2007
 * @license public domain
 * 
 */
function ajaxObject(url, callbackFunction) {
  var that=this;      
  this.updating = false;
  this.abort = function() {
    if (that.updating) {
      that.updating=false;
      that.AJAX.abort();
      that.AJAX=null;
    }
  }

  this.update = function(passData,postMethod) { 
  var urlCall = url;  
	if (that.updating) { return false; }
    that.AJAX = null;                          
    if (window.XMLHttpRequest) {              
      that.AJAX=new XMLHttpRequest();              
    } else {                                  
      that.AJAX=new ActiveXObject("Microsoft.XMLHTTP");
    }                                             
    if (that.AJAX==null) {                             
      return false;                               
    } else {
      that.AJAX.onreadystatechange = function() {  
        if (that.AJAX.readyState==4) {             
          that.updating=false;                
          that.callback(that.AJAX.responseText,that.AJAX.status,that.AJAX.responseXML);        
          that.AJAX=null;                                         
        }                                                      
      }                                                        
      that.updating = new Date(); 
	  /**
	   * Patched by DWJ
	   */
	  var urlCall = urlCall + (/\?/.test(urlCall) ? '&timestamp=' : '?timestamp=');     
	  
      if (/post/i.test(postMethod)) {
        var uri=urlCall+that.updating.getTime();
        that.AJAX.open("POST", uri, true);
        that.AJAX.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        that.AJAX.setRequestHeader("Content-Length", passData.length);
        that.AJAX.send(passData);
      } else {
        var uri=urlCall + (that.updating.getTime()) + (passData ? "&" + passData : ""); 
        that.AJAX.open("GET", uri, true);                             
        that.AJAX.send(null);                                         
      }              
      return true;                                             
    }                                                                           
  }  
  this.callback = callbackFunction || function () { };
}/**
 * Telegraph
 * Bicyle Chain Form Handler
 * @projectDescription Extends Bicycle Chain by adding support for form handling 
 * @author             Marco "DWJ" Solazzi
 * @license            CC-GNU GPL (where applicable)
 * @version            0.1a revision 23/05/2008
 * @namespace          $b
 */

(function(){
	/**
	 * _$F: selects the form and fills an array with it's children elements excluding buttons
	 * revision 17/01/2008
	 * @param {String} id id assigned to the form
	 * @return {Object} Returns a new Form Object
	 * @constructor
	 */
	function _$F(id){
		for (a=0; a<document.forms.length; a++) {
			if (document.forms[a].id == id[0]) {	
				this.f = document.forms[a];
				this.fId = id[0];
				this.fields = [];
				this.buttons = {submits:[],resets:[],buttons:[]};
				
				var excluded = ["submit", "reset", "button"];
				for (i = 0; i < this.f.elements.length; i++) {
					var field = this.f.elements[i];
					//console.log(field.type);
					if (!excluded.inArray(field.type)) {
						if ($type(field.type)) { this.fields.push(field); }
					} else {
						this.buttons[field.type+"s"].push(field);
					}
				}
				
			}
		}
		return this;
	}
	/**
	 * add methods
	 */
	_$F.prototype = {
		/**
		 * field: fills the form fields array with specific named field(s)
		 * revision 17/01/2008
		 * @param {String} name Name of the field(s)
		 * @return {Object} Returns the form object 
		 * @method
		 */
		field : function (name) {
			this.fields = new Array();
			if (this.f.elements[name].length > 1) {
				this.fields = this.f.elements[name];
			}
			else {
				this.fields.push(this.f.elements[name]);
			}
			return this;
		},
		
		/**
		 * todo
		 */
		fieldsByType : function (type) {
			var obj = {};
			var types = this.getTypes(true);
			if ($type(types[type])) {
				var searchtype = types[type];
				var _this = this;
				searchtype.map(function(el){
					obj[el] = _this.field(el).value();
				});
			}
			return obj;
		},
		
		/**
		 * value: if param v is set sets the field value else gets the field value
		 * revision 17/01/2008
		 * @param {String} [v] Set the field value 
		 * @return {Object,String,Array} If param v is set returns the form object, else returns the field value or an array of checked fields values (for checkboxes) 
		 * @method
		 */
		value : function (v) {
			if (v) {
				for (i=0; i < this.fields.length; i++) {
						var el = this.fields[i];
						switch (el.type) {
							case "radio":
							case "checkbox":
								if (el.value == v) {
									el.checked = true;
								}
							break;
							case "select-one":
								el.selectedOption = v;
							break;
							default:
								el.value = v;
							break;
						}
				}
				return this;
			}
			else {
				var value = []; 
				if (this.fields.length > 1) {
					if (this.fields.type != "select-one") {
						for (i = 0; i < this.fields.length; i++) {
							var el = this.fields[i];
							switch (el.type) {
								case "radio":
									if (el.checked) {
										return el.value;
									}
									break;
								case "checkbox":
									if (el.checked) {
										value.push(el.value);
									}
									break;
								default:
									if (el.value != "") {
										value.push(el.value);
									}
									break;
							}
						}
					} else {
						return this.fields.value;
					}
					return value.length > 0 ? value : false;
				}
				if (this.fields[0].type == "checkbox" && !this.fields[0].checked) {
					return false;
				}
				else {
					return this.fields[0].value != "" ? this.fields[0].value : false;
				}
				
			}
		},
/*		encode : function (value) {
			return encodeURIComponent(value);
		},
		*/
		
		/**
		 * serialize : builds an object from the fields array excluding empty fields
		 * revision 25/01/2008
		 * @param {String} [o] an object to be added to the fields' serialization 
		 * @return {Object} Returns an object with pairs fieldName : fieldValue (String or Array)  
		 */
		serialize: function(o){
			var obj = {};
			var _this = this;
			this.fields.map(function(el){
				var value = _this.field(el.name).value();
				if (value) {
					obj[el.name] = value;
				}
			});
			return $merge(obj,o || {});
		},
		
		/** 
		 * toQuery : converts fields name and value to a query string
		 * revision 25/01/2008
		 * @param {String} [d] Defaults to ":". Delimiter to use for joined array values on fields with same name
		 * @return {String} Returns a query string of not empty fields: fieldName=fieldValue&... 
		 */
		toQuery: function(d){
			var query = "";
			var obj = this.serialize();
			for (var prop in obj)
			{
				query += prop + "=" + (isArray(obj[prop]) ? obj[prop].join(d || ":") : obj[prop]) + "&";
			}
			return query.substr(0,query.length - 1);
		},
		
		/**
		 * send : shortcut to send form data via AJAX
		 * revision 25/01/2008
		 * @param {String} url Url to processing script
		 * @param {String} [m] Defaults to "POST". Method to send data: "POST" or "GET"
		 * @param {Function} [fn] A callback function to launch on AJAX complete
		 * @see #toQuery
		 * @return {Object} Returns the Form Object
		 */
		send: function(url, fn, m){
			var query = this.toQuery();
			var ajax = new ajaxObject(url);
			if (fn) {
				ajax.callback = fn;
			}
			ajax.update(query, m || 'POST');
			return this;
		},
		/**
		 * ajax : intialize a new ajax call 
		 * revision 25/01/2008
		 * @param {Object} opts a bunch of options, see code below  
		 */
		ajax: function(opts){
			var options = $merge({
				url : '', // the url to send data
				method : 'GET', // method GET or POST
				data : {}, // Additional data object to attach to the request
				onComplete : function () {} // a callback function
			},opts || {});
			var dataObject = this.serialize(options.data);
			var ajax = new ajaxObject(options.url);
			ajax.callback = options.onComplete;
			ajax.update(dataObject,options.method);
		},
		
		preventSubmit : function (el) {
			var els = $b(this.buttons.submits);
			if (el) {
				els.add(el);
			}
			els.on("click",function (e){
				$b.preventDefault(e);
			});
			$b(this.f).on("submit",function (e){
				$b.preventDefault(e);
			});
		},
		getTypes : function (buttons) {
			var obj = {};
			var _this = this;
			this.fields.map(function(el){
				var name = el.name;
				var type = el.type;
				if (isArray(obj[type])) {
					if (!obj[type].inArray(name)) {
						obj[type].push(name);
					}
				} else {
					obj[type] = [name];
				}
			});
			return (buttons || false) ? $merge(obj,this.buttons) : obj;		
		}
	};
	/**
	 * Attach the new class to the $b namespace as getForm
	 */
	window.$b.getForm = function(){
		return new _$F(arguments);
	};
})();
extendObject(window.$b.components,{
	telegraph: "0.1a"
});

/**
 * Some addon functions
 * @author Valerio Proietti (from MooTools 1.11)
 */
/*
Function: $defined
	Returns true if the passed in value/object is defined, that means is not null or undefined.

Arguments:
	obj - object to inspect
*/

function $defined(obj){
	return (obj != undefined);
};

/*
Function: $type
	Returns the type of object that matches the element passed in.

Arguments:
	obj - the object to inspect.

Example:
	>var myString = 'hello';
	>$type(myString); //returns "string"

Returns:
	'element' - if obj is a DOM element node
	'textnode' - if obj is a DOM text node
	'whitespace' - if obj is a DOM whitespace node
	'arguments' - if obj is an arguments object
	'object' - if obj is an object
	'string' - if obj is a string
	'number' - if obj is a number
	'boolean' - if obj is a boolean
	'function' - if obj is a function
	'regexp' - if obj is a regular expression
	'class' - if obj is a Class. (created with new Class, or the extend of another class).
	'collection' - if obj is a native htmlelements collection, such as childNodes, getElementsByTagName .. etc.
	false - (boolean) if the object is not defined or none of the above.
*/

function $type(obj){
	if (!$defined(obj)) return false;
	if (obj.htmlElement) return 'element';
	var type = typeof obj;
	if (type == 'object' && obj.nodeName){
		switch(obj.nodeType){
			case 1: return 'element';
			case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
		}
	}
	if (type == 'object' || type == 'function'){
		switch(obj.constructor){
			case Array: return 'array';
			case RegExp: return 'regexp';
			//case Class: return 'class';
		}
		if (typeof obj.length == 'number'){
			if (obj.item) return 'collection';
			if (obj.callee) return 'arguments';
		}
	}
	return type;
};


/**
 * $merge : merges a number of objects recursively without referencing them or their sub-objects.
 * @author Valerio Proietti (from MooTools 1.11)
 * @param {Object} any number of objects
*/
function $merge(){
	var mix = {};
	for (var i = 0; i < arguments.length; i++) {
		for (var property in arguments[i]) {
			var ap = arguments[i][property];
			var mp = mix[property];
			if (mp && $type(ap) == 'object' && $type(mp) == 'object') 
				mix[property] = $merge(mp, ap);
			else 
				mix[property] = ap;
		}
	}
	return mix;
}
