/*
 * jQuery.id
 * 
 * Get By ID with try-char-3 
 * $.id('myNode'); //returns DOM Element Node with id = 'myNode'
 * $.id('yourNode', contextNode); //returns DOM Element Node with id = 'yourNode' within DOM Element Node referenced by context.
 * $.id('foo', document, '#'); //returns DOM Element Node with id = 'foo' or '#foo' or '##foo' or '###foo'
 * 
 */

jQuery.id = function(id, context, c) {
	return jQuery('#' + id, context)[0] || jQuery('#' + c + id, context)[0] || jQuery('#' + c + c + id, context)[0] || jQuery('#' + c + c + c + id, context)[0];
};
/***********************************************/



/******************************************************************************/
/*********************************** EASING ***********************************/
/******************************************************************************/

/*
* jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
*
* Uses the built in easing capabilities added In jQuery 1.1
* to offer multiple easing options
*
* TERMS OF USE - jQuery Easing
*
* Open source under the BSD License.
*
* Copyright 2008 George McGinley Smith
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the author nor the names of contributors may be used to endorse
* or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

// t: current time, b: begInnIng value, c: change In value, d: duration
jQuery.easing.jswing = jQuery.easing.swing;

jQuery.extend(jQuery.easing,
{
	def: 'easeOutQuad',
	swing: function(x, t, b, c, d) {
		//alert($.easing.default);
		return $.easing[$.easing.def](x, t, b, c, d);
	},
	easeInQuad: function(x, t, b, c, d) {
		return c * (t /= d) * t + b;
	},
	easeOutQuad: function(x, t, b, c, d) {
		return -c * (t /= d) * (t - 2) + b;
	},
	easeInOutQuad: function(x, t, b, c, d) {
		if ((t /= d / 2) < 1) return c / 2 * t * t + b;
		return -c / 2 * ((--t) * (t - 2) - 1) + b;
	},
	easeInCubic: function(x, t, b, c, d) {
		return c * (t /= d) * t * t + b;
	},
	easeOutCubic: function(x, t, b, c, d) {
		return c * ((t = t / d - 1) * t * t + 1) + b;
	},
	easeInOutCubic: function(x, t, b, c, d) {
		if ((t /= d / 2) < 1) return c / 2 * t * t * t + b;
		return c / 2 * ((t -= 2) * t * t + 2) + b;
	},
	easeInQuart: function(x, t, b, c, d) {
		return c * (t /= d) * t * t * t + b;
	},
	easeOutQuart: function(x, t, b, c, d) {
		return -c * ((t = t / d - 1) * t * t * t - 1) + b;
	},
	easeInOutQuart: function(x, t, b, c, d) {
		if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b;
		return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
	},
	easeInQuint: function(x, t, b, c, d) {
		return c * (t /= d) * t * t * t * t + b;
	},
	easeOutQuint: function(x, t, b, c, d) {
		return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
	},
	easeInOutQuint: function(x, t, b, c, d) {
		if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b;
		return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
	},
	easeInSine: function(x, t, b, c, d) {
		return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
	},
	easeOutSine: function(x, t, b, c, d) {
		return c * Math.sin(t / d * (Math.PI / 2)) + b;
	},
	easeInOutSine: function(x, t, b, c, d) {
		return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
	},
	easeInExpo: function(x, t, b, c, d) {
		return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
	},
	easeOutExpo: function(x, t, b, c, d) {
		return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
	},
	easeInOutExpo: function(x, t, b, c, d) {
		if (t == 0) return b;
		if (t == d) return b + c;
		if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
		return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
	},
	easeInCirc: function(x, t, b, c, d) {
		return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
	},
	easeOutCirc: function(x, t, b, c, d) {
		return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
	},
	easeInOutCirc: function(x, t, b, c, d) {
		if ((t /= d / 2) < 1) return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
		return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
	},
	easeInElastic: function(x, t, b, c, d) {
		var s = 1.70158; var p = 0; var a = c;
		if (t == 0) return b; if ((t /= d) == 1) return b + c; if (!p) p = d * .3;
		if (a < Math.abs(c)) { a = c; var s = p / 4; }
		else var s = p / (2 * Math.PI) * Math.asin(c / a);
		return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
	},
	easeOutElastic: function(x, t, b, c, d) {
		var s = 1.70158; var p = 0; var a = c;
		if (t == 0) return b; if ((t /= d) == 1) return b + c; if (!p) p = d * .3;
		if (a < Math.abs(c)) { a = c; var s = p / 4; }
		else var s = p / (2 * Math.PI) * Math.asin(c / a);
		return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
	},
	easeInOutElastic: function(x, t, b, c, d) {
		var s = 1.70158; var p = 0; var a = c;
		if (t == 0) return b; if ((t /= d / 2) == 2) return b + c; if (!p) p = d * (.3 * 1.5);
		if (a < Math.abs(c)) { a = c; var s = p / 4; }
		else var s = p / (2 * Math.PI) * Math.asin(c / a);
		if (t < 1) return -.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
		return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
	},
	easeInBack: function(x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c * (t /= d) * t * ((s + 1) * t - s) + b;
	},
	easeOutBack: function(x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
	},
	easeInOutBack: function(x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		if ((t /= d / 2) < 1) return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
		return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
	},
	easeInBounce: function(x, t, b, c, d) {
		return c - $.easing.easeOutBounce(x, d - t, 0, c, d) + b;
	},
	easeOutBounce: function(x, t, b, c, d) {
		if ((t /= d) < (1 / 2.75)) {
			return c * (7.5625 * t * t) + b;
		} else if (t < (2 / 2.75)) {
			return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
		} else if (t < (2.5 / 2.75)) {
			return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
		} else {
			return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
		}
	},
	easeInOutBounce: function(x, t, b, c, d) {
		if (t < d / 2) return $.easing.easeInBounce(x, t * 2, 0, c, d) * .5 + b;
		return $.easing.easeOutBounce(x, t * 2 - d, 0, c, d) * .5 + c * .5 + b;
	}
});

/*
*
* TERMS OF USE - EASING EQUATIONS
*
* Open source under the BSD License.
*
* Copyright 2001 Robert Penner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the author nor the names of contributors may be used to endorse
* or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/


/*
 * PopUp
 * 
 * jQuery.popup(name) - Create a PopUp with string name
 * jQuery.popup(options) - Create a PopUps with object options
 * jQuery.toPopup() - Create a PopUp from the (first) matched element using jQuery.popup
 * jQuery.fn.popupOpen(options) - If matched elements are popups they will be opened
 * jQuery.fn.popupClose(options) - If matched elements are popups they will be closed
 * jQuery.fn.popupOption(map) - If matched elements are popups their options will be set using passed in map.
 * 
 * jQuery.popup.default - Default configuration for popups.
 * 
 */
jQuery.fn.popupMove = function(left, top, speed) {
	var data = this.data('popup');
	if (data) {
		var $items = this.add(data.$iframe).add(data.$shim);
		//alert($items);
		if (data.$iframe) $items.animate({ left: left, top: top }, speed);
		else $items.animate({ left: left, top: top }, speed);
	}
	return this;
};
jQuery.fn.stopAnim = function() {
    if (this.stop) {
        this.stop();
    } else {
        console.debug('stopAnim error: no \'stop\' on ' + this);
    }
};
jQuery.fn.popupMiddle = function(speed) {
    var data = this.data('popup');
    if (data) {
        var coords = jQuery.dim('centerObj', this.width(), this.height());
        //fb.debug('coords', coords);
        this.popupMove(coords.x, coords.y, speed || 0);
        //fb.debug('', );
    }
    return this;
};
jQuery.fn.popupOpen = function(options, callback) {
	var data = this.data('popup');
	if (data) {
		options = jQuery.extend({
			speed: 400,
			type: 'fade'
		}, options);
		//this.css('visibility', 'hidden');
		this.show();
		this.popupMiddle();
		if (data.ie6) {
			data.resizeIframe();
			data.moveIframe();
			data.$iframe.show();
		}
		this.hide();
		this.css('visibility', 'visible');
		if (options && options.type == 'fade') {
			this.fadeIn(options.speed, callback);
			//if (data.modal) jQuery('#modal_pane').fadeIn(options.speed);
		}
		else if (options && options.type == 'flash') {
			//_$popup.effect('pulsate', { times: 2 }, (options.speed / 3) * 2);
			//if (data.modal) jQuery('#modal_pane').fadeIn(options.speed);
			this.fadeIn(80).fadeOut(80).fadeIn(90).fadeOut(90).fadeIn(100, callback);
		}
		else {
			//if (data.modal) jQuery('#modal_pane').show(1);
			this.show(1, callback);
		}
		if (jQuery.browser.name == 'safari' || jQuery.browser.name == 'chrome') {
			//$get('popup_pane').style.height = '100%';
		}
		data.open = true;
	}
	return this;
};
jQuery.fn.popupClose = function(options, callback) {
	var data = this.data('popup');
	if (data) {
		options = jQuery.extend({
			speed: 400,
			type: 'fade'
		}, options);
		if (options && options.type == 'fade') {
			//if (data.modal) jQuery('#modal_pane').fadeOut(options.speed);
			this.fadeOut(options.speed, callback);
		}
		else {
			//if (data.modal) jQuery('#modal_pane').hide(1);
			this.hide(1, callback);
		}
		if (data.$iframe) {
			data.$iframe/*.add(data.$shim)*/.hide();
			//jQuery('body').css("cursor", 'pointer');
			jQuery('body').css("cursor", '');
		}
		if (jQuery.browser.name == 'safari' || jQuery.browser.name == 'chrome') {
			//$get('popup_pane').style.height = '100%';
		}
		data.open = true;
	}
	return this;
};
jQuery.fn.popSlide = function() {
	var data = this.data('popup');
	console.debug('popSlide', this, data, arguments);
	if (arguments[1] == 0) this.css("left", arguments[0][0] + "px").css("top", arguments[0][1] + "px");
	this.animate({ "left": arguments[0][0] + "px", "top": arguments[0][1] + "px" }, arguments[1]);
};
jQuery.fn.popupToggle = function(arg) {
	var data = this.data('popup');
	if (data) {
		if (data.open == false) return this.popupOpen();
		else return this.popupClose();
	}
};
jQuery.fn.popupOption = function() {
	var data = this.data('popup');
	fb.debug('popOption', this, data, arguments);
	//?
};
jQuery.popup = function(options_in) {
    // Setup a new popup
    // set options
    var options = {};
    jQuery().extend(options, jQuery.popup.defaults, options_in);
    // find/create container
    var $container = (typeof options.container == 'string') ? jQuery('#' + options.container) : jQuery(options.container);
    if (!$container[0] || !$container[0].tagName) {
        // create container
        $container = (typeof options.container == 'string') ? jQuery('<div id="' + options.container + '"></div>') : jQuery('<div id="' + defaults.container + '"></div>');
        $container.appendTo(document.body)
    }

    // find/create content
    var $content = jQuery(options.content);
    if (!$content.length) {
        $content = jQuery('<div>' + options.content + '</div>');
        //fb.log('<div>' + options.content + '</div>');
    }

    //$content.css('position', 'static'); // why? where from?

    // find/create buttons
    var $topButtons = jQuery().eq(), $bottomButtons = jQuery().eq(), $temp;
    for (var button in options.buttons.top) {
        $temp = jQuery(options.buttons.top[button]);
        if ($temp[0]) {
            if (!$temp[0].id.length) $temp[0].id = 'popup_button_' + button;
            $topButtons = $topButtons.add($temp);
        }
    }
    for (var button in options.buttons.bottom) {
        $temp = jQuery(options.buttons.bottom[button]);
        if ($temp[0]) {
            if (!$temp[0].id.length) $temp[0].id = 'popup_button_' + button;
            $bottomButtons = $bottomButtons.add($temp);
        }
    }
    // parse template and create DOM Fragment
    options.data.id = options.id = options.name + '_' + Math.random().toString().replace('.', '');
    options.data.title = options.title || '';
    options.data.title = options.data.title.charAt(0).toUpperCase() + options.data.title.slice(1);

    var $table = jQuery(jQuery.parseTemplate(jQuery.popup.htmlTemplate, options.data));
    // append top buttons to popup
    jQuery('#' + options.data.id + '_header_buttons', $table).append($topButtons);
    // append bottom buttons to popup
    jQuery('#' + options.data.id + '_footer_right', $table).append($bottomButtons);
    // append content to popup
    jQuery('#' + options.data.id + '_content', $table).append($content);

    // create data and iframe
    var data = {};
    data.open = false;
    if (options.modal) data.modal = true;

    if (jQuery.browser.msie && jQuery.browser.version.charAt(0) / 1 < 7) {
        // if ie6 add render fixing iframe
        data.ie6 = true;

        // try changing to a wrapper div for the table, iframe and shim
        // then they would all move together!

        var iframe = jQuery('<IFRAME id="popup-iframe-ie6fix-' + options.name + '" class="popup-iframe-ie6fix" src="/blank.htm" frameBorder="0" scrolling="no" width="100%" height="100%" ></IFRAME>').appendTo($container).css({ left: $table.css('left'), right: $table.css('right'), background: 'transparent' }).hide();
        var shim = jQuery('<div id="popup-shim-ie6fix-' + options.name + '" class="popup-shim-ie6fix"></div>').appendTo($container).css({ left: $table.css('left'), right: $table.css('right') }).hide();
        //data.$behind = jQuery('#popup-iframe-ie6fix-' + options.name + ', #popup-shim-ie6fix-' + options.name);

        data.$ifame = jQuery('#popup-iframe-ie6fix-' + options.name + ', #popup-shim-ie6fix-' + options.name);
        data.moveIframe = (function() {
            return function() {
                var $layers = jQuery('#popup-iframe-ie6fix-' + options.name + ', #popup-shim-ie6fix-' + options.name);
                //fb.debug('move', $layers);
                $layers.css({ top: $table.css('top'), left: $table.css('left') });
            }
        })();
        data.resizeIframe = (function() {
            return function() {
                var $layers = jQuery('#popup-iframe-ie6fix-' + options.name + ', #popup-shim-ie6fix-' + options.name);
                fb.debug('resize', $layers, $table.width(), $table.height())
                $layers.css({ width: $table.width(), height: $table.height() });
            }
        })();

        data.$iframe = jQuery('#popup-iframe-ie6fix-' + options.name + ', #popup-shim-ie6fix-' + options.name);
        $table.blackfix = function() {
            var $layers = jQuery('#popup-iframe-ie6fix-' + options.name + ', #popup-shim-ie6fix-' + options.name);
            $layers.css({ top: $table.css('top'), left: $table.css('left') });
            return 'blackfix';
        }
    }

    // store popup data
    $table.data('popup', (function() {
        return data;
    })());

    $table.css('position', 'absolute');
    // append popup to container
    $container.append($table);
    // Draggable
    if ($table.data('popup').ie6) {
        $table.draggable({ stack: '.popup-table', handle: '.drag-control', revert: false, scroll: false, containment: 'window', iframeFix: true, cursor: 'move', drag: data.moveIframe });
    }
    else {
        $table.draggable({ stack: '.popup-table', handle: '.drag-control', revert: false, scroll: false, containment: '#modal_pane', iframeFix: true, cursor: 'move'});
    }
    return $table;
};

jQuery.popup.htmlTemplate = '\n<table id="[=id=]" class="popup-table" border="0" cellspacing="0" cellpadding="0">\n	<tr class="top">\n		<td class="topleft drag-control"></td>\n		<td class="topmiddle drag-control" width="*" >\n			<div id="[=id=]_title" class="title drag-control">[=title=]</div>\n			<div id="[=id=]_header_buttons" class="header-buttons"></div>\n		</td>\n		<td class="topright drag-control"></td>\n	</tr>\n	<tr class="middle">\n		<td class="middleleft drag-control"></td>\n		<td class="middlemiddle drag-control" width="*">\n			<div id="[=id=]_content" class="content" style="padding-bottom: 1ex;"></div>\n		</td>\n		<td class="middleright drag-control"></td>\n	</tr>\n	<tr class="footer">\n		<td class="footerleft drag-control"></td>\n		<td class="footermiddle drag-control" width="*">\n				<div id="[=id=]_footer_left" class="footer-left drag-control"></div>\n				<div id="[=id=]_footer_right" class="footer-right"></div>\n		</td>\n		<td class="footerright drag-control"></td>\n	</tr>\n	<tr class="bottom">\n		<td class="bottomleft drag-control"></td>\n		<td class="bottommiddle drag-control" width="*"></td>\n		<td class="bottomright drag-control"></td>\n	</tr>\n</table>\n';
jQuery.popup.defaults = {
	title: '',
	name: 'popup',
	className: 'popup-table',
	container: 'popup_pane', /* string id or Dom Element */
	constrain: window, /* string id, Dom Element, [30, 50, 300, 200] */
	buttons: {
				top: {
					x: '<button onclick="jQuery(this).parents(\'.popup-table\').popupClose();" class="btnX"><span>X</span></button>' /* string HTML or Dom Element */
				},
				bottom: {
					//Close: '<button onclick="jQuery(this).parents(\'.popup-table\').popupClose();" class="btnClose"><span>Close</span></button>' /* string HTML or Dom Element */
				}
	},
	effect: {
		type: 'fade',
		speed: 'fast',
		easingNames: 'none'
	},
	content: '<div></div>', /* string or Dom Fragment */
	stylesheet: '',
	modal: false,
	data: {}
};

/***********************************************/



/*
 * jQuery.parseTemplate
 * 
 * search string replacing all [=var=] with value from data where var is found as a key in data.
 * where value is DOM Node, will convert to outerHTML string (excluding events).
 *
 */
jQuery.parseTemplate = function(template, data, c) {
	function translate(match, key) {
		if (data.hasOwnProperty(key)) {
			if (data[key].tagName) return jQuery(data[key]).wrap;
			else return data[key];
		}
		else return '<!-- ' + key + ' -->';
	}
	c = (c) ? c[0] : '=';
	var regexp = new RegExp('\\[\\' + c + '(.*?)\\' + c + '\\]', 'igm');
	//return template.replace(/\[=(.*?)=\]/igm, translate);
	return template.replace(regexp, translate);
}
/***********************************************/



/*
 * jQuery.dim
 * 
 * Dimensions
 *
 */

jQuery.dim = function(a, b, c) {
    var $ = jQuery || function() { return 'Error jQuery not defined!' };
    switch (a) {
        case 'window':
            return { width: jq(window).width(), height: jq(window).height() };
            break;
        case 'body':
            return { width: document.body.offsetWidth, height: document.body.offsetHeight };
            break;
        case 'screen':
            return { width: window.screen.width, height: window.screen.height };
            break;
        case 'scroll':
            return { x: window.screen.scrollLeft || document.documentElement.scrollLeft, y: window.screen.scrollTop || document.documentElement.scrollTop };
            break;
        case 'mouse':
            if (b && b.screenX) return { x: b.screenX, y: b.screenY };
            else return { x: -1, y: -1 };
            break;
        case 'all':
            if (b) return { width: jq(b).width(), height: jq(b).height(), offset: { x: jq(b).offset().left, y: jq(b).offset().left }, position: { x: jq(b).position().left, y: jq(b).position().left} };
            else return { width: -1, height: -1, offset: { x: -1, y: -1 }, position: { x: -1, y: -1} };
            break;
        case 'centerObj':
            var win = { width: jq(window).width(), height: jq(window).height() };
            if (window._ && (_.DeviceType == 'tablet' || _.DeviceType == 'phone')) {
                win = { width: self.innerWidth, height: self.innerHeight };
            }
            if (document.body.offsetHeight < win.height) win.height = (document.body.offsetHeight + win.height) / 2;
            if (b && b.tagName) var size = { width: jq(b).width(), height: jq(b).height() };
            else if (b && c) var size = { width: b, height: c };
            else var size = { width: 0, height: 0 };
            return { x: (win.width / 2 - size.width / 2), y: (win.height / 2 - size.height / 2) };
            break;
    }
};



/*
*	scroll (like newsScroller)
*	scrolls div up so top element goes out of view then moves it to the end of the div in the dom
*/
jQuery.fn.scrollIt = function(selector, callback) {
	//console.info('scrollIt start (', selector, ')');
	var items = (selector) ? this.children(selector) : this.children();
	if (items.length >= 2) { // no point scrolling a single element - (could call another scroller that moves a single item goes up then down instead?)
		// get height of first element
		var amount = items.outerHeight();
		// scroll out of view
		this.startAnimation({ 'scrollTop': amount + 'px' }, 1000, 'easeInSine', function() {
			//console.log('complete');
			// when animated, move 1st item (this) to after the last item!
			var scrollingDiv = jQuery(this);
			var item1 = (selector) ? jQuery(selector + ':first', scrollingDiv) : jQuery(':first', scrollingDiv);
			var temp = item1.detach();
			scrollingDiv.scrollTop(0);
			temp.appendTo(scrollingDiv);
			temp = null;
			if (callback && callback.call) callback.apply(scrollingDiv);
			//console.info('scrollIt end');
		}); /**//*
		this.animate(
		{ 'scrollTop': amount + 'px' },
		{
			'duration': 1000,
			'easing': 'easeInSine',
			'step': function(now, fx) {
				//console.debug('step', now, fx, this);
				var paused = jQuery(this).data('paused');
				//console.log('paused?', paused);
				return !(paused === true); // return false in step callback to pause animation
			},
			'complete': function() {
				console.log('complete');
				// when animated, move 1st item (this) to after the last item!
				var scrollingDiv = jQuery(this);
				var item1 = (selector) ? jQuery(selector + ':first', scrollingDiv) : jQuery(':first', scrollingDiv);
				var temp = item1.detach();
				scrollingDiv.scrollTop(0);
				temp.appendTo(scrollingDiv);
				temp = null;
				if (callback && callback.call) callback.apply(scrollingDiv);
				//console.info('scrollIt end');
			}
		}
		);*/
	}
	return this;
};



/*
jQuery plugin : pause resume animation
Created by Joe Weitzel
BOX Creative LLC
http://plugins.jquery.com/project/Pause-Resume-animation
*/
jQuery.fn.startAnimation = function(params, duration, easing, callback) {
	jQuery(this).animate(params, duration, easing, callback);
	var data = { target: this.get(0), params: params, duration: duration, easing: easing, callback: callback,
		startTime: new Date().getTime(), timePlayed: 0, timeRemaining: 0
	};
	if (!jQuery.pauseableAnimations) {
		jQuery.extend({ pauseableAnimations: new Array(data) });
	} else {
		for (var i in jQuery.pauseableAnimations) {
			if (jQuery.pauseableAnimations[i].target == this.get(0)) {
				jQuery.pauseableAnimations[i] = data;
			} else {
				jQuery.pauseableAnimations.push(data);
			};
		};
	};
};
jQuery.fn.pauseAnimation = function() {
	if (jQuery.pauseableAnimations) {
		for (var i in jQuery.pauseableAnimations) {
			if (jQuery.pauseableAnimations[i].target == this.get(0)) {
				jQuery(this).stop();
				var now = new Date().getTime();
				var data = jQuery.pauseableAnimations[i];
				data.timePlayed += (now - data.startTime);
				data.timeRemaining = data.duration - data.timePlayed;
				if (data.timePlayed > data.duration) {
					var newArray = new Array();
					for (var p in jQuery.pauseableAnimations) {
						if (jQuery.pauseableAnimations[p] != data) newArray.push(jQuery.pauseableAnimations[p]);
					};
					jQuery.pauseableAnimations = newArray.length > 0 ? newArray : null;
					delete newArray;
					return this;
				};
				break;
			};
		};
	};
	return this;
};
jQuery.fn.resumeAnimation = function() {
	if (jQuery.pauseableAnimations) {
		for (var i in jQuery.pauseableAnimations) {
			var data = jQuery.pauseableAnimations[i];
			if (data.target == this.get(0)) {
				this.animate(data.params, data.timeRemaining, data.easing, data.callback);
				data.startTime = new Date().getTime();
				return this;
			};
		};
	};
};
/*
// Pause animation by returning boolean false in 'step' callback function!

jQuery.extend(jQuery.fx.prototype, {
	'pauseStartTime': null
});

jQuery.extend(jQuery.fx.prototype, {
	'update': function() { // Simple function for setting a style value
		//console.debug('MODIFIED jQuery.fx.prototype.update this:', this);
		var pause = false;
		if (this.options.step) {
			if (this.options.step.call(this.elem, this.now, this) === false) {
				console.warn('pause');
				pause = true;
			}
		}
		if (pause) {
			if (this.pauseStartTime === null) {
				// set pause start time
				this.pauseStartTime = jQuery.now();
			}
			else {
				var nowTime = jQuery.now();
				// add pause time to start time
				this.startTime += this.pauseStartTime + nowTime;
				// update duration
				this.options.duration += (nowTime - this.pauseStartTime);
			}
		} else {
			// reset pause start time
			this.pauseStartTime = null;
			// step
			(jQuery.fx.step[this.prop] || jQuery.fx.step._default)(this);
		}
	}
});

*/


/***********************************************/


/*
* jquery.tools 1.1.2 - The missing UI library for the Web
* 
* [tools.tabs-1.0.4, tools.tooltip-1.1.2, tools.tooltip.slide-1.0.0, tools.tooltip.dynamic-1.0.1]
* 
* Copyright (c) 2009 Tero Piirainen
* http://flowplayer.org/tools/
*
* Dual licensed under MIT and GPL 2+ licenses
* http://www.opensource.org/licenses
* 
* -----
* 
* Generated: Thu Oct 08 19:47:18 GMT+00:00 2009
*/
(function(d) { d.tools = d.tools || {}; d.tools.tabs = { version: "1.0.4", conf: { tabs: "a", current: "current", onBeforeClick: null, onClick: null, effect: "default", initialIndex: 0, event: "click", api: false, rotate: false }, addEffect: function(e, f) { c[e] = f } }; var c = { "default": function(f, e) { this.getPanes().hide().eq(f).show(); e.call() }, fade: function(g, e) { var f = this.getConf(), j = f.fadeOutSpeed, h = this.getPanes(); if (j) { h.fadeOut(j) } else { h.hide() } h.eq(g).fadeIn(f.fadeInSpeed, e) }, slide: function(f, e) { this.getPanes().slideUp(200); this.getPanes().eq(f).slideDown(400, e) }, ajax: function(f, e) { this.getPanes().eq(0).load(this.getTabs().eq(f).attr("href"), e) } }; var b; d.tools.tabs.addEffect("horizontal", function(f, e) { if (!b) { b = this.getPanes().eq(0).width() } this.getCurrentPane().animate({ width: 0 }, function() { d(this).hide() }); this.getPanes().eq(f).animate({ width: b }, function() { d(this).show(); e.call() }) }); function a(g, h, f) { var e = this, j = d(this), i; d.each(f, function(k, l) { if (d.isFunction(l)) { j.bind(k, l) } }); d.extend(this, { click: function(k, n) { var o = e.getCurrentPane(); var l = g.eq(k); if (typeof k == "string" && k.replace("#", "")) { l = g.filter("[href*=" + k.replace("#", "") + "]"); k = Math.max(g.index(l), 0) } if (f.rotate) { var m = g.length - 1; if (k < 0) { return e.click(m, n) } if (k > m) { return e.click(0, n) } } if (!l.length) { if (i >= 0) { return e } k = f.initialIndex; l = g.eq(k) } if (k === i) { return e } n = n || d.Event(); n.type = "onBeforeClick"; j.trigger(n, [k]); if (n.isDefaultPrevented()) { return } c[f.effect].call(e, k, function() { n.type = "onClick"; j.trigger(n, [k]) }); n.type = "onStart"; j.trigger(n, [k]); if (n.isDefaultPrevented()) { return } i = k; g.removeClass(f.current); l.addClass(f.current); return e }, getConf: function() { return f }, getTabs: function() { return g }, getPanes: function() { return h }, getCurrentPane: function() { return h.eq(i) }, getCurrentTab: function() { return g.eq(i) }, getIndex: function() { return i }, next: function() { return e.click(i + 1) }, prev: function() { return e.click(i - 1) }, bind: function(k, l) { j.bind(k, l); return e }, onBeforeClick: function(k) { return this.bind("onBeforeClick", k) }, onClick: function(k) { return this.bind("onClick", k) }, unbind: function(k) { j.unbind(k); return e } }); g.each(function(k) { d(this).bind(f.event, function(l) { e.click(k, l); return false }) }); if (location.hash) { e.click(location.hash) } else { if (f.initialIndex === 0 || f.initialIndex > 0) { e.click(f.initialIndex) } } h.find("a[href^=#]").click(function(k) { e.click(d(this).attr("href"), k) }) } d.fn.tabs = function(i, f) { var g = this.eq(typeof f == "number" ? f : 0).data("tabs"); if (g) { return g } if (d.isFunction(f)) { f = { onBeforeClick: f} } var h = d.extend({}, d.tools.tabs.conf), e = this.length; f = d.extend(h, f); this.each(function(l) { var j = d(this); var k = j.find(f.tabs); if (!k.length) { k = j.children() } var m = i.jquery ? i : j.children(i); if (!m.length) { m = e == 1 ? d(i) : j.parent().find(i) } g = new a(k, m, f); j.data("tabs", g) }); return f.api ? g : this } })(jQuery);
(function(c){var d=[];c.tools=c.tools||{};c.tools.tooltip={version:"1.1.2",conf:{effect:"toggle",fadeOutSpeed:"fast",tip:null,predelay:0,delay:30,opacity:1,lazy:undefined,position:["top","center"],offset:[0,0],cancelDefault:true,relative:false,oneInstance:true,events:{def:"mouseover,mouseout",input:"focus,blur",widget:"focus mouseover,blur mouseout",tooltip:"mouseover,mouseout"},api:false},addEffect:function(e,g,f){b[e]=[g,f]}};var b={toggle:[function(e){var f=this.getConf(),g=this.getTip(),h=f.opacity;if(h<1){g.css({opacity:h})}g.show();e.call()},function(e){this.getTip().hide();e.call()}],fade:[function(e){this.getTip().fadeIn(this.getConf().fadeInSpeed,e)},function(e){this.getTip().fadeOut(this.getConf().fadeOutSpeed,e)}]};function a(f,g){var p=this,k=c(this);f.data("tooltip",p);var l=f.next();if(g.tip){l=c(g.tip);if(l.length>1){l=f.nextAll(g.tip).eq(0);if(!l.length){l=f.parent().nextAll(g.tip).eq(0)}}}function o(u){var t=g.relative?f.position().top:f.offset().top,s=g.relative?f.position().left:f.offset().left,v=g.position[0];t-=l.outerHeight()-g.offset[0];s+=f.outerWidth()+g.offset[1];var q=l.outerHeight()+f.outerHeight();if(v=="center"){t+=q/2}if(v=="bottom"){t+=q}v=g.position[1];var r=l.outerWidth()+f.outerWidth();if(v=="center"){s-=r/2}if(v=="left"){s-=r}return{top:t,left:s}}var i=f.is(":input"),e=i&&f.is(":checkbox, :radio, select, :button"),h=f.attr("type"),n=g.events[h]||g.events[i?(e?"widget":"input"):"def"];n=n.split(/,\s*/);if(n.length!=2){throw"Tooltip: bad events configuration for "+h}f.bind(n[0],function(r){if(g.oneInstance){c.each(d,function(){this.hide()})}var q=l.data("trigger");if(q&&q[0]!=this){l.hide().stop(true,true)}r.target=this;p.show(r);n=g.events.tooltip.split(/,\s*/);l.bind(n[0],function(){p.show(r)});if(n[1]){l.bind(n[1],function(){p.hide(r)})}});f.bind(n[1],function(q){p.hide(q)});if(!c.browser.msie&&!i&&!g.predelay){f.mousemove(function(){if(!p.isShown()){f.triggerHandler("mouseover")}})}if(g.opacity<1){l.css("opacity",g.opacity)}var m=0,j=f.attr("title");if(j&&g.cancelDefault){f.removeAttr("title");f.data("title",j)}c.extend(p,{show:function(r){if(r){f=c(r.target)}clearTimeout(l.data("timer"));if(l.is(":animated")||l.is(":visible")){return p}function q(){l.data("trigger",f);var t=o(r);if(g.tip&&j){l.html(f.data("title"))}r=r||c.Event();r.type="onBeforeShow";k.trigger(r,[t]);if(r.isDefaultPrevented()){return p}t=o(r);l.css({position:"absolute",top:t.top,left:t.left});var s=b[g.effect];if(!s){throw'Nonexistent effect "'+g.effect+'"'}s[0].call(p,function(){r.type="onShow";k.trigger(r)})}if(g.predelay){clearTimeout(m);m=setTimeout(q,g.predelay)}else{q()}return p},hide:function(r){clearTimeout(l.data("timer"));clearTimeout(m);if(!l.is(":visible")){return}function q(){r=r||c.Event();r.type="onBeforeHide";k.trigger(r);if(r.isDefaultPrevented()){return}b[g.effect][1].call(p,function(){r.type="onHide";k.trigger(r)})}if(g.delay&&r){l.data("timer",setTimeout(q,g.delay))}else{q()}return p},isShown:function(){return l.is(":visible, :animated")},getConf:function(){return g},getTip:function(){return l},getTrigger:function(){return f},bind:function(q,r){k.bind(q,r);return p},onHide:function(q){return this.bind("onHide",q)},onBeforeShow:function(q){return this.bind("onBeforeShow",q)},onShow:function(q){return this.bind("onShow",q)},onBeforeHide:function(q){return this.bind("onBeforeHide",q)},unbind:function(q){k.unbind(q);return p}});c.each(g,function(q,r){if(c.isFunction(r)){p.bind(q,r)}})}c.prototype.tooltip=function(e){var f=this.eq(typeof e=="number"?e:0).data("tooltip");if(f){return f}var g=c.extend(true,{},c.tools.tooltip.conf);if(c.isFunction(e)){e={onBeforeShow:e}}else{if(typeof e=="string"){e={tip:e}}}e=c.extend(true,g,e);if(typeof e.position=="string"){e.position=e.position.split(/,?\s/)}if(e.lazy!==false&&(e.lazy===true||this.length>20)){this.one("mouseover",function(h){f=new a(c(this),e);f.show(h);d.push(f)})}else{this.each(function(){f=new a(c(this),e);d.push(f)})}return e.api?f:this}})(jQuery);
(function(b){var a=b.tools.tooltip;a.effects=a.effects||{};a.effects.slide={version:"1.0.0"};b.extend(a.conf,{direction:"up",bounce:false,slideOffset:10,slideInSpeed:200,slideOutSpeed:200,slideFade:!b.browser.msie});var c={up:["-","top"],down:["+","top"],left:["-","left"],right:["+","left"]};b.tools.tooltip.addEffect("slide",function(d){var f=this.getConf(),g=this.getTip(),h=f.slideFade?{opacity:f.opacity}:{},e=c[f.direction]||c.up;h[e[1]]=e[0]+"="+f.slideOffset;if(f.slideFade){g.css({opacity:0})}g.show().animate(h,f.slideInSpeed,d)},function(e){var g=this.getConf(),i=g.slideOffset,h=g.slideFade?{opacity:0}:{},f=c[g.direction]||c.up;var d=""+f[0];if(g.bounce){d=d=="+"?"-":"+"}h[f[1]]=d+"="+i;this.getTip().animate(h,g.slideOutSpeed,function(){b(this).hide();e.call()})})})(jQuery);
(function(d){var c=d.tools.tooltip;c.plugins=c.plugins||{};c.plugins.dynamic={version:"1.0.1",conf:{api:false,classNames:"top right bottom left"}};function b(h){var e=d(window);var g=e.width()+e.scrollLeft();var f=e.height()+e.scrollTop();return[h.offset().top<=e.scrollTop(),g<=h.offset().left+h.width(),f<=h.offset().top+h.height(),e.scrollLeft()>=h.offset().left]}function a(f){var e=f.length;while(e--){if(f[e]){return false}}return true}d.fn.dynamic=function(g){var h=d.extend({},c.plugins.dynamic.conf),f;if(typeof g=="number"){g={speed:g}}g=d.extend(h,g);var e=g.classNames.split(/\s/),i;this.each(function(){if(d(this).tooltip().jquery){throw"Lazy feature not supported by dynamic plugin. set lazy: false for tooltip"}var j=d(this).tooltip().onBeforeShow(function(n,o){var m=this.getTip(),l=this.getConf();if(!i){i=[l.position[0],l.position[1],l.offset[0],l.offset[1],d.extend({},l)]}d.extend(l,i[4]);l.position=[i[0],i[1]];l.offset=[i[2],i[3]];m.css({visibility:"hidden",position:"absolute",top:o.top,left:o.left}).show();var k=b(m);if(!a(k)){if(k[2]){d.extend(l,g.top);l.position[0]="top";m.addClass(e[0])}if(k[3]){d.extend(l,g.right);l.position[1]="right";m.addClass(e[1])}if(k[0]){d.extend(l,g.bottom);l.position[0]="bottom";m.addClass(e[2])}if(k[1]){d.extend(l,g.left);l.position[1]="left";m.addClass(e[3])}if(k[0]||k[2]){l.offset[0]*=-1}if(k[1]||k[3]){l.offset[1]*=-1}}m.css({visibility:"visible"}).hide()});j.onShow(function(){var l=this.getConf(),k=this.getTip();l.position=[i[0],i[1]];l.offset=[i[2],i[3]]});j.onHide(function(){var k=this.getTip();k.removeClass(g.classNames)});f=j});return g.api?f:this}})(jQuery);
/***********************************************/

/*
 * jQuery.serialise 
 * jQuery.serialiseArray
 * 
 * Gets the values from a form.
 * English spelling alias of standard jQuery functions.
 */
 
jQuery.fn.serialise = jQuery.fn.serialize;
jQuery.fn.serialiseArray = jQuery.fn.serializeArray;
/***********************************************/


/*
 * jQuery.iif
 * 
 * Inline If - allows you to chain only if the condition succeeds instead of starting a new statement.
 */
jQuery.fn.iif = function (condition) {
	if (condition) {
		// TRUE: return the current jQuery objet just like any normal plugin.
		return this;
	}
	else {
		// FALSE: reduce the jQuery object to 0 elements before returning it.
		return this.eq();
	}
	// use .end() to step back up to the original (full) jQuery set of elements.
};
/***********************************************/

/**
 * labs_json Script by Giraldo Rosales.
 * Version 1.0
 * Visit www.liquidgear.net for documentation and updates.
 *
 * jQuery.json plugin
 * Copyright (c) 2009 Nitrogen Design, Inc. All rights reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following  conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 **/
 
jQuery.json = {
    encode:function(value, replacer, space) {
		var i;
		gap = '';
		var indent = '';
		
		if (typeof space === 'number') {
			for (i = 0; i < space; i += 1) {
				indent += ' ';
			}
			
		} else if (typeof space === 'string') {
			indent = space;
		}
		
		rep = replacer;
		if (replacer && typeof replacer !== 'function' &&
				(typeof replacer !== 'object' ||
				 typeof replacer.length !== 'number')) {
			throw new Error('JSON.encode');
		}
		
		return this.str('', {'': value});
    },
    
    decode:function(text, reviver) {
		var j;
		var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
		
		function walk(holder, key) {
			var k, v, value = holder[key];
			
			if (value && typeof value === 'object') {
				for (k in value) {
					if (Object.hasOwnProperty.call(value, k)) {
						v = walk(value, k);
						if (v !== undefined) {
							value[k] = v;
						} else {
							delete value[k];
						}
					}
				}
			}
			return reviver.call(holder, key, value);
		}
		
		cx.lastIndex = 0;
		
		if (cx.test(text)) {
			text = text.replace(cx, function (a) {
				return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
			});
		}
		
		if (/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
			j = eval('(' + text + ')');
			return typeof reviver === 'function' ? walk({'': j}, '') : j;
		}
		
		throw new SyntaxError('JSON.parse');
	},
	
    f:function(n) {
        return n < 10 ? '0' + n : n;
    },
	
	DateToJSON:function(key) {
		return this.getUTCFullYear() + '-' + this.f(this.getUTCMonth() + 1) + '-' + this.f(this.getUTCDate())      + 'T' + this.f(this.getUTCHours())     + ':' + this.f(this.getUTCMinutes())   + ':' + this.f(this.getUTCSeconds())   + 'Z';
	},
	
	StringToJSON:function(key) {
		return this.valueOf();
    },
    
    quote:function(string) {
        var meta = {'\b': '\\b','\t': '\\t','\n': '\\n','\f': '\\f','\r': '\\r','"' : '\\"','\\': '\\\\'};
        var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
        
        escapable.lastIndex = 0;
        return escapable.test(string) ?
            '"' + string.replace(escapable, function (a) {
                var c = meta[a];
                return typeof c === 'string' ? c :
                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
            }) + '"' :
            '"' + string + '"';
    },
    
    str:function(key, holder) {
        var indent='', gap = '', i, k, v, length, mind = gap, partial, value = holder[key];
        
		//        if (value && typeof value === 'object') {
		//            return (value instanceof Date) ? this.DateToJSON(key) : this.StringToJSON(key);
        //        }
        switch ((typeof value)) {
        	case 'date':
        		this.DateToJSON(key);
        		break;
        	default:
        		this.StringToJSON(key);
        		break;
        }

        
        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }
        switch (typeof value) {
        	case 'string':
        	    return this.quote(value);
			case 'number':
	            return isFinite(value) ? String(value) : 'null';
			case 'boolean':
			case 'null':
	            return String(value);
	        case 'object':
				if (!value) {
					return 'null';
				}
				gap += indent;
				partial = [];
				
				if (Object.prototype.toString.apply(value) === '[object Array]') {
					length = value.length;
					
					for (i = 0; i < length; i += 1) {
						partial[i] = this.str(i, value) || 'null';
					}
	
					v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']';
					gap = mind;
					return v;
				}
					
				if (rep && typeof rep === 'object') {
					length = rep.length;
					for (i = 0; i < length; i += 1) {
						k = rep[i];
						if (typeof k === 'string') {
							v = this.str(k, value);
							if (v) {
								partial.push(this.quote(k) + (gap ? ': ' : ':') + v);
							}
						}
					}
				} else {
					for (k in value) {
						if (Object.hasOwnProperty.call(value, k)) {
							v = this.str(k, value);
							if (v) {
								partial.push(this.quote(k) + (gap ? ': ' : ':') + v);
							}
						}
					}
				}

				v = partial.length === 0 ? '{}' :
					gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
							mind + '}' : '{' + partial.join(',') + '}';
				gap = mind;
				return v;
		}
	}
};
/***********************************************/


/*
 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
 *
 * Uses the built in easing capabilities added In jQuery 1.1
 * to offer multiple easing options
 *
 * TERMS OF USE - jQuery Easing
 * 
 * Open source under the BSD License. 
 * 
 * Copyright Â© 2008 George McGinley Smith
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list 
 * of conditions and the following disclaimer in the documentation and/or other materials 
 * provided with the distribution.
 * 
 * Neither the name of the author nor the names of contributors may be used to endorse 
 * or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 *
*/

// t: current time, b: begInnIng value, c: change In value, d: duration
jQuery.easing['jswing'] = jQuery.easing['swing'];

jQuery.extend( jQuery.easing,
{
	def: 'easeOutQuad',
	swing: function (x, t, b, c, d) {
		//alert(jQuery.easing.default);
		return jQuery.easing[jQuery.easing.def](x, t, b, c, d);
	},
	easeInQuad: function (x, t, b, c, d) {
		return c*(t/=d)*t + b;
	},
	easeOutQuad: function (x, t, b, c, d) {
		return -c *(t/=d)*(t-2) + b;
	},
	easeInOutQuad: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t + b;
		return -c/2 * ((--t)*(t-2) - 1) + b;
	},
	easeInCubic: function (x, t, b, c, d) {
		return c*(t/=d)*t*t + b;
	},
	easeOutCubic: function (x, t, b, c, d) {
		return c*((t=t/d-1)*t*t + 1) + b;
	},
	easeInOutCubic: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t + b;
		return c/2*((t-=2)*t*t + 2) + b;
	},
	easeInQuart: function (x, t, b, c, d) {
		return c*(t/=d)*t*t*t + b;
	},
	easeOutQuart: function (x, t, b, c, d) {
		return -c * ((t=t/d-1)*t*t*t - 1) + b;
	},
	easeInOutQuart: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
		return -c/2 * ((t-=2)*t*t*t - 2) + b;
	},
	easeInQuint: function (x, t, b, c, d) {
		return c*(t/=d)*t*t*t*t + b;
	},
	easeOutQuint: function (x, t, b, c, d) {
		return c*((t=t/d-1)*t*t*t*t + 1) + b;
	},
	easeInOutQuint: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
		return c/2*((t-=2)*t*t*t*t + 2) + b;
	},
	easeInSine: function (x, t, b, c, d) {
		return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
	},
	easeOutSine: function (x, t, b, c, d) {
		return c * Math.sin(t/d * (Math.PI/2)) + b;
	},
	easeInOutSine: function (x, t, b, c, d) {
		return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
	},
	easeInExpo: function (x, t, b, c, d) {
		return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
	},
	easeOutExpo: function (x, t, b, c, d) {
		return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
	},
	easeInOutExpo: function (x, t, b, c, d) {
		if (t==0) return b;
		if (t==d) return b+c;
		if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
		return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
	},
	easeInCirc: function (x, t, b, c, d) {
		return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
	},
	easeOutCirc: function (x, t, b, c, d) {
		return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
	},
	easeInOutCirc: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
		return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
	},
	easeInElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
	},
	easeOutElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
	},
	easeInOutElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
		return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
	},
	easeInBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*(t/=d)*t*((s+1)*t - s) + b;
	},
	easeOutBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
	},
	easeInOutBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158; 
		if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
		return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
	},
	easeInBounce: function (x, t, b, c, d) {
		return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
	},
	easeOutBounce: function (x, t, b, c, d) {
		if ((t/=d) < (1/2.75)) {
			return c*(7.5625*t*t) + b;
		} else if (t < (2/2.75)) {
			return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
		} else if (t < (2.5/2.75)) {
			return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
		} else {
			return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
		}
	},
	easeInOutBounce: function (x, t, b, c, d) {
		if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
		return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
	}
});
/***********************************************/



/**
* jQuery.Rule - Css Rules manipulation, the jQuery way.
* Copyright (c) 2007-2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
* Dual licensed under MIT and GPL.
* Date: 02/27/2008
* Compatible with jQuery 1.2.x, tested on FF 2, Opera 9, Safari 3, and IE 6, on Windows.
*
* http://flesler.blogspot.com/2007/11/jqueryrule.html
*
* @author Ariel Flesler
* @version 1.0.1
*
* @id jQuery.rule
* @param {Undefined|String|jQuery.Rule} The rules, can be a selector, or literal CSS rules. Many can be given, comma separated.
* @param {Undefined|String|DOMElement|jQuery) The context stylesheets, all of them by default.
* @return {jQuery.Rule} Returns a jQuery.Rule object.
*
* @example $.rule('p,div').filter(function(){ return this.style.display != 'block'; }).remove();
*
* @example $.rule('div{ padding:20px;background:#CCC}, p{ border:1px red solid; }').appendTo('style');
*
* @example $.rule('div{}').append('margin:40px').css('margin-left',0).appendTo('link:eq(1)');
*
* @example $.rule().not('div, p.magic').fadeOut('slow');
*
* @example var text = $.rule('#screen h2').add('h4').end().eq(4).text();
*/
;(function($) {

	/**
	* Notes
	*	Some styles and animations might fail, please report it.
	*	The plugin needs a style node to stay in the DOM all along to temporarily hold rules. DON'T TOUCH IT.
	*	Opera requires this style to have alternate in the rel to allow disabling it.
	*	Rules in IE don't have .parentStylesheet. We need to find it each time(slow).
	*	Animations need close attention. Programatically knowing which rule has precedence, would require a LOT of work.
	*	This plugin adds $.rule and also 4 methods to $.fn: ownerNode, sheet, cssRules and cssText
	*	Note that rules are not directly inside nodes, you need to do: $('style').sheet().cssRules().
	*/

	var storageNode = $('<style rel="alternate stylesheet" type="text/css" />').appendTo('head')[0], //we must append to get a stylesheet
		sheet = storageNode.sheet ? 'sheet' : 'styleSheet',
		storage = storageNode[sheet], //css rules must remain in a stylesheet for IE and FF
		rules = storage.rules ? 'rules' : 'cssRules',
		remove = storage.deleteRule ? 'deleteRule' : 'removeRule',
		owner = storage.ownerNode ? 'ownerNode' : 'owningElement',
		reRule = /^([^{]+)\{([^}]*)\}/m,
		reStyle = /([^:]+):([^;}]+)/;

	storage.disabled = true; //let's ignore your rules 

	var $rule = $.rule = function(r, c) {
		if (!(this instanceof $rule))
			return new $rule(r, c);

		this.sheets = $rule.sheets(c);
		if (r && reRule.test(r))
			r = $rule.clean(r);
		if (typeof r == 'object' && !r.exec)
			return this.setArray(r.get ? r.get() : r.splice ? r : [r]);
		this.setArray(this.sheets.cssRules().get());
		return r ? this.filter(r) : this;
	};

	$.extend($rule, {
		sheets: function(c) {
			var o = c;
			if (typeof o != 'object')
				o = $.makeArray(document.styleSheets);
			o = $(o).not(storage); //skip our stylesheet
			if (typeof c == 'string')
				o = o.ownerNode().filter(c).sheet();
			return o;
		},
		rule: function(str) {
			if (str.selectorText)/* * */
				return ['', str.selectorText, str.style.cssText];
			return reRule.exec(str);
		},
		appendTo: function(r, ss, skip) {
			switch (typeof ss) {//find the desired stylesheet
				case 'string': ss = this.sheets(ss);
				case 'object':
					if (ss[0]) ss = ss[0];
					if (ss[sheet]) ss = ss[sheet];
					if (ss[rules]) break; //only if the stylesheet is valid
				default:
					if (typeof r == 'object') return r; //let's not waist time, it is parsed
					ss = storage;
			}
			var p;
			if (!skip && (p = this.parent(r)))//if this is an actual rule, and it's appended.
				r = this.remove(r, p);

			var rule = this.rule(r);
			if (ss.addRule)
				ss.addRule(rule[1], rule[2] || ';'); //IE won't allow empty rules
			else if (ss.insertRule)
				ss.insertRule(rule[1] + '{' + rule[2] + '}', ss[rules].length);

			return ss[rules][ss[rules].length - 1]; //return the added/parsed rule
		},
		remove: function(r, p) {
			p = p || this.parent(r);
			if (p != storage) {//let's save some unnecesary cycles.
				var i = p ? $.inArray(r, p[rules]) : -1;
				if (i != -1) {//if not stored before removal, IE will crash eventually, and some rules in FF get messed up
					r = this.appendTo(r, 0 /*storage*/, true); //is faster and shorter to imply storage
					p[remove](i);
				}
			}
			return r;
		},
		clean: function(r) {
			return $.map(r.split('}'), function(txt) {
				if (txt)
					return $rule.appendTo(txt + '}' /*, storage*/); //parse the string, storage implied
			});
		},
		parent: function(r) {//CSS rules in IE don't have parentStyleSheet attribute
			if (typeof r == 'string' || !$.browser.msie)//if it's a string, just return undefined.
				return r.parentStyleSheet;

			var par;
			this.sheets().each(function() {
				if ($.inArray(r, this[rules]) != -1) {
					par = this;
					return false;
				}
			});
			return par;
		},
		outerText: function(rule) {
			return !rule ? '' : [rule.selectorText + '{', '\t' + rule.style.cssText, '}'].join('\n').toLowerCase();
		},
		text: function(rule, txt) {
			if (txt !== undefined)
				rule.style.cssText = txt;
			return !rule ? '' : rule.style.cssText.toLowerCase();
		}
	});

	$rule.fn = $rule.prototype = {
		pushStack: function(rs, sh) {
			var ret = $rule(rs, sh || this.sheets);
			ret.prevObject = this;
			return ret;
		},
		end: function() {
			return this.prevObject || $rule(0, []);
		},
		filter: function(s) {
			var o;
			if (!s) s = /./; //just keep them all.
			if (s.split) {
				o = $.trim(s).toLowerCase().split(/\s*,\s*/);
				s = function() {
					return !!$.grep(this.selectorText.toLowerCase().split(/\s*,\s*/), function(sel) {
						return $.inArray(sel, o) != -1;
					}).length;
				};
			} else if (s.exec) {//string regex, or actual regex
				o = s;
				s = function() { return o.test(this.selectorText); };
			}
			return this.pushStack($.grep(this, function(e, i) {
				return s.call(e, i);
			}));
		},
		add: function(rs, c) {
			return this.pushStack($.merge(this.get(), $rule(rs, c)));
		},
		is: function(s) {
			return !!(s && this.filter(s).length);
		},
		not: function(n, c) {
			n = $rule(n, c);
			return this.filter(function() {
				return $.inArray(this, n) == -1;
			});
		},
		append: function(s) {
			var rules = this, rule;
			$.each(s.split(/\s*;\s*/), function(i, v) {
				if ((rule = reStyle.exec(v)))
					rules.css(rule[1], rule[2]);
			});
			return this;
		},
		text: function(txt) {
			return !arguments.length ? $rule.text(this[0])
				: this.each(function() { $rule.text(this, txt); });
		},
		outerText: function() {
			return $rule.outerText(this[0]);
		}
	};

	$.each({
		ownerNode: owner, //when having the stylesheet, get the node that contains it
		sheet: sheet, //get the stylesheet from the node
		cssRules: rules //get the rules from the stylesheet.
	}, function(m, a) {
		var many = a == rules; //the rules need some more processing
		$.fn[m] = function() {
			return this.map(function() {
				return many ? $.makeArray(this[a]) : this[a];
			});
		};
	});

	$.fn.cssText = function() {
		return this.filter('link,style').eq(0).sheet().cssRules().map(function() {
			return $rule.outerText(this);
		}).get().join('\n');
	};

	$.each('remove,appendTo,parent'.split(','), function(k, f) {
		$rule.fn[f] = function() {
			var args = $.makeArray(arguments), that = this;
			args.unshift(0);
			return this.each(function(i) {
				args[0] = this;
				that[i] = $rule[f].apply($rule, args) || that[i];
			});
		};
	});

	$.each(('each,index,setArray,get,size,eq,slice,map,attr,andSelf,css,show,hide,toggle,' +
			'queue,dequeue,stop,animate,fadeIn,fadeOut,fadeTo').split(','), function(k, f) {
				$rule.fn[f] = $.fn[f];
			});

	var curCSS = $.curCSS;
	$.curCSS = function(e, a) {//this hack is still quite exprimental
		return ('selectorText' in e) ?
			e.style[a] || $.prop(e, a == 'opacity' ? 1 : 0, 'curCSS', 0, a)//TODO: improve these defaults
		: curCSS.apply(this, arguments);
	};

	/**
	* Time to hack jQuery.data for animations.
	* Only IE really needs this, but to keep the behavior consistent, I'll hack it for all browsers.
	* TODO: This kind of id doesn't seem to be good enough
	* TODO: Avoid animating similar rules simultaneously
	* TODO: Avoid rules' precedence from interfering on animations ?
	*/
	$rule.cache = {};
	var mediator = function(original) {
		return function(elm) {
			var id = elm.selectorText;
			if (id)
				arguments[0] = $rule.cache[id] = $rule.cache[id] || {};
			return original.apply($, arguments);
		};
	};
	$.data = mediator($.data);
	$.removeData = mediator($.removeData);

	$(window).unload(function() {
		$(storage).cssRules().remove(); //empty our rules bin
	});

})(jQuery);
/***********************************************/


/**
* jquery.string - Prototype string functions for jQuery
* (c) 2008 David E. Still (http://stilldesigning.com)
* Original Prototype extensions (c) 2005-2008 Sam Stephenson (http://prototypejs.org)
* http://www.stilldesigning.com/dotstring/jquery.string.1.0.js
*/

jQuery.extend({
	__stringPrototype: {
		/**
		 * ScriptFragmet, specialChar, and JSONFilter borrowed from Prototype 1.6.0.2
		 */
	 	JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
		ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
		specialChar: {
			'\b': '\\b',
			'\t': '\\t',
			'\n': '\\n',
			'\f': '\\f',
			'\r': '\\r',
			'\\': '\\\\'
		},
	
		/**
		 * Check if the string is blank (white-space only or empty).
		 * @param {String} s string to be evaluated
		 * @return {Boolean} boolean of result
		 */
		blank: function(s) {
			return /^\s*$/.test(this.s(s) || ' ');
		},
		/**
		 * Converts a string separated by dashes into a camelCase equivalent.
		 * For instance, 'foo-bar' would be converted to 'fooBar'.
		 * @param {String} s string to be evaluated
		 * @return {Boolean} boolean of result
		 */
		camelize: function(s) {
			var a = this.s(s).split('-'), i;
			s = [a[0]];
			for (i=1; i<a.length; i++){
				s.push(a[i].charAt(0).toUpperCase() + a[i].substring(1));
			}
			s = s.join('');
			return this.r(arguments,0,s);
		},
		/**
		 * Capitalizes the first letter of a string and downcases all the others.
		 * @param {String} s string to be evaluated
		 * @return {Boolean} boolean of result
		 */
		capitalize: function(s) {
			s = this.s(s);
			s = s.charAt(0).toUpperCase() + s.substring(1).toLowerCase();
			return this.r(arguments,0,s);
		},
		/**
		 * Replaces every instance of the underscore character ("_") by a dash ("-").
		 * @param {String} s string to be evaluated
		 * @return {Boolean} boolean of result
		 */
		dasherize: function(s) {
			s = this.s(s).split('_').join('-');
			return this.r(arguments,0,s);
		},
		/**
		 * Check if the string is empty.
		 * @param {String} s string to be evaluated
		 * @return {Boolean} boolean of result
		 */
		empty: function(s) {
			return this.s(s) === '';
		},
		/**
		 * Tests whether the end of a string matches pattern.
		 * @param {Object} pattern
		 * @param {String} s string to be evaluated
		 * @return {Boolean} boolean of result
		 */
		endsWith: function(pattern, s) {
			s = this.s(s);
			var d = s.length - pattern.length;
			return d >= 0 && s.lastIndexOf(pattern) === d;
		},
		/**
		 * escapeHTML from Prototype-1.6.0.2 -- If it's good enough for Webkit and IE, it's good enough for Gecko!
		 * Converts HTML special characters to their entity equivalents.
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		escapeHTML: function(s) {
			s = this.s(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
			return this.r(arguments,0,s);
		},
		/**
		 * evalJSON from Prototype-1.6.0.2
		 * Evaluates the JSON in the string and returns the resulting object. If the optional sanitize parameter
		 * is set to true, the string is checked for possible malicious attempts and eval is not called if one
		 * is detected.
		 * @param {String} s string to be evaluated
		 * @return {Object} evaluated JSON result
		 */
		evalJSON: function(sanitize, s) {
			s = this.s(s);
			var json = this.unfilterJSON(false, s);
			try {
				if (!sanitize || this.isJSON(json)) { return eval('(' + json + ')'); }
			} catch (e) { }
			throw new SyntaxError('Badly formed JSON string: ' + s);
		},
		/**
		 * evalScripts from Prototype-1.6.0.2
		 * Evaluates the content of any script block present in the string. Returns an array containing
		 * the value returned by each script.
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		evalScripts: function(s) {
			var scriptTags = this.extractScripts(this.s(s)), results = [];
			if (scriptTags.length > 0) {
				for (var i = 0; i < scriptTags.length; i++) {
					results.push(eval(scriptTags[i]));
				}
			}
			return results;
		},
		/**
		 * extractScripts from Prototype-1.6.0.2
		 * Extracts the content of any script block present in the string and returns them as an array of strings.
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		extractScripts: function(s) {
			var matchAll = new RegExp(this.ScriptFragment, 'img'), matchOne = new RegExp(this.ScriptFragment, 'im'), scriptMatches = this.s(s).match(matchAll) || [], scriptTags = [];
			if (scriptMatches.length > 0) {
				for (var i = 0; i < scriptMatches.length; i++) {
					scriptTags.push(scriptMatches[i].match(matchOne)[1] || '');
				}
			}
			return scriptTags;
		},
		/**
		 * Returns a string with all occurances of pattern replaced by either a regular string
		 * or the returned value of a function.  Calls sub internally.
		 * @param {Object} pattern RegEx pattern or string to replace
		 * @param {Object} replacement string or function to replace matched patterns
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 * @see sub
		 */
		gsub: function(pattern, replacement, s) {
			s = this.s(s);
			if (jQuery.isFunction(replacement)) { s = this.sub(pattern, replacement, -1, s); }
			/* if replacement is not a function, do this the easy way; it's quicker */
			else { s = s.split(pattern).join(replacement); }
			return this.r(arguments,2,s);
		},
		/**
		 * Check if the string contains a substring.
		 * @param {Object} pattern RegEx pattern or string to find
		 * @param {String} s string to be evaluated
		 * @return {Boolean} boolean result
		 */
		include: function(pattern, s) {
			return this.s(s).indexOf(pattern) > -1;
		},
		/**
		 * Returns a debug-oriented version of the string (i.e. wrapped in single or double quotes,
		 * with backslashes and quotes escaped).
		 * @param {Object} useDoubleQuotes escape double-quotes instead of single-quotes
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		inspect: function(useDoubleQuotes, s) {
			s = this.s(s);
			var escapedString;
			try {
				escapedString = this.sub(/[\x00-\x1f\\]/, function(match) {
					var character = jQuery.__stringPrototype.specialChar[match[0]];
					return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
			    }, -1, s);
			} catch(e) { escapedString = s; }
			s = (useDoubleQuotes) ? '"' + escapedString.replace(/"/g, '\\"') + '"' : "'" + escapedString.replace(/'/g, '\\\'') + "'";
			return this.r(arguments,1,s);
		},
		/**
		 * Treats the string as a Prototype-style Template and fills it with objectÕs properties.
		 * @param {Object} obj object of values to replace in string
		 * @param {Object} pattern RegEx pattern for template replacement (default matches Ruby-style '#{attribute}')
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		interpolate: function(obj, pattern, s) {
			s = this.s(s);
			if (!pattern) { pattern = /(\#\{\s*(\w+)\s*\})/; }
			var gpattern = new RegExp(pattern.source, "g");
			var matches = s.match(gpattern), i;
			for (i=0; i<matches.length; i++) {
				s = s.replace(matches[i], obj[matches[i].match(pattern)[2]]);
			}
			return this.r(arguments,2,s);
		},
		/**
		 * isJSON from Prototype-1.6.0.2
		 * Check if the string is valid JSON by the use of regular expressions. This security method is called internally.
		 * @param {String} s string to be evaluated
		 * @return {Boolean} boolean result
		 */
		isJSON: function(s) {
			s = this.s(s);
			if (this.blank(s)) { return false; }
			s = s.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
			return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(s);
		},
		/**
		 * Evaluates replacement for each match of pattern in string and returns the original string.
		 * Calls sub internally.
		 * @param {Object} pattern RegEx pattern or string to replace
		 * @param {Object} replacement string or function to replace matched patterns
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 * @see sub
		 */
		scan: function(pattern, replacement, s) {
			s = this.s(s);
			this.sub(pattern, replacement, -1, s);
			return this.r(arguments,2,s);
		},
		/**
		 * Tests whether the beginning of a string matches pattern.
		 * @param {Object} pattern
		 * @param {String} s string to be evaluated
		 * @return {Boolean} boolean of result
		 */
		startsWith: function(pattern, s) {
			return this.s(s).indexOf(pattern) === 0;
		},
		/**
		 * Trims white space from the beginning and end of a string.
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		strip: function(s) {
			s = jQuery.trim(this.s(s));
			return this.r(arguments,0,s);
		},
		/**
		 * Strips a string of anything that looks like an HTML script block.
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		stripScripts: function(s) {
			s = this.s(s).replace(new RegExp(this.ScriptFragment, 'img'), '');
			return this.r(arguments,0,s);
		},
		/**
		 * Strips a string of any HTML tags.
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		stripTags: function(s) {
			s = this.s(s).replace(/<\/?[^>]+>/gi, '');
			return this.r(arguments,0,s);
		},
		/**
		 * Returns a string with the first count occurances of pattern replaced by either a regular string
		 * or the returned value of a function.
		 * @param {Object} pattern RegEx pattern or string to replace
		 * @param {Object} replacement string or function to replace matched patterns
		 * @param {Integer} count number of (default = 1, -1 replaces all)
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		sub: function(pattern, replacement, count, s) {
			s = this.s(s);
			if (pattern.source && !pattern.global) {
				var patternMods = (pattern.ignoreCase)?"ig":"g";
				patternMods += (pattern.multiline)?"m":"";
				pattern = new RegExp(pattern.source, patternMods);
			}
			var sarray = s.split(pattern), matches = s.match(pattern);
			if (jQuery.browser.msie) {
				if (s.indexOf(matches[0]) == 0) sarray.unshift("");
				if (s.lastIndexOf(matches[matches.length-1]) == s.length - matches[matches.length-1].length) sarray.push("");
			}
			count = (count < 0)?(sarray.length-1):count || 1;
			s = sarray[0];
			for (var i=1; i<sarray.length; i++) {
				if (i <= count) {
					if (jQuery.isFunction(replacement)) {
						s += replacement(matches[i-1] || matches) + sarray[i];
					} else { s += replacement + sarray[i]; }
				} else { s += (matches[i-1] || matches) + sarray[i]; }
			}
			return this.r(arguments,3,s);
		},
		/**
		 * 
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		succ: function(s) {
			s = this.s(s);
			s = s.slice(0, s.length - 1) + String.fromCharCode(s.charCodeAt(s.length - 1) + 1);
			return this.r(arguments,0,s);
		},
		/**
		 * Concatenate count number of copies of s together and return result.
		 * @param {Integer} count Number of times to repeat s
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		times: function(count, s) {
			s = this.s(s);
			var newS = "";
			for (var i=0; i<count; i++) {
				newS += s;
			}
			return this.r(arguments,1,newS);
		},
		/**
		 * Returns a JSON string
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		toJSON: function(s) {
			return this.r(arguments,0,this.inspect(true, this.s(s)));
		},
		/**
		 * Parses a URI-like query string and returns an object composed of parameter/value pairs.
		 * This method is mainly targeted at parsing query strings (hence the default value of '&'
		 * for the seperator argument). For this reason, it does not consider anything that is either
		 * before a question mark (which signals the beginning of a query string) or beyond the hash 
		 * symbol ("#"), and runs decodeURIComponent() on each parameter/value pair.
		 * @param {Object} separator string to separate parameters (default = '&')
		 * @param {Object} s
		 * @return {Object} object
		 */
		toQueryParams: function(separator, s) {
			s = this.s(s);
			var paramsList = s.substring(s.indexOf('?')+1).split('#')[0].split(separator || '&'), params = {}, i, key, value, pair;
			for (i=0; i<paramsList.length; i++) {
				pair = paramsList[i].split('=');
				key = decodeURIComponent(pair[0]);
				value = (pair[1])?decodeURIComponent(pair[1]):undefined;
				if (params[key]) {
					if (typeof params[key] == "string") { params[key] = [params[key]]; }
					params[key].push(value);
				} else { params[key] = value; }
			}
			return params;
		},
		/**
		 * truncate from Prototype-1.6.0.2
		 * Truncates a string to the given length and appends a suffix to it (indicating that it is only an excerpt).
		 * @param {Object} length length of string to truncate to
		 * @param {Object} truncation string to concatenate onto truncated string (default = '...')
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		truncate: function(length, truncation, s) {
			s = this.s(s);
			length = length || 30;
			truncation = (!truncation) ? '...' : truncation;
			s = (s.length > length) ? s.slice(0, length - truncation.length) + truncation : String(s);
			return this.r(arguments,2,s);
		},
		/**
		 * Converts a camelized string into a series of words separated by an underscore ("_").
		 * e.g. $.string('borderBottomWidth').underscore().str = 'border_bottom_width'
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		underscore: function(s) {
			s = this.sub(/[A-Z]/, function(c) { return "_"+c.toLowerCase(); }, -1, this.s(s));
			if (s.charAt(0) == "_") s = s.substring(1);
			return this.r(arguments,0,s);
		},
		/**
		 * unescapeHTML from Prototype-1.6.0.2 -- If it's good enough for Webkit and IE, it's good enough for Gecko!
		 * Strips tags and converts the entity forms of special HTML characters to their normal form.
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		unescapeHTML: function(s) {
			s = this.stripTags(this.s(s)).replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
			return this.r(arguments,0,s);
		},
		/**
		 * unfilterJSON from Prototype-1.6.0.2.
		 * @param {Function} filter
		 * @param {String} s string to be evaluated
		 * @return {Object} .string object (or string if internal)
		 */
		unfilterJSON: function(filter, s) {
			s = this.s(s);
			filter = filter || this.JSONFilter;
			var filtered = s.match(filter);
			s = (filtered !== null)?filtered[1]:s;
			return this.r(arguments,1,jQuery.trim(s));
		},
	
		/**
		 * Sets .str property and returns $.string object.
		 * @param {String} s string to be evaluated
		 */
		r: function(args, size, s) {
			if (args.length > size || this.str === undefined) {
				return s;
			} else {
				this.str = ''+s;
				return this;
			};
		},
		s: function(s) {
			if (s === '' || s) { return s; }
			if (this.str === '' || this.str) { return this.str; }
			return this;
		}
	},
	string: function(str) {
		if (str === String.prototype) { jQuery.extend(String.prototype, jQuery.__stringPrototype); }
		else { return jQuery.extend({ str: str }, jQuery.__stringPrototype); }
	}
});
jQuery.__stringPrototype.parseQuery = jQuery.__stringPrototype.toQueryParams;
/***********************************************/


/*
jQuery Browser Plugin
	* Version 2.3
	* 2008-09-17 19:27:05
	* URL: http://jquery.thewikies.com/browser
	* Description: jQuery Browser Plugin extends browser detection capabilities and can assign browser selectors to CSS classes.
	* Author: Nate Cavanaugh, Minhchau Dang, & Jonathan Neal
	* Copyright: Copyright (c) 2008 Jonathan Neal under dual MIT/GPL license.
	* JSLint: This javascript file passes JSLint verification.
*//*jslint
		bitwise: true,
		browser: true,
		eqeqeq: true,
		forin: true,
		nomen: true,
		plusplus: true,
		undef: true,
		white: true
*//*global
		jQuery
*/

(function ($) {
	$.browserTest = function (a, z) {
		var u = 'unknown', x = 'X', m = function (r, h) {
			for (var i = 0; i < h.length; i = i + 1) {
				r = r.replace(h[i][0], h[i][1]);
			}

			return r;
		}, c = function (i, a, b, c) {
			var r = {
				name: m((a.exec(i) || [u, u])[1], b)
			};

			r[r.name] = true;

			r.version = (c.exec(i) || [x, x, x, x])[3];

			if (r.name.match(/safari/) && r.version > 400) {
				r.version = '2.0';
			}

			if (r.name === 'presto') {
				r.version = ($.browser.version > 9.27) ? 'futhark' : 'linear_b';
			}
			r.versionNumber = parseFloat(r.version, 10) || 0;
			r.versionX = (r.version !== x) ? (r.version + '').substr(0, 1) : x;
			r.className = r.name + r.versionX;

			return r;
		};

		a = (a.match(/Opera|Navigator|Minefield|KHTML|Chrome/) ? m(a, [
			[/(Firefox|MSIE|KHTML,\slike\sGecko|Konqueror)/, ''],
			['Chrome Safari', 'Chrome'],
			['KHTML', 'Konqueror'],
			['Minefield', 'Firefox'],
			['Navigator', 'Netscape']
		]) : a).toLowerCase();

		$.browser = $.extend((!z) ? $.browser : {}, c(a, /(camino|chrome|firefox|netscape|konqueror|lynx|msie|opera|safari)/, [], /(camino|chrome|firefox|netscape|netscape6|opera|version|konqueror|lynx|msie|safari)(\/|\s)([a-z0-9\.\+]*?)(\;|dev|rel|\s|$)/));

		$.layout = c(a, /(gecko|konqueror|msie|opera|webkit)/, [
			['konqueror', 'khtml'],
			['msie', 'trident'],
			['opera', 'presto']
		], /(applewebkit|rv|konqueror|msie)(\:|\/|\s)([a-z0-9\.]*?)(\;|\)|\s)/);

		$.os = {
			name: (/(win|mac|linux|sunos|solaris|iphone)/.exec(navigator.platform.toLowerCase()) || [u])[0].replace('sunos', 'solaris')
		};

		if (!z) {
			$('html').addClass([$.os.name, $.browser.name, $.browser.className, $.layout.name, $.layout.className].join(' '));
		}
	};

	$.browserTest(navigator.userAgent);
})(jQuery);
/***********************************************/

if (window.jQuery) {
	/*
	 * stickyfloat - jQuery plugin for verticaly floating anything in a constrained area
	 * EDITED TO accept "scrollElement" DomNode to allow a different element (other than window itself) to scroll
	 *
	 * Example: jQuery('#menu').stickyfloat({duration: 400});
	 * Example: jQuery('#menu').stickyfloat({duration: 400, scrollElement: document.getElementById('body_pane')});
	 * parameters:
	 * 		duration 	- the duration of the animation
	 *		startOffset - the amount of scroll offset after it the animations kicks in
	 *		offsetY		- the offset from the top when the object is animated
	 *		lockBottom	- 'true' by default, set to false if you don't want your floating box to stop at parent's bottom
	 * $Version: 05.16.2009 r1
	 * Copyright (c) 2009 Yair Even-Or
	 * vsync.design@gmail.com
	 */

	jQuery.fn.stickyfloat = function(options, lockBottom) {
		var $obj 				= this;
		var parentPaddingTop 	= parseInt($obj.parent().css('padding-top'));
		var startOffset 		= $obj.parent().offset().top;
		var opts 				= jQuery.extend({ scrollElement: window, startOffset: startOffset, offsetY: parentPaddingTop, duration: 200, lockBottom:true }, options);
		if (!opts.scrollElement) {opts.scrollElement = window;}
		
		$obj.css({ position: 'absolute' });
		
		if(opts.lockBottom){
			var bottomPos = $obj.parent().height() - $obj.height() + parentPaddingTop; //get the maximum scrollTop value
			if(bottomPos < 0) bottomPos = 0;
		}
		
		jQuery(opts.scrollElement).scroll(function () { 
			$obj.stop(); // stop all calculations on scroll event // calculations or animations?!
			var scrollElement = (opts.scrollElement == window) ? document : opts.scrollElement;
			var pastStartOffset			= jQuery(scrollElement).scrollTop() > opts.startOffset;	// check if the window was scrolled down more than the start offset declared.
			var objFartherThanTopPos	= $obj.offset().top > startOffset;	// check if the object is at it's top position (starting point)
			var objBiggerThanWindow 	= $obj.outerHeight() < jQuery(window).height();	// if the window size is smaller than the Obj size, then do not animate.
			
			// if window scrolled down more than startOffset OR obj position is greater than
			// the top position possible (+ offsetY) AND window size must be bigger than Obj size
			if( (pastStartOffset || objFartherThanTopPos) && objBiggerThanWindow ){ 
				// calc needed top position
				var newpos = (jQuery(scrollElement).scrollTop() -startOffset + opts.offsetY );
				// if beyond bottom
				if ( newpos > bottomPos ) newpos = bottomPos;
				// if window scrolled < starting offset, then reset Obj position (opts.offsetY);
				if ( jQuery(scrollElement).scrollTop() < opts.startOffset ) newpos = parentPaddingTop;
	
				$obj.animate({ top: newpos }, opts.duration );
			}
		});
	};
}


/*!
* jQuery resize event - v1.1 - 3/14/2010
* http://benalman.com/projects/jquery-resize-plugin/
* 
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/

// Script: jQuery resize event
//
// *Version: 1.1, Last updated: 3/14/2010*
// 
// Project Home - http://benalman.com/projects/jquery-resize-plugin/
// GitHub       - http://github.com/cowboy/jquery-resize/
// Source       - http://github.com/cowboy/jquery-resize/raw/master/jquery.ba-resize.js
// (Minified)   - http://github.com/cowboy/jquery-resize/raw/master/jquery.ba-resize.min.js (1.0kb)
// 
// About: License
// 
// Copyright (c) 2010 "Cowboy" Ben Alman,
// Dual licensed under the MIT and GPL licenses.
// http://benalman.com/about/license/
// 
// About: Examples
// 
// This working example, complete with fully commented code, illustrates a few
// ways in which this plugin can be used.
// 
// resize event - http://benalman.com/code/projects/jquery-resize/examples/resize/
// 
// About: Support and Testing
// 
// Information about what version or versions of jQuery this plugin has been
// tested with, what browsers it has been tested in, and where the unit tests
// reside (so you can test it yourself).
// 
// jQuery Versions - 1.3.2, 1.4.1, 1.4.2
// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.6, Safari 3-4, Chrome, Opera 9.6-10.1.
// Unit Tests      - http://benalman.com/code/projects/jquery-resize/unit/
// 
// About: Release History
// 
// 1.1 - (3/14/2010) Fixed a minor bug that was causing the event to trigger
//       immediately after bind in some circumstances. Also changed $.fn.data
//       to $.data to improve performance.
// 1.0 - (2/10/2010) Initial release

(function($, window, undefined) {
    '$:nomunge'; // Used by YUI compressor.

    // A jQuery object containing all non-window elements to which the resize
    // event is bound.
    var elems = $([]),

    // Extend $.resize if it already exists, otherwise create it.
    jq_resize = $.resize = $.extend($.resize, {}),

    timeout_id,

    // Reused strings.
    str_setTimeout = 'setTimeout',
    str_resize = 'resize',
    str_data = str_resize + '-special-event',
    str_delay = 'delay',
    str_throttle = 'throttleWindow';

    // Property: jQuery.resize.delay
    // 
    // The numeric interval (in milliseconds) at which the resize event polling
    // loop executes. Defaults to 250.

    jq_resize[str_delay] = 0;

    // Property: jQuery.resize.throttleWindow
    // 
    // Throttle the native window object resize event to fire no more than once
    // every <jQuery.resize.delay> milliseconds. Defaults to true.
    // 
    // Because the window object has its own resize event, it doesn't need to be
    // provided by this plugin, and its execution can be left entirely up to the
    // browser. However, since certain browsers fire the resize event continuously
    // while others do not, enabling this will throttle the window resize event,
    // making event behavior consistent across all elements in all browsers.
    // 
    // While setting this property to false will disable window object resize
    // event throttling, please note that this property must be changed before any
    // window object resize event callbacks are bound.

    jq_resize[str_throttle] = true;

    // Event: resize event
    // 
    // Fired when an element's width or height changes. Because browsers only
    // provide this event for the window element, for other elements a polling
    // loop is initialized, running every <jQuery.resize.delay> milliseconds
    // to see if elements' dimensions have changed. You may bind with either
    // .resize( fn ) or .bind( "resize", fn ), and unbind with .unbind( "resize" ).
    // 
    // Usage:
    // 
    // > jQuery('selector').bind( 'resize', function(e) {
    // >   // element's width or height has changed!
    // >   ...
    // > });
    // 
    // Additional Notes:
    // 
    // * The polling loop is not created until at least one callback is actually
    //   bound to the 'resize' event, and this single polling loop is shared
    //   across all elements.
    // 
    // Double firing issue in jQuery 1.3.2:
    // 
    // While this plugin works in jQuery 1.3.2, if an element's event callbacks
    // are manually triggered via .trigger( 'resize' ) or .resize() those
    // callbacks may double-fire, due to limitations in the jQuery 1.3.2 special
    // events system. This is not an issue when using jQuery 1.4+.
    // 
    // > // While this works in jQuery 1.4+
    // > $(elem).css({ width: new_w, height: new_h }).resize();
    // > 
    // > // In jQuery 1.3.2, you need to do this:
    // > var elem = $(elem);
    // > elem.css({ width: new_w, height: new_h });
    // > elem.data( 'resize-special-event', { width: elem.width(), height: elem.height() } );
    // > elem.resize();

    $.event.special[str_resize] = {

        // Called only when the first 'resize' event callback is bound per element.
        setup: function() {
            // Since window has its own native 'resize' event, return false so that
            // jQuery will bind the event using DOM methods. Since only 'window'
            // objects have a .setTimeout method, this should be a sufficient test.
            // Unless, of course, we're throttling the 'resize' event for window.
            if (!jq_resize[str_throttle] && this[str_setTimeout]) { return false; }

            var elem = $(this);

            // Add this element to the list of internal elements to monitor.
            elems = elems.add(elem);

            // Initialize data store on the element.
            $.data(this, str_data, { w: elem.width(), h: elem.height() });

            // If this is the first element added, start the polling loop.
            if (elems.length === 1) {
                loopy();
            }
        },

        // Called only when the last 'resize' event callback is unbound per element.
        teardown: function() {
            // Since window has its own native 'resize' event, return false so that
            // jQuery will unbind the event using DOM methods. Since only 'window'
            // objects have a .setTimeout method, this should be a sufficient test.
            // Unless, of course, we're throttling the 'resize' event for window.
            if (!jq_resize[str_throttle] && this[str_setTimeout]) { return false; }

            var elem = $(this);

            // Remove this element from the list of internal elements to monitor.
            elems = elems.not(elem);

            // Remove any data stored on the element.
            elem.removeData(str_data);

            // If this is the last element removed, stop the polling loop.
            if (!elems.length) {
                clearTimeout(timeout_id);
            }
        },

        // Called every time a 'resize' event callback is bound per element (new in
        // jQuery 1.4).
        add: function(handleObj) {
            // Since window has its own native 'resize' event, return false so that
            // jQuery doesn't modify the event object. Unless, of course, we're
            // throttling the 'resize' event for window.
            if (!jq_resize[str_throttle] && this[str_setTimeout]) { return false; }

            var old_handler;

            // The new_handler function is executed every time the event is triggered.
            // This is used to update the internal element data store with the width
            // and height when the event is triggered manually, to avoid double-firing
            // of the event callback. See the "Double firing issue in jQuery 1.3.2"
            // comments above for more information.

            function new_handler(e, w, h) {
                var elem = $(this),
          data = $.data(this, str_data);

                // If called from the polling loop, w and h will be passed in as
                // arguments. If called manually, via .trigger( 'resize' ) or .resize(),
                // those values will need to be computed.
                data.w = w !== undefined ? w : elem.width();
                data.h = h !== undefined ? h : elem.height();

                old_handler.apply(this, arguments);
            };

            // This may seem a little complicated, but it normalizes the special event
            // .add method between jQuery 1.4/1.4.1 and 1.4.2+
            if ($.isFunction(handleObj)) {
                // 1.4, 1.4.1
                old_handler = handleObj;
                return new_handler;
            } else {
                // 1.4.2+
                old_handler = handleObj.handler;
                handleObj.handler = new_handler;
            }
        }

    };

    function loopy() {

        // Start the polling loop, asynchronously.
        timeout_id = window[str_setTimeout](function() {

            // Iterate over all elements to which the 'resize' event is bound.
            elems.each(function() {
                var elem = $(this),
          width = elem.width(),
          height = elem.height(),
          data = $.data(this, str_data);

                // If element size has changed since the last time, update the element
                // data store and trigger the 'resize' event.
                if (width !== data.w || height !== data.h) {
                    elem.trigger(str_resize, [data.w = width, data.h = height]);
                }

            });

            // Loop.
            loopy();

        }, jq_resize[str_delay]);

    };

})(jQuery, this);

