/**********************************************************************************/
/*                              /scripts/startup.js                               */
/**********************************************************************************/

/**
 * Загрузочный файл скриптов.
 *
 * @version	$Id: startup.js 6266 2008-12-03 16:14:00Z morozov $
 */

// сторонние компоненты
/**********************************************************************************/
/*                             /scripts/lib/patch.js                              */
/**********************************************************************************/

if (!Array.prototype.map) {
	Array.prototype.map = function(fun /*, thisp*/) {
		var len = this.length;
		if (typeof fun != "function")
			throw new TypeError();
		var res = new Array(len);
		var thisp = arguments[1];
		for (var i = 0; i < len; i++) {
			if (i in this)
			res[i] = fun.call(thisp, this[i], i, this);
		}
		return res;
	};
}

if (!Array.prototype.filter) {
	Array.prototype.filter = function(fun /*, thisp*/) {
		var len = this.length;
		if (typeof fun != "function")
			throw new TypeError();
		var res = new Array();
		var thisp = arguments[1];
		for (var i = 0; i < len; i++) {
			if (i in this) {
				var val = this[i]; // in case fun mutates this
				if (fun.call(thisp, val, i, this))
				res.push(val);
			}
		}
		return res;
	};
}

if (!Array.prototype.indexOf) {
	Array.prototype.indexOf = function(elt /*, from*/) {
		var len = this.length;
		var from = Number(arguments[1]) || 0;
		from = (from < 0)
				? Math.ceil(from)
				: Math.floor(from);
		if (from < 0) {
			from += len;
		}
		for (; from < len; from++) {
			if (from in this && this[from] === elt) {
				return from;
			}
		}
		return -1;
	};
}

Array.prototype.unique = function() {
	var a = [];
	for (var i = 0, length = this.length; i < length; i++) {
		if (a.indexOf(this[i], 0) < 0) {
			a.push(this[i]);
		}
	}
	return a;
};

// Return elements which are in A but not in arg0 through argn
Array.prototype.diff = function() {
	var a1 = this;
	var a = a2 = null;
	var n = 0;
	while (n < arguments.length) {
		a = [];
		a2 = arguments[n];
		var l = a1.length;
		var l2 = a2.length;
		var diff = true;
		for (var i = 0; i < l; i++) {
			for (var j = 0; j < l2; j++) {
				if (a1[i] === a2[j]) {
					diff = false;
					break;
				}
			}
			diff ? a.push(a1[i]) : diff = true;
		}
		a1 = a;
		n++;
	}
	return a.unique();
};

/**
 * Проверяет корректность указанной даты.
 *
 * @param	{Integer}	year
 * @param	{Integer}	month
 * @param	{Integer}	day
 */
Date.isValid = function(year, month, day) {
	var date = new Date(year, month, day);
	return date.getFullYear() == year
		&& date.getMonth() == month
		&& date.getDate() == day;
};

// **************************************************************************
// Copyright 2007 - 2008 The JSLab Team, Tavs Dokkedahl
// Contact: http://www.jslab.dk/contact.php
//
// This file is part of the JSLab Standard Library (JSL) Program.
//
// JSL is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// any later version.
//
// JSL is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// ***************************************************************************

// Compute the intersection of n arrays
Array.prototype.intersect = function() {
	if (!arguments.length)
		return [];
	var a1 = this;
	var a = a2 = null;
	var n = 0;
	while(n < arguments.length) {
		a = [];
		a2 = arguments[n];
		var l = a1.length;
		var l2 = a2.length;
		for(var i=0; i<l; i++) {
			for(var j=0; j<l2; j++) {
				if (a1[i] === a2[j])
					a.push(a1[i]);
			}
		}
		a1 = a;
		n++;
	}
	return a.unique();
};


/**********************************************************************************/
/*                     /scripts/lib/jquery/interface/iutil.js                     */
/**********************************************************************************/

/**
 * Interface Elements for jQuery
 * utility function
 *
 * http://interface.eyecon.ro
 *
 * Copyright (c) 2006 Stefan Petre
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 *
 */

jQuery.iUtil = {
	getPosition : function(e)
	{
		var x = 0;
		var y = 0;
		var es = e.style;
		var restoreStyles = false;
		if (jQuery(e).css('display') == 'none') {
			var oldVisibility = es.visibility;
			var oldPosition = es.position;
			restoreStyles = true;
			es.visibility = 'hidden';
			es.display = 'block';
			es.position = 'absolute';
		}
		var el = e;
		while (el){
			x += el.offsetLeft + (el.currentStyle && !jQuery.browser.opera ?parseInt(el.currentStyle.borderLeftWidth)||0:0);
			y += el.offsetTop + (el.currentStyle && !jQuery.browser.opera ?parseInt(el.currentStyle.borderTopWidth)||0:0);
			el = el.offsetParent;
		}
		el = e;
		while (el && el.tagName  && el.tagName.toLowerCase() != 'body')
		{
			x -= el.scrollLeft||0;
			y -= el.scrollTop||0;
			el = el.parentNode;
		}
		if (restoreStyles == true) {
			es.display = 'none';
			es.position = oldPosition;
			es.visibility = oldVisibility;
		}
		return {x:x, y:y};
	},
	getPositionLite : function(el)
	{
		var x = 0, y = 0;
		while(el) {
			x += el.offsetLeft || 0;
			y += el.offsetTop || 0;
			el = el.offsetParent;
		}
		return {x:x, y:y};
	},
	getSize : function(e)
	{
		var w = jQuery.css(e,'width');
		var h = jQuery.css(e,'height');
		var wb = 0;
		var hb = 0;
		var es = e.style;
		if (jQuery(e).css('display') != 'none') {
			wb = e.offsetWidth;
			hb = e.offsetHeight;
		} else {
			var oldVisibility = es.visibility;
			var oldPosition = es.position;
			es.visibility = 'hidden';
			es.display = 'block';
			es.position = 'absolute';
			wb = e.offsetWidth;
			hb = e.offsetHeight;
			es.display = 'none';
			es.position = oldPosition;
			es.visibility = oldVisibility;
		}
		return {w:w, h:h, wb:wb, hb:hb};
	},
	getSizeLite : function(el)
	{
		return {
			wb:el.offsetWidth||0,
			hb:el.offsetHeight||0
		};
	},
	getClient : function(e)
	{
		var h, w, de;
		if (e) {
			w = e.clientWidth;
			h = e.clientHeight;
		} else {
			de = document.documentElement;
			w = window.innerWidth || self.innerWidth || (de&&de.clientWidth) || document.body.clientWidth;
			h = window.innerHeight || self.innerHeight || (de&&de.clientHeight) || document.body.clientHeight;
		}
		return {w:w,h:h};
	},
	getScroll : function (e)
	{
		var t=0, l=0, w=0, h=0, iw=0, ih=0;
		if (e && e.nodeName.toLowerCase() != 'body') {
			t = e.scrollTop;
			l = e.scrollLeft;
			w = e.scrollWidth;
			h = e.scrollHeight;
			iw = 0;
			ih = 0;
		} else  {
			if (document.documentElement) {
				t = document.documentElement.scrollTop;
				l = document.documentElement.scrollLeft;
				w = document.documentElement.scrollWidth;
				h = document.documentElement.scrollHeight;
			} else if (document.body) {
				t = document.body.scrollTop;
				l = document.body.scrollLeft;
				w = document.body.scrollWidth;
				h = document.body.scrollHeight;
			}
			iw = self.innerWidth||document.documentElement.clientWidth||document.body.clientWidth||0;
			ih = self.innerHeight||document.documentElement.clientHeight||document.body.clientHeight||0;
		}
		return { t: t, l: l, w: w, h: h, iw: iw, ih: ih };
	},
	getMargins : function(e, toInteger)
	{
		var el = jQuery(e);
		var t = el.css('marginTop') || '';
		var r = el.css('marginRight') || '';
		var b = el.css('marginBottom') || '';
		var l = el.css('marginLeft') || '';
		if (toInteger)
			return {
				t: parseInt(t)||0,
				r: parseInt(r)||0,
				b: parseInt(b)||0,
				l: parseInt(l)
			};
		else
			return {t: t, r: r,	b: b, l: l};
	},
	getPadding : function(e, toInteger)
	{
		var el = jQuery(e);
		var t = el.css('paddingTop') || '';
		var r = el.css('paddingRight') || '';
		var b = el.css('paddingBottom') || '';
		var l = el.css('paddingLeft') || '';
		if (toInteger)
			return {
				t: parseInt(t)||0,
				r: parseInt(r)||0,
				b: parseInt(b)||0,
				l: parseInt(l)
			};
		else
			return {t: t, r: r,	b: b, l: l};
	},
	getBorder : function(e, toInteger)
	{
		var el = jQuery(e);
		var t = el.css('borderTopWidth') || '';
		var r = el.css('borderRightWidth') || '';
		var b = el.css('borderBottomWidth') || '';
		var l = el.css('borderLeftWidth') || '';
		if (toInteger)
			return {
				t: parseInt(t)||0,
				r: parseInt(r)||0,
				b: parseInt(b)||0,
				l: parseInt(l)||0
			};
		else
			return {t: t, r: r,	b: b, l: l};
	},
	getPointer : function(event)
	{
		var x = event.pageX || (event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft)) || 0;
		var y = event.pageY || (event.clientY + (document.documentElement.scrollTop || document.body.scrollTop)) || 0;
		return {x:x, y:y};
	},
	traverseDOM : function(nodeEl, func)
	{
		func(nodeEl);
		nodeEl = nodeEl.firstChild;
		while(nodeEl){
			jQuery.iUtil.traverseDOM(nodeEl, func);
			nodeEl = nodeEl.nextSibling;
		}
	},
	purgeEvents : function(nodeEl)
	{
		jQuery.iUtil.traverseDOM(
			nodeEl,
			function(el)
			{
				for(var attr in el){
					if(typeof el[attr] === 'function') {
						el[attr] = null;
					}
				}
			}
		);
	},
	centerEl : function(el, axis)
	{
		var clientScroll = jQuery.iUtil.getScroll();
		var windowSize = jQuery.iUtil.getSize(el);
		if (!axis || axis == 'vertically')
			jQuery(el).css(
				{
					top: clientScroll.t + ((Math.max(clientScroll.h,clientScroll.ih) - clientScroll.t - windowSize.hb)/2) + 'px'
				}
			);
		if (!axis || axis == 'horizontally')
			jQuery(el).css(
				{
					left:	clientScroll.l + ((Math.max(clientScroll.w,clientScroll.iw) - clientScroll.l - windowSize.wb)/2) + 'px'
				}
			);
	},
	fixPNG : function (el, emptyGIF) {
		var images = jQuery('img[@src*="png"]', el||document), png;
		images.each( function() {
			png = this.src;
			this.src = emptyGIF;
			this.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + png + "')";
		});
	}
};

// Helper function to support older browsers!
[].indexOf || (Array.prototype.indexOf = function(v, n){
	n = (n == null) ? 0 : n;
	var m = this.length;
	for (var i=n; i<m; i++)
		if (this[i] == v)
			return i;
	return -1;
});


/**********************************************************************************/
/*                     /scripts/lib/jquery/interface/idrag.js                     */
/**********************************************************************************/

/**
 * Interface Elements for jQuery
 * Draggable
 *
 * http://interface.eyecon.ro
 *
 * Copyright (c) 2006 Stefan Petre
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 */

/**
 * Create a draggable element with a number of advanced options including callback, Google Maps type draggables,
 * reversion, ghosting, and grid dragging.
 *
 * @name Draggable
 * @descr Creates draggable elements that can be moved across the page.
 * @param Hash hash A hash of parameters. All parameters are optional.
 * @option String handle (optional) The jQuery selector matching the handle that starts the draggable
 * @option DOMElement handle (optional) The DOM Element of the handle that starts the draggable
 * @option Boolean revert (optional) When true, on stop-drag the element returns to initial position
 * @option Boolean ghosting (optional) When true, a copy of the element is moved
 * @option Integer zIndex (optional) zIndex depth for the element while it is being dragged
 * @option Float opacity (optional) A number between 0 and 1 that indicates the opacity of the element while being dragged
 * @option Integer grid (optional) (optional) A number of pixels indicating the grid that the element should snap to
 * @option Array grid (optional) A number of x-pixels and y-pixels indicating the grid that the element should snap to
 * @option Integer fx (optional) Duration for the effect (like ghosting or revert) applied to the draggable
 * @option String containment (optional) Define the zone where the draggable can be moved. 'parent' moves it inside parent
 *                           element, while 'document' prevents it from leaving the document and forcing additional
 *                           scrolling
 * @option Array containment An 4-element array (left, top, width, height) indicating the containment of the element
 * @option String axis (optional) Set an axis: vertical (with 'vertically') or horizontal (with 'horizontally')
 * @option Function onStart (optional) Callback function triggered when the dragging starts
 * @option Function onStop (optional) Callback function triggered when the dragging stops
 * @option Function onChange (optional) Callback function triggered when the dragging stop *and* the element was moved at least
 *                          one pixel
 * @option Function onDrag (optional) Callback function triggered while the element is dragged. Receives two parameters: x and y
 *                        coordinates. You can return an object with new coordinates {x: x, y: y} so this way you can
 *                        interact with the dragging process (for instance, build your containment)
 * @option Boolean insideParent Forces the element to remain inside its parent when being dragged (like Google Maps)
 * @option Integer snapDistance (optional) The element is not moved unless it is dragged more than snapDistance. You can prevent
 *                             accidental dragging and keep regular clicking enabled (for links or form elements,
 *                             for instance)
 * @option Object cursorAt (optional) The dragged element is moved to the cursor position with the offset specified. Accepts value
 *                        for top, left, right and bottom offset. Basically, this forces the cursor to a particular
 *                        position during the entire drag operation.
 * @option Boolean autoSize (optional) When true, the drag helper is resized to its content, instead of the dragged element's sizes
 * @option String frameClass (optional) When is set the cloned element is hidden so only a frame is dragged
 * @type jQuery
 * @cat Plugins/Interface
 * @author Stefan Petre
 */

jQuery.iDrag =	{
	helper : null,
	dragged: null,
	destroy : function()
	{
		return this.each(
			function ()
			{
				if (this.isDraggable) {
					this.dragCfg.dhe.unbind('mousedown', jQuery.iDrag.draginit);
					this.dragCfg = null;
					this.isDraggable = false;
					if(jQuery.browser.msie) {
						this.unselectable = "off";
					} else {
						this.style.MozUserSelect = '';
						this.style.KhtmlUserSelect = '';
						this.style.userSelect = '';
					}
				}
			}
		);
	},
	draginit : function (e)
	{
		if (jQuery.iDrag.dragged != null) {
			jQuery.iDrag.dragstop(e);
			return false;
		}
		var elm = this.dragElem;
		jQuery(document)
			.bind('mousemove', jQuery.iDrag.dragmove)
			.bind('mouseup', jQuery.iDrag.dragstop);
		elm.dragCfg.pointer = jQuery.iUtil.getPointer(e);
		elm.dragCfg.currentPointer = elm.dragCfg.pointer;
		elm.dragCfg.init = false;
		elm.dragCfg.fromHandler = this != this.dragElem;
		jQuery.iDrag.dragged = elm;
		if (elm.dragCfg.si && this != this.dragElem) {
				parentPos = jQuery.iUtil.getPosition(elm.parentNode);
				sliderSize = jQuery.iUtil.getSize(elm);
				sliderPos = {
					x : parseInt(jQuery.css(elm,'left')) || 0,
					y : parseInt(jQuery.css(elm,'top')) || 0
				};
				dx = elm.dragCfg.currentPointer.x - parentPos.x - sliderSize.wb/2 - sliderPos.x;
				dy = elm.dragCfg.currentPointer.y - parentPos.y - sliderSize.hb/2 - sliderPos.y;
				jQuery.iSlider.dragmoveBy(elm, [dx, dy]);
		}
		return jQuery.selectKeyHelper||false;
	},

	dragstart : function(e)
	{
		var elm = jQuery.iDrag.dragged;
		elm.dragCfg.init = true;

		var dEs = elm.style;

		elm.dragCfg.oD = jQuery.css(elm,'display');
		elm.dragCfg.oP = jQuery.css(elm,'position');
		if (!elm.dragCfg.initialPosition)
			elm.dragCfg.initialPosition = elm.dragCfg.oP;

		elm.dragCfg.oR = {
			x : parseInt(jQuery.css(elm,'left')) || 0,
			y : parseInt(jQuery.css(elm,'top')) || 0
		};
		elm.dragCfg.diffX = 0;
		elm.dragCfg.diffY = 0;
		if (jQuery.browser.msie) {
			var oldBorder = jQuery.iUtil.getBorder(elm, true);
			elm.dragCfg.diffX = oldBorder.l||0;
			elm.dragCfg.diffY = oldBorder.t||0;
		}

		elm.dragCfg.oC = jQuery.extend(
			jQuery.iUtil.getPosition(elm),
			jQuery.iUtil.getSize(elm)
		);
		if (elm.dragCfg.oP != 'relative' && elm.dragCfg.oP != 'absolute') {
			dEs.position = 'relative';
		}

		jQuery.iDrag.helper.empty();
		var clonedEl = jQuery(elm).clone(true).get(0);

		jQuery(clonedEl).css(
			{
				display:	'block',
				left:		'0px',
				top: 		'0px'
			}
		);
		clonedEl.style.marginTop = '0';
		clonedEl.style.marginRight = '0';
		clonedEl.style.marginBottom = '0';
		clonedEl.style.marginLeft = '0';
		jQuery.iDrag.helper.append(clonedEl);

		var dhs = jQuery.iDrag.helper.get(0).style;

		if (elm.dragCfg.autoSize) {
			dhs.width = 'auto';
			dhs.height = 'auto';
		} else {
			dhs.height = elm.dragCfg.oC.hb + 'px';
			dhs.width = elm.dragCfg.oC.wb + 'px';
		}

		dhs.display = 'block';
		dhs.marginTop = '0px';
		dhs.marginRight = '0px';
		dhs.marginBottom = '0px';
		dhs.marginLeft = '0px';

		//remeasure the clone to check if the size was changed by user's functions
		jQuery.extend(
			elm.dragCfg.oC,
			jQuery.iUtil.getSize(clonedEl)
		);

		if (elm.dragCfg.cursorAt) {
			if (elm.dragCfg.cursorAt.left) {
				elm.dragCfg.oR.x += elm.dragCfg.pointer.x - elm.dragCfg.oC.x - elm.dragCfg.cursorAt.left;
				elm.dragCfg.oC.x = elm.dragCfg.pointer.x - elm.dragCfg.cursorAt.left;
			}
			if (elm.dragCfg.cursorAt.top) {
				elm.dragCfg.oR.y += elm.dragCfg.pointer.y - elm.dragCfg.oC.y - elm.dragCfg.cursorAt.top;
				elm.dragCfg.oC.y = elm.dragCfg.pointer.y - elm.dragCfg.cursorAt.top;
			}
			if (elm.dragCfg.cursorAt.right) {
				elm.dragCfg.oR.x += elm.dragCfg.pointer.x - elm.dragCfg.oC.x -elm.dragCfg.oC.hb + elm.dragCfg.cursorAt.right;
				elm.dragCfg.oC.x = elm.dragCfg.pointer.x - elm.dragCfg.oC.wb + elm.dragCfg.cursorAt.right;
			}
			if (elm.dragCfg.cursorAt.bottom) {
				elm.dragCfg.oR.y += elm.dragCfg.pointer.y - elm.dragCfg.oC.y - elm.dragCfg.oC.hb + elm.dragCfg.cursorAt.bottom;
				elm.dragCfg.oC.y = elm.dragCfg.pointer.y - elm.dragCfg.oC.hb + elm.dragCfg.cursorAt.bottom;
			}
		}
		elm.dragCfg.nx = elm.dragCfg.oR.x;
		elm.dragCfg.ny = elm.dragCfg.oR.y;

		if (elm.dragCfg.insideParent || elm.dragCfg.containment == 'parent') {
			parentBorders = jQuery.iUtil.getBorder(elm.parentNode, true);
			elm.dragCfg.oC.x = elm.offsetLeft + (jQuery.browser.msie ? 0 : jQuery.browser.opera ? -parentBorders.l : parentBorders.l);
			elm.dragCfg.oC.y = elm.offsetTop + (jQuery.browser.msie ? 0 : jQuery.browser.opera ? -parentBorders.t : parentBorders.t);
			jQuery(elm.parentNode).append(jQuery.iDrag.helper.get(0));
		}
		if (elm.dragCfg.containment) {
			jQuery.iDrag.getContainment(elm);
			elm.dragCfg.onDragModifier.containment = jQuery.iDrag.fitToContainer;
		}

		if (elm.dragCfg.si) {
			jQuery.iSlider.modifyContainer(elm);
		}

		dhs.left = elm.dragCfg.oC.x - elm.dragCfg.diffX + 'px';
		dhs.top = elm.dragCfg.oC.y - elm.dragCfg.diffY + 'px';
		//resize the helper to fit the clone
		dhs.width = elm.dragCfg.oC.wb + 'px';
		dhs.height = elm.dragCfg.oC.hb + 'px';

		jQuery.iDrag.dragged.dragCfg.prot = false;

		if (elm.dragCfg.gx) {
			elm.dragCfg.onDragModifier.grid = jQuery.iDrag.snapToGrid;
		}
		if (elm.dragCfg.zIndex != false) {
			jQuery.iDrag.helper.css('zIndex', elm.dragCfg.zIndex);
		}
		if (elm.dragCfg.opacity) {
			jQuery.iDrag.helper.css('opacity', elm.dragCfg.opacity);
			if (window.ActiveXObject) {
				jQuery.iDrag.helper.css('filter', 'alpha(opacity=' + elm.dragCfg.opacity * 100 + ')');
			}
		}

		if(elm.dragCfg.frameClass) {
			jQuery.iDrag.helper.addClass(elm.dragCfg.frameClass);
			jQuery.iDrag.helper.get(0).firstChild.style.display = 'none';
		}
		if (elm.dragCfg.onStart)
			elm.dragCfg.onStart.apply(elm, [clonedEl, elm.dragCfg.oR.x, elm.dragCfg.oR.y]);
		if (jQuery.iDrop && jQuery.iDrop.count > 0 ){
			jQuery.iDrop.highlight(elm);
		}
		if (elm.dragCfg.ghosting == false) {
			dEs.display = 'none';
		}
		return false;
	},

	getContainment : function(elm)
	{
		if (elm.dragCfg.containment.constructor == String) {
			if (elm.dragCfg.containment == 'parent') {
				elm.dragCfg.cont = jQuery.extend(
					{x:0,y:0},
					jQuery.iUtil.getSize(elm.parentNode)
				);
				var contBorders = jQuery.iUtil.getBorder(elm.parentNode, true);
				elm.dragCfg.cont.w = elm.dragCfg.cont.wb - contBorders.l - contBorders.r;
				elm.dragCfg.cont.h = elm.dragCfg.cont.hb - contBorders.t - contBorders.b;
			} else if (elm.dragCfg.containment == 'document') {
				var clnt = jQuery.iUtil.getClient();
				elm.dragCfg.cont = {
					x : 0,
					y : 0,
					w : clnt.w,
					h : clnt.h
				};
			}
		} else if (elm.dragCfg.containment.constructor == Array) {
			elm.dragCfg.cont = {
				x : parseInt(elm.dragCfg.containment[0])||0,
				y : parseInt(elm.dragCfg.containment[1])||0,
				w : parseInt(elm.dragCfg.containment[2])||0,
				h : parseInt(elm.dragCfg.containment[3])||0
			};
		}
		elm.dragCfg.cont.dx = elm.dragCfg.cont.x - elm.dragCfg.oC.x;
		elm.dragCfg.cont.dy = elm.dragCfg.cont.y - elm.dragCfg.oC.y;
	},

	hidehelper : function(dragged)
	{
		if (dragged.dragCfg.insideParent || dragged.dragCfg.containment == 'parent') {
			jQuery('body', document).append(jQuery.iDrag.helper.get(0));
		}
		jQuery.iDrag.helper.empty().hide().css('opacity', 1);
		if (window.ActiveXObject) {
			jQuery.iDrag.helper.css('filter', 'alpha(opacity=100)');
		}
	},

	dragstop : function(e)
	{

		jQuery(document)
			.unbind('mousemove', jQuery.iDrag.dragmove)
			.unbind('mouseup', jQuery.iDrag.dragstop);

		if (jQuery.iDrag.dragged == null) {
			return;
		}
		var dragged = jQuery.iDrag.dragged;

		jQuery.iDrag.dragged = null;

		if (dragged.dragCfg.init == false) {
			return false;
		}
		if (dragged.dragCfg.so == true) {
			jQuery(dragged).css('position', dragged.dragCfg.oP);
		}
		var dEs = dragged.style;

		if (dragged.si) {
			jQuery.iDrag.helper.css('cursor', 'move');
		}
		if(dragged.dragCfg.frameClass) {
			jQuery.iDrag.helper.removeClass(dragged.dragCfg.frameClass);
		}

		if (dragged.dragCfg.revert == false) {
			if (dragged.dragCfg.fx > 0) {
				if (!dragged.dragCfg.axis || dragged.dragCfg.axis == 'horizontally') {
					var x = new jQuery.fx(dragged,{duration:dragged.dragCfg.fx}, 'left');
					x.custom(dragged.dragCfg.oR.x,dragged.dragCfg.nRx);
				}
				if (!dragged.dragCfg.axis || dragged.dragCfg.axis == 'vertically') {
					var y = new jQuery.fx(dragged,{duration:dragged.dragCfg.fx}, 'top');
					y.custom(dragged.dragCfg.oR.y,dragged.dragCfg.nRy);
				}
			} else {
				if (!dragged.dragCfg.axis || dragged.dragCfg.axis == 'horizontally')
					dragged.style.left = dragged.dragCfg.nRx + 'px';
				if (!dragged.dragCfg.axis || dragged.dragCfg.axis == 'vertically')
					dragged.style.top = dragged.dragCfg.nRy + 'px';
			}
			jQuery.iDrag.hidehelper(dragged);
			if (dragged.dragCfg.ghosting == false) {
				jQuery(dragged).css('display', dragged.dragCfg.oD);
			}
		} else if (dragged.dragCfg.fx > 0) {
			dragged.dragCfg.prot = true;
			var dh = false;
			if(jQuery.iDrop && jQuery.iSort && dragged.dragCfg.so) {
				dh = jQuery.iUtil.getPosition(jQuery.iSort.helper.get(0));
			}
			jQuery.iDrag.helper.animate(
				{
					left : dh ? dh.x : dragged.dragCfg.oC.x,
					top : dh ? dh.y : dragged.dragCfg.oC.y
				},
				dragged.dragCfg.fx,
				function()
				{
					dragged.dragCfg.prot = false;
					if (dragged.dragCfg.ghosting == false) {
						dragged.style.display = dragged.dragCfg.oD;
					}
					jQuery.iDrag.hidehelper(dragged);
				}
			);
		} else {
			jQuery.iDrag.hidehelper(dragged);
			if (dragged.dragCfg.ghosting == false) {
				jQuery(dragged).css('display', dragged.dragCfg.oD);
			}
		}

		if (jQuery.iDrop && jQuery.iDrop.count > 0 ){
			jQuery.iDrop.checkdrop(dragged);
		}
		if (jQuery.iSort && dragged.dragCfg.so) {
			jQuery.iSort.check(dragged);
		}
		if (dragged.dragCfg.onChange && (dragged.dragCfg.nRx != dragged.dragCfg.oR.x || dragged.dragCfg.nRy != dragged.dragCfg.oR.y)){
			dragged.dragCfg.onChange.apply(dragged, dragged.dragCfg.lastSi||[0,0,dragged.dragCfg.nRx,dragged.dragCfg.nRy]);
		}
		if (dragged.dragCfg.onStop)
			dragged.dragCfg.onStop.apply(dragged);
		return false;
	},

	snapToGrid : function(x, y, dx, dy)
	{
		if (dx != 0)
			dx = parseInt((dx + (this.dragCfg.gx * dx/Math.abs(dx))/2)/this.dragCfg.gx) * this.dragCfg.gx;
		if (dy != 0)
			dy = parseInt((dy + (this.dragCfg.gy * dy/Math.abs(dy))/2)/this.dragCfg.gy) * this.dragCfg.gy;
		return {
			dx : dx,
			dy : dy,
			x: 0,
			y: 0
		};
	},

	fitToContainer : function(x, y, dx, dy)
	{
		dx = Math.min(
				Math.max(dx,this.dragCfg.cont.dx),
				this.dragCfg.cont.w + this.dragCfg.cont.dx - this.dragCfg.oC.wb
			);
		dy = Math.min(
				Math.max(dy,this.dragCfg.cont.dy),
				this.dragCfg.cont.h + this.dragCfg.cont.dy - this.dragCfg.oC.hb
			);

		return {
			dx : dx,
			dy : dy,
			x: 0,
			y: 0
		}
	},

	dragmove : function(e)
	{
		if (jQuery.iDrag.dragged == null || jQuery.iDrag.dragged.dragCfg.prot == true) {
			return;
		}

		var dragged = jQuery.iDrag.dragged;

		dragged.dragCfg.currentPointer = jQuery.iUtil.getPointer(e);
		if (dragged.dragCfg.init == false) {
			distance = Math.sqrt(Math.pow(dragged.dragCfg.pointer.x - dragged.dragCfg.currentPointer.x, 2) + Math.pow(dragged.dragCfg.pointer.y - dragged.dragCfg.currentPointer.y, 2));
			if (distance < dragged.dragCfg.snapDistance){
				return;
			} else {
				jQuery.iDrag.dragstart(e);
			}
		}

		var dx = dragged.dragCfg.currentPointer.x - dragged.dragCfg.pointer.x;
		var dy = dragged.dragCfg.currentPointer.y - dragged.dragCfg.pointer.y;

		for (var i in dragged.dragCfg.onDragModifier) {
			var newCoords = dragged.dragCfg.onDragModifier[i].apply(dragged, [dragged.dragCfg.oR.x + dx, dragged.dragCfg.oR.y + dy, dx, dy]);
			if (newCoords && newCoords.constructor == Object) {
				dx = i != 'user' ? newCoords.dx : (newCoords.x - dragged.dragCfg.oR.x);
				dy = i != 'user' ? newCoords.dy : (newCoords.y - dragged.dragCfg.oR.y);
			}
		}

		dragged.dragCfg.nx = dragged.dragCfg.oC.x + dx - dragged.dragCfg.diffX;
		dragged.dragCfg.ny = dragged.dragCfg.oC.y + dy - dragged.dragCfg.diffY;

		if (dragged.dragCfg.si && (dragged.dragCfg.onSlide || dragged.dragCfg.onChange)) {
			jQuery.iSlider.onSlide(dragged, dragged.dragCfg.nx, dragged.dragCfg.ny);
		}

		if(dragged.dragCfg.onDrag)
			dragged.dragCfg.onDrag.apply(dragged, [dragged.dragCfg.oR.x + dx, dragged.dragCfg.oR.y + dy]);

		if (!dragged.dragCfg.axis || dragged.dragCfg.axis == 'horizontally') {
			dragged.dragCfg.nRx = dragged.dragCfg.oR.x + dx;
			jQuery.iDrag.helper.get(0).style.left = dragged.dragCfg.nx + 'px';
		}
		if (!dragged.dragCfg.axis || dragged.dragCfg.axis == 'vertically') {
			dragged.dragCfg.nRy = dragged.dragCfg.oR.y + dy;
			jQuery.iDrag.helper.get(0).style.top = dragged.dragCfg.ny + 'px';
		}

		if (jQuery.iDrop && jQuery.iDrop.count > 0 ){
			jQuery.iDrop.checkhover(dragged);
		}
		return false;
	},

	build : function(o)
	{
		if (!jQuery.iDrag.helper) {
			jQuery('body',document).append('<div id="dragHelper"></div>');
			jQuery.iDrag.helper = jQuery('#dragHelper');
			var el = jQuery.iDrag.helper.get(0);
			var els = el.style;
			els.position = 'absolute';
			els.display = 'none';
			els.cursor = 'move';
			els.listStyle = 'none';
			els.overflow = 'hidden';
			if (window.ActiveXObject) {
				el.unselectable = "on";
			} else {
				els.mozUserSelect = 'none';
				els.userSelect = 'none';
				els.KhtmlUserSelect = 'none';
			}
		}
		if (!o) {
			o = {};
		}
		return this.each(
			function()
			{
				if (this.isDraggable || !jQuery.iUtil)
					return;
				if (window.ActiveXObject) {
					this.onselectstart = function(){return false;};
					this.ondragstart = function(){return false;};
				}
				var el = this;
				var dhe = o.handle ? jQuery(this).find(o.handle) : jQuery(this);
				if(jQuery.browser.msie) {
					dhe.each(
						function()
						{
							this.unselectable = "on";
						}
					);
				} else {
					dhe.css('-moz-user-select', 'none');
					dhe.css('user-select', 'none');
					dhe.css('-khtml-user-select', 'none');
				}
				this.dragCfg = {
					dhe: dhe,
					revert : o.revert ? true : false,
					ghosting : o.ghosting ? true : false,
					so : o.so ? o.so : false,
					si : o.si ? o.si : false,
					insideParent : o.insideParent ? o.insideParent : false,
					zIndex : o.zIndex ? parseInt(o.zIndex)||0 : false,
					opacity : o.opacity ? parseFloat(o.opacity) : false,
					fx : parseInt(o.fx)||null,
					hpc : o.hpc ? o.hpc : false,
					onDragModifier : {},
					pointer : {},
					onStart : o.onStart && o.onStart.constructor == Function ? o.onStart : false,
					onStop : o.onStop && o.onStop.constructor == Function ? o.onStop : false,
					onChange : o.onChange && o.onChange.constructor == Function ? o.onChange : false,
					axis : /vertically|horizontally/.test(o.axis) ? o.axis : false,
					snapDistance : o.snapDistance ? parseInt(o.snapDistance)||0 : 0,
					cursorAt: o.cursorAt ? o.cursorAt : false,
					autoSize : o.autoSize ? true : false,
					frameClass : o.frameClass || false

				};
				if (o.onDragModifier && o.onDragModifier.constructor == Function)
					this.dragCfg.onDragModifier.user = o.onDragModifier;
				if (o.onDrag && o.onDrag.constructor == Function)
					this.dragCfg.onDrag = o.onDrag;
				if (o.containment && ((o.containment.constructor == String && (o.containment == 'parent' || o.containment == 'document')) || (o.containment.constructor == Array && o.containment.length == 4) )) {
					this.dragCfg.containment = o.containment;
				}
				if(o.fractions) {
					this.dragCfg.fractions = o.fractions;
				}
				if(o.grid){
					if(typeof o.grid == 'number'){
						this.dragCfg.gx = parseInt(o.grid)||1;
						this.dragCfg.gy = parseInt(o.grid)||1;
					} else if (o.grid.length == 2) {
						this.dragCfg.gx = parseInt(o.grid[0])||1;
						this.dragCfg.gy = parseInt(o.grid[1])||1;
					}
				}
				if (o.onSlide && o.onSlide.constructor == Function) {
					this.dragCfg.onSlide = o.onSlide;
				}

				this.isDraggable = true;
				dhe.each(
					function(){
						this.dragElem = el;
					}
				);
				dhe.bind('mousedown', jQuery.iDrag.draginit);
			}
		)
	}
};

/**
 * Destroy an existing draggable on a collection of elements
 *
 * @name DraggableDestroy
 * @descr Destroy a draggable
 * @type jQuery
 * @cat Plugins/Interface
 * @example $('#drag2').DraggableDestroy();
 */

jQuery.fn.extend(
	{
		DraggableDestroy : jQuery.iDrag.destroy,
		Draggable : jQuery.iDrag.build
	}
);

/**********************************************************************************/
/*                     /scripts/lib/jquery/interface/idrop.js                     */
/**********************************************************************************/

/**
 * Interface Elements for jQuery
 * Droppables
 *
 * http://interface.eyecon.ro
 *
 * Copyright (c) 2006 Stefan Petre
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 *
 */

/**
 * With the Draggables plugin, Droppable allows you to create drop zones for draggable elements.
 *
 * @name Droppable
 * @cat Plugins/Interface
 * @param Hash options A hash of options
 * @option String accept The class name for draggables to get accepted by the droppable (mandatory)
 * @option String activeclass When an acceptable draggable is moved, the droppable gets this class
 * @option String hoverclass When an acceptable draggable is inside the droppable, the droppable gets
 *                           this class
 * @option String tolerance  Choose from 'pointer', 'intersect', or 'fit'. The pointer options means
 *                           that the pointer must be inside the droppable in order for the draggable
 *                           to be dropped. The intersect option means that the draggable must intersect
 *                           the droppable. The fit option means that the entire draggable must be
 *                           inside the droppable.
 * @option Function onDrop   When an acceptable draggable is dropped on a droppable, this callback is
 *                           called. It passes the draggable DOMElement as a parameter.
 * @option Function onHover  When an acceptable draggable is hovered over a droppable, this callback
 *                           is called. It passes the draggable DOMElement as a parameter.
 * @option Function onOut    When an acceptable draggable leaves a droppable, this callback is called.
 *                           It passes the draggable DOMElement as a parameter.
 * @example                  $('#dropzone1').Droppable(
 *                             {
 *                               accept : 'dropaccept',
 *                               activeclass: 'dropzoneactive',
 *                               hoverclass:	'dropzonehover',
 *                               ondrop:	function (drag) {
 *                                              alert(this); //the droppable
 *                                              alert(drag); //the draggable
 *                                        },
 *                               fit: true
 *                             }
 *                           )
 */

jQuery.iDrop = {
	fit : function (zonex, zoney, zonew, zoneh)
	{
		return 	zonex <= jQuery.iDrag.dragged.dragCfg.nx &&
				(zonex + zonew) >= (jQuery.iDrag.dragged.dragCfg.nx + jQuery.iDrag.dragged.dragCfg.oC.w) &&
				zoney <= jQuery.iDrag.dragged.dragCfg.ny &&
				(zoney + zoneh) >= (jQuery.iDrag.dragged.dragCfg.ny + jQuery.iDrag.dragged.dragCfg.oC.h) ? true :false;
	},
	intersect : function (zonex, zoney, zonew, zoneh)
	{
		return 	! ( zonex > (jQuery.iDrag.dragged.dragCfg.nx + jQuery.iDrag.dragged.dragCfg.oC.w)
				|| (zonex + zonew) < jQuery.iDrag.dragged.dragCfg.nx
				|| zoney > (jQuery.iDrag.dragged.dragCfg.ny + jQuery.iDrag.dragged.dragCfg.oC.h)
				|| (zoney + zoneh) < jQuery.iDrag.dragged.dragCfg.ny
				) ? true :false;
	},
	pointer : function (zonex, zoney, zonew, zoneh)
	{
		return	zonex < jQuery.iDrag.dragged.dragCfg.currentPointer.x
				&& (zonex + zonew) > jQuery.iDrag.dragged.dragCfg.currentPointer.x
				&& zoney < jQuery.iDrag.dragged.dragCfg.currentPointer.y
				&& (zoney + zoneh) > jQuery.iDrag.dragged.dragCfg.currentPointer.y
				? true :false;
	},
	overzone : false,
	highlighted : {},
	count : 0,
	zones : {},

	highlight : function (elm)
	{
		if (jQuery.iDrag.dragged == null) {
			return;
		}
		var i;
		jQuery.iDrop.highlighted = {};
		var oneIsSortable = false;
		for (i in jQuery.iDrop.zones) {
			if (jQuery.iDrop.zones[i] != null) {
				var iEL = jQuery.iDrop.zones[i].get(0);
				if (jQuery(jQuery.iDrag.dragged).is('.' + iEL.dropCfg.a)) {
					if (iEL.dropCfg.m == false) {
						iEL.dropCfg.p = jQuery.extend(
							jQuery.iUtil.getPositionLite(iEL),
							jQuery.iUtil.getSizeLite(iEL)
						);//jQuery.iUtil.getPos(iEL);
						iEL.dropCfg.m = true;
					}
					if (iEL.dropCfg.ac) {
						jQuery.iDrop.zones[i].addClass(iEL.dropCfg.ac);
					}
					jQuery.iDrop.highlighted[i] = jQuery.iDrop.zones[i];
					//if (jQuery.iSort && jQuery.iDrag.dragged.dragCfg.so) {
					if (jQuery.iSort && iEL.dropCfg.s && jQuery.iDrag.dragged.dragCfg.so) {
						iEL.dropCfg.el = jQuery('.' + iEL.dropCfg.a, iEL);
						elm.style.display = 'none';
						jQuery.iSort.measure(iEL);
						iEL.dropCfg.os = jQuery.iSort.serialize(jQuery.attr(iEL, 'id')).hash;
						elm.style.display = elm.dragCfg.oD;
						oneIsSortable = true;
					}
					if (iEL.dropCfg.onActivate) {
						iEL.dropCfg.onActivate.apply(jQuery.iDrop.zones[i].get(0), [jQuery.iDrag.dragged]);
					}
				}
			}
		}
		//if (jQuery.iSort && jQuery.iDrag.dragged.dragCfg.so) {
		if (oneIsSortable) {
			jQuery.iSort.start();
		}
	},
	/**
	 * remeasure the droppable
	 *
	 * useful when the positions/dimensions for droppables
	 * are changed while dragging a element
	 *
	 * this works for sortables too but with a greate processor
	 * penality because remeasures each sort items too
	 */
	remeasure : function()
	{
		jQuery.iDrop.highlighted = {};
		for (i in jQuery.iDrop.zones) {
			if (jQuery.iDrop.zones[i] != null) {
				var iEL = jQuery.iDrop.zones[i].get(0);
				if (jQuery(jQuery.iDrag.dragged).is('.' + iEL.dropCfg.a)) {
					iEL.dropCfg.p = jQuery.extend(
						jQuery.iUtil.getPositionLite(iEL),
						jQuery.iUtil.getSizeLite(iEL)
					);
					if (iEL.dropCfg.ac) {
						jQuery.iDrop.zones[i].addClass(iEL.dropCfg.ac);
					}
					jQuery.iDrop.highlighted[i] = jQuery.iDrop.zones[i];

					if (jQuery.iSort && iEL.dropCfg.s && jQuery.iDrag.dragged.dragCfg.so) {
						iEL.dropCfg.el = jQuery('.' + iEL.dropCfg.a, iEL);
						elm.style.display = 'none';
						jQuery.iSort.measure(iEL);
						elm.style.display = elm.dragCfg.oD;
					}
				}
			}
		}
	},

	checkhover : function (e)
	{
		if (jQuery.iDrag.dragged == null) {
			return;
		}
		jQuery.iDrop.overzone = false;
		var i;
		var applyOnHover = false;
		var hlt = 0;
		for (i in jQuery.iDrop.highlighted)
		{
			var iEL = jQuery.iDrop.highlighted[i].get(0);
			if (
					jQuery.iDrop.overzone == false
					 &&
					jQuery.iDrop[iEL.dropCfg.t](
					 	iEL.dropCfg.p.x,
						iEL.dropCfg.p.y,
						iEL.dropCfg.p.wb,
						iEL.dropCfg.p.hb
					)

			) {
				if (iEL.dropCfg.hc && iEL.dropCfg.h == false) {
					jQuery.iDrop.highlighted[i].addClass(iEL.dropCfg.hc);
				}
				//chec if onHover function has to be called
				if (iEL.dropCfg.h == false &&iEL.dropCfg.onHover) {
					applyOnHover = true;
				}
				iEL.dropCfg.h = true;
				jQuery.iDrop.overzone = iEL;
				//if(jQuery.iSort && jQuery.iDrag.dragged.dragCfg.so) {
				if(jQuery.iSort && iEL.dropCfg.s && jQuery.iDrag.dragged.dragCfg.so) {
					jQuery.iSort.helper.get(0).className = iEL.dropCfg.shc;
					jQuery.iSort.checkhover(iEL);
				}
				hlt ++;
			} else if(iEL.dropCfg.h == true) {
				//onOut function
				if (iEL.dropCfg.onOut) {
					iEL.dropCfg.onOut.apply(iEL, [e, jQuery.iDrag.helper.get(0).firstChild, iEL.dropCfg.fx]);
				}
				if (iEL.dropCfg.hc) {
					jQuery.iDrop.highlighted[i].removeClass(iEL.dropCfg.hc);
				}
				iEL.dropCfg.h = false;
			}
		}
		if (jQuery.iSort && !jQuery.iDrop.overzone && jQuery.iDrag.dragged.so) {
			jQuery.iSort.helper.get(0).style.display = 'none';
			//jQuery('body').append(jQuery.iSort.helper.get(0));
		}
		//call onhover
		if(applyOnHover) {
			jQuery.iDrop.overzone.dropCfg.onHover.apply(jQuery.iDrop.overzone, [e, jQuery.iDrag.helper.get(0).firstChild]);
		}
	},
	checkdrop : function (e)
	{
		var i;
		for (i in jQuery.iDrop.highlighted) {
			var iEL = jQuery.iDrop.highlighted[i].get(0);
			if (iEL.dropCfg.ac) {
				jQuery.iDrop.highlighted[i].removeClass(iEL.dropCfg.ac);
			}
			if (iEL.dropCfg.hc) {
				jQuery.iDrop.highlighted[i].removeClass(iEL.dropCfg.hc);
			}
			if(iEL.dropCfg.s) {
				jQuery.iSort.changed[jQuery.iSort.changed.length] = i;
			}
			if (iEL.dropCfg.onDrop && iEL.dropCfg.h == true) {
				iEL.dropCfg.h = false;
				iEL.dropCfg.onDrop.apply(iEL, [e, iEL.dropCfg.fx]);
			}
			iEL.dropCfg.m = false;
			iEL.dropCfg.h  = false;
		}
		jQuery.iDrop.highlighted = {};
	},
	destroy : function()
	{
		return this.each(
			function()
			{
				if (this.isDroppable) {
					if (this.dropCfg.s) {
						id = jQuery.attr(this,'id');
						jQuery.iSort.collected[id] = null;
						jQuery('.' + this.dropCfg.a, this).DraggableDestroy();
					}
					jQuery.iDrop.zones['d' + this.idsa] = null;
					this.isDroppable = false;
					this.f = null;
				}
			}
		);
	},
	build : function (o)
	{
		return this.each(
			function()
			{
				if (this.isDroppable == true || !o.accept || !jQuery.iUtil || !jQuery.iDrag){
					return;
				}
				this.dropCfg = {
					a : o.accept,
					ac: o.activeclass||false,
					hc:	o.hoverclass||false,
					shc: o.helperclass||false,
					onDrop:	o.ondrop||o.onDrop||false,
					onHover: o.onHover||o.onhover||false,
					onOut: o.onOut||o.onout||false,
					onActivate: o.onActivate||false,
					t: o.tolerance && ( o.tolerance == 'fit' || o.tolerance == 'intersect') ? o.tolerance : 'pointer',
					fx: o.fx ? o.fx : false,
					m: false,
					h: false
				};
				if (o.sortable == true && jQuery.iSort) {
					id = jQuery.attr(this,'id');
					jQuery.iSort.collected[id] = this.dropCfg.a;
					this.dropCfg.s = true;
					if(o.onChange) {
						this.dropCfg.onChange = o.onChange;
						this.dropCfg.os = jQuery.iSort.serialize(id).hash;
					}
				}
				this.isDroppable = true;
				this.idsa = parseInt(Math.random() * 10000);
				jQuery.iDrop.zones['d' + this.idsa] = jQuery(this);
				jQuery.iDrop.count ++;
			}
		);
	}
};

/**
 * Destroy an existing droppable on a collection of elements
 *
 * @name DroppableDestroy
 * @descr Destroy a droppable
 * @type jQuery
 * @cat Plugins/Interface
 * @example $('#drag2').DroppableDestroy();
 */

jQuery.fn.extend(
	{
		DroppableDestroy : jQuery.iDrop.destroy,
		Droppable : jQuery.iDrop.build
	}
);


/**
 * Recalculate all Droppables
 *
 * @name $.recallDroppables
 * @type jQuery
 * @cat Plugins/Interface
 * @example $.recallDroppable();
 */

jQuery.recallDroppables = jQuery.iDrop.remeasure;

/**********************************************************************************/
/*                  /scripts/lib/jquery/interface/isortables.js                   */
/**********************************************************************************/

/**
 * Interface Elements for jQuery
 * Sortables
 *
 * http://interface.eyecon.ro
 *
 * Copyright (c) 2006 Stefan Petre
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 *
 */

/**
 * Allows you to resort elements within a container by dragging and dropping. Requires
 * the Draggables and Droppables plugins. The container and each item inside the container
 * must have an ID. Sortables are especially useful for lists.
 *
 * @see Plugins/Interface/Draggable
 * @see Plugins/Interface/Droppable
 * @author Stefan Petre
 * @name Sortable
 * @cat Plugins/Interface
 * @param Hash options        A hash of options
 * @option String accept      The class name for items inside the container (mandatory)
 * @option String activeclass The class for the container when one of its items has started to move
 * @option String hoverclass  The class for the container when an acceptable item is inside it
 * @option String helperclass The helper is used to point to the place where the item will be
 *                            moved. This is the class for the helper.
 * @option Float opacity      Opacity (between 0 and 1) of the item while being dragged
 * @option Boolean ghosting   When true, the sortable is ghosted when dragged
 * @option String tolerance   Either 'pointer', 'intersect', or 'fit'. See Droppable for more details
 * @option Boolean fit        When true, sortable must be inside the container in order to drop
 * @option Integer fx         Duration for the effect applied to the sortable
 * @option Function onchange  Callback that gets called when the sortable list changed. It takes
 *                            an array of serialized elements
 * @option Boolean floats     True if the sorted elements are floated
 * @option String containment Use 'parent' to constrain the drag to the container
 * @option String axis        Use 'horizontally' or 'vertically' to constrain dragging to an axis
 * @option String handle      The jQuery selector that indicates the draggable handle
 * @option DOMElement handle  The node that indicates the draggable handle
 * @option Function onHover   Callback that is called when an acceptable item is dragged over the
 *                            container. Gets the hovering DOMElement as a parameter
 * @option Function onOut     Callback that is called when an acceptable item leaves the container.
 *                            Gets the leaving DOMElement as a parameter
 * @option Object cursorAt    The mouse cursor will be moved to the offset on the dragged item
 *                            indicated by the object, which takes "top", "bottom", "left", and
 *                            "right" keys
 * @option Function onStart   Callback function triggered when the dragging starts
 * @option Function onStop    Callback function triggered when the dragging stops
 * @example                   $('ul').Sortable(
 *                            	{
 *                            		accept : 'sortableitem',
 *                            		activeclass : 'sortableactive',
 *                             		hoverclass : 'sortablehover',
 *                             		helperclass : 'sorthelper',
 *                             		opacity: 	0.5,
 *                             		fit :	false
 *                             	}
 *                             )
 */

jQuery.iSort = {
	changed : [],
	collected : {},
	helper : false,
	inFrontOf: null,

	start : function ()
	{
		if (jQuery.iDrag.dragged == null) {
			return;
		}
		var shs, margins,c, cs;

		jQuery.iSort.helper.get(0).className = jQuery.iDrag.dragged.dragCfg.hpc;
		shs = jQuery.iSort.helper.get(0).style;
		shs.display = 'block';
		jQuery.iSort.helper.oC = jQuery.extend(
			jQuery.iUtil.getPosition(jQuery.iSort.helper.get(0)),
			jQuery.iUtil.getSize(jQuery.iSort.helper.get(0))
		);

		shs.width = jQuery.iDrag.dragged.dragCfg.oC.wb + 'px';
		shs.height = jQuery.iDrag.dragged.dragCfg.oC.hb + 'px';
		//shs.cssFloat = jQuery.iDrag.dragged.dragCfg.oF;
		margins = jQuery.iUtil.getMargins(jQuery.iDrag.dragged);
		shs.marginTop = margins.t;
		shs.marginRight = margins.r;
		shs.marginBottom = margins.b;
		shs.marginLeft = margins.l;
		if (jQuery.iDrag.dragged.dragCfg.ghosting == true) {
			c = jQuery.iDrag.dragged.cloneNode(true);
			cs = c.style;
			cs.marginTop = '0px';
			cs.marginRight = '0px';
			cs.marginBottom = '0px';
			cs.marginLeft = '0px';
			cs.display = 'block';
			jQuery.iSort.helper.empty().append(c);
		}
		jQuery(jQuery.iDrag.dragged).after(jQuery.iSort.helper.get(0));
		jQuery.iDrag.dragged.style.display = 'none';
	},

	check : function (e)
	{
		if (!e.dragCfg.so && jQuery.iDrop.overzone.sortable) {
			if (e.dragCfg.onStop)
				e.dragCfg.onStop.apply(dragged);
			jQuery(e).css('position', e.dragCfg.initialPosition || e.dragCfg.oP);
			jQuery(e).DraggableDestroy();
			jQuery(jQuery.iDrop.overzone).SortableAddItem(e);
		}
		jQuery.iSort.helper.removeClass(e.dragCfg.hpc).html('&nbsp;');
		jQuery.iSort.inFrontOf = null;
		var shs = jQuery.iSort.helper.get(0).style;
		shs.display = 'none';
		jQuery.iSort.helper.after(e);
		if (e.dragCfg.fx > 0) {
			jQuery(e).fadeIn(e.dragCfg.fx);
		}
		jQuery('body').append(jQuery.iSort.helper.get(0));
		var ts = [];
		var fnc = false;
		for(var i=0; i<jQuery.iSort.changed.length; i++){
			var iEL = jQuery.iDrop.zones[jQuery.iSort.changed[i]].get(0);
			var id = jQuery.attr(iEL, 'id');
			var ser = jQuery.iSort.serialize(id);
			if (iEL.dropCfg.os != ser.hash) {
				iEL.dropCfg.os = ser.hash;
				if (fnc == false && iEL.dropCfg.onChange) {
					fnc = iEL.dropCfg.onChange;
				}
				ser.id = id;
				ts[ts.length] = ser;
			}
		}
		jQuery.iSort.changed = [];
		if (fnc != false && ts.length > 0) {
			fnc(ts);
		}
	},

	checkhover : function(e,o)
	{
		if (!jQuery.iDrag.dragged)
			return;
		var cur = false;
		var i = 0;
		if ( e.dropCfg.el.size() > 0) {
			for (i = e.dropCfg.el.size(); i >0; i--) {
				if (e.dropCfg.el.get(i-1) != jQuery.iDrag.dragged) {
					if (!e.sortCfg.floats) {
						if (
						(e.dropCfg.el.get(i-1).pos.y + e.dropCfg.el.get(i-1).pos.hb/2) > jQuery.iDrag.dragged.dragCfg.ny
						) {
							cur = e.dropCfg.el.get(i-1);
						} else {
							break;
						}
					} else {
						if (
						(e.dropCfg.el.get(i-1).pos.x + e.dropCfg.el.get(i-1).pos.wb/2) > jQuery.iDrag.dragged.dragCfg.nx &&
						(e.dropCfg.el.get(i-1).pos.y + e.dropCfg.el.get(i-1).pos.hb/2) > jQuery.iDrag.dragged.dragCfg.ny
						) {
							cur = e.dropCfg.el.get(i-1);
						}
					}
				}
			}
		}
		//helpos = jQuery.iUtil.getPos(jQuery.iSort.helper.get(0));
		if (cur && jQuery.iSort.inFrontOf != cur) {
			jQuery.iSort.inFrontOf = cur;
			jQuery(cur).before(jQuery.iSort.helper.get(0));
		} else if(!cur && (jQuery.iSort.inFrontOf != null || jQuery.iSort.helper.get(0).parentNode != e) ) {
			jQuery.iSort.inFrontOf = null;
			jQuery(e).append(jQuery.iSort.helper.get(0));
		}
		jQuery.iSort.helper.get(0).style.display = 'block';
	},

	measure : function (e)
	{
		if (jQuery.iDrag.dragged == null) {
			return;
		}
		e.dropCfg.el.each (
			function ()
			{
				this.pos = jQuery.extend(
					jQuery.iUtil.getSizeLite(this),
					jQuery.iUtil.getPositionLite(this)
				);
			}
		);
	},

	serialize : function(s)
	{
		var i;
		var h = '';
		var o = {};
		if (s) {
			if (jQuery.iSort.collected[s] ) {
				o[s] = [];
				jQuery('#' + s + ' .' + jQuery.iSort.collected[s]).each(
					function ()
					{
						if (h.length > 0) {
							h += '&';
						}
						h += s + '[]=' + jQuery.attr(this,'id');
						o[s][o[s].length] = jQuery.attr(this,'id');
					}
				);
			} else {
				for ( a in s) {
					if (jQuery.iSort.collected[s[a]] ) {
						o[s[a]] = [];
						jQuery('#' + s[a] + ' .' + jQuery.iSort.collected[s[a]]).each(
							function ()
							{
								if (h.length > 0) {
									h += '&';
								}
								h += s[a] + '[]=' + jQuery.attr(this,'id');
								o[s[a]][o[s[a]].length] = jQuery.attr(this,'id');
							}
						);
					}
				}
			}
		} else {
			for ( i in jQuery.iSort.collected){
				o[i] = [];
				jQuery('#' + i + ' .' + jQuery.iSort.collected[i]).each(
					function ()
					{
						if (h.length > 0) {
							h += '&';
						}
						h += i + '[]=' + jQuery.attr(this,'id');
						o[i][o[i].length] = jQuery.attr(this,'id');
					}
				);
			}
		}
		return {hash:h, o:o};
	},

	addItem : function (e)
	{
		if ( !e.childNodes ) {
			return;
		}
		return this.each(
			function ()
			{
				if(!this.sortCfg || !jQuery(e).is('.' +  this.sortCfg.accept))
					jQuery(e).addClass(this.sortCfg.accept);
				jQuery(e).Draggable(this.sortCfg.dragCfg);
			}
		);
	},

	destroy: function()
	{
		return this.each(
			function()
			{
				jQuery('.' + this.sortCfg.accept).DraggableDestroy();
				jQuery(this).DroppableDestroy();
				this.sortCfg = null;
				this.isSortable = null;
			}
		);
	},

	build : function (o)
	{
		if (o.accept && jQuery.iUtil && jQuery.iDrag && jQuery.iDrop) {
			if (!jQuery.iSort.helper) {
				jQuery('body',document).append('<div id="sortHelper">&nbsp;</div>');
				jQuery.iSort.helper = jQuery('#sortHelper');
				jQuery.iSort.helper.get(0).style.display = 'none';
			}
			this.Droppable(
				{
					accept :  o.accept,
					activeclass : o.activeclass ? o.activeclass : false,
					hoverclass : o.hoverclass ? o.hoverclass : false,
					helperclass : o.helperclass ? o.helperclass : false,
					/*onDrop: function (drag, fx)
							{
								jQuery.iSort.helper.after(drag);
								if (fx > 0) {
									jQuery(drag).fadeIn(fx);
								}
							},*/
					onHover: o.onHover||o.onhover,
					onOut: o.onOut||o.onout,
					sortable : true,
					onChange : 	o.onChange||o.onchange,
					fx : o.fx ? o.fx : false,
					ghosting : o.ghosting ? true : false,
					tolerance: o.tolerance ? o.tolerance : 'intersect'
				}
			);

			return this.each(
				function()
				{
					var dragCfg = {
						revert : o.revert? true : false,
						zindex : 3000,
						opacity : o.opacity ? parseFloat(o.opacity) : false,
						hpc : o.helperclass ? o.helperclass : false,
						fx : o.fx ? o.fx : false,
						so : true,
						ghosting : o.ghosting ? true : false,
						handle: o.handle ? o.handle : null,
						containment: o.containment ? o.containment : null,
						onStart : o.onStart && o.onStart.constructor == Function ? o.onStart : false,
						onDrag : o.onDrag && o.onDrag.constructor == Function ? o.onDrag : false,
						onStop : o.onStop && o.onStop.constructor == Function ? o.onStop : false,
						axis : /vertically|horizontally/.test(o.axis) ? o.axis : false,
						snapDistance : o.snapDistance ? parseInt(o.snapDistance)||0 : false,
						cursorAt: o.cursorAt ? o.cursorAt : false
					};
					jQuery('.' + o.accept, this).Draggable(dragCfg);
					this.isSortable = true;
					this.sortCfg = {
						accept :  o.accept,
						revert : o.revert? true : false,
						zindex : 3000,
						opacity : o.opacity ? parseFloat(o.opacity) : false,
						hpc : o.helperclass ? o.helperclass : false,
						fx : o.fx ? o.fx : false,
						so : true,
						ghosting : o.ghosting ? true : false,
						handle: o.handle ? o.handle : null,
						containment: o.containment ? o.containment : null,
						floats: o.floats ? true : false,
						dragCfg : dragCfg
					}
				}
			);
		}
	}
};

jQuery.fn.extend(
	{
		Sortable : jQuery.iSort.build,
		/**
		 * A new item can be added to a sortable by adding it to the DOM and then adding it via
		 * SortableAddItem.
		 *
		 * @name SortableAddItem
		 * @param DOMElement elem A DOM Element to add to the sortable list
		 * @example $('#sortable1').append('<li id="newitem">new item</li>')
		 *                         .SortableAddItem($("#new_item")[0])
		 * @type jQuery
		 * @cat Plugins/Interface
		 */
		SortableAddItem : jQuery.iSort.addItem,
		/**
		 * Destroy a sortable
		 *
		 * @name SortableDestroy
		 * @example $('#sortable1').SortableDestroy();
		 * @type jQuery
		 * @cat Plugins/Interface
		 */
		SortableDestroy: jQuery.iSort.destroy
	}
);

/**
 * This function returns the hash and an object (can be used as arguments for $.post) for every
 * sortable in the page or specific sortables. The hash is based on the 'id' attributes of
 * container and items.
 *
 * @params String sortable The id of the sortable to serialize
 * @name $.SortSerialize
 * @type String
 * @cat Plugins/Interface
 */

jQuery.SortSerialize = jQuery.iSort.serialize;

/**********************************************************************************/
/*                    /scripts/lib/jquery/jquery.jcarousel.js                     */
/**********************************************************************************/

/**
 * jCarousel - Riding carousels with jQuery
 *   http://sorgalla.com/jcarousel/
 *
 * Copyright (c) 2006 Jan Sorgalla (http://sorgalla.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * Built on top of the jQuery library
 *   http://jquery.com
 *
 * Inspired by the "Carousel Component" by Bill Scott
 *   http://billwscott.com/carousel/
 */

(function($) {
    /**
     * Creates a carousel for all matched elements.
     *
     * @example $("#mycarousel").jcarousel();
     * @before <ul id="mycarousel" class="jcarousel-skin-name"><li>First item</li><li>Second item</li></ul>
     * @result
     *
     * <div class="jcarousel-skin-name">
     *   <div class="jcarousel-container">
     *     <div disabled="disabled" class="jcarousel-prev jcarousel-prev-disabled"></div>
     *     <div class="jcarousel-next"></div>
     *     <div class="jcarousel-clip">
     *       <ul class="jcarousel-list">
     *         <li class="jcarousel-item-1">First item</li>
     *         <li class="jcarousel-item-2">Second item</li>
     *       </ul>
     *     </div>
     *   </div>
     * </div>
     *
     * @name jcarousel
     * @type jQuery
     * @param Hash o A set of key/value pairs to set as configuration properties.
     * @cat Plugins/jCarousel
     */
    $.fn.jcarousel = function(o) {
        return this.each(function() {
            new $jc(this, o);
        });
    };

    // Default configuration properties.
    var defaults = {
        vertical: false,
        start: 1,
        offset: 1,
        size: null,
        scroll: 3,
        visible: null,
        animation: 'normal',
        easing: 'swing',
        auto: 0,
        wrap: null,
        initCallback: null,
        reloadCallback: null,
        itemLoadCallback: null,
        itemFirstInCallback: null,
        itemFirstOutCallback: null,
        itemLastInCallback: null,
        itemLastOutCallback: null,
        itemVisibleInCallback: null,
        itemVisibleOutCallback: null,
        buttonNextHTML: '<div></div>',
        buttonPrevHTML: '<div></div>',
        buttonNextEvent: 'click',
        buttonPrevEvent: 'click',
        buttonNextCallback: null,
        buttonPrevCallback: null
    };

    /**
     * The jCarousel object.
     *
     * @constructor
     * @name $.jcarousel
     * @param Object e The element to create the carousel for.
     * @param Hash o A set of key/value pairs to set as configuration properties.
     * @cat Plugins/jCarousel
     */
    $.jcarousel = function(e, o) {
        this.options    = $.extend({}, defaults, o || {});

        this.locked     = false;

        this.container  = null;
        this.clip       = null;
        this.list       = null;
        this.buttonNext = null;
        this.buttonPrev = null;

        this.wh = !this.options.vertical ? 'width' : 'height';
        this.lt = !this.options.vertical ? 'left' : 'top';

        // Extract skin class
        var skin = '', split = e.className.split(' ');

        for (var i = 0; i < split.length; i++) {
            if (split[i].indexOf('jcarousel-skin') != -1) {
                $(e).removeClass(split[i]);
                var skin = split[i];
                break;
            }
        }

        if (e.nodeName == 'UL' || e.nodeName == 'OL') {
            this.list = $(e);
            this.container = this.list.parent();

            if (this.container.hasClass('jcarousel-clip')) {
                if (!this.container.parent().hasClass('jcarousel-container'))
                    this.container = this.container.wrap('<div></div>');

                this.container = this.container.parent();
            } else if (!this.container.hasClass('jcarousel-container'))
                this.container = this.list.wrap('<div></div>').parent();
        } else {
            this.container = $(e);
            this.list = $(e).find('>ul,>ol,div>ul,div>ol');
        }

        if (skin != '' && this.container.parent()[0].className.indexOf('jcarousel-skin') == -1)
        	this.container.wrap('<div class=" '+ skin + '"></div>');

        this.clip = this.list.parent();

        if (!this.clip.length || !this.clip.hasClass('jcarousel-clip'))
            this.clip = this.list.wrap('<div></div>').parent();

        this.buttonPrev = $('.jcarousel-prev', this.container);

        if (this.buttonPrev.size() == 0 && this.options.buttonPrevHTML != null)
            this.buttonPrev = this.clip.before(this.options.buttonPrevHTML).prev();

        this.buttonPrev.addClass(this.className('jcarousel-prev'));

        this.buttonNext = $('.jcarousel-next', this.container);

        if (this.buttonNext.size() == 0 && this.options.buttonNextHTML != null)
            this.buttonNext = this.clip.before(this.options.buttonNextHTML).prev();

        this.buttonNext.addClass(this.className('jcarousel-next'));

        this.clip.addClass(this.className('jcarousel-clip'));
        this.list.addClass(this.className('jcarousel-list'));
        this.container.addClass(this.className('jcarousel-container'));

        var di = this.options.visible != null ? Math.ceil(this.clipping() / this.options.visible) : null;
        var li = this.list.children('li');

        var self = this;

        if (li.size() > 0) {
            var wh = 0, i = this.options.offset;
            li.each(function() {
                self.format(this, i++);
                wh += self.dimension(this, di);
            });

            this.list.css(this.wh, wh + 'px');

            // Only set if not explicitly passed as option
            if (!o || o.size === undefined)
                this.options.size = li.size();
        }

        // For whatever reason, .show() does not work in Safari...
        this.container.css('display', 'block');
        this.buttonNext.css('display', 'block');
        this.buttonPrev.css('display', 'block');

        this.funcNext   = function() { self.next(); };
        this.funcPrev   = function() { self.prev(); };
        this.funcResize = function() { self.reload(); };

        if (this.options.initCallback != null)
            this.options.initCallback(this, 'init');

        if ($.browser.safari) {
            this.buttons(false, false);
            $(window).bind('load', function() { self.setup(); });
        } else
            this.setup();
    };

    // Create shortcut for internal use
    var $jc = $.jcarousel;

    $jc.fn = $jc.prototype = {
        jcarousel: '0.2.3'
    };

    $jc.fn.extend = $jc.extend = $.extend;

    $jc.fn.extend({
        /**
         * Setups the carousel.
         *
         * @name setup
         * @type undefined
         * @cat Plugins/jCarousel
         */
        setup: function() {
            this.first     = null;
            this.last      = null;
            this.prevFirst = null;
            this.prevLast  = null;
            this.animating = false;
            this.timer     = null;
            this.tail      = null;
            this.inTail    = false;

            if (this.locked)
                return;

            this.list.css(this.lt, this.pos(this.options.offset) + 'px');
            var p = this.pos(this.options.start);
            this.prevFirst = this.prevLast = null;
            this.animate(p, false);

            $(window).unbind('resize', this.funcResize).bind('resize', this.funcResize);
        },

        /**
         * Clears the list and resets the carousel.
         *
         * @name reset
         * @type undefined
         * @cat Plugins/jCarousel
         */
        reset: function() {
            this.list.empty();

            this.list.css(this.lt, '0px');
            this.list.css(this.wh, '10px');

            if (this.options.initCallback != null)
                this.options.initCallback(this, 'reset');

            this.setup();
        },

        /**
         * Reloads the carousel and adjusts positions.
         *
         * @name reload
         * @type undefined
         * @cat Plugins/jCarousel
         */
        reload: function() {
            if (this.tail != null && this.inTail)
                this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) + this.tail);

            this.tail   = null;
            this.inTail = false;

            if (this.options.reloadCallback != null)
                this.options.reloadCallback(this);

            if (this.options.visible != null) {
                var self = this;
                var di = Math.ceil(this.clipping() / this.options.visible), wh = 0, lt = 0;
                $('li', this.list).each(function(i) {
                    wh += self.dimension(this, di);
                    if (i + 1 < self.first)
                        lt = wh;
                });

                this.list.css(this.wh, wh + 'px');
                this.list.css(this.lt, -lt + 'px');
            }

            this.scroll(this.first, false);
        },

        /**
         * Locks the carousel.
         *
         * @name lock
         * @type undefined
         * @cat Plugins/jCarousel
         */
        lock: function() {
            this.locked = true;
            this.buttons();
        },

        /**
         * Unlocks the carousel.
         *
         * @name unlock
         * @type undefined
         * @cat Plugins/jCarousel
         */
        unlock: function() {
            this.locked = false;
            this.buttons();
        },

        /**
         * Sets the size of the carousel.
         *
         * @name size
         * @type undefined
         * @param Number s The size of the carousel.
         * @cat Plugins/jCarousel
         */
        size: function(s) {
            if (s != undefined) {
                this.options.size = s;
                if (!this.locked)
                    this.buttons();
            }

            return this.options.size;
        },

        /**
         * Checks whether a list element exists for the given index (or index range).
         *
         * @name get
         * @type bool
         * @param Number i The index of the (first) element.
         * @param Number i2 The index of the last element.
         * @cat Plugins/jCarousel
         */
        has: function(i, i2) {
            if (i2 == undefined || !i2)
                i2 = i;

            if (this.options.size !== null && i2 > this.options.size)
            	i2 = this.options.size;

            for (var j = i; j <= i2; j++) {
                var e = this.get(j);
                if (!e.length || e.hasClass('jcarousel-item-placeholder'))
                    return false;
            }

            return true;
        },

        /**
         * Returns a jQuery object with list element for the given index.
         *
         * @name get
         * @type jQuery
         * @param Number i The index of the element.
         * @cat Plugins/jCarousel
         */
        get: function(i) {
            return $('.jcarousel-item-' + i, this.list);
        },

        /**
         * Adds an element for the given index to the list.
         * If the element already exists, it updates the inner html.
         * Returns the created element as jQuery object.
         *
         * @name add
         * @type jQuery
         * @param Number i The index of the element.
         * @param String s The innerHTML of the element.
         * @cat Plugins/jCarousel
         */
        add: function(i, s) {
            var e = this.get(i), old = 0, add = 0;

            if (e.length == 0) {
                var c, e = this.create(i), j = $jc.intval(i);
                while (c = this.get(--j)) {
                    if (j <= 0 || c.length) {
                        j <= 0 ? this.list.prepend(e) : c.after(e);
                        break;
                    }
                }
            } else
                old = this.dimension(e);

            e.removeClass(this.className('jcarousel-item-placeholder'));
            typeof s == 'string' ? e.html(s) : e.empty().append(s);

            var di = this.options.visible != null ? Math.ceil(this.clipping() / this.options.visible) : null;
            var wh = this.dimension(e, di) - old;

            if (i > 0 && i < this.first)
                this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) - wh + 'px');

            this.list.css(this.wh, $jc.intval(this.list.css(this.wh)) + wh + 'px');

            return e;
        },

        /**
         * Removes an element for the given index from the list.
         *
         * @name remove
         * @type undefined
         * @param Number i The index of the element.
         * @cat Plugins/jCarousel
         */
        remove: function(i) {
            var e = this.get(i);

            // Check if item exists and is not currently visible
            if (!e.length || (i >= this.first && i <= this.last))
                return;

            var d = this.dimension(e);

            if (i < this.first)
                this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) + d + 'px');

            e.remove();

            this.list.css(this.wh, $jc.intval(this.list.css(this.wh)) - d + 'px');
        },

        /**
         * Moves the carousel forwards.
         *
         * @name next
         * @type undefined
         * @cat Plugins/jCarousel
         */
        next: function() {
            this.stopAuto();

            if (this.tail != null && !this.inTail)
                this.scrollTail(false);
            else
                this.scroll(((this.options.wrap == 'both' || this.options.wrap == 'last') && this.options.size != null && this.last == this.options.size) ? 1 : this.first + this.options.scroll);
        },

        /**
         * Moves the carousel backwards.
         *
         * @name prev
         * @type undefined
         * @cat Plugins/jCarousel
         */
        prev: function() {
            this.stopAuto();

            if (this.tail != null && this.inTail)
                this.scrollTail(true);
            else
                this.scroll(((this.options.wrap == 'both' || this.options.wrap == 'first') && this.options.size != null && this.first == 1) ? this.options.size : this.first - this.options.scroll);
        },

        /**
         * Scrolls the tail of the carousel.
         *
         * @name scrollTail
         * @type undefined
         * @param Bool b Whether scroll the tail back or forward.
         * @cat Plugins/jCarousel
         */
        scrollTail: function(b) {
            if (this.locked || this.animating || !this.tail)
                return;

            var pos  = $jc.intval(this.list.css(this.lt));

            !b ? pos -= this.tail : pos += this.tail;
            this.inTail = !b;

            // Save for callbacks
            this.prevFirst = this.first;
            this.prevLast  = this.last;

            this.animate(pos);
        },

        /**
         * Scrolls the carousel to a certain position.
         *
         * @name scroll
         * @type undefined
         * @param Number i The index of the element to scoll to.
         * @param Bool a Flag indicating whether to perform animation.
         * @cat Plugins/jCarousel
         */
        scroll: function(i, a) {
            if (this.locked || this.animating)
                return;

            this.animate(this.pos(i), a);
        },

        /**
         * Prepares the carousel and return the position for a certian index.
         *
         * @name pos
         * @type Number
         * @param Number i The index of the element to scoll to.
         * @cat Plugins/jCarousel
         */
        pos: function(i) {
            if (this.locked || this.animating)
                return;

            if (this.options.wrap != 'circular')
                i = i < 1 ? 1 : (this.options.size && i > this.options.size ? this.options.size : i);

            var back = this.first > i;
            var pos  = $jc.intval(this.list.css(this.lt));

            // Create placeholders, new list width/height
            // and new list position
            var f = this.options.wrap != 'circular' && this.first <= 1 ? 1 : this.first;
            var c = back ? this.get(f) : this.get(this.last);
            var j = back ? f : f - 1;
            var e = null, l = 0, p = false, d = 0;

            while (back ? --j >= i : ++j < i) {
                e = this.get(j);
                p = !e.length;
                if (e.length == 0) {
                    e = this.create(j).addClass(this.className('jcarousel-item-placeholder'));
                    c[back ? 'before' : 'after' ](e);
                }

                c = e;
                d = this.dimension(e);

                if (p)
                    l += d;

                if (this.first != null && (this.options.wrap == 'circular' || (j >= 1 && (this.options.size == null || j <= this.options.size))))
                    pos = back ? pos + d : pos - d;
            }

            // Calculate visible items
            var clipping = this.clipping();
            var cache = [];
            var visible = 0, j = i, v = 0;
            var c = this.get(i - 1);

            while (++visible) {
                e = this.get(j);
                p = !e.length;
                if (e.length == 0) {
                    e = this.create(j).addClass(this.className('jcarousel-item-placeholder'));
                    // This should only happen on a next scroll
                    c.length == 0 ? this.list.prepend(e) : c[back ? 'before' : 'after' ](e);
                }

                c = e;
                var d = this.dimension(e);
                if (d == 0) {
                    alert('jCarousel: No width/height set for items. This will cause an infinite loop. Aborting...');
                    return 0;
                }

                if (this.options.wrap != 'circular' && this.options.size !== null && j > this.options.size)
                    cache.push(e);
                else if (p)
                    l += d;

                v += d;

                if (v >= clipping)
                    break;

                j++;
            }

             // Remove out-of-range placeholders
            for (var x = 0; x < cache.length; x++)
                cache[x].remove();

            // Resize list
            if (l > 0) {
                this.list.css(this.wh, this.dimension(this.list) + l + 'px');

                if (back) {
                    pos -= l;
                    this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) - l + 'px');
                }
            }

            // Calculate first and last item
            var last = i + visible - 1;
            if (this.options.wrap != 'circular' && this.options.size && last > this.options.size)
                last = this.options.size;

            if (j > last) {
                visible = 0, j = last, v = 0;
                while (++visible) {
                    var e = this.get(j--);
                    if (!e.length)
                        break;
                    v += this.dimension(e);
                    if (v >= clipping)
                        break;
                }
            }

            var first = last - visible + 1;
            if (this.options.wrap != 'circular' && first < 1)
                first = 1;

            if (this.inTail && back) {
                pos += this.tail;
                this.inTail = false;
            }

            this.tail = null;
            if (this.options.wrap != 'circular' && last == this.options.size && (last - visible + 1) >= 1) {
                var m = $jc.margin(this.get(last), !this.options.vertical ? 'marginRight' : 'marginBottom');
                if ((v - m) > clipping)
                    this.tail = v - clipping - m;
            }

            // Adjust position
            while (i-- > first)
                pos += this.dimension(this.get(i));

            // Save visible item range
            this.prevFirst = this.first;
            this.prevLast  = this.last;
            this.first     = first;
            this.last      = last;

            return pos;
        },

        /**
         * Animates the carousel to a certain position.
         *
         * @name animate
         * @type undefined
         * @param mixed p Position to scroll to.
         * @param Bool a Flag indicating whether to perform animation.
         * @cat Plugins/jCarousel
         */
        animate: function(p, a) {
            if (this.locked || this.animating)
                return;

            this.animating = true;

            var self = this;
            var scrolled = function() {
                self.animating = false;

                if (p == 0)
                    self.list.css(self.lt,  0);

                if (self.options.wrap == 'both' || self.options.wrap == 'last' || self.options.size == null || self.last < self.options.size)
                    self.startAuto();

                self.buttons();
                self.notify('onAfterAnimation');
            };

            this.notify('onBeforeAnimation');

            // Animate
            if (!this.options.animation || a == false) {
                this.list.css(this.lt, p + 'px');
                scrolled();
            } else {
                var o = !this.options.vertical ? {'left': p} : {'top': p};
                this.list.animate(o, this.options.animation, this.options.easing, scrolled);
            }
        },

        /**
         * Starts autoscrolling.
         *
         * @name auto
         * @type undefined
         * @param Number s Seconds to periodically autoscroll the content.
         * @cat Plugins/jCarousel
         */
        startAuto: function(s) {
            if (s != undefined)
                this.options.auto = s;

            if (this.options.auto == 0)
                return this.stopAuto();

            if (this.timer != null)
                return;

            var self = this;
            this.timer = setTimeout(function() { self.next(); }, this.options.auto * 1000);
        },

        /**
         * Stops autoscrolling.
         *
         * @name stopAuto
         * @type undefined
         * @cat Plugins/jCarousel
         */
        stopAuto: function() {
            if (this.timer == null)
                return;

            clearTimeout(this.timer);
            this.timer = null;
        },

        /**
         * Sets the states of the prev/next buttons.
         *
         * @name buttons
         * @type undefined
         * @cat Plugins/jCarousel
         */
        buttons: function(n, p) {
            if (n == undefined || n == null) {
                var n = !this.locked && this.options.size !== 0 && ((this.options.wrap && this.options.wrap != 'first') || this.options.size == null || this.last < this.options.size);
                if (!this.locked && (!this.options.wrap || this.options.wrap == 'first') && this.options.size != null && this.last >= this.options.size)
                    n = this.tail != null && !this.inTail;
            }

            if (p == undefined || p == null) {
                var p = !this.locked && this.options.size !== 0 && ((this.options.wrap && this.options.wrap != 'last') || this.first > 1);
                if (!this.locked && (!this.options.wrap || this.options.wrap == 'last') && this.options.size != null && this.first == 1)
                    p = this.tail != null && this.inTail;
            }

            var self = this;

            this.buttonNext[n ? 'bind' : 'unbind'](this.options.buttonNextEvent, this.funcNext)[n ? 'removeClass' : 'addClass'](this.className('jcarousel-next-disabled')).attr('disabled', n ? false : true);
            this.buttonPrev[p ? 'bind' : 'unbind'](this.options.buttonPrevEvent, this.funcPrev)[p ? 'removeClass' : 'addClass'](this.className('jcarousel-prev-disabled')).attr('disabled', p ? false : true);

            if (this.buttonNext.length > 0 && (this.buttonNext[0].jcarouselstate == undefined || this.buttonNext[0].jcarouselstate != n) && this.options.buttonNextCallback != null) {
                this.buttonNext.each(function() { self.options.buttonNextCallback(self, this, n); });
                this.buttonNext[0].jcarouselstate = n;
            }

            if (this.buttonPrev.length > 0 && (this.buttonPrev[0].jcarouselstate == undefined || this.buttonPrev[0].jcarouselstate != p) && this.options.buttonPrevCallback != null) {
                this.buttonPrev.each(function() { self.options.buttonPrevCallback(self, this, p); });
                this.buttonPrev[0].jcarouselstate = p;
            }
        },

        notify: function(evt) {
            var state = this.prevFirst == null ? 'init' : (this.prevFirst < this.first ? 'next' : 'prev');

            // Load items
            this.callback('itemLoadCallback', evt, state);

            if (this.prevFirst !== this.first) {
                this.callback('itemFirstInCallback', evt, state, this.first);
                this.callback('itemFirstOutCallback', evt, state, this.prevFirst);
            }

            if (this.prevLast !== this.last) {
                this.callback('itemLastInCallback', evt, state, this.last);
                this.callback('itemLastOutCallback', evt, state, this.prevLast);
            }

            this.callback('itemVisibleInCallback', evt, state, this.first, this.last, this.prevFirst, this.prevLast);
            this.callback('itemVisibleOutCallback', evt, state, this.prevFirst, this.prevLast, this.first, this.last);
        },

        callback: function(cb, evt, state, i1, i2, i3, i4) {
            if (this.options[cb] == undefined || (typeof this.options[cb] != 'object' && evt != 'onAfterAnimation'))
                return;

            var callback = typeof this.options[cb] == 'object' ? this.options[cb][evt] : this.options[cb];

            if (!$.isFunction(callback))
                return;

            var self = this;

            if (i1 === undefined)
                callback(self, state, evt);
            else if (i2 === undefined)
                this.get(i1).each(function() { callback(self, this, i1, state, evt); });
            else {
                for (var i = i1; i <= i2; i++)
                    if (i !== null && !(i >= i3 && i <= i4))
                        this.get(i).each(function() { callback(self, this, i, state, evt); });
            }
        },

        create: function(i) {
            return this.format('<li></li>', i);
        },

        format: function(e, i) {
            var $e = $(e).addClass(this.className('jcarousel-item')).addClass(this.className('jcarousel-item-' + i));
            $e.attr('jcarouselindex', i);
            return $e;
        },

        className: function(c) {
            return c + ' ' + c + (!this.options.vertical ? '-horizontal' : '-vertical');
        },

        dimension: function(e, d) {
            var el = e.jquery != undefined ? e[0] : e;

            var old = !this.options.vertical ?
                el.offsetWidth + $jc.margin(el, 'marginLeft') + $jc.margin(el, 'marginRight') :
                el.offsetHeight + $jc.margin(el, 'marginTop') + $jc.margin(el, 'marginBottom');

            if (d == undefined || old == d)
                return old;

            var w = !this.options.vertical ?
                d - $jc.margin(el, 'marginLeft') - $jc.margin(el, 'marginRight') :
                d - $jc.margin(el, 'marginTop') - $jc.margin(el, 'marginBottom');

            $(el).css(this.wh, w + 'px');

            return this.dimension(el);
        },

        clipping: function() {
            return !this.options.vertical ?
                this.clip[0].offsetWidth - $jc.intval(this.clip.css('borderLeftWidth')) - $jc.intval(this.clip.css('borderRightWidth')) :
                this.clip[0].offsetHeight - $jc.intval(this.clip.css('borderTopWidth')) - $jc.intval(this.clip.css('borderBottomWidth'));
        },

        index: function(i, s) {
            if (s == undefined)
                s = this.options.size;

            return Math.round((((i-1) / s) - Math.floor((i-1) / s)) * s) + 1;
        }
    });

    $jc.extend({
        /**
         * Gets/Sets the global default configuration properties.
         *
         * @name defaults
         * @descr Gets/Sets the global default configuration properties.
         * @type Hash
         * @param Hash d A set of key/value pairs to set as configuration properties.
         * @cat Plugins/jCarousel
         */
        defaults: function(d) {
            return $.extend(defaults, d || {});
        },

        margin: function(e, p) {
            if (!e)
                return 0;

            var el = e.jquery != undefined ? e[0] : e;

            if (p == 'marginRight' && $.browser.safari) {
                var old = {'display': 'block', 'float': 'none', 'width': 'auto'}, oWidth, oWidth2;

                $.swap(el, old, function() { oWidth = el.offsetWidth; });

                old['marginRight'] = 0;
                $.swap(el, old, function() { oWidth2 = el.offsetWidth; });

                return oWidth2 - oWidth;
            }

            return $jc.intval($.css(el, p));
        },

        intval: function(v) {
            v = parseInt(v);
            return isNaN(v) ? 0 : v;
        }
    });

})(jQuery);


/**********************************************************************************/
/*                      /scripts/lib/jquery/jquery.cookie.js                      */
/**********************************************************************************/

/**
 * Cookie plugin
 *
 * Copyright (c) 2006 Klaus Hartl (stilbuero.de)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 */

/**
 * Create a cookie with the given name and value and other optional parameters.
 *
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Set the value of a cookie.
 * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
 * @desc Create a cookie with all available options.
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Create a session cookie.
 * @example $.cookie('the_cookie', null);
 * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
 *       used when the cookie was set.
 *
 * @param String name The name of the cookie.
 * @param String value The value of the cookie.
 * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
 * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
 *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
 *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
 *                             when the the browser exits.
 * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
 * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
 * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
 *                        require a secure protocol (like HTTPS).
 * @type undefined
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */

/**
 * Get the value of a cookie with the given name.
 *
 * @example $.cookie('the_cookie');
 * @desc Get the value of a cookie.
 *
 * @param String name The name of the cookie.
 * @return The value of the cookie.
 * @type String
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */
jQuery.cookie = function(name, value, options) {
    if (typeof value != 'undefined') { // name and value given, set cookie
        options = options || {};
        if (value === null) {
            value = '';
            options.expires = -1;
        }
        var expires = '';
        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
            var date;
            if (typeof options.expires == 'number') {
                date = new Date();
                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
            } else {
                date = options.expires;
            }
            expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
        }
        // CAUTION: Needed to parenthesize options.path and options.domain
        // in the following expressions, otherwise they evaluate to undefined
        // in the packed version for some reason...
        var path = options.path ? '; path=' + (options.path) : '';
        var domain = options.domain ? '; domain=' + (options.domain) : '';
        var secure = options.secure ? '; secure' : '';
        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
    } else { // only name given, get cookie
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
};

/**********************************************************************************/
/*                       /scripts/lib/jquery/jquery.json.js                       */
/**********************************************************************************/

/*
 * jQuery JSON Plugin
 * version: 1.0 (2008-04-17)
 *
 * This document is licensed as free software under the terms of the
 * MIT License: http://www.opensource.org/licenses/mit-license.php
 *
 * Brantley Harris technically wrote this plugin, but it is based somewhat
 * on the JSON.org website's http://www.json.org/json2.js, which proclaims:
 * "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
 * I uphold.  I really just cleaned it up.
 *
 * It is also based heavily on MochiKit's serializeJSON, which is
 * copywrited 2005 by Bob Ippolito.
 */

(function($) {
    function toIntegersAtLease(n)
    // Format integers to have at least two digits.
    {
        return n < 10 ? '0' + n : n;
    }

    Date.prototype.toJSON = function(date)
    // Yes, it polutes the Date namespace, but we'll allow it here, as
    // it's damned usefull.
    {
        return date.getUTCFullYear()   + '-' +
             toIntegersAtLease(date.getUTCMonth() + 1) + '-' +
             toIntegersAtLease(date.getUTCDate());
    };

    var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g;
    var meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        }

    $.quoteString = function(string)
    // Places quotes around a string, inteligently.
    // If the string contains no control characters, no quote characters, and no
    // backslash characters, then we can safely slap some quotes around it.
    // Otherwise we must also replace the offending characters with safe escape
    // sequences.
    {
        if (escapeable.test(string))
        {
            return '"' + string.replace(escapeable, function (a)
            {
                var c = meta[a];
                if (typeof c === 'string') {
                    return c;
                }
                c = a.charCodeAt();
                return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
            }) + '"'
        }
        return '"' + string + '"';
    }

    $.toJSON = function(o)
    {
        var type = typeof(o);

        if (type == "undefined")
            return "undefined";
        else if (type == "number" || type == "boolean")
            return o + "";
        else if (o === null)
            return "null";

        // Is it a string?
        if (type == "string")
        {
            return $.quoteString(o);
        }

        // Does it have a .toJSON function?
        if (type == "object" && typeof o.toJSON == "function")
            return o.toJSON();

        // Is it an array?
        if (type != "function" && typeof(o.length) == "number")
        {
            var ret = [];
            for (var i = 0; i < o.length; i++) {
                ret.push( $.toJSON(o[i]) );
            }
            return "[" + ret.join(", ") + "]";
        }

        // If it's a function, we have to warn somebody!
        if (type == "function") {
            throw new TypeError("Unable to convert object of type 'function' to json.");
        }

        // It's probably an object, then.
        ret = [];
        for (var k in o) {
            var name;
            var type = typeof(k);

            if (type == "number")
                name = '"' + k + '"';
            else if (type == "string")
                name = $.quoteString(k);
            else
                continue;  //skip non-string or number keys

            val = $.toJSON(o[k]);
            if (typeof(val) != "string") {
                // skip non-serializable values
                continue;
            }

            ret.push(name + ": " + val);
        }
        return "{" + ret.join(", ") + "}";
    }

    $.evalJSON = function(src)
    // Evals JSON that we know to be safe.
    {
        return eval("(" + src + ")");
    }

    $.secureEvalJSON = function(src)
    // Evals JSON in a way that is *more* secure.
    {
        var filtered = src;
        filtered = filtered.replace(/\\["\\\/bfnrtu]/g, '@');
        filtered = filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
        filtered = filtered.replace(/(?:^|:|,)(?:\s*\[)+/g, '');

        if (/^[\],:{}\s]*$/.test(filtered))
            return eval("(" + src + ")");
        else
            throw new SyntaxError("Error parsing JSON, source is not valid.");
    }
})(jQuery);

/// Tests ///
/*

// 'console' comes from FireBug
function testJSON(o, expected) {
    try {
        out = $.toJSON(o);
    } catch (e) {
        if (e.name === expected.name) return;
        return console.error(e, o); }
    if (out != expected) console.error("%s != %s", out, expected);
}

testJSON('hi', "\"hi\"");
testJSON({apple: 2}, "{\"apple\": 2}");
testJSON({apple: {apple: 2}}, "{\"apple\": {\"apple\": 2}}");
testJSON(2.5, "2.5");
testJSON(25, "25");
testJSON([2, 5], "[2, 5]");
testJSON(function() {}, TypeError);
//*/


/**********************************************************************************/
/*                       /scripts/lib/jquery/ui/ui.core.js                        */
/**********************************************************************************/

/*
 * jQuery UI 1.5.2
 *
 * Copyright (c) 2008 Paul Bakaus (ui.jquery.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI
 */
;(function($) {

$.ui = {
	plugin: {
		add: function(module, option, set) {
			var proto = $.ui[module].prototype;
			for(var i in set) {
				proto.plugins[i] = proto.plugins[i] || [];
				proto.plugins[i].push([option, set[i]]);
			}
		},
		call: function(instance, name, args) {
			var set = instance.plugins[name];
			if(!set) { return; }

			for (var i = 0; i < set.length; i++) {
				if (instance.options[set[i][0]]) {
					set[i][1].apply(instance.element, args);
				}
			}
		}
	},
	cssCache: {},
	css: function(name) {
		if ($.ui.cssCache[name]) { return $.ui.cssCache[name]; }
		var tmp = $('<div class="ui-gen">').addClass(name).css({position:'absolute', top:'-5000px', left:'-5000px', display:'block'}).appendTo('body');

		//if (!$.browser.safari)
			//tmp.appendTo('body');

		//Opera and Safari set width and height to 0px instead of auto
		//Safari returns rgba(0,0,0,0) when bgcolor is not set
		$.ui.cssCache[name] = !!(
			(!(/auto|default/).test(tmp.css('cursor')) || (/^[1-9]/).test(tmp.css('height')) || (/^[1-9]/).test(tmp.css('width')) ||
			!(/none/).test(tmp.css('backgroundImage')) || !(/transparent|rgba\(0, 0, 0, 0\)/).test(tmp.css('backgroundColor')))
		);
		try { $('body').get(0).removeChild(tmp.get(0));	} catch(e){}
		return $.ui.cssCache[name];
	},
	disableSelection: function(el) {
		$(el).attr('unselectable', 'on').css('MozUserSelect', 'none');
	},
	enableSelection: function(el) {
		$(el).attr('unselectable', 'off').css('MozUserSelect', '');
	},
	hasScroll: function(e, a) {
		var scroll = /top/.test(a||"top") ? 'scrollTop' : 'scrollLeft', has = false;
		if (e[scroll] > 0) return true; e[scroll] = 1;
		has = e[scroll] > 0 ? true : false; e[scroll] = 0;
		return has;
	}
};


/** jQuery core modifications and additions **/

var _remove = $.fn.remove;
$.fn.remove = function() {
	$("*", this).add(this).triggerHandler("remove");
	return _remove.apply(this, arguments );
};

// $.widget is a factory to create jQuery plugins
// taking some boilerplate code out of the plugin code
// created by Scott GonzГЎlez and JГ¶rn Zaefferer
function getter(namespace, plugin, method) {
	var methods = $[namespace][plugin].getter || [];
	methods = (typeof methods == "string" ? methods.split(/,?\s+/) : methods);
	return ($.inArray(method, methods) != -1);
}

$.widget = function(name, prototype) {
	var namespace = name.split(".")[0];
	name = name.split(".")[1];

	// create plugin method
	$.fn[name] = function(options) {
		var isMethodCall = (typeof options == 'string'),
			args = Array.prototype.slice.call(arguments, 1);

		if (isMethodCall && getter(namespace, name, options)) {
			var instance = $.data(this[0], name);
			return (instance ? instance[options].apply(instance, args)
				: undefined);
		}

		return this.each(function() {
			var instance = $.data(this, name);
			if (isMethodCall && instance && $.isFunction(instance[options])) {
				instance[options].apply(instance, args);
			} else if (!isMethodCall) {
				$.data(this, name, new $[namespace][name](this, options));
			}
		});
	};

	// create widget constructor
	$[namespace][name] = function(element, options) {
		var self = this;

		this.widgetName = name;
		this.widgetBaseClass = namespace + '-' + name;

		this.options = $.extend({}, $.widget.defaults, $[namespace][name].defaults, options);
		this.element = $(element)
			.bind('setData.' + name, function(e, key, value) {
				return self.setData(key, value);
			})
			.bind('getData.' + name, function(e, key) {
				return self.getData(key);
			})
			.bind('remove', function() {
				return self.destroy();
			});
		this.init();
	};

	// add widget prototype
	$[namespace][name].prototype = $.extend({}, $.widget.prototype, prototype);
};

$.widget.prototype = {
	init: function() {},
	destroy: function() {
		this.element.removeData(this.widgetName);
	},

	getData: function(key) {
		return this.options[key];
	},
	setData: function(key, value) {
		this.options[key] = value;

		if (key == 'disabled') {
			this.element[value ? 'addClass' : 'removeClass'](
				this.widgetBaseClass + '-disabled');
		}
	},

	enable: function() {
		this.setData('disabled', false);
	},
	disable: function() {
		this.setData('disabled', true);
	}
};

$.widget.defaults = {
	disabled: false
};


/** Mouse Interaction Plugin **/

$.ui.mouse = {
	mouseInit: function() {
		var self = this;

		this.element.bind('mousedown.'+this.widgetName, function(e) {
			return self.mouseDown(e);
		});

		// Prevent text selection in IE
		if ($.browser.msie) {
			this._mouseUnselectable = this.element.attr('unselectable');
			this.element.attr('unselectable', 'on');
		}

		this.started = false;
	},

	// TODO: make sure destroying one instance of mouse doesn't mess with
	// other instances of mouse
	mouseDestroy: function() {
		this.element.unbind('.'+this.widgetName);

		// Restore text selection in IE
		($.browser.msie
			&& this.element.attr('unselectable', this._mouseUnselectable));
	},

	mouseDown: function(e) {
		// we may have missed mouseup (out of window)
		(this._mouseStarted && this.mouseUp(e));

		this._mouseDownEvent = e;

		var self = this,
			btnIsLeft = (e.which == 1),
			elIsCancel = (typeof this.options.cancel == "string" ? $(e.target).parents().add(e.target).filter(this.options.cancel).length : false);
		if (!btnIsLeft || elIsCancel || !this.mouseCapture(e)) {
			return true;
		}

		this._mouseDelayMet = !this.options.delay;
		if (!this._mouseDelayMet) {
			this._mouseDelayTimer = setTimeout(function() {
				self._mouseDelayMet = true;
			}, this.options.delay);
		}

		if (this.mouseDistanceMet(e) && this.mouseDelayMet(e)) {
			this._mouseStarted = (this.mouseStart(e) !== false);
			if (!this._mouseStarted) {
				e.preventDefault();
				return true;
			}
		}

		// these delegates are required to keep context
		this._mouseMoveDelegate = function(e) {
			return self.mouseMove(e);
		};
		this._mouseUpDelegate = function(e) {
			return self.mouseUp(e);
		};
		$(document)
			.bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
			.bind('mouseup.'+this.widgetName, this._mouseUpDelegate);

		return false;
	},

	mouseMove: function(e) {
		// IE mouseup check - mouseup happened when mouse was out of window
		if ($.browser.msie && !e.button) {
			return this.mouseUp(e);
		}

		if (this._mouseStarted) {
			this.mouseDrag(e);
			return false;
		}

		if (this.mouseDistanceMet(e) && this.mouseDelayMet(e)) {
			this._mouseStarted =
				(this.mouseStart(this._mouseDownEvent, e) !== false);
			(this._mouseStarted ? this.mouseDrag(e) : this.mouseUp(e));
		}

		return !this._mouseStarted;
	},

	mouseUp: function(e) {
		$(document)
			.unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
			.unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);

		if (this._mouseStarted) {
			this._mouseStarted = false;
			this.mouseStop(e);
		}

		return false;
	},

	mouseDistanceMet: function(e) {
		return (Math.max(
				Math.abs(this._mouseDownEvent.pageX - e.pageX),
				Math.abs(this._mouseDownEvent.pageY - e.pageY)
			) >= this.options.distance
		);
	},

	mouseDelayMet: function(e) {
		return this._mouseDelayMet;
	},

	// These are placeholder methods, to be overriden by extending plugin
	mouseStart: function(e) {},
	mouseDrag: function(e) {},
	mouseStop: function(e) {},
	mouseCapture: function(e) { return true; }
};

$.ui.mouse.defaults = {
	cancel: null,
	distance: 1,
	delay: 0
};

})(jQuery);


/**********************************************************************************/
/*                       /scripts/lib/jquery/ui/ui.tabs.js                        */
/**********************************************************************************/

/*
 * jQuery UI Tabs
 *
 * Copyright (c) 2007, 2008 Klaus Hartl (stilbuero.de)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI/Tabs
 *
 * Depends:
 *	ui.core.js
 */
(function($) {

$.widget("ui.tabs", {
	init: function() {
		this.options.event += '.tabs'; // namespace event

		// create tabs
		this.tabify(true);
	},
	setData: function(key, value) {
		if ((/^selected/).test(key))
			this.select(value);
		else {
			this.options[key] = value;
			this.tabify();
		}
	},
	length: function() {
		return this.$tabs.length;
	},
	tabId: function(a) {
		return a.title && a.title.replace(/\s/g, '_').replace(/[^A-Za-z0-9\-_:\.]/g, '')
			|| this.options.idPrefix + $.data(a);
	},
	ui: function(tab, panel) {
		return {
			options: this.options,
			tab: tab,
			panel: panel,
			index: this.$tabs.index(tab)
		};
	},
	tabify: function(init) {

		this.$lis = $('li:has(a[href])', this.element);
		this.$tabs = this.$lis.map(function() { return $('a', this)[0]; });
		this.$panels = $([]);

		var self = this, o = this.options;

		this.$tabs.each(function(i, a) {
			// inline tab
			if (a.hash && a.hash.replace('#', '')) // Safari 2 reports '#' for an empty hash
				self.$panels = self.$panels.add(a.hash);
			// remote tab
			else if ($(a).attr('href') != '#') { // prevent loading the page itself if href is just "#"
				$.data(a, 'href.tabs', a.href); // required for restore on destroy
				$.data(a, 'load.tabs', a.href); // mutable
				var id = self.tabId(a);
				a.href = '#' + id;
				var $panel = $('#' + id);
				if (!$panel.length) {
					$panel = $(o.panelTemplate).attr('id', id).addClass(o.panelClass)
						.insertAfter( self.$panels[i - 1] || self.element );
					$panel.data('destroy.tabs', true);
				}
				self.$panels = self.$panels.add( $panel );
			}
			// invalid tab href
			else
				o.disabled.push(i + 1);
		});

		if (init) {

			// attach necessary classes for styling if not present
			this.element.addClass(o.navClass);
			this.$panels.each(function() {
				var $this = $(this);
				$this.addClass(o.panelClass);
			});

			// Selected tab
			// use "selected" option or try to retrieve:
			// 1. from fragment identifier in url
			// 2. from cookie
			// 3. from selected class attribute on <li>
			if (o.selected === undefined) {
				if (location.hash) {
					this.$tabs.each(function(i, a) {
						if (a.hash == location.hash) {
							o.selected = i;
							// prevent page scroll to fragment
							if ($.browser.msie || $.browser.opera) { // && !o.remote
								var $toShow = $(location.hash), toShowId = $toShow.attr('id');
								$toShow.attr('id', '');
								setTimeout(function() {
									$toShow.attr('id', toShowId); // restore id
								}, 500);
							}
							scrollTo(0, 0);
							return false; // break
						}
					});
				}
				else if (o.cookie) {
					var index = parseInt($.preferences('ui-tabs' + $.data(self.element)),10);
					if (index && self.$tabs[index])
						o.selected = index;
				}
				else if (self.$lis.filter('.' + o.selectedClass).length)
					o.selected = self.$lis.index( self.$lis.filter('.' + o.selectedClass)[0] );
			}
			o.selected = o.selected === null || o.selected !== undefined ? o.selected : 0; // first tab selected by default

			// Take disabling tabs via class attribute from HTML
			// into account and update option properly.
			// A selected tab cannot become disabled.
			o.disabled = $.unique(o.disabled.concat(
				$.map(this.$lis.filter('.' + o.disabledClass),
					function(n, i) { return self.$lis.index(n); } )
			)).sort();
			if ($.inArray(o.selected, o.disabled) != -1)
				o.disabled.splice($.inArray(o.selected, o.disabled), 1);

			// highlight selected tab
			this.$panels.addClass(o.hideClass);
			this.$lis.removeClass(o.selectedClass);
			if (o.selected !== null) {
				this.$panels.eq(o.selected).show().removeClass(o.hideClass); // use show and remove class to show in any case no matter how it has been hidden before
				this.$lis.eq(o.selected).addClass(o.selectedClass);

				// seems to be expected behavior that the show callback is fired
				var onShow = function() {
					$(self.element).triggerHandler('tabsshow',
						[self.fakeEvent('tabsshow'), self.ui(self.$tabs[o.selected], self.$panels[o.selected])], o.show);
				};

				// load if remote tab
				if ($.data(this.$tabs[o.selected], 'load.tabs'))
					this.load(o.selected, onShow);
				// just trigger show event
				else
					onShow();

			}

			// clean up to avoid memory leaks in certain versions of IE 6
			$(window).bind('unload', function() {
				self.$tabs.unbind('.tabs');
				self.$lis = self.$tabs = self.$panels = null;
			});

		}

		// disable tabs
		for (var i = 0, li; li = this.$lis[i]; i++)
			$(li)[$.inArray(i, o.disabled) != -1 && !$(li).hasClass(o.selectedClass) ? 'addClass' : 'removeClass'](o.disabledClass);

		// reset cache if switching from cached to not cached
		if (o.cache === false)
			this.$tabs.removeData('cache.tabs');

		// set up animations
		var hideFx, showFx, baseFx = { 'min-width': 0, duration: 1 }, baseDuration = 'normal';
		if (o.fx && o.fx.constructor == Array)
			hideFx = o.fx[0] || baseFx, showFx = o.fx[1] || baseFx;
		else
			hideFx = showFx = o.fx || baseFx;

		// reset some styles to maintain print style sheets etc.
		var resetCSS = { display: '', overflow: '', height: '' };
		if (!$.browser.msie) // not in IE to prevent ClearType font issue
			resetCSS.opacity = '';

		// Hide a tab, animation prevents browser scrolling to fragment,
		// $show is optional.
		function hideTab(clicked, $hide, $show) {
			$hide.animate(hideFx, hideFx.duration || baseDuration, function() { //
				$hide.addClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
				if ($.browser.msie && hideFx.opacity)
					$hide[0].style.filter = '';
				if ($show)
					showTab(clicked, $show, $hide);
			});
		}

		// Show a tab, animation prevents browser scrolling to fragment,
		// $hide is optional.
		function showTab(clicked, $show, $hide) {
			if (showFx === baseFx)
				$show.css('display', 'block'); // prevent occasionally occuring flicker in Firefox cause by gap between showing and hiding the tab panels
			$show.animate(showFx, showFx.duration || baseDuration, function() {
				$show.removeClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
				if ($.browser.msie && showFx.opacity)
					$show[0].style.filter = '';

				// callback
				$(self.element).triggerHandler('tabsshow',
					[self.fakeEvent('tabsshow'), self.ui(clicked, $show[0])], o.show);

			});
		}

		// switch a tab
		function switchTab(clicked, $li, $hide, $show) {
			/*if (o.bookmarkable && trueClick) { // add to history only if true click occured, not a triggered click
				$.ajaxHistory.update(clicked.hash);
			}*/
			$li.addClass(o.selectedClass)
				.siblings().removeClass(o.selectedClass);
			hideTab(clicked, $hide, $show);
		}

		// attach tab event handler, unbind to avoid duplicates from former tabifying...
		this.$tabs.unbind('.tabs').bind(o.event, function() {

			//var trueClick = e.clientX; // add to history only if true click occured, not a triggered click
			var $li = $(this).parents('li:eq(0)'),
				$hide = self.$panels.filter(':visible'),
				$show = $(this.hash);

			// If tab is already selected and not unselectable or tab disabled or
			// or is already loading or click callback returns false stop here.
			// Check if click handler returns false last so that it is not executed
			// for a disabled or loading tab!
			if (($li.hasClass(o.selectedClass) && !o.unselect)
				|| $li.hasClass(o.disabledClass)
				|| $(this).hasClass(o.loadingClass)
				|| $(self.element).triggerHandler('tabsselect', [self.fakeEvent('tabsselect'), self.ui(this, $show[0])], o.select) === false
				) {
				this.blur();
				return false;
			}

			self.options.selected = self.$tabs.index(this);

			// if tab may be closed
			if (o.unselect) {
				if ($li.hasClass(o.selectedClass)) {
					self.options.selected = null;
					$li.removeClass(o.selectedClass);
					self.$panels.stop();
					hideTab(this, $hide);
					this.blur();
					return false;
				} else if (!$hide.length) {
					self.$panels.stop();
					var a = this;
					self.load(self.$tabs.index(this), function() {
						$li.addClass(o.selectedClass).addClass(o.unselectClass);
						showTab(a, $show);
					});
					this.blur();
					return false;
				}
			}

			if (o.cookie)
				$.preferences('ui-tabs' + $.data(self.element), self.options.selected, o.cookie);

			// stop possibly running animations
			self.$panels.stop();

			// show new tab
			if ($show.length) {

				// prevent scrollbar scrolling to 0 and than back in IE7, happens only if bookmarking/history is enabled
				/*if ($.browser.msie && o.bookmarkable) {
					var showId = this.hash.replace('#', '');
					$show.attr('id', '');
					setTimeout(function() {
						$show.attr('id', showId); // restore id
					}, 0);
				}*/

				var a = this;
				self.load(self.$tabs.index(this), $hide.length ?
					function() {
						switchTab(a, $li, $hide, $show);
					} :
					function() {
						$li.addClass(o.selectedClass);
						showTab(a, $show);
					}
				);

				// Set scrollbar to saved position - need to use timeout with 0 to prevent browser scroll to target of hash
				/*var scrollX = window.pageXOffset || document.documentElement && document.documentElement.scrollLeft || document.body.scrollLeft || 0;
				var scrollY = window.pageYOffset || document.documentElement && document.documentElement.scrollTop || document.body.scrollTop || 0;
				setTimeout(function() {
					scrollTo(scrollX, scrollY);
				}, 0);*/

			} else
				throw 'jQuery UI Tabs: Mismatching fragment identifier.';

			// Prevent IE from keeping other link focussed when using the back button
			// and remove dotted border from clicked link. This is controlled in modern
			// browsers via CSS, also blur removes focus from address bar in Firefox
			// which can become a usability and annoying problem with tabsRotate.
			if ($.browser.msie)
				this.blur();

			//return o.bookmarkable && !!trueClick; // convert trueClick == undefined to Boolean required in IE
			return false;

		});

		// disable click if event is configured to something else
		if (!(/^click/).test(o.event))
			this.$tabs.bind('click.tabs', function() { return false; });

	},
	add: function(url, label, index) {
		if (index == undefined)
			index = this.$tabs.length; // append by default

		var o = this.options;
		var $li = $(o.tabTemplate.replace(/#\{href\}/g, url).replace(/#\{label\}/g, label));
		$li.data('destroy.tabs', true);

		var id = url.indexOf('#') == 0 ? url.replace('#', '') : this.tabId( $('a:first-child', $li)[0] );

		// try to find an existing element before creating a new one
		var $panel = $('#' + id);
		if (!$panel.length) {
			$panel = $(o.panelTemplate).attr('id', id)
				.addClass(o.hideClass)
				.data('destroy.tabs', true);
		}
		$panel.addClass(o.panelClass);
		if (index >= this.$lis.length) {
			$li.appendTo(this.element);
			$panel.appendTo(this.element[0].parentNode);
		} else {
			$li.insertBefore(this.$lis[index]);
			$panel.insertBefore(this.$panels[index]);
		}

		o.disabled = $.map(o.disabled,
			function(n, i) { return n >= index ? ++n : n });

		this.tabify();

		if (this.$tabs.length == 1) {
			$li.addClass(o.selectedClass);
			$panel.removeClass(o.hideClass);
			var href = $.data(this.$tabs[0], 'load.tabs');
			if (href)
				this.load(index, href);
		}

		// callback
		this.element.triggerHandler('tabsadd',
			[this.fakeEvent('tabsadd'), this.ui(this.$tabs[index], this.$panels[index])], o.add
		);
	},
	remove: function(index) {
		var o = this.options, $li = this.$lis.eq(index).remove(),
			$panel = this.$panels.eq(index).remove();

		// If selected tab was removed focus tab to the right or
		// in case the last tab was removed the tab to the left.
		if ($li.hasClass(o.selectedClass) && this.$tabs.length > 1)
			this.select(index + (index + 1 < this.$tabs.length ? 1 : -1));

		o.disabled = $.map($.grep(o.disabled, function(n, i) { return n != index; }),
			function(n, i) { return n >= index ? --n : n });

		this.tabify();

		// callback
		this.element.triggerHandler('tabsremove',
			[this.fakeEvent('tabsremove'), this.ui($li.find('a')[0], $panel[0])], o.remove
		);
	},
	enable: function(index) {
		var o = this.options;
		if ($.inArray(index, o.disabled) == -1)
			return;

		var $li = this.$lis.eq(index).removeClass(o.disabledClass);
		if ($.browser.safari) { // fix disappearing tab (that used opacity indicating disabling) after enabling in Safari 2...
			$li.css('display', 'inline-block');
			setTimeout(function() {
				$li.css('display', 'block');
			}, 0);
		}

		o.disabled = $.grep(o.disabled, function(n, i) { return n != index; });

		// callback
		this.element.triggerHandler('tabsenable',
			[this.fakeEvent('tabsenable'), this.ui(this.$tabs[index], this.$panels[index])], o.enable
		);

	},
	disable: function(index) {
		var self = this, o = this.options;
		if (index != o.selected) { // cannot disable already selected tab
			this.$lis.eq(index).addClass(o.disabledClass);

			o.disabled.push(index);
			o.disabled.sort();

			// callback
			this.element.triggerHandler('tabsdisable',
				[this.fakeEvent('tabsdisable'), this.ui(this.$tabs[index], this.$panels[index])], o.disable
			);
		}
	},
	select: function(index) {
		if (typeof index == 'string')
			index = this.$tabs.index( this.$tabs.filter('[href$=' + index + ']')[0] );
		this.$tabs.eq(index).trigger(this.options.event);
	},
	load: function(index, callback) { // callback is for internal usage only

		var self = this, o = this.options, $a = this.$tabs.eq(index), a = $a[0],
				bypassCache = callback == undefined || callback === false, url = $a.data('load.tabs');

		callback = callback || function() {};

		// no remote or from cache - just finish with callback
		if (!url || !bypassCache && $.data(a, 'cache.tabs')) {
			callback();
			return;
		}

		// load remote from here on

		var inner = function(parent) {
			var $parent = $(parent), $inner = $parent.find('*:last');
			return $inner.length && $inner.is(':not(img)') && $inner || $parent;
		};
		var cleanup = function() {
			self.$tabs.filter('.' + o.loadingClass).removeClass(o.loadingClass)
						.each(function() {
							if (o.spinner)
								inner(this).parent().html(inner(this).data('label.tabs'));
						});
			self.xhr = null;
		};

		if (o.spinner) {
			var label = inner(a).html();
			inner(a).wrapInner('<em></em>')
				.find('em').data('label.tabs', label).html(o.spinner);
		}

		var ajaxOptions = $.extend({}, o.ajaxOptions, {
			url: url,
			success: function(r, s) {
				$(a.hash).html(r);
				cleanup();

				if (o.cache)
					$.data(a, 'cache.tabs', true); // if loaded once do not load them again

				// callbacks
				$(self.element).triggerHandler('tabsload',
					[self.fakeEvent('tabsload'), self.ui(self.$tabs[index], self.$panels[index])], o.load
				);
				o.ajaxOptions.success && o.ajaxOptions.success(r, s);

				// This callback is required because the switch has to take
				// place after loading has completed. Call last in order to
				// fire load before show callback...
				callback();
			}
		});
		if (this.xhr) {
			// terminate pending requests from other tabs and restore tab label
			this.xhr.abort();
			cleanup();
		}
		$a.addClass(o.loadingClass);
		setTimeout(function() { // timeout is again required in IE, "wait" for id being restored
			self.xhr = $.ajax(ajaxOptions);
		}, 0);

	},
	url: function(index, url) {
		this.$tabs.eq(index).removeData('cache.tabs').data('load.tabs', url);
	},
	destroy: function() {
		var o = this.options;
		this.element.unbind('.tabs')
			.removeClass(o.navClass).removeData('tabs');
		this.$tabs.each(function() {
			var href = $.data(this, 'href.tabs');
			if (href)
				this.href = href;
			var $this = $(this).unbind('.tabs');
			$.each(['href', 'load', 'cache'], function(i, prefix) {
				$this.removeData(prefix + '.tabs');
			});
		});
		this.$lis.add(this.$panels).each(function() {
			if ($.data(this, 'destroy.tabs'))
				$(this).remove();
			else
				$(this).removeClass([o.selectedClass, o.unselectClass,
					o.disabledClass, o.panelClass, o.hideClass].join(' '));
		});
	},
	fakeEvent: function(type) {
		return $.event.fix({
			type: type,
			target: this.element[0]
		});
	}
});

$.ui.tabs.defaults = {
	// basic setup
	unselect: false,
	event: 'click',
	disabled: [],
	cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
	// TODO history: false,

	// Ajax
	spinner: 'Loading&#8230;',
	cache: false,
	idPrefix: 'ui-tabs-',
	ajaxOptions: {},

	// animations
	fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 }

	// templates
	tabTemplate: '<li><a href="#{href}"><span>#{label}</span></a></li>',
	panelTemplate: '<div></div>',

	// CSS classes
	navClass: 'ui-tabs-nav',
	selectedClass: 'ui-tabs-selected',
	unselectClass: 'ui-tabs-unselect',
	disabledClass: 'ui-tabs-disabled',
	panelClass: 'ui-tabs-panel',
	hideClass: 'ui-tabs-hide',
	loadingClass: 'ui-tabs-loading'
};

$.ui.tabs.getter = "length";

/*
 * Tabs Extensions
 */

/*
 * Rotate
 */
$.extend($.ui.tabs.prototype, {
	rotation: null,
	rotate: function(ms, continuing) {

		continuing = continuing || false;

		var self = this, t = this.options.selected;

		function start() {
			self.rotation = setInterval(function() {
				t = ++t < self.$tabs.length ? t : 0;
				self.select(t);
			}, ms);
		}

		function stop(e) {
			if (!e || e.clientX) { // only in case of a true click
				clearInterval(self.rotation);
			}
		}

		// start interval
		if (ms) {
			start();
			if (!continuing)
				this.$tabs.bind(this.options.event, stop);
			else
				this.$tabs.bind(this.options.event, function() {
					stop();
					t = self.options.selected;
					start();
				});
		}
		// stop interval
		else {
			stop();
			this.$tabs.unbind(this.options.event, stop);
		}
	}
});

})(jQuery);


/**********************************************************************************/
/*                  /scripts/lib/jquery/ui/ui.datetimepicker.js                   */
/**********************************************************************************/

/* jQuery UI Date Picker v3.4.3 (previously jQuery Calendar)
   Written by Marc Grabanski (m@marcgrabanski.com) and Keith Wood (kbwood@virginbroadband.com.au).

   Copyright (c) 2007 Marc Grabanski (http://marcgrabanski.com/code/ui-datetimepicker)
   Dual licensed under the MIT (MIT-LICENSE.txt)
   and GPL (GPL-LICENSE.txt) licenses.
   Date: 09-03-2007  */
/*
 * Time functionality added by Stanislav Dobry (stanislav.dobry@datesoft.cz)
 * Date: 2008-06-04
 */

(function($) { // hide the namespace

/* Date picker manager.
   Use the singleton instance of this class, $.datetimepicker, to interact with the date picker.
   Settings for (groups of) date pickers are maintained in an instance object
   (DatepickerInstance), allowing multiple different settings on the same page. */

function DateTimepicker() {
	this.debug = false; // Change this to true to start debugging
	this._nextId = 0; // Next ID for a date picker instance
	this._inst = []; // List of instances indexed by ID
	this._curInst = null; // The current instance in use
	this._disabledInputs = []; // List of date picker inputs that have been disabled
	this._datetimepickerShowing = false; // True if the popup picker is showing , false if not
	this._inDialog = false; // True if showing within a "dialog", false if not
	this.regional = []; // Available regional settings, indexed by language code
	this.regional[''] = { // Default regional settings
		clearText: 'Очистить', // Display text for clear link
		clearStatus: 'Erase the current date', // Status text for clear link
		closeText: 'Закрыть', // Display text for close link
		closeStatus: 'Close without change', // Status text for close link
		prevText: '&laquo;Пред.', // Display text for previous month link
		prevStatus: 'Show the previous month', // Status text for previous month link
		nextText: 'След.&raquo;', // Display text for next month link
		nextStatus: 'Show the next month', // Status text for next month link
		currentText: 'Сегодня', // Display text for current month link
		currentStatus: 'Show the current month', // Status text for current month link
		monthNames: ['Январь','Февраль','Март','Апрель','Май','Июнь',
			'Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'], // Names of months for drop-down and formatting
		monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
		monthStatus: 'Show a different month', // Status text for selecting a month
		yearStatus: 'Show a different year', // Status text for selecting a year
		weekHeader: 'Wk', // Header for the week of the year column
		weekStatus: 'Week of the year', // Status text for the week of the year column
		dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
		dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
		dayNamesMin: ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'], // Column headings for days starting at Sunday
		dayStatus: 'Set DD as first week day', // Status text for the day of the week selection
		dateStatus: 'Select DD, M d', // Status text for the date selection
		dateFormat: 'mm/dd/yy', // See format options on parseDate
		timeFormat: 'hh:ii:00',
		firstDay: 1, // The first day of the week, Sun = 0, Mon = 1, ...
		initStatus: 'Select a date', // Initial Status text on opening
		isRTL: false // True if right-to-left language, false if left-to-right
	};
	this._defaults = { // Global defaults for all the date picker instances
		showOn: 'focus', // 'focus' for popup on focus,
			// 'button' for trigger button, or 'both' for either
		showAnim: 'show', // Name of jQuery animation for popup
		defaultDate: null, // Used when field is blank: actual date,
			// +/-number for offset from today, null for today
		appendText: '', // Display text following the input box, e.g. showing the format
		buttonText: '...', // Text for trigger button
		buttonImage: '', // URL for trigger button image
		buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
		closeAtTop: true, // True to have the clear/close at the top,
			// false to have them at the bottom
		mandatory: false, // True to hide the Clear link, false to include it
		hideIfNoPrevNext: false, // True to hide next/previous month links
			// if not applicable, false to just disable them
		changeMonth: true, // True if month can be selected directly, false if only prev/next
		changeYear: true, // True if year can be selected directly, false if only prev/next
		yearRange: '-10:+10', // Range of years to display in drop-down,
			// either relative to current year (-nn:+nn) or absolute (nnnn:nnnn)
		changeFirstDay: true, // True to click on day name to change, false to remain as set
		showOtherMonths: false, // True to show dates in other months, false to leave blank
		showWeeks: false, // True to show week of the year, false to omit
		calculateWeek: this.iso8601Week, // How to calculate the week of the year,
			// takes a Date and returns the number of the week for it
		shortYearCutoff: '+10', // Short year values < this are in the current century,
			// > this are in the previous century,
			// string value starting with '+' for current year + value
		showStatus: false, // True to show status bar at bottom, false to not show it
		statusForDate: this.dateStatus, // Function to provide status text for a date -
			// takes date and instance as parameters, returns display text
		minDate: null, // The earliest selectable date, or null for no limit
		maxDate: null, // The latest selectable date, or null for no limit
		speed: 'normal', // Speed of display/closure
		beforeShowDay: null, // Function that takes a date and returns an array with
			// [0] = true if selectable, false if not,
			// [1] = custom CSS class name(s) or '', e.g. $.datetimepicker.noWeekends
		beforeShow: null, // Function that takes an input field and
			// returns a set of custom settings for the date picker
		onSelect: null, // Define a callback function when a date is selected
		onClose: null, // Define a callback function when the datetimepicker is closed
		numberOfMonths: 1, // Number of months to show at a time
		stepMonths: 1, // Number of months to step back/forward
		rangeSelect: false, // Allows for selecting a date range on one date picker
		rangeSeparator: ' - ' // Text between two dates in a range
	};
	$.extend(this._defaults, this.regional['']);
	this._datetimepickerDiv = $('<div id="datetimepicker_div" class="ui-datetimepicker" style="display: none"></div>');
}

$.extend(DateTimepicker.prototype, {
	/* Class name added to elements to indicate already configured with a date picker. */
	markerClassName: 'hasDatepicker',

	/* Debug logging (if enabled). */
	log: function () {
		if (this.debug)
			console.log.apply('', arguments);
	},

	/* Register a new date picker instance - with custom settings. */
	_register: function(inst) {
		var id = this._nextId++;
		this._inst[id] = inst;
		return id;
	},

	/* Retrieve a particular date picker instance based on its ID. */
	_getInst: function(id) {
		return this._inst[id] || id;
	},

	/* Override the default settings for all instances of the date picker.
	   @param  settings  object - the new settings to use as defaults (anonymous object)
	   @return the manager object */
	setDefaults: function(settings) {
		extendRemove(this._defaults, settings || {});
		return this;
	},

	/* Attach the date picker to a jQuery selection.
	   @param  target    element - the target input field or division or span
	   @param  settings  object - the new settings to use for this date picker instance (anonymous) */
	_attachDatepicker: function(target, settings) {
		// check for settings on the control itself - in namespace 'date:'
		var inlineSettings = null;
		for (attrName in this._defaults) {
			var attrValue = target.getAttribute('date:' + attrName);
			if (attrValue) {
				inlineSettings = inlineSettings || {};
				try {
					inlineSettings[attrName] = eval(attrValue);
				} catch (err) {
					inlineSettings[attrName] = attrValue;
				}
			}
		}

		var nodeName = target.nodeName.toLowerCase();
		var instSettings = (inlineSettings ?
			$.extend(settings || {}, inlineSettings || {}) : settings);
		if (nodeName == 'input') {

			var inst = (inst && !inlineSettings ? inst :
				new DateTimepickerInstance(instSettings, false));
			this._connectDatepicker(target, inst);
		} else if (nodeName == 'div' || nodeName == 'span') {
			var inst = new DateTimepickerInstance(instSettings, true);
			this._inlineDatepicker(target, inst);
		}
	},

	/* Detach a datetimepicker from its control.
	   @param  target    element - the target input field or division or span */
	_destroyDatepicker: function(target) {
		var nodeName = target.nodeName.toLowerCase();
		var calId = target._calId;
		target._calId = null;
		var $target = $(target);
		if (nodeName == 'input') {
			$target.siblings('.datetimepicker_append').replaceWith('').end()
				.siblings('.datetimepicker_trigger').replaceWith('').end()
				.removeClass(this.markerClassName)
				.unbind('focus', this._showDatepicker)
				.unbind('keydown', this._doKeyDown)
				.unbind('keypress', this._doKeyPress);
			var wrapper = $target.parents('.datetimepicker_wrap');
			if (wrapper)
				wrapper.replaceWith(wrapper.html());
		} else if (nodeName == 'div' || nodeName == 'span')
			$target.removeClass(this.markerClassName).empty();
		if ($('input[_calId=' + calId + ']').length == 0)
			// clean up if last for this ID
			this._inst[calId] = null;
	},

	/* Enable the date picker to a jQuery selection.
	   @param  target    element - the target input field or division or span */
	_enableDatepicker: function(target) {
		target.disabled = false;
		$(target).siblings('button.datetimepicker_trigger').each(function() { this.disabled = false; }).end()
			.siblings('img.datetimepicker_trigger').css({opacity: '1.0', cursor: ''});
		this._disabledInputs = $.map(this._disabledInputs,
			function(value) { return (value == target ? null : value); }); // delete entry
	},

	/* Disable the date picker to a jQuery selection.
	   @param  target    element - the target input field or division or span */
	_disableDatepicker: function(target) {
		target.disabled = true;
		$(target).siblings('button.datetimepicker_trigger').each(function() { this.disabled = true; }).end()
			.siblings('img.datetimepicker_trigger').css({opacity: '0.5', cursor: 'default'});
		this._disabledInputs = $.map($.datetimepicker._disabledInputs,
			function(value) { return (value == target ? null : value); }); // delete entry
		this._disabledInputs[$.datetimepicker._disabledInputs.length] = target;
	},

	/* Is the first field in a jQuery collection disabled as a datetimepicker?
	   @param  target    element - the target input field or division or span
	   @return boolean - true if disabled, false if enabled */
	_isDisabledDatepicker: function(target) {
		if (!target)
			return false;
		for (var i = 0; i < this._disabledInputs.length; i++) {
			if (this._disabledInputs[i] == target)
				return true;
		}
		return false;
	},

	/* Update the settings for a date picker attached to an input field or division.
	   @param  target  element - the target input field or division or span
	   @param  name    string - the name of the setting to change or
	                   object - the new settings to update
	   @param  value   any - the new value for the setting (omit if above is an object) */
	_changeDatepicker: function(target, name, value) {
		var settings = name || {};
		if (typeof name == 'string') {
			settings = {};
			settings[name] = value;
		}
		if (inst = this._getInst(target._calId)) {
			extendRemove(inst._settings, settings);
			this._updateDatepicker(inst);
		}
	},

	/* Set the dates for a jQuery selection.
	   @param  target   element - the target input field or division or span
	   @param  date     Date - the new date
	   @param  endDate  Date - the new end date for a range (optional) */
	_setDateDatepicker: function(target, date, endDate) {
		if (inst = this._getInst(target._calId)) {
			inst._setDate(date, endDate);
			this._updateDatepicker(inst);
		}
	},

	/* Get the date(s) for the first entry in a jQuery selection.
	   @param  target  element - the target input field or division or span
	   @return Date - the current date or
	           Date[2] - the current dates for a range */
	_getDateDatepicker: function(target) {
		var inst = this._getInst(target._calId);
		return (inst ? inst._getDate() : null);
	},

	/* Handle keystrokes. */
	_doKeyDown: function(e) {
		var inst = $.datetimepicker._getInst(this._calId);
		if ($.datetimepicker._datetimepickerShowing)
			switch (e.keyCode) {
				case 9:  $.datetimepicker._hideDatepicker(null, '');
						break; // hide on tab out
				case 13: $.datetimepicker._selectDay(inst, inst._selectedMonth, inst._selectedYear,
							$('td.datetimepicker_daysCellOver', inst._datetimepickerDiv)[0]);
						return false; // don't submit the form
						break; // select the value on enter
				case 27: $.datetimepicker._hideDatepicker(null, inst._get('speed'));
						break; // hide on escape
				case 33: $.datetimepicker._adjustDate(inst,
							(e.ctrlKey ? -1 : -inst._get('stepMonths')), (e.ctrlKey ? 'Y' : 'M'));
						break; // previous month/year on page up/+ ctrl
				case 34: $.datetimepicker._adjustDate(inst,
							(e.ctrlKey ? +1 : +inst._get('stepMonths')), (e.ctrlKey ? 'Y' : 'M'));
						break; // next month/year on page down/+ ctrl
				case 35: if (e.ctrlKey) $.datetimepicker._clearDate(inst);
						break; // clear on ctrl+end
				case 36: if (e.ctrlKey) $.datetimepicker._gotoToday(inst);
						break; // current on ctrl+home
				case 37: if (e.ctrlKey) $.datetimepicker._adjustDate(inst, -1, 'D');
						break; // -1 day on ctrl+left
				case 38: if (e.ctrlKey) $.datetimepicker._adjustDate(inst, -7, 'D');
						break; // -1 week on ctrl+up
				case 39: if (e.ctrlKey) $.datetimepicker._adjustDate(inst, +1, 'D');
						break; // +1 day on ctrl+right
				case 40: if (e.ctrlKey) $.datetimepicker._adjustDate(inst, +7, 'D');
						break; // +1 week on ctrl+down
			}
		else if (e.keyCode == 36 && e.ctrlKey) // display the date picker on ctrl+home
			$.datetimepicker._showDatepicker(this);
	},

	/* Filter entered characters - based on date format. */
	_doKeyPress: function(e) {
		var inst = $.datetimepicker._getInst(this._calId);
		var chars = $.datetimepicker._possibleChars(inst._get('dateFormat')+' '+inst._get('timeFormat'));
		var chr = String.fromCharCode(e.charCode == undefined ? e.keyCode : e.charCode);
		return e.ctrlKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
	},

	/* Attach the date picker to an input field. */
	_connectDatepicker: function(target, inst) {
		var input = $(target);
		if (input.is('.' + this.markerClassName))
			return;
		var appendText = inst._get('appendText');
		var isRTL = inst._get('isRTL');
		if (appendText) {
			if (isRTL)
				input.before('<span class="datetimepicker_append">' + appendText);
			else
				input.after('<span class="datetimepicker_append">' + appendText);
		}
		var showOn = inst._get('showOn');
		if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field
			input.focus(this._showDatepicker);
		if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked
			input.wrap('<span class="datetimepicker_wrap">');
			var buttonText = inst._get('buttonText');
			var buttonImage = inst._get('buttonImage');
			var trigger = null;
			if (inst._get('buttonImageOnly')) {
				trigger = $('<img>').addClass('datetimepicker_trigger').attr({
					src: buttonImage,
					alt: buttonText,
					title: buttonText
				});
			} else {
				trigger = $('<input type="button" value="' + buttonText + '">').addClass('datetimepicker_trigger');//.html(buttonText);
			}

			/*$(inst._get('buttonImageOnly') ?
				$('<img>').addClass('datetimepicker_trigger').attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
				$('<button>').addClass('datetimepicker_trigger').attr({ type: 'button' }).html(buttonImage != '' ?
						$('<img>').attr({ src:buttonImage, alt:buttonText, title:buttonText }) : buttonText));*/
			if (isRTL)
				input.before(trigger);
			else
				input.after(trigger);
			trigger.click(function() {
				if ($.datetimepicker._datetimepickerShowing && $.datetimepicker._lastInput == target)
					$.datetimepicker._hideDatepicker();
				else
					$.datetimepicker._showDatepicker(target);
			});
        }
		input.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress)
			.bind("setData.datetimepicker", function(event, key, value) {
				inst._settings[key] = value;
			}).bind("getData.datetimepicker", function(event, key) {
				return inst._get(key);
			});
		input[0]._calId = inst._id;
	},

	/* Attach an inline date picker to a div. */
	_inlineDatepicker: function(target, inst) {
		var input = $(target);
		if (input.is('.' + this.markerClassName))
			return;
		input.addClass(this.markerClassName).append(inst._datetimepickerDiv)
			.bind("setData.datetimepicker", function(event, key, value){
				inst._settings[key] = value;
			}).bind("getData.datetimepicker", function(event, key){
				return inst._get(key);
			});
		input[0]._calId = inst._id;
		this._updateDatepicker(inst);
	},

	/* Tidy up after displaying the date picker. */
	_inlineShow: function(inst) {
		var numMonths = inst._getNumberOfMonths(); // fix width for dynamic number of date pickers
		inst._datetimepickerDiv.width(numMonths[1] * $('.datetimepicker', inst._datetimepickerDiv[0]).width());
	},

	/* Pop-up the date picker in a "dialog" box.
	   @param  input     element - ignored
	   @param  dateText  string - the initial date to display (in the current format)
	   @param  onSelect  function - the function(dateText) to call when a date is selected
	   @param  settings  object - update the dialog date picker instance's settings (anonymous object)
	   @param  pos       int[2] - coordinates for the dialog's position within the screen or
	                     event - with x/y coordinates or
	                     leave empty for default (screen centre)
	   @return the manager object */
	_dialogDatepicker: function(input, dateText, onSelect, settings, pos) {
		var inst = this._dialogInst; // internal instance
		if (!inst) {
			inst = this._dialogInst = new DateTimepickerInstance({}, false);
			this._dialogInput = $('<input type="text" size="1" style="position: absolute; top: -100px;"/>');
			this._dialogInput.keydown(this._doKeyDown);
			$('body').append(this._dialogInput);
			this._dialogInput[0]._calId = inst._id;
		}
		extendRemove(inst._settings, settings || {});
		this._dialogInput.val(dateText);

		this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
		if (!this._pos) {
			var browserWidth = window.innerWidth || document.documentElement.clientWidth ||	document.body.clientWidth;
			var browserHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
			var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
			var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
			this._pos = // should use actual width/height below
				[(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
		}

		// move input on screen for focus, but hidden behind dialog
		this._dialogInput.css('left', this._pos[0] + 'px').css('top', this._pos[1] + 'px');
		inst._settings.onSelect = onSelect;
		this._inDialog = true;
		this._datetimepickerDiv.addClass('datetimepicker_dialog');
		this._showDatepicker(this._dialogInput[0]);
		if ($.blockUI)
			$.blockUI(this._datetimepickerDiv);
		return this;
	},

	/* Pop-up the date picker for a given input field.
	   @param  input  element - the input field attached to the date picker or
	                  event - if triggered by focus */
	_showDatepicker: function(input) {
		input = input.target || input;
		if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
			input = $('input', input.parentNode)[0];
		if ($.datetimepicker._isDisabledDatepicker(input) || $.datetimepicker._lastInput == input) // already here
			return;
		var inst = $.datetimepicker._getInst(input._calId);
		var beforeShow = inst._get('beforeShow');
		extendRemove(inst._settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
		$.datetimepicker._hideDatepicker(null, '');
		$.datetimepicker._lastInput = input;
		inst._setDateFromField(input);
		if ($.datetimepicker._inDialog) // hide cursor
			input.value = '';
		if (!$.datetimepicker._pos) { // position below input
			$.datetimepicker._pos = $.datetimepicker._findPos(input);
			$.datetimepicker._pos[1] += input.offsetHeight; // add the height
		}
		var isFixed = false;
		$(input).parents().each(function() {
			isFixed |= $(this).css('position') == 'fixed';
		});
		if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
			$.datetimepicker._pos[0] -= document.documentElement.scrollLeft;
			$.datetimepicker._pos[1] -= document.documentElement.scrollTop;
		}
		inst._datetimepickerDiv.css('position', ($.datetimepicker._inDialog && $.blockUI ?
			'static' : (isFixed ? 'fixed' : 'absolute')))
			.css({ left: $.datetimepicker._pos[0] + 'px', top: $.datetimepicker._pos[1] + 'px' });
		$.datetimepicker._pos = null;
		inst._rangeStart = null;
		$.datetimepicker._updateDatepicker(inst);
		if (!inst._inline) {
			var speed = inst._get('speed');
			var postProcess = function() {
				$.datetimepicker._datetimepickerShowing = true;
				$.datetimepicker._afterShow(inst);
			};
			var showAnim = inst._get('showAnim') || 'show';
			inst._datetimepickerDiv[showAnim](speed, postProcess);
			if (speed == '')
				postProcess();
			if (inst._input[0].type != 'hidden')
				inst._input[0].focus();
			$.datetimepicker._curInst = inst;
		}
	},

	/* Generate the date picker content. */
	_updateDatepicker: function(inst) {
		inst._datetimepickerDiv.empty().append(inst._generateDatepicker());
		var numMonths = inst._getNumberOfMonths();
		if (numMonths[0] != 1 || numMonths[1] != 1)
			inst._datetimepickerDiv.addClass('datetimepicker_multi');
		else
			inst._datetimepickerDiv.removeClass('datetimepicker_multi');

		if (inst._get('isRTL'))
			inst._datetimepickerDiv.addClass('datetimepicker_rtl');
		else
			inst._datetimepickerDiv.removeClass('datetimepicker_rtl');

		if (inst._input && inst._input[0].type != 'hidden')
			$(inst._input[0]).focus();
	},

	/* Tidy up after displaying the date picker. */
	_afterShow: function(inst) {
		var numMonths = inst._getNumberOfMonths(); // fix width for dynamic number of date pickers
		inst._datetimepickerDiv.width(numMonths[1] * $('.datetimepicker', inst._datetimepickerDiv[0])[0].offsetWidth);
		if ($.browser.msie && parseInt($.browser.version) < 7) { // fix IE < 7 select problems
			$('iframe.datetimepicker_cover').css({width: inst._datetimepickerDiv.width() + 4,
				height: inst._datetimepickerDiv.height() + 4});
		}
		// re-position on screen if necessary
		var isFixed = inst._datetimepickerDiv.css('position') == 'fixed';
		var pos = inst._input ? $.datetimepicker._findPos(inst._input[0]) : null;
		var browserWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
		var browserHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
		var scrollX = (isFixed ? 0 : document.documentElement.scrollLeft || document.body.scrollLeft);
		var scrollY = (isFixed ? 0 : document.documentElement.scrollTop || document.body.scrollTop);
		// reposition date picker horizontally if outside the browser window
		if ((inst._datetimepickerDiv.offset().left + inst._datetimepickerDiv.width() -
				(isFixed && $.browser.msie ? document.documentElement.scrollLeft : 0)) >
				(browserWidth + scrollX)) {
			inst._datetimepickerDiv.css('left', Math.max(scrollX,
				pos[0] + (inst._input ? $(inst._input[0]).width() : null) - inst._datetimepickerDiv.width() -
				(isFixed && $.browser.opera ? document.documentElement.scrollLeft : 0)) + 'px');
		}
		// reposition date picker vertically if outside the browser window
		if ((inst._datetimepickerDiv.offset().top + inst._datetimepickerDiv.height() -
				(isFixed && $.browser.msie ? document.documentElement.scrollTop : 0)) >
				(browserHeight + scrollY) ) {
			inst._datetimepickerDiv.css('top', Math.max(scrollY,
				pos[1] - (this._inDialog ? 0 : inst._datetimepickerDiv.height()) -
				(isFixed && $.browser.opera ? document.documentElement.scrollTop : 0)) + 'px');
		}
	},

	/* Find an object's position on the screen. */
	_findPos: function(obj) {
        while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
            obj = obj.nextSibling;
        }
        var position = $(obj).offset();
	    return [position.left, position.top];
	},

	/* Hide the date picker from view.
	   @param  input  element - the input field attached to the date picker
	   @param  speed  string - the speed at which to close the date picker */
	_hideDatepicker: function(input, speed) {
		var inst = this._curInst;
		if (!inst)
			return;
		var rangeSelect = inst._get('rangeSelect');
		if (rangeSelect && this._stayOpen) {
			this._selectDate(inst, inst._formatDateTime(
				inst._currentDay, inst._currentMonth, inst._currentYear, inst._currentHour, inst.currentMinute));
		}
		this._stayOpen = false;
		if (this._datetimepickerShowing) {
			speed = (speed != null ? speed : inst._get('speed'));
			var showAnim = inst._get('showAnim');
			inst._datetimepickerDiv[(showAnim == 'slideDown' ? 'slideUp' :
				(showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))](speed, function() {
				$.datetimepicker._tidyDialog(inst);
			});
			if (speed == '')
				this._tidyDialog(inst);
			var onClose = inst._get('onClose');
			if (onClose) {
				onClose.apply((inst._input ? inst._input[0] : null),
					[inst._getDate(), inst]);  // trigger custom callback
			}
			this._datetimepickerShowing = false;
			this._lastInput = null;
			inst._settings.prompt = null;
			if (this._inDialog) {
				this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
				if ($.blockUI) {
					$.unblockUI();
					$('body').append(this._datetimepickerDiv);
				}
			}
			this._inDialog = false;
		}
		this._curInst = null;
	},

	/* Tidy up after a dialog display. */
	_tidyDialog: function(inst) {
		inst._datetimepickerDiv.removeClass('datetimepicker_dialog').unbind('.datetimepicker');
		$('.datetimepicker_prompt', inst._datetimepickerDiv).remove();
	},

	/* Close date picker if clicked elsewhere. */
	_checkExternalClick: function(event) {
		if (!$.datetimepicker._curInst)
			return;
		var $target = $(event.target);
		if (($target.parents("#datetimepicker_div").length == 0) &&
				($target.attr('class') != 'datetimepicker_trigger') &&
				$.datetimepicker._datetimepickerShowing && !($.datetimepicker._inDialog && $.blockUI)) {
			$.datetimepicker._hideDatepicker(null, '');
		}
	},

	/* Adjust one of the date sub-fields. */
	_adjustDate: function(id, offset, period) {
		var inst = this._getInst(id);
		inst._adjustDate(offset, period);
		this._updateDatepicker(inst);
	},

	/* Action for current link. */
	_gotoToday: function(id) {
		var date = new Date();
		var inst = this._getInst(id);
		inst._selectedDay = date.getDate();
		inst._drawMonth = inst._selectedMonth = date.getMonth();
		inst._drawYear = inst._selectedYear = date.getFullYear();
		inst._drawHour = inst._selectedHour = date.getHours();
		inst._drawMinute = inst._selectedMinute = date.getMinutes();
		this._adjustDate(inst);
	},

	/* Action for selecting a new month/year. */
	_selectMonthYear: function(id, select, period) {
		var inst = this._getInst(id);
		inst._selectingMonthYear = false;
		inst[period == 'M' ? '_drawMonth' : '_drawYear'] =
			select.options[select.selectedIndex].value - 0;
		this._adjustDate(inst);
	},
	_selectTime: function(id, select, period) {
		var inst = this._getInst(id);
		inst._selectingMonthYear = false;
		inst[period == 'M' ? '_drawMinute' : '_drawHour'] =
			select.options[select.selectedIndex].value - 0;
		this._adjustDate(inst);

		this._doNotHide = true;
		$('td.datetimepicker_currentDay').each(function(){
			$.datetimepicker._selectDay(inst, inst._selectedMonth, inst._selectedYear,$(this));
		});
		this._doNotHide = false;
	},

	/* Restore input focus after not changing month/year. */
	_clickMonthYear: function(id) {
		var inst = this._getInst(id);
		if (inst._input && inst._selectingMonthYear && !$.browser.msie)
			inst._input[0].focus();
		inst._selectingMonthYear = !inst._selectingMonthYear;
	},

	_clickTime: function(id) {
		var inst = this._getInst(id);
		if (inst._input && inst._selectingTime && !$.browser.msie)
			inst._input[0].focus();
		inst._selectingTime = !inst._selectingTime;
	},

	/* Action for changing the first week day. */
	_changeFirstDay: function(id, day) {
		var inst = this._getInst(id);
		inst._settings.firstDay = day;
		this._updateDatepicker(inst);
	},

	/* Action for selecting a day. */
	_selectDay: function(id, month, year, td) {
		if ($(td).is('.datetimepicker_unselectable'))
			return;
		var inst = this._getInst(id);
		var rangeSelect = inst._get('rangeSelect');
		if (rangeSelect) {
			if (!this._stayOpen) {
				$('.datetimepicker td').removeClass('datetimepicker_currentDay');
				$(td).addClass('datetimepicker_currentDay');
			}
			this._stayOpen = !this._stayOpen;
		}
		inst._selectedDay = inst._currentDay = $('a', td).html();
		inst._selectedMonth = inst._currentMonth = month;
		inst._selectedYear = inst._currentYear = year;
		inst._selectedHour = inst._currentHour = $('select.datetimepicker_newHour option:selected').val();
		inst._selectedMinute = inst._currentMinute = $('select.datetimepicker_newMinute option:selected').val();
		this._selectDate(id, inst._formatDateTime(
			inst._currentDay, inst._currentMonth, inst._currentYear, inst._currentHour, inst._currentMinute));
		if (this._stayOpen) {
			inst._endDay = inst._endMonth = inst._endYear = null;
			inst._rangeStart = new Date(inst._currentYear, inst._currentMonth, inst._currentDay);
			this._updateDatepicker(inst);
		}
		else if (rangeSelect) {
			inst._endDay = inst._currentDay;
			inst._endMonth = inst._currentMonth;
			inst._endYear = inst._currentYear;
			inst._selectedDay = inst._currentDay = inst._rangeStart.getDate();
			inst._selectedMonth = inst._currentMonth = inst._rangeStart.getMonth();
			inst._selectedYear = inst._currentYear = inst._rangeStart.getFullYear();
			inst._rangeStart = null;
			if (inst._inline)
				this._updateDatepicker(inst);
		}
	},

	/* Erase the input field and hide the date picker. */
	_clearDate: function(id) {
		var inst = this._getInst(id);
		if (inst._get('mandatory'))
			return;
		this._stayOpen = false;
		inst._endDay = inst._endMonth = inst._endYear = inst._rangeStart = null;
		this._selectDate(inst, '');
	},

	/* Update the input field with the selected date. */
	_selectDate: function(id, dateStr) {
		var inst = this._getInst(id);
		dateStr = (dateStr != null ? dateStr : inst._formatDateTime());
		if (inst._rangeStart)
			dateStr = inst._formatDateTime(inst._rangeStart) + inst._get('rangeSeparator') + dateStr;
		if (inst._input)
			inst._input.val(dateStr);
		var onSelect = inst._get('onSelect');
		if (onSelect)
			onSelect.apply((inst._input ? inst._input[0] : null), [dateStr, inst]);  // trigger custom callback
		else if (inst._input)
			inst._input.trigger('change'); // fire the change event
		if (inst._inline)
			this._updateDatepicker(inst);
		else if (!this._stayOpen) {
			if (! this._doNotHide) {
				this._hideDatepicker(null, inst._get('speed'));
				this._lastInput = inst._input[0];
				if (typeof(inst._input[0]) != 'object')
					inst._input[0].focus(); // restore focus
				this._lastInput = null;
			}
		}
	},

	/* Set as beforeShowDay function to prevent selection of weekends.
	   @param  date  Date - the date to customise
	   @return [boolean, string] - is this date selectable?, what is its CSS class? */
	noWeekends: function(date) {
		var day = date.getDay();
		return [(day > 0 && day < 6), ''];
	},

	/* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
	   @param  date  Date - the date to get the week for
	   @return  number - the number of the week within the year that contains this date */
	iso8601Week: function(date) {
		var checkDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), (date.getTimezoneOffset() / -60));
		var firstMon = new Date(checkDate.getFullYear(), 1 - 1, 4); // First week always contains 4 Jan
		var firstDay = firstMon.getDay() || 7; // Day of week: Mon = 1, ..., Sun = 7
		firstMon.setDate(firstMon.getDate() + 1 - firstDay); // Preceding Monday
		if (firstDay < 4 && checkDate < firstMon) { // Adjust first three days in year if necessary
			checkDate.setDate(checkDate.getDate() - 3); // Generate for previous year
			return $.datetimepicker.iso8601Week(checkDate);
		} else if (checkDate > new Date(checkDate.getFullYear(), 12 - 1, 28)) { // Check last three days in year
			firstDay = new Date(checkDate.getFullYear() + 1, 1 - 1, 4).getDay() || 7;
			if (firstDay > 4 && (checkDate.getDay() || 7) < firstDay - 3) { // Adjust if necessary
				checkDate.setDate(checkDate.getDate() + 3); // Generate for next year
				return $.datetimepicker.iso8601Week(checkDate);
			}
		}
		return Math.floor(((checkDate - firstMon) / 86400000) / 7) + 1; // Weeks to given date
	},

	/* Provide status text for a particular date.
	   @param  date  the date to get the status for
	   @param  inst  the current datetimepicker instance
	   @return  the status display text for this date */
	dateStatus: function(date, inst) {
		return $.datetimepicker.formatDate(inst._get('dateStatus'), date, inst._getFormatConfig());
	},

	/* Parse a string value into a date object.
	   The format can be combinations of the following:
	   d  - day of month (no leading zero)
	   dd - day of month (two digit)
	   D  - day name short
	   DD - day name long
	   m  - month of year (no leading zero)
	   mm - month of year (two digit)
	   M  - month name short
	   MM - month name long
	   y  - year (two digit)
	   yy - year (four digit)
	   '...' - literal text
	   '' - single quote

	   @param  format           String - the expected format of the date
	   @param  value            String - the date in the above format
	   @param  settings  Object - attributes include:
	                     shortYearCutoff  Number - the cutoff year for determining the century (optional)
	                     dayNamesShort    String[7] - abbreviated names of the days from Sunday (optional)
	                     dayNames         String[7] - names of the days from Sunday (optional)
	                     monthNamesShort  String[12] - abbreviated names of the months (optional)
	                     monthNames       String[12] - names of the months (optional)
	   @return  Date - the extracted date value or null if value is blank */
	parseDate: function (format, value, settings) {
		if (format == null || value == null)
			throw 'Invalid arguments';
		value = (typeof value == 'object' ? value.toString() : value + '');
		if (value == '')
			return null;
		var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
		var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
		var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
		var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
		var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
		var year = -1;
		var month = -1;
		var day = -1;
		var hour = -1;
		var minute = -1;
		var literal = false;
		// Check whether a format character is doubled
		var lookAhead = function(match) {
			var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
			if (matches)
				iFormat++;
			return matches;
		};
		// Extract a number from the string value
		var getNumber = function(match) {
			lookAhead(match);
			var size = (match == 'y' ? 4 : 2);
			var num = 0;
			while (size > 0 && iValue < value.length &&
					value.charAt(iValue) >= '0' && value.charAt(iValue) <= '9') {
				num = num * 10 + (value.charAt(iValue++) - 0);
				size--;
			}
			if (size == (match == 'y' ? 4 : 2))
				throw 'Missing number at position ' + iValue;
			return num;
		};
		// Extract a name from the string value and convert to an index
		var getName = function(match, shortNames, longNames) {
			var names = (lookAhead(match) ? longNames : shortNames);
			var size = 0;
			for (var j = 0; j < names.length; j++)
				size = Math.max(size, names[j].length);
			var name = '';
			var iInit = iValue;
			while (size > 0 && iValue < value.length) {
				name += value.charAt(iValue++);
				for (var i = 0; i < names.length; i++)
					if (name == names[i])
						return i + 1;
				size--;
			}
			throw 'Unknown name at position ' + iInit;
		};
		// Confirm that a literal character matches the string value
		var checkLiteral = function() {
			if (value.charAt(iValue) != format.charAt(iFormat))
				throw 'Unexpected literal at position ' + iValue;
			iValue++;
		};
		var iValue = 0;
		for (var iFormat = 0; iFormat < format.length; iFormat++) {
			if (literal)
				if (format.charAt(iFormat) == "'" && !lookAhead("'"))
					literal = false;
				else
					checkLiteral();
			else
				switch (format.charAt(iFormat)) {
					case 'h':
						hour = getNumber('h');
						break;
					case 'i':
						minute = getNumber('i');
						break;
					case 'd':
						day = getNumber('d');
						break;
					case 'D':
						getName('D', dayNamesShort, dayNames);
						break;
					case 'm':
						month = getNumber('m');
						break;
					case 'M':
						month = getName('M', monthNamesShort, monthNames);
						break;
					case 'y':
						year = getNumber('y');
						break;
					case "'":
						if (lookAhead("'"))
							checkLiteral();
						else
							literal = true;
						break;
					default:
						checkLiteral();
				}
		}
		if (year < 100) {
			year += new Date().getFullYear() - new Date().getFullYear() % 100 +
				(year <= shortYearCutoff ? 0 : -100);
		}
		var date = new Date(year, month - 1, day,hour,minute);
		if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day) {
			throw 'Invalid date'; // E.g. 31/02/*
		}
		return date;
	},

	/* Format a date object into a string value.
	   The format can be combinations of the following:
	   d  - day of month (no leading zero)
	   dd - day of month (two digit)
	   D  - day name short
	   DD - day name long
	   m  - month of year (no leading zero)
	   mm - month of year (two digit)
	   M  - month name short
	   MM - month name long
	   y  - year (two digit)
	   yy - year (four digit)
	   '...' - literal text
	   '' - single quote

	   @param  format    String - the desired format of the date
	   @param  date      Date - the date value to format
	   @param  settings  Object - attributes include:
	                     dayNamesShort    String[7] - abbreviated names of the days from Sunday (optional)
	                     dayNames         String[7] - names of the days from Sunday (optional)
	                     monthNamesShort  String[12] - abbreviated names of the months (optional)
	                     monthNames       String[12] - names of the months (optional)
	   @return  String - the date in the above format */
	formatDate: function (format, date, settings) {
		if (!date)
			return '';
		var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
		var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
		var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
		var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
		// Check whether a format character is doubled
		var lookAhead = function(match) {
			var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
			if (matches)
				iFormat++;
			return matches;
		};
		// Format a number, with leading zero if necessary
		var formatNumber = function(match, value) {
			return (lookAhead(match) && value < 10 ? '0' : '') + value;
		};
		// Format a name, short or long as requested
		var formatName = function(match, value, shortNames, longNames) {
			return (lookAhead(match) ? longNames[value] : shortNames[value]);
		};
		var output = '';
		var literal = false;
		if (date) {
			for (var iFormat = 0; iFormat < format.length; iFormat++) {
				if (literal)
					if (format.charAt(iFormat) == "'" && !lookAhead("'"))
						literal = false;
					else
						output += format.charAt(iFormat);
				else
					switch (format.charAt(iFormat)) {
						case 'h':
							output += formatNumber('h', date.getHours());
							break;
						case 'i':
							output += formatNumber('i', date.getMinutes());
							break;
						case 'd':
							output += formatNumber('d', date.getDate());
							break;
						case 'D':
							output += formatName('D', date.getDay(), dayNamesShort, dayNames);
							break;
						case 'm':
							output += formatNumber('m', date.getMonth() + 1);
							break;
						case 'M':
							output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
							break;
						case 'y':
							output += (lookAhead('y') ? date.getFullYear() :
								(date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
							break;
						case "'":
							if (lookAhead("'"))
								output += "'";
							else
								literal = true;
							break;
						default:
							output += format.charAt(iFormat);
					}
			}
		}
		return output;
	},

	/* Extract all possible characters from the date format. */
	_possibleChars: function (format) {
		var chars = '';
		var literal = false;
		for (var iFormat = 0; iFormat < format.length; iFormat++)
			if (literal)
				if (format.charAt(iFormat) == "'" && !lookAhead("'"))
					literal = false;
				else
					chars += format.charAt(iFormat);
			else
				switch (format.charAt(iFormat)) {
					case 'd' || 'm' || 'y':
						chars += '0123456789';
						break;
					case 'D' || 'M':
						return null; // Accept anything
					case "'":
						if (lookAhead("'"))
							chars += "'";
						else
							literal = true;
						break;
					default:
						chars += format.charAt(iFormat);
				}
		return chars;
	}
});

/* Individualised settings for date picker functionality applied to one or more related inputs.
   Instances are managed and manipulated through the Datepicker manager. */
function DateTimepickerInstance(settings, inline) {
	this._id = $.datetimepicker._register(this);
	this._selectedDay = 0; // Current date for selection
	this._selectedMonth = 0; // 0-11
	this._selectedYear = 0; // 4-digit year
	this._drawMonth = 0; // Current month at start of datetimepicker
	this._drawYear = 0;
	this._drawHour = 0;
	this._drawMinute = 0;
	this._input = null; // The attached input field
	this._inline = inline; // True if showing inline, false if used in a popup
	this._datetimepickerDiv = (!inline ? $.datetimepicker._datetimepickerDiv :
		$('<div id="datetimepicker_div_' + this._id + '" class="datetimepicker_inline">'));
	// customise the date picker object - uses manager defaults if not overridden
	this._settings = extendRemove(settings || {}); // clone
	if (inline)
		this._setDate(this._getDefaultDate());
}

$.extend(DateTimepickerInstance.prototype, {
	/* Get a setting value, defaulting if necessary. */
	_get: function(name) {
		var result = this._settings[name] !== undefined ? this._settings[name] : $.datetimepicker._defaults[name];
		return result;
	},

	/* Parse existing date and initialise date picker. */
	_setDateFromField: function(input) {
		this._input = $(input);
		var dateFormat = this._get('dateFormat')+' '+this._get('timeFormat');
		var dates = this._input ? this._input.val().split(this._get('rangeSeparator')) : null;
		this._endDay = this._endMonth = this._endYear = null;
		var date = defaultDate = this._getDefaultDate();
		if (dates.length > 0) {
			var settings = this._getFormatConfig();
			if (dates.length > 1) {
				date = $.datetimepicker.parseDate(dateFormat, dates[1], settings) || defaultDate;
				this._endDay = date.getDate();
				this._endMonth = date.getMonth();
				this._endYear = date.getFullYear();
			}
			try {
				date = $.datetimepicker.parseDate(dateFormat, dates[0], settings) || defaultDate;
			} catch (e) {
				$.datetimepicker.log(e);
				date = defaultDate;
			}
		}
		this._selectedDay = date.getDate();
		this._drawMonth = this._selectedMonth = date.getMonth();
		this._drawYear = this._selectedYear = date.getFullYear();
		this._drawHour = this._selectedHour = date.getHours();
		this._drawMinute = this._selectedMinute = date.getMinutes();
		this._currentDay = (dates[0] ? date.getDate() : 0);
		this._currentMonth = (dates[0] ? date.getMonth() : 0);
		this._currentYear = (dates[0] ? date.getFullYear() : 0);
		this._adjustDate();
	},

	/* Retrieve the default date shown on opening. */
	_getDefaultDate: function() {
		var date = this._determineDate('defaultDate', new Date());
		var minDate = this._getMinMaxDate('min', true);
		var maxDate = this._getMinMaxDate('max');
		date = (minDate && date < minDate ? minDate : date);
		date = (maxDate && date > maxDate ? maxDate : date);
		return date;
	},

	/* A date may be specified as an exact value or a relative one. */
	_determineDate: function(name, defaultDate) {
		var offsetNumeric = function(offset) {
			var date = new Date();
			date.setDate(date.getDate() + offset);
			return date;
		};
		var offsetString = function(offset, getDaysInMonth) {
			var date = new Date();
			var matches = /^([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?$/.exec(offset);
			if (matches) {
				var year = date.getFullYear();
				var month = date.getMonth();
				var day = date.getDate();
				switch (matches[2] || 'd') {
					case 'd' : case 'D' :
						day += (matches[1] - 0); break;
					case 'w' : case 'W' :
						day += (matches[1] * 7); break;
					case 'm' : case 'M' :
						month += (matches[1] - 0);
						day = Math.min(day, getDaysInMonth(year, month));
						break;
					case 'y': case 'Y' :
						year += (matches[1] - 0);
						day = Math.min(day, getDaysInMonth(year, month));
						break;
				}
				date = new Date(year, month, day);
			}
			return date;
		};
		var date = this._get(name);
		return (date == null ? defaultDate :
			(typeof date == 'string' ? offsetString(date, this._getDaysInMonth) :
			(typeof date == 'number' ? offsetNumeric(date) : date)));
	},

	/* Set the date(s) directly. */
	_setDate: function(date, endDate) {
		this._selectedDay = this._currentDay = date.getDate();
		this._drawMonth = this._selectedMonth = this._currentMonth = date.getMonth();
		this._drawYear = this._selectedYear = this._currentYear = date.getFullYear();
		this._drawHour = this._selectedHour = this._currentHour = date.getHours();
		this._drawMinute = this._selectedMinute = this._currentMinute = date.getMinutes();
		if (this._get('rangeSelect')) {
			if (endDate) {
				this._endDay = endDate.getDate();
				this._endMonth = endDate.getMonth();
				this._endYear = endDate.getFullYear();
			} else {
				this._endDay = this._currentDay;
				this._endMonth = this._currentMonth;
				this._endYear = this._currentYear;
			}
		}
		this._adjustDate();
	},

	/* Retrieve the date(s) directly. */
	_getDate: function() {
		var startDate = (!this._currentYear || (this._input && this._input.val() == '') ? null :
			new Date(this._currentYear, this._currentMonth, this._currentDay));
		if (this._get('rangeSelect')) {
			return [startDate, (!this._endYear ? null :
				new Date(this._endYear, this._endMonth, this._endDay))];
		} else
			return startDate;
	},

	/* Generate the HTML for the current state of the date picker. */
	_generateDatepicker: function() {
		var today = new Date();
		today = new Date(today.getFullYear(), today.getMonth(), today.getDate()); // clear time
		var showStatus = this._get('showStatus');
		var isRTL = this._get('isRTL');
		// build the date picker HTML
		var clear = (this._get('mandatory') ? '' :
			'<div class="datetimepicker_clear"><a onclick="jQuery.datetimepicker._clearDate(' + this._id + ');"' +
			(showStatus ? this._addStatus(this._get('clearStatus') || '&#xa0;') : '') + '>' +
			this._get('clearText') + '</a></div>');
		var controls = '<div class="datetimepicker_control">' + (isRTL ? '' : clear) +
			'<div class="datetimepicker_close"><a onclick="jQuery.datetimepicker._hideDatepicker();"' +
			(showStatus ? this._addStatus(this._get('closeStatus') || '&#xa0;') : '') + '>' +
			this._get('closeText') + '</a></div>' + (isRTL ? clear : '')  + '</div>';
		var prompt = this._get('prompt');
		var closeAtTop = this._get('closeAtTop');
		var hideIfNoPrevNext = this._get('hideIfNoPrevNext');
		var numMonths = this._getNumberOfMonths();
		var stepMonths = this._get('stepMonths');
		var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
		var minDate = this._getMinMaxDate('min', true);
		var maxDate = this._getMinMaxDate('max');
		var drawMonth = this._drawMonth;
		var drawYear = this._drawYear;
		var drawHour = this._drawHour;
		var drawMinute = this._drawMinute;
		if (maxDate) {
			var maxDraw = new Date(maxDate.getFullYear(),
				maxDate.getMonth() - numMonths[1] + 1, maxDate.getDate());
			maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
			while (new Date(drawYear, drawMonth, 1) > maxDraw) {
				drawMonth--;
				if (drawMonth < 0) {
					drawMonth = 11;
					drawYear--;
				}
			}
		}
		// controls and links
		var prev = '<div class="datetimepicker_prev">' + (this._canAdjustMonth(-1, drawYear, drawMonth) ?
			'<a onclick="jQuery.datetimepicker._adjustDate(' + this._id + ', -' + stepMonths + ', \'M\');"' +
			(showStatus ? this._addStatus(this._get('prevStatus') || '&#xa0;') : '') + '>' +
			this._get('prevText') + '</a>' :
			(hideIfNoPrevNext ? '' : '<label>' + this._get('prevText') + '</label>')) + '</div>';
		var next = '<div class="datetimepicker_next">' + (this._canAdjustMonth(+1, drawYear, drawMonth) ?
			'<a onclick="jQuery.datetimepicker._adjustDate(' + this._id + ', +' + stepMonths + ', \'M\');"' +
			(showStatus ? this._addStatus(this._get('nextStatus') || '&#xa0;') : '') + '>' +
			this._get('nextText') + '</a>' :
			(hideIfNoPrevNext ? '>' : '<label>' + this._get('nextText') + '</label>')) + '</div>';
		var html = (prompt ? '<div class="datetimepicker_prompt">' + prompt + '</div>' : '') +
			(closeAtTop && !this._inline ? controls : '') +
			'<div class="datetimepicker_links">' + (isRTL ? next : prev) +
			(this._isInRange(today) ? '<div class="datetimepicker_current">' +
			'<a onclick="jQuery.datetimepicker._gotoToday(' + this._id + ');"' +
			(showStatus ? this._addStatus(this._get('currentStatus') || '&#xa0;') : '') + '>' +
			this._get('currentText') + '</a></div>' : '') + (isRTL ? prev : next) + '</div>';
		var showWeeks = this._get('showWeeks');
		for (var row = 0; row < numMonths[0]; row++)
			for (var col = 0; col < numMonths[1]; col++) {
				var selectedDate = new Date(drawYear, drawMonth, this._selectedDay, drawHour, drawMinute);
				html += '<div class="datetimepicker_oneMonth' + (col == 0 ? ' datetimepicker_newRow' : '') + '">' +
					this._generateMonthYearHeader(drawMinute,drawHour,drawMonth, drawYear, minDate, maxDate,
					selectedDate, row > 0 || col > 0) + // draw month headers
					'<table class="datetimepicker" cellpadding="0" cellspacing="0"><thead>' +
					'<tr class="datetimepicker_titleRow">' +
					(showWeeks ? '<td>' + this._get('weekHeader') + '</td>' : '');
				var firstDay = this._get('firstDay');
				var changeFirstDay = this._get('changeFirstDay');
				var dayNames = this._get('dayNames');
				var dayNamesShort = this._get('dayNamesShort');
				var dayNamesMin = this._get('dayNamesMin');
				for (var dow = 0; dow < 7; dow++) { // days of the week
					var day = (dow + firstDay) % 7;
					var status = this._get('dayStatus') || '&#xa0;';
					status = (status.indexOf('DD') > -1 ? status.replace(/DD/, dayNames[day]) :
						status.replace(/D/, dayNamesShort[day]));
					html += '<td' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="datetimepicker_weekEndCell"' : '') + '>' +
						(!changeFirstDay ? '<span' :
						'<a onclick="jQuery.datetimepicker._changeFirstDay(' + this._id + ', ' + day + ');"') +
						(showStatus ? this._addStatus(status) : '') + ' title="' + dayNames[day] + '">' +
						dayNamesMin[day] + (changeFirstDay ? '</a>' : '</span>') + '</td>';
				}
				html += '</tr></thead><tbody>';
				var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
				if (drawYear == this._selectedYear && drawMonth == this._selectedMonth) {
					this._selectedDay = Math.min(this._selectedDay, daysInMonth);
				}
				var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
				var currentDate = (!this._currentDay ? new Date(9999, 9, 9) :
					new Date(this._currentYear, this._currentMonth, this._currentDay));
				var endDate = this._endDay ? new Date(this._endYear, this._endMonth, this._endDay) : currentDate;
				var printDate = new Date(drawYear, drawMonth, 1 - leadDays);
				var numRows = (isMultiMonth ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); // calculate the number of rows to generate
				var beforeShowDay = this._get('beforeShowDay');
				var showOtherMonths = this._get('showOtherMonths');
				var calculateWeek = this._get('calculateWeek') || $.datetimepicker.iso8601Week;
				var dateStatus = this._get('statusForDate') || $.datetimepicker.dateStatus;
				for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
					html += '<tr class="datetimepicker_daysRow">' +
						(showWeeks ? '<td class="datetimepicker_weekCol">' + calculateWeek(printDate) + '</td>' : '');
					for (var dow = 0; dow < 7; dow++) { // create date picker days
						var daySettings = (beforeShowDay ?
							beforeShowDay.apply((this._input ? this._input[0] : null), [printDate]) : [true, '']);
						var otherMonth = (printDate.getMonth() != drawMonth);
						var unselectable = otherMonth || !daySettings[0] ||
							(minDate && printDate < minDate) || (maxDate && printDate > maxDate);
						html += '<td class="datetimepicker_daysCell' +
							((dow + firstDay + 6) % 7 >= 5 ? ' datetimepicker_weekEndCell' : '') + // highlight weekends
							(otherMonth ? ' datetimepicker_otherMonth' : '') + // highlight days from other months
							(printDate.getTime() == selectedDate.getTime() && drawMonth == this._selectedMonth ?
							' datetimepicker_daysCellOver' : '') + // highlight selected day
							(unselectable ? ' datetimepicker_unselectable' : '') +  // highlight unselectable days
							(otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates
							(printDate.getTime() >= currentDate.getTime() && printDate.getTime() <= endDate.getTime() ?  // in current range
							' datetimepicker_currentDay' : '') + // highlight selected day
							(printDate.getTime() == today.getTime() ? ' datetimepicker_today' : '')) + '"' + // highlight today (if different)
							(unselectable ? '' : ' onmouseover="jQuery(this).addClass(\'datetimepicker_daysCellOver\');' +
							(!showStatus || (otherMonth && !showOtherMonths) ? '' : 'jQuery(\'#datetimepicker_status_' +
							this._id + '\').html(\'' + (dateStatus.apply((this._input ? this._input[0] : null),
							[printDate, this]) || '&#xa0;') +'\');') + '"' +
							' onmouseout="jQuery(this).removeClass(\'datetimepicker_daysCellOver\');' +
							(!showStatus || (otherMonth && !showOtherMonths) ? '' : 'jQuery(\'#datetimepicker_status_' +
							this._id + '\').html(\'&#xa0;\');') + '" onclick="jQuery.datetimepicker._selectDay(' +
							this._id + ',' + drawMonth + ',' + drawYear + ', this);"') + '>' + // actions
							(otherMonth ? (showOtherMonths ? printDate.getDate() : '&#xa0;') : // display for other months
							(unselectable ? printDate.getDate() : '<a>' + printDate.getDate() + '</a>')) + '</td>'; // display for this month
						printDate.setDate(printDate.getDate() + 1);
					}
					html += '</tr>';
				}
				drawMonth++;
				if (drawMonth > 11) {
					drawMonth = 0;
					drawYear++;
				}
				html += '</tbody></table></div>';
			}
		html += (showStatus ? '<div style="clear: both;"></div><div id="datetimepicker_status_' + this._id +
			'" class="datetimepicker_status">' + (this._get('initStatus') || '&#xa0;') + '</div>' : '') +
			(!closeAtTop && !this._inline ? controls : '') +
			'<div style="clear: both;"></div>' +
			($.browser.msie && parseInt($.browser.version) < 7 && !this._inline ?
			'<iframe src="javascript:false;" class="datetimepicker_cover"></iframe>' : '');
		return html;
	},

	/* Generate the month and year header. */
	_generateMonthYearHeader: function(drawMinute,drawHour,drawMonth, drawYear, minDate, maxDate, selectedDate, secondary) {
		minDate = (this._rangeStart && minDate && selectedDate < minDate ? selectedDate : minDate);
		var showStatus = this._get('showStatus');
		var html = '<div class="datetimepicker_header">';
		// month selection
		var monthNames = this._get('monthNames');
		if (secondary || !this._get('changeMonth'))
			html += monthNames[drawMonth] + '&#xa0;';

		else {
			var inMinYear = (minDate && minDate.getFullYear() == drawYear);
			var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
			html += '<select class="datetimepicker_newMonth" ' +
				'onchange="jQuery.datetimepicker._selectMonthYear(' + this._id + ', this, \'M\');" ' +
				'onclick="jQuery.datetimepicker._clickMonthYear(' + this._id + ');"' +
				(showStatus ? this._addStatus(this._get('monthStatus') || '&#xa0;') : '') + '>';
			for (var month = 0; month < 12; month++) {
				if ((!inMinYear || month >= minDate.getMonth()) &&
						(!inMaxYear || month <= maxDate.getMonth())) {
					html += '<option value="' + month + '"' +
						(month == drawMonth ? ' selected="selected"' : '') +
						'>' + monthNames[month] + '</option>';
				}
			}
			html += '</select>';
		}
		// year selection
		if (secondary || !this._get('changeYear'))
			html += drawYear;
		else {
			// determine range of years to display
			var years = this._get('yearRange').split(':');
			var year = 0;
			var endYear = 0;
			if (years.length != 2) {
				year = drawYear - 10;
				endYear = drawYear + 10;
			} else if (years[0].charAt(0) == '+' || years[0].charAt(0) == '-') {
				year = drawYear + parseInt(years[0], 10);
				endYear = drawYear + parseInt(years[1], 10);
			} else {
				year = parseInt(years[0], 10);
				endYear = parseInt(years[1], 10);
			}
			year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
			endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
			html += '<select class="datetimepicker_newYear" ' +
				'onchange="jQuery.datetimepicker._selectMonthYear(' + this._id + ', this, \'Y\');" ' +
				'onclick="jQuery.datetimepicker._clickMonthYear(' + this._id + ');"' +
				(showStatus ? this._addStatus(this._get('yearStatus') || '&#xa0;') : '') + '>';
			for (; year <= endYear; year++) {
				html += '<option value="' + year + '"' +
					(year == drawYear ? ' selected="selected"' : '') +
					'>' + year + '</option>';
			}
			html += '</select>';
		}
		// if (this._get('timeFormat') != '')
		{
			var has_time = this._get('timeFormat') != '';
			html += '<div' + (has_time ? '': ' style="display: none;"') + '>';
			html += '<select class="datetimepicker_newHour" ' +
				'onchange="jQuery.datetimepicker._selectTime(' + this._id + ', this, \'H\');" ' +
				'onclick="jQuery.datetimepicker._clickMonthYear(' + this._id + ');"' +
				(showStatus ? this._addStatus(this._get('hourStatus') || '&#xa0;') : '') + '>';
			for (hour=0; hour < 24; hour++) {
				html += '<option value="' + hour + '"' +
					(hour == drawHour ? ' selected="selected"' : '') +
					'>' + ((hour<10)?'0'+hour:hour) + '</option>';
			}
			html += '</select>';
			html += '&nbsp;:&nbsp;';
			html += '<select class="datetimepicker_newMinute" ' +
				'onchange="jQuery.datetimepicker._selectTime(' + this._id + ', this, \'M\');" ' +
				'onclick="jQuery.datetimepicker._clickMonthYear(' + this._id + ');"' +
				(showStatus ? this._addStatus(this._get('minuteStatus') || '&#xa0;') : '') + '>';
			for (minute=0; minute < 60; minute++) {
				html += '<option value="' + minute + '"' +
					(minute == drawMinute ? ' selected="selected"' : '') +
					'>' + ((minute<10)?'0'+minute:minute) + '</option>';
			}
			html += '</select>';
			html += '</div>';
		}
		html += '</div>'; // Close datetimepicker_header
		return html;
	},

	/* Provide code to set and clear the status panel. */
	_addStatus: function(text) {
		return ' onmouseover="jQuery(\'#datetimepicker_status_' + this._id + '\').html(\'' + text + '\');" ' +
			'onmouseout="jQuery(\'#datetimepicker_status_' + this._id + '\').html(\'&#xa0;\');"';
	},

	/* Adjust one of the date sub-fields. */
	_adjustDate: function(offset, period) {
		var year = this._drawYear + (period == 'Y' ? offset : 0);
		var month = this._drawMonth + (period == 'M' ? offset : 0);
		var day = Math.min(this._selectedDay, this._getDaysInMonth(year, month)) +
			(period == 'D' ? offset : 0);
		var hour = this._drawHour + (period == 'H' ? offset : 0);
		var minute = this._drawMinute + (period == 'I' ? offset : 0);
		var date = new Date(year, month, day, hour, minute);
		// ensure it is within the bounds set
		var minDate = this._getMinMaxDate('min', true);
		var maxDate = this._getMinMaxDate('max');
		date = (minDate && date < minDate ? minDate : date);
		date = (maxDate && date > maxDate ? maxDate : date);
		this._selectedDay = date.getDate();
		this._drawMonth = this._selectedMonth = date.getMonth();
		this._drawYear = this._selectedYear = date.getFullYear();
		this._drawHour = this._selectedHour = date.getHours();
		this._drawMinute = this._selectedMinute = date.getMinutes();
	},

	/* Determine the number of months to show. */
	_getNumberOfMonths: function() {
		var numMonths = this._get('numberOfMonths');
		return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
	},

	/* Determine the current maximum date - ensure no time components are set - may be overridden for a range. */
	_getMinMaxDate: function(minMax, checkRange) {
		var date = this._determineDate(minMax + 'Date', null);
		if (date) {
			date.setHours(0);
			date.setMinutes(0);
			date.setSeconds(0);
			date.setMilliseconds(0);
		}
		return date || (checkRange ? this._rangeStart : null);
	},

	/* Find the number of days in a given month. */
	_getDaysInMonth: function(year, month) {
		return 32 - new Date(year, month, 32).getDate();
	},

	/* Find the day of the week of the first of a month. */
	_getFirstDayOfMonth: function(year, month) {
		return new Date(year, month, 1).getDay();
	},

	/* Determines if we should allow a "next/prev" month display change. */
	_canAdjustMonth: function(offset, curYear, curMonth) {
		var numMonths = this._getNumberOfMonths();
		var date = new Date(curYear, curMonth + (offset < 0 ? offset : numMonths[1]), 1);
		if (offset < 0)
			date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
		return this._isInRange(date);
	},

	/* Is the given date in the accepted range? */
	_isInRange: function(date) {
		// during range selection, use minimum of selected date and range start
		var newMinDate = (!this._rangeStart ? null :
			new Date(this._selectedYear, this._selectedMonth, this._selectedDay));
		newMinDate = (newMinDate && this._rangeStart < newMinDate ? this._rangeStart : newMinDate);
		var minDate = newMinDate || this._getMinMaxDate('min');
		var maxDate = this._getMinMaxDate('max');
		return ((!minDate || date >= minDate) && (!maxDate || date <= maxDate));
	},

	/* Provide the configuration settings for formatting/parsing. */
	_getFormatConfig: function() {
		var shortYearCutoff = this._get('shortYearCutoff');
		shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
			new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
		return {shortYearCutoff: shortYearCutoff,
			dayNamesShort: this._get('dayNamesShort'), dayNames: this._get('dayNames'),
			monthNamesShort: this._get('monthNamesShort'), monthNames: this._get('monthNames')};
	},

	/* Format the given date for display. */
	_formatDateTime: function(day, month, year, hour, minute) {
		if (!day) {
			this._currentDay = this._selectedDay;
			this._currentMonth = this._selectedMonth;
			this._currentYear = this._selectedYear;
			this._currentHour = this._selectedHour;
			this._currentMinute = this._selectedMinute;
		}
		var date = (day ? (typeof day == 'object' ? day : new Date(year, month, day, hour, minute)) :
			new Date(this._currentYear, this._currentMonth, this._currentDay, this._currentHour, this._currentMinute));
		return $.datetimepicker.formatDate(this._get('dateFormat')+' '+this._get('timeFormat'), date, this._getFormatConfig());
	}
});

/* jQuery extend now ignores nulls! */
function extendRemove(target, props) {
	$.extend(target, props);
	for (var name in props)
		if (props[name] == null)
			target[name] = null;
	return target;
};

/* Invoke the datetimepicker functionality.
   @param  options  String - a command, optionally followed by additional parameters or
                    Object - settings for attaching new datetimepicker functionality
   @return  jQuery object */
$.fn.datetimepicker = function(options){
	var otherArgs = Array.prototype.slice.call(arguments, 1);

	if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate')) {
		return $.datetimepicker['_' + options + 'Datepicker'].apply($.datetimepicker, [this[0]].concat(otherArgs));
	}

	return $(this).each(function() {
		typeof options == 'string' ?
			$.datetimepicker['_' + options + 'Datepicker'].apply($.datetimepicker, [this].concat(otherArgs)) :
			$.datetimepicker._attachDatepicker(this, options);
	});
};

$.datetimepicker = new DateTimepicker(); // singleton instance

/* Initialise the date picker. */
$(document).ready(function() {
	$(document.body).append($.datetimepicker._datetimepickerDiv)
		.mousedown($.datetimepicker._checkExternalClick);
});

})(jQuery);

/**********************************************************************************/
/*                   /scripts/lib/jquery/jquery.autocomplete.js                   */
/**********************************************************************************/

(function($) {
jQuery.autocomplete = function(input, options) {
	// Create a link to self
	var me = this;

	// Create jQuery object for input element
	var $input = $(input).attr("autocomplete", "off");

	// Apply inputClass if necessary
	if (options.inputClass) {
		$input.addClass(options.inputClass);
	}

	// Create results
	var results = document.createElement("div");

	// Create jQuery object for results
	// var $results = $(results);
	var $results = $(results).hide().addClass(options.resultsClass).css("position", "absolute");
	if( options.width > 0 ) {
		$results.css("width", options.width);
	}

	// Add to body element
	$("body").append(results);
	// $('#autocompleters_wrapper').append(results);

	input.autocompleter = me;

	var timeout = null;
	var prev = "";
	var active = -1;
	var cache = {};
	var keyb = false;
	var hasFocus = false;
	var lastKeyPressCode = null;
	var mouseDownOnSelect = false;
	var hidingResults = false;

	// flush cache
	function flushCache(){
		cache = {};
		cache.data = {};
		cache.length = 0;
	};



	// flush cache
	flushCache();

	// if there is a data array supplied
	if( options.data != null ){
		var sFirstChar = "", stMatchSets = {}, row = [];

		// no url was specified, we need to adjust the cache length to make sure it fits the local data store
		if( typeof options.url != "string" ) {
			options.cacheLength = 1;
		}

		// loop through the array and create a lookup structure
		for( var i=0; i < options.data.length; i++ ){
			// if row is a string, make an array otherwise just reference the array
			row = ((typeof options.data[i] == "string") ? [options.data[i]] : options.data[i]);

			// if the length is zero, don't add to list
			if( row[0].length > 0 ){
				// get the first character
				sFirstChar = row[0].substring(0, 1).toLowerCase();
				// if no lookup array for this character exists, look it up now
				if( !stMatchSets[sFirstChar] ) stMatchSets[sFirstChar] = [];
				// if the match is a string
				stMatchSets[sFirstChar].push(row);
			}
		}

		// add the data items to the cache
		for( var k in stMatchSets ) {
			// increase the cache size
			options.cacheLength++;
			// add to the cache
			addToCache(k, stMatchSets[k]);
		}
	}

	$input
	.keydown(function(e) {
		// track last key pressed
		lastKeyPressCode = e.keyCode;
		switch(e.keyCode) {
			case 38: // up
				e.preventDefault();
				moveSelect(-1);
				break;
			case 40: // down
				e.preventDefault();
				moveSelect(1);
				break;
			case 9:  // tab
			case 13: // return
				if( selectCurrent() ){
					// make sure to blur off the current field
					$input.get(0).blur();
					e.preventDefault();
				}
				break;
			default:
				active = -1;
				if (timeout) clearTimeout(timeout);
				timeout = setTimeout(function(){onChange();}, options.delay);
				break;
		}
	})
	.focus(function(){
		// track whether the field has focus, we shouldn't process any results if the field no longer has focus
		hasFocus = true;
	})
	.blur(function() {
		// track whether the field has focus
		hasFocus = false;
		if (!mouseDownOnSelect) {
			hideResults();
		}
	});

	hideResultsNow();

	function onChange() {
		// ignore if the following keys are pressed: [del] [shift] [capslock]
		if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ) return $results.hide();
		var v = $input.val();
		if (v == prev) return;
		prev = v;
		if (v.length >= options.minChars) {
			$input.addClass(options.loadingClass);
			requestData(v);
		} else {
			$input.removeClass(options.loadingClass);
			$results.hide();
		}
	};

	function moveSelect(step) {

		var lis = $("li", results);
		if (!lis) return;

		active += step;

		if (active < 0) {
			active = 0;
		} else if (active >= lis.size()) {
			active = lis.size() - 1;
		}

		lis.removeClass("ac_over");

		$(lis[active]).addClass("ac_over");

		// Weird behaviour in IE
		// if (lis[active] && lis[active].scrollIntoView) {
		// 	lis[active].scrollIntoView(false);
		// }

	};

	function selectCurrent() {
		var li = $("li.ac_over", results)[0];
		if (!li) {
			var $li = $("li", results);
			if (options.selectOnly) {
				if ($li.length == 1) li = $li[0];
			} else if (options.selectFirst) {
				li = $li[0];
			}
		}
		if (li) {
			selectItem(li);
			return true;
		} else {
			return false;
		}
	};

	function selectItem(li) {
		if (!li) {
			li = document.createElement("li");
			li.extra = [];
			li.selectValue = "";
		}
		var v = $.trim(li.selectValue ? li.selectValue : li.innerHTML);
		input.lastSelected = v;
		prev = v;
		$results.html("");
		$input.val(v);
		hideResultsNow();
		if (options.onItemSelect) {
			setTimeout(function() { options.onItemSelect(li, $input) }, 1);
		}
	};

	// selects a portion of the input string
	function createSelection(start, end){
		// get a reference to the input element
		var field = $input.get(0);
		if( field.createTextRange ){
			var selRange = field.createTextRange();
			selRange.collapse(true);
			selRange.moveStart("character", start);
			selRange.moveEnd("character", end);
			selRange.select();
		} else if( field.setSelectionRange ){
			field.setSelectionRange(start, end);
		} else {
			if( field.selectionStart ){
				field.selectionStart = start;
				field.selectionEnd = end;
			}
		}
		field.focus();
	};

	// fills in the input box w/the first match (assumed to be the best match)
	function autoFill(sValue){
		// if the last user key pressed was backspace, don't autofill
		if( lastKeyPressCode != 8 ){
			// fill in the value (keep the case the user has typed)
			$input.val($input.val() + sValue.substring(prev.length));
			// select the portion of the value not typed by the user (so the next character will erase)
			createSelection(prev.length, sValue.length);
		}
	};

	function showResults() {
		// get the position of the input field right now (in case the DOM is shifted)
		var pos = findPos(input);
		// either use the specified width, or autocalculate based on form element
		var iWidth = (options.width > 0) ? options.width : $input.width();
		// reposition
		$results.css({
			width: parseInt(iWidth) + "px",
			top: (pos.y + input.offsetHeight) + "px",
			left: pos.x + "px"
		}).show();
	};

	function hideResults() {
		if (timeout) clearTimeout(timeout);
		timeout = setTimeout(hideResultsNow, 200);
	};

	function hideResultsNow() {
		if (hidingResults) {
			return;
		}
		hidingResults = true;

		if (timeout) {
			clearTimeout(timeout);
		}

		var v = $input.removeClass(options.loadingClass).val();

		if ($results.is(":visible")) {
			$results.hide();
		}

		if (options.mustMatch) {
			if (!input.lastSelected || input.lastSelected != v) {
				selectItem(null);
			}
		}

		hidingResults = false;
	};

	function receiveData(q, data) {
		if (data) {
			$input.removeClass(options.loadingClass);
			results.innerHTML = "";

			// if the field no longer has focus or if there are no matches, do not display the drop down
			if( !hasFocus || data.length == 0 ) return hideResultsNow();

			if ($.browser.msie) {
				// we put a styled iframe behind the calendar so HTML SELECT elements don't show through
				$results.append(document.createElement('iframe'));
			}
			results.appendChild(dataToDom(data));
			// autofill in the complete box w/the first match as long as the user hasn't entered in more data
			if( options.autoFill && ($input.val().toLowerCase() == q.toLowerCase()) ) autoFill(data[0][0]);
			showResults();
		} else {
			hideResultsNow();
		}
	};

	function parseData(data) {
		if (!data) return null;
		var parsed = [];
		var rows = data.split(options.lineSeparator);
		for (var i=0; i < rows.length; i++) {
			var row = $.trim(rows[i]);
			if (row) {
				parsed[parsed.length] = row.split(options.cellSeparator);
			}
		}
		return parsed;
	};

	function dataToDom(data) {
		var ul = document.createElement("ul");
		var num = data.length;

		// limited results to a max number
		if( (options.maxItemsToShow > 0) && (options.maxItemsToShow < num) ) num = options.maxItemsToShow;

		for (var i=0; i < num; i++) {
			var row = data[i];
			if (!row) continue;
			var li = document.createElement("li");
			if (options.formatItem) {
				li.innerHTML = options.formatItem(row, i, num);
				li.selectValue = row[0];
			} else {
				li.innerHTML = row[0];
				li.selectValue = row[0];
			}
			var extra = null;
			if (row.length > 1) {
				extra = [];
				for (var j=1; j < row.length; j++) {
					extra[extra.length] = row[j];
				}
			}
			li.extra = extra;
			ul.appendChild(li);

			$(li).hover(
				function() { $("li", ul).removeClass("ac_over"); $(this).addClass("ac_over"); active = $("li", ul).indexOf($(this).get(0)); },
				function() { $(this).removeClass("ac_over"); }
			).click(function(e) {
				e.preventDefault();
				e.stopPropagation();
				selectItem(this)
			});

		}
		$(ul).mousedown(function() {
			mouseDownOnSelect = true;
		}).mouseup(function() {
			mouseDownOnSelect = false;
		});
		return ul;
	};

	function requestData(q) {
		if (!options.matchCase) q = q.toLowerCase();
		var data = options.cacheLength ? loadFromCache(q) : null;
		// recieve the cached data
		if (data) {
			receiveData(q, data);
		// if an AJAX url has been supplied, try loading the data now
		} else if( (typeof options.url == "string") && (options.url.length > 0) ){
			$.get(makeUrl(q), function(data) {
				data = parseData(data);
				addToCache(q, data);
				receiveData(q, data);
			});
		// if there's been no data found, remove the loading class
		} else {
			$input.removeClass(options.loadingClass);
		}
	};

	function makeUrl(q) {
		var sep = options.url.indexOf('?') == -1 ? '?' : '&';
		var url = options.url + sep + "q=" + encodeURI(q);
		for (var i in options.extraParams) {
			url += "&" + i + "=" + encodeURI(options.extraParams[i]);
		}
		return url;
	};

	function loadFromCache(q) {
		if (!q) return null;
		if (cache.data[q]) return cache.data[q];
		if (options.matchSubset) {
			for (var i = q.length - 1; i >= options.minChars; i--) {
				var qs = q.substr(0, i);
				var c = cache.data[qs];
				if (c) {
					var csub = [];
					for (var j = 0; j < c.length; j++) {
						var x = c[j];
						var x0 = x[0];
						if (matchSubset(x0, q)) {
							csub[csub.length] = x;
						}
					}
					return csub;
				}
			}
		}
		return null;
	};

	function matchSubset(s, sub) {
		if (!options.matchCase) s = s.toLowerCase();
		var i = s.indexOf(sub);
		if (i == -1) return false;
		return i == 0 || options.matchContains;
	};

	this.flushCache = function() {
		flushCache();
	};

	this.setExtraParams = function(p) {
		options.extraParams = p;
	};

	this.setExtraParam = function(name, value) {
		options.extraParams[name] = value;
	};

	this.getExtraParams = function() {
		return options.extraParams;
	};

	this.findValue = function(){
		var q = $input.val();

		if (!options.matchCase) q = q.toLowerCase();
		var data = options.cacheLength ? loadFromCache(q) : null;
		if (data) {
			findValueCallback(q, data);
		} else if( (typeof options.url == "string") && (options.url.length > 0) ){
			$.get(makeUrl(q), function(data) {
				data = parseData(data)
				addToCache(q, data);
				findValueCallback(q, data);
			});
		} else {
			// no matches
			findValueCallback(q, null);
		}
	}

	function findValueCallback(q, data){
		if (data) $input.removeClass(options.loadingClass);

		var num = (data) ? data.length : 0;
		var li = null;

		for (var i=0; i < num; i++) {
			var row = data[i];

			if( row[0].toLowerCase() == q.toLowerCase() ){
				li = document.createElement("li");
				if (options.formatItem) {
					li.innerHTML = options.formatItem(row, i, num);
					li.selectValue = row[0];
				} else {
					li.innerHTML = row[0];
					li.selectValue = row[0];
				}
				var extra = null;
				if( row.length > 1 ){
					extra = [];
					for (var j=1; j < row.length; j++) {
						extra[extra.length] = row[j];
					}
				}
				li.extra = extra;
			}
		}

		if( options.onFindValue ) setTimeout(function() { options.onFindValue(li) }, 1);
	}

	function addToCache(q, data) {
		if (!data || !q || !options.cacheLength) return;
		if (!cache.length || cache.length > options.cacheLength) {
			flushCache();
			cache.length++;
		} else if (!cache[q]) {
			cache.length++;
		}
		cache.data[q] = data;
	};

	function findPos(obj) {
		var curleft = obj.offsetLeft || 0;
		var curtop = obj.offsetTop || 0;
		while (obj = obj.offsetParent) {
			curleft += obj.offsetLeft
			curtop += obj.offsetTop
		}
		return {x:curleft,y:curtop};
	}
}

jQuery.fn.autocomplete = function(url, options, data) {
	// Make sure options exists
	options = options || {};
	// Set url as option
	options.url = url;
	// set some bulk local data
	options.data = ((typeof data == "object") && (data.constructor == Array)) ? data : null;

	// Set default values for required options
	options = $.extend({
		inputClass: "ac_input",
		resultsClass: "ac_results",
		lineSeparator: "\n",
		cellSeparator: "|",
		minChars: 1,
		delay: 400,
		matchCase: 0,
		matchSubset: 1,
		matchContains: 0,
		cacheLength: 1,
		mustMatch: 0,
		extraParams: {},
		loadingClass: "ac_loading",
		selectFirst: false,
		selectOnly: false,
		maxItemsToShow: -1,
		autoFill: false,
		width: 0
	}, options);
	options.width = parseInt(options.width, 10);

	this.each(function() {
		var input = this;
		new jQuery.autocomplete(input, options);
	});

	// Don't break the chain
	return this;
}

jQuery.fn.autocompleteArray = function(data, options) {
	return this.autocomplete(null, options, data);
}

jQuery.fn.indexOf = function(e){
	for( var i=0; i<this.length; i++ ){
		if( this[i] == e ) return i;
	}
	return -1;
};
})(jQuery);

/**********************************************************************************/
/*                       /scripts/lib/jquery/jquery.boxy.js                       */
/**********************************************************************************/

/**
 * Boxy 0.1.3 - Facebook-style dialog, with frills
 *
 * (c) 2008 Jason Frame
 * Licensed under the MIT License (LICENSE)
 */

/*
 * jQuery plugin
 *
 * Options:
 *   cache: if true, data retrieved from AJAX calls will be cached. Inline data
 *          will always be cached as we need to stash it someplace outside the DOM
 *          to avoid having multiple elements with the same ID.
 *   message: confirmation message for form submit hook (default: "Please confirm:")
 *   method: AJAX method to use for loading remote content (default: GET)
 * (any leftover options - e.g. 'clone' - will be passed onto the boxy constructor)
 */
jQuery.fn.boxy = function(options) {
    options = options || {};
    return this.each(function() {
        var node = this.nodeName.toLowerCase(), self = this;
        if (node == 'a') {
            jQuery(this).click(function() {
                var active = Boxy.linkedTo(this),
                    href = this.getAttribute('href'),
                    localOptions = jQuery.extend({actuator: this, title: this.title}, options);

                if (active) {
                    active.show();
                } else if (href.indexOf('#') >= 0) {
                    var content = jQuery(href.substr(href.indexOf('#'))),
                        newContent = content.clone(true);
                    content.remove();
                    localOptions.unloadOnHide = false;
                    new Boxy(newContent, localOptions);
                } else { // fall back to AJAX; could do with a same-origin check
                    if (!localOptions.cache) localOptions.unloadOnHide = true;
                    Boxy.load(this.href, localOptions);
                }

                return false;
            });
        } else if (node == 'form') {
            jQuery(this).bind('submit.boxy', function() {
                Boxy.confirm(options.message || 'Please confirm:', function() {
                    jQuery(self).unbind('submit.boxy').submit();
                });
                return false;
            });
        }
    });
};

//
// Boxy Class

function Boxy(element, options) {

    this.boxy = jQuery(Boxy.WRAPPER);
    jQuery.data(this.boxy[0], 'boxy', this);

    this.visible = false;
    this.options = jQuery.extend({}, Boxy.DEFAULTS, options || {});

    if (this.options.modal) {
        this.options = jQuery.extend(this.options, {center: true, draggable: false});
    }

    // options.actuator == DOM element that opened this boxy
    // association will be automatically deleted when this boxy is remove()d
    if (this.options.actuator) {
        jQuery.data(this.options.actuator, 'active.boxy', this);
    }

    this.setContent(element || "<div></div>");
    this._setupTitleBar();

    this.boxy.css('display', 'none').appendTo(document.body);
    this.toTop();

    if (this.options.fixed) {
        if (jQuery.browser.msie && jQuery.browser.version < 7) {
            this.options.fixed = false; // IE6 doesn't support fixed positioning
        } else {
            this.boxy.addClass('fixed');
        }
    }

    if (this.options.center && Boxy._u(this.options.x, this.options.y)) {
        this.center();
    } else {
        this.moveTo(
            Boxy._u(this.options.x) ? this.options.x : Boxy.DEFAULT_X,
            Boxy._u(this.options.y) ? this.options.y : Boxy.DEFAULT_Y
        );
    }

    if (this.options.show) this.show();

};

Boxy.EF = function() {};

jQuery.extend(Boxy, {

    WRAPPER:    "<table cellspacing='0' cellpadding='0' border='0' class='boxy-wrapper'>" +
                "<tr><td class='top-left'></td><td class='top'></td><td class='top-right'></td></tr>" +
                "<tr><td class='left'></td><td class='boxy-inner'></td><td class='right'></td></tr>" +
                "<tr><td class='bottom-left'></td><td class='bottom'></td><td class='bottom-right'></td></tr>" +
                "</table>",

    DEFAULTS: {
        title:                  null,           // titlebar text. titlebar will not be visible if not set.
        closeable:              true,           // display close link in titlebar?
        draggable:              true,           // can this dialog be dragged?
        clone:                  false,          // clone content prior to insertion into dialog?
        center:                 true,           // center dialog in viewport?
        show:                   true,           // show dialog immediately?
        modal:                  false,          // make dialog modal?
        fixed:                  true,           // use fixed positioning, if supported? absolute positioning used otherwise
        closeText:              '[close]',      // text to use for default close link
        unloadOnHide:           false,          // should this dialog be removed from the DOM after being hidden?
        clickToFront:           false,          // bring dialog to foreground on any click (not just titlebar)?
        behaviours:             Boxy.EF,        // function used to apply behaviours to all content embedded in dialog.
        afterDrop:              Boxy.EF,        // callback fired after dialog is dropped. executes in context of Boxy instance.
        afterShow:              Boxy.EF,        // callback fired after dialog becomes visible. executes in context of Boxy instance.
        afterHide:              Boxy.EF,        // callback fired after dialog is hidden. executed in context of Boxy instance.
        beforeUnload:           Boxy.EF         // callback fired after dialog is unloaded. executed in context of Boxy instance.
    },

    DEFAULT_X:          50,
    DEFAULT_Y:          50,
    zIndex:             1337,
    dragConfigured:     false, // only set up one drag handler for all boxys
    resizeConfigured:   false,
    dragging:           null,

    // load a URL and display in boxy
    // url - url to load
    // options keys (any not listed below are passed to boxy constructor)
    //   type: HTTP method, default: GET
    //   cache: cache retrieved content? default: false
    //   filter: jQuery selector used to filter remote content
    load: function(url, options) {

        options = options || {};

        var ajax = {
            url: url, type: 'GET', dataType: 'html', cache: false, success: function(html) {
                html = jQuery(html);
                if (options.filter) html = jQuery(options.filter, html);
                new Boxy(html, options);
            }
        };

        jQuery.each(['type', 'cache'], function() {
            if (this in options) {
                ajax[this] = options[this];
                delete options[this];
            }
        });

        jQuery.ajax(ajax);

    },

    // allows you to get a handle to the containing boxy instance of any element
    // e.g. <a href='#' onclick='alert(Boxy.get(this));'>inspect!</a>.
    // this returns the actual instance of the boxy 'class', not just a DOM element.
    // Boxy.get(this).hide() would be valid, for instance.
    get: function(ele) {
        var p = jQuery(ele).parents('.boxy-wrapper');
        return p.length ? jQuery.data(p[0], 'boxy') : null;
    },

    // returns the boxy instance which has been linked to a given element via the
    // 'actuator' constructor option.
    linkedTo: function(ele) {
        return jQuery.data(ele, 'active.boxy');
    },

    // displays an alert box with a given message, calling optional callback
    // after dismissal.
    alert: function(message, callback, options) {
        return Boxy.ask(message, ['OK'], callback, options);
    },

    // displays an alert box with a given message, calling after callback iff
    // user selects OK.
    confirm: function(message, after, options) {
        return Boxy.ask(message, ['OK', 'Cancel'], function(response) {
            if (response == 'OK') after();
        }, options);
    },

    // asks a question with multiple responses presented as buttons
    // selected item is returned to a callback method.
    // answers may be either an array or a hash. if it's an array, the
    // the callback will received the selected value. if it's a hash,
    // you'll get the corresponding key.
    ask: function(question, answers, callback, options) {

        options = jQuery.extend({modal: true, closeable: false},
                                options || {},
                                {show: true, unloadOnHide: true});

        var body = jQuery('<div></div>').append(jQuery('<div class="question"></div>').html(question));

        // ick
        var map = {}, answerStrings = [];
        if (answers instanceof Array) {
            for (var i = 0; i < answers.length; i++) {
                map[answers[i]] = answers[i];
                answerStrings.push(answers[i]);
            }
        } else {
            for (var k in answers) {
                map[answers[k]] = k;
                answerStrings.push(answers[k]);
            }
        }

        var buttons = jQuery('<form class="answers"></form>');
        buttons.html(jQuery.map(answerStrings, function(v) {
            return "<input type='button' value='" + v + "' />";
        }).join(' '));

        jQuery('input[type=button]', buttons).click(function() {
            var clicked = this;
            Boxy.get(this).hide(function() {
                if (callback) callback(map[clicked.value]);
            });
        });

        body.append(buttons);

        new Boxy(body, options);

    },

    // returns true if a modal boxy is visible, false otherwise
    isModalVisible: function() {
        return jQuery('.boxy-modal-blackout').length > 0;
    },

    _u: function() {
        for (var i = 0; i < arguments.length; i++)
            if (typeof arguments[i] != 'undefined') return false;
        return true;
    },

    _handleResize: function(evt) {
        var d = jQuery(document);
        jQuery('.boxy-modal-blackout').css('display', 'none').css({
            width: d.width(), height: d.height()
        }).css('display', 'block');
    },

    _handleDrag: function(evt) {
        var d;
        if (d = Boxy.dragging) {
            d[0].boxy.css({left: evt.pageX - d[1], top: evt.pageY - d[2]});
        }
    },

    _nextZ: function() {
        return Boxy.zIndex++;
    },

    _viewport: function() {
        var d = document.documentElement, b = document.body, w = window;
        return jQuery.extend(
            jQuery.browser.msie ?
                { left: b.scrollLeft || d.scrollLeft, top: b.scrollTop || d.scrollTop } :
                { left: w.pageXOffset, top: w.pageYOffset },
            !Boxy._u(w.innerWidth) ?
                { width: w.innerWidth, height: w.innerHeight } :
                (!Boxy._u(d) && !Boxy._u(d.clientWidth) && d.clientWidth != 0 ?
                    { width: d.clientWidth, height: d.clientHeight } :
                    { width: b.clientWidth, height: b.clientHeight }) );
    }

});

Boxy.prototype = {

    // Returns the size of this boxy instance without displaying it.
    // Do not use this method if boxy is already visible, use getSize() instead.
    estimateSize: function() {
        this.boxy.css({visibility: 'hidden', display: 'block'});
        var dims = this.getSize();
        this.boxy.css('display', 'none').css('visibility', 'visible');
        return dims;
    },

    // Returns the dimensions of the entire boxy dialog as [width,height]
    getSize: function() {
        return [this.boxy.width(), this.boxy.height()];
    },

    // Returns the dimensions of the content region as [width,height]
    getContentSize: function() {
        var c = this.getContent();
        return [c.width(), c.height()];
    },

    // Returns the position of this dialog as [x,y]
    getPosition: function() {
        var b = this.boxy[0];
        return [b.offsetLeft, b.offsetTop];
    },

    // Returns the center point of this dialog as [x,y]
    getCenter: function() {
        var p = this.getPosition();
        var s = this.getSize();
        return [Math.floor(p[0] + s[0] / 2), Math.floor(p[1] + s[1] / 2)];
    },

    // Returns a jQuery object wrapping the inner boxy region.
    // Not much reason to use this, you're probably more interested in getContent()
    getInner: function() {
        return jQuery('.boxy-inner', this.boxy);
    },

    // Returns a jQuery object wrapping the boxy content region.
    // This is the user-editable content area (i.e. excludes titlebar)
    getContent: function() {
        return jQuery('.boxy-content', this.boxy);
    },

    // Replace dialog content
    setContent: function(newContent) {
        newContent = jQuery(newContent).css({display: 'block'}).addClass('boxy-content');
        if (this.options.clone) newContent = newContent.clone(true);
        this.getContent().remove();
        this.getInner().append(newContent);
        this._setupDefaultBehaviours(newContent);
        this.options.behaviours.call(this, newContent);
        return this;
    },

    // Move this dialog to some position, funnily enough
    moveTo: function(x, y) {
        this.moveToX(x).moveToY(y);
        return this;
    },

    // Move this dialog (x-coord only)
    moveToX: function(x) {
        if (typeof x == 'number') this.boxy.css({left: x});
        else this.centerX();
        return this;
    },

    // Move this dialog (y-coord only)
    moveToY: function(y) {
        if (typeof y == 'number') this.boxy.css({top: y});
        else this.centerY();
        return this;
    },

    // Move this dialog so that it is centered at (x,y)
    centerAt: function(x, y) {
        var s = this[this.visible ? 'getSize' : 'estimateSize']();
        if (typeof x == 'number') this.moveToX(x - s[0] / 2);
        if (typeof y == 'number') this.moveToY(y - s[1] / 2);
        return this;
    },

    centerAtX: function(x) {
        return this.centerAt(x, null);
    },

    centerAtY: function(y) {
        return this.centerAt(null, y);
    },

    // Center this dialog in the viewport
    // axis is optional, can be 'x', 'y'.
    center: function(axis) {
        var v = Boxy._viewport();
        var o = this.options.fixed ? [0, 0] : [v.left, v.top];
        if (!axis || axis == 'x') this.centerAt(o[0] + v.width / 2, null);
        if (!axis || axis == 'y') this.centerAt(null, o[1] + v.height / 2);
        return this;
    },

    // Center this dialog in the viewport (x-coord only)
    centerX: function() {
        return this.center('x');
    },

    // Center this dialog in the viewport (y-coord only)
    centerY: function() {
        return this.center('y');
    },

    // Resize the content region to a specific size
    resize: function(width, height, after) {
        if (!this.visible) return;
        var bounds = this._getBoundsForResize(width, height);
        this.boxy.css({left: bounds[0], top: bounds[1]});
        this.getContent().css({width: bounds[2], height: bounds[3]});
        if (after) after(this);
        return this;
    },

    // Tween the content region to a specific size
    tween: function(width, height, after) {
        if (!this.visible) return;
        var bounds = this._getBoundsForResize(width, height);
        var self = this;
        this.boxy.stop().animate({left: bounds[0], top: bounds[1]});
        this.getContent().stop().animate({width: bounds[2], height: bounds[3]}, function() {
            if (after) after(self);
        });
        return this;
    },

    // Returns true if this dialog is visible, false otherwise
    isVisible: function() {
        return this.visible;
    },

    // Make this boxy instance visible
    show: function() {
        if (this.visible) return;
        if (this.options.modal) {
            var self = this;
            if (!Boxy.resizeConfigured) {
                Boxy.resizeConfigured = true;
                jQuery(window).resize(function() { Boxy._handleResize(); });
            }
            this.modalBlackout = jQuery('<div class="boxy-modal-blackout"></div>')
                .css({zIndex: Boxy._nextZ(),
                      opacity: 0.7,
                      width: jQuery(document).width(),
                      height: jQuery(document).height()})
                .appendTo(document.body);
            this.toTop();
            if (this.options.closeable) {
                jQuery(document.body).bind('keypress.boxy', function(evt) {
                    var key = evt.which || evt.keyCode;
                    if (key == 27) {
                        self.hide();
                        jQuery(document.body).unbind('keypress.boxy');
                    }
                });
            }
        }
        this.boxy.stop().css({opacity: 1}).show();
        this.visible = true;
        this._fire('afterShow');
        return this;
    },

    // Hide this boxy instance
    hide: function(after) {
        if (!this.visible) return;
        var self = this;
        if (this.options.modal) {
            jQuery(document.body).unbind('keypress.boxy');
            this.modalBlackout.animate({opacity: 0}, function() {
                jQuery(this).remove();
            });
        }
        this.boxy.stop().animate({opacity: 0}, 300, function() {
            self.boxy.css({display: 'none'});
            self.visible = false;
            self._fire('afterHide');
            if (after) after(self);
            if (self.options.unloadOnHide) self.unload();
        });
        return this;
    },

    toggle: function() {
        this[this.visible ? 'hide' : 'show']();
    },

    hideAndUnload: function(after) {
        this.options.unloadOnHide = true;
        this.hide(after);
        return this;
    },

    unload: function() {
        this._fire('beforeUnload');
        this.boxy.remove();
        if (this.options.actuator) {
            jQuery.data(this.options.actuator, 'active.boxy', false);
        }
    },

    // Move this dialog box above all other boxy instances
    toTop: function() {
        this.boxy.css({zIndex: Boxy._nextZ()});
        return this;
    },

    // Returns the title of this dialog
    getTitle: function() {
        return jQuery('> .title-bar h2', this.getInner()).html();
    },

    // Sets the title of this dialog
    setTitle: function(t) {
        jQuery('> .title-bar h2', this.getInner()).html(t);
        return this;
    },

    //
    // Don't touch these privates

    _getBoundsForResize: function(width, height) {
        var csize = this.getContentSize();
        var delta = [width - csize[0], height - csize[1]];
        var p = this.getPosition();
        return [Math.max(p[0] - delta[0] / 2, 0),
                Math.max(p[1] - delta[1] / 2, 0), width, height];
    },

    _setupTitleBar: function() {
        if (this.options.title) {
            var self = this;
            var tb = jQuery("<div class='title-bar'></div>").html("<h2>" + this.options.title + "</h2>");
            if (this.options.closeable) {
                tb.append(jQuery("<a href='#' class='close'></a>").html(this.options.closeText));
            }
            if (this.options.draggable) {
                tb[0].onselectstart = function() { return false; }
                tb[0].unselectable = 'on';
                tb[0].style.MozUserSelect = 'none';
                if (!Boxy.dragConfigured) {
                    jQuery(document).mousemove(Boxy._handleDrag);
                    Boxy.dragConfigured = true;
                }
                tb.mousedown(function(evt) {
                    self.toTop();
                    Boxy.dragging = [self, evt.pageX - self.boxy[0].offsetLeft, evt.pageY - self.boxy[0].offsetTop];
                    jQuery(this).addClass('dragging');
                }).mouseup(function() {
                    jQuery(this).removeClass('dragging');
                    Boxy.dragging = null;
                    self._fire('afterDrop');
                });
            }
            this.getInner().prepend(tb);
            this._setupDefaultBehaviours(tb);
        }
    },

    _setupDefaultBehaviours: function(root) {
        var self = this;
        if (this.options.clickToFront) {
            root.click(function() { self.toTop(); });
        }
        jQuery('.close', root).click(function() {
            self.hide();
            return false;
        }).mousedown(function(evt) { evt.stopPropagation(); });
    },

    _fire: function(event) {
        this.options[event].call(this);
    }

};


/**********************************************************************************/
/*                     /scripts/lib/jquery/jquery.filetree.js                     */
/**********************************************************************************/

// jQuery File Tree Plugin
//
// Version 1.01
//
// Cory S.N. LaViska
// A Beautiful Site (http://abeautifulsite.net/)
// 24 March 2008
//
// Visit http://abeautifulsite.net/notebook.php?article=58 for more information
//
// Usage: $('.fileTreeDemo').fileTree( options, callback )
//
// Options:  root           - root folder to display; default = /
//           script         - location of the serverside AJAX file to use; default = jqueryFileTree.php
//           folderEvent    - event to trigger expand/collapse; default = click
//           expandSpeed    - default = 500 (ms); use -1 for no animation
//           collapseSpeed  - default = 500 (ms); use -1 for no animation
//           expandEasing   - easing function to use on expand (optional)
//           collapseEasing - easing function to use on collapse (optional)
//           multiFolder    - whether or not to limit the browser to one subfolder at a time
//           loadMessage    - Message to display while initial tree loads (can be HTML)
//
// History:
//
// 1.01 - updated to work with foreign characters in directory/file names (12 April 2008)
// 1.00 - released (24 March 2008)
//
// TERMS OF USE
//
// jQuery File Tree is licensed under a Creative Commons License and is copyrighted (C)2008 by Cory S.N. LaViska.
// For details, visit http://creativecommons.org/licenses/by/3.0/us/
//
if(jQuery) (function($){

	$.extend($.fn, {
		fileTree: function(o, h) {
			// Defaults
			if( !o ) var o = {};
			if( o.root == undefined ) o.root = '/';
			if( o.script == undefined ) o.script = 'jqueryFileTree.php';
			if( o.folderEvent == undefined ) o.folderEvent = 'click';
			if( o.expandSpeed == undefined ) o.expandSpeed= 300;
			if( o.collapseSpeed == undefined ) o.collapseSpeed= 300;
			if( o.expandEasing == undefined ) o.expandEasing = null;
			if( o.collapseEasing == undefined ) o.collapseEasing = null;
			if( o.multiFolder == undefined ) o.multiFolder = true;
			if( o.loadMessage == undefined ) o.loadMessage = 'Loading...';

			$(this).each( function() {

				function showTree(c, t) {
					$(c).addClass('wait');
					$(".jqueryFileTree.start").remove();
					$.post(o.script, { dir: t }, function(data) {
						$(c).find('.start').html('');
						$(c).removeClass('wait').append(data);
						if( o.root == t ) $(c).find('UL:hidden').show(); else $(c).find('UL:hidden').slideDown({ duration: o.expandSpeed, easing: o.expandEasing });
						bindTree(c);
					});
				}

				function bindTree(t) {
					$(t).find('LI A').bind(o.folderEvent, function() {
						if( $(this).parent().hasClass('directory') ) {
							if( $(this).parent().hasClass('collapsed') ) {
								// Expand
								if( !o.multiFolder ) {
									$(this).parent().parent().find('UL').slideUp({ duration: o.collapseSpeed, easing: o.collapseEasing });
									$(this).parent().parent().find('LI.directory').removeClass('expanded').addClass('collapsed');
								}
								$(this).parent().find('UL').remove(); // cleanup
								showTree( $(this).parent(), escape($(this).attr('rel').match( /.*\// )) );
								$(this).parent().removeClass('collapsed').addClass('expanded');
							} else {
								// Collapse
								$(this).parent().find('UL').slideUp({ duration: o.collapseSpeed, easing: o.collapseEasing });
								$(this).parent().removeClass('expanded').addClass('collapsed');
							}
						} else {
							h($(this).attr('rel'));
						}
						return false;
					});
					// Prevent A from triggering the # on non-click events
					if( o.folderEvent.toLowerCase != 'click' ) $(t).find('LI A').bind('click', function() { return false; });
				}
				// Loading message
				$(this).html('<ul class="jqueryFileTree start"><li class="wait">' + o.loadMessage + '<li></ul>');
				// Get the initial file list
				showTree( $(this), escape(o.root) );
			});
		}
	});

})(jQuery);

/**********************************************************************************/
/*                       /scripts/lib/jquery/jquery.form.js                       */
/**********************************************************************************/

/*
 * jQuery Form Plugin
 * version: 2.12 (06/07/2008)
 * @requires jQuery v1.2.2 or later
 *
 * Examples and documentation at: http://malsup.com/jquery/form/
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Revision: $Id: jquery.form.js 5051 2008-10-08 11:28:53Z rudy $
 */
(function($) {

/*
    Usage Note:
    -----------
    Do not use both ajaxSubmit and ajaxForm on the same form.  These
    functions are intended to be exclusive.  Use ajaxSubmit if you want
    to bind your own submit handler to the form.  For example,

    $(document).ready(function() {
        $('#myForm').bind('submit', function() {
            $(this).ajaxSubmit({
                target: '#output'
            });
            return false; // <-- important!
        });
    });

    Use ajaxForm when you want the plugin to manage all the event binding
    for you.  For example,

    $(document).ready(function() {
        $('#myForm').ajaxForm({
            target: '#output'
        });
    });

    When using ajaxForm, the ajaxSubmit function will be invoked for you
    at the appropriate time.
*/

/**
 * ajaxSubmit() provides a mechanism for immediately submitting
 * an HTML form using AJAX.
 */
$.fn.ajaxSubmit = function(options) {
    // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
    if (!this.length) {
        log('ajaxSubmit: skipping submit process - no element selected');
        return this;
    }

    if (typeof options == 'function')
        options = { success: options };

    options = $.extend({
        url:  this.attr('action') || window.location.toString(),
        type: this.attr('method') || 'GET'
    }, options || {});

    // hook for manipulating the form data before it is extracted;
    // convenient for use with rich editors like tinyMCE or FCKEditor
    var veto = {};
    this.trigger('form-pre-serialize', [this, options, veto]);
    if (veto.veto) {
        log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
        return this;
   }

    var a = this.formToArray(options.semantic);
    if (options.data) {
        options.extraData = options.data;
        for (var n in options.data)
            a.push( { name: n, value: options.data[n] } );
    }

    // give pre-submit callback an opportunity to abort the submit
    if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
        log('ajaxSubmit: submit aborted via beforeSubmit callback');
        return this;
    }

    // fire vetoable 'validate' event
    this.trigger('form-submit-validate', [a, this, options, veto]);
    if (veto.veto) {
        log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
        return this;
    }

    var q = $.param(a);

    if (options.type.toUpperCase() == 'GET') {
        options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
        options.data = null;  // data is null for 'get'
    }
    else
        options.data = q; // data is the query string for 'post'

    var $form = this, callbacks = [];
    if (options.resetForm) callbacks.push(function() { $form.resetForm(); });
    if (options.clearForm) callbacks.push(function() { $form.clearForm(); });

    // perform a load on the target only if dataType is not provided
    if (!options.dataType && options.target) {
        var oldSuccess = options.success || function(){};
        callbacks.push(function(data) {
            $(options.target).html(data).each(oldSuccess, arguments);
        });
    }
    else if (options.success)
        callbacks.push(options.success);

    options.success = function(data, status) {
        for (var i=0, max=callbacks.length; i < max; i++)
            callbacks[i](data, status, $form);
    };

    // are there files to upload?
    var files = $('input:file', this).fieldValue();
    var found = false;
    for (var j=0; j < files.length; j++)
        if (files[j])
            found = true;

    // options.iframe allows user to force iframe mode
   if (options.iframe || found) {
       // hack to fix Safari hang (thanks to Tim Molendijk for this)
       // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
       if ($.browser.safari && options.closeKeepAlive)
           $.get(options.closeKeepAlive, fileUpload);
       else
           fileUpload();
       }
   else
       $.ajax(options);

    // fire 'notify' event
    this.trigger('form-submit-notify', [this, options]);
    return this;


    // private function for handling file uploads (hat tip to YAHOO!)
    function fileUpload() {
        var form = $form[0];

        if ($(':input[@name=submit]', form).length) {
            alert('Error: Form elements must not be named "submit".');
            return;
        }

        var opts = $.extend({}, $.ajaxSettings, options);

        var id = 'jqFormIO' + (new Date().getTime());
        var $io = $('<iframe id="' + id + '" name="' + id + '" />');
        var io = $io[0];

        if ($.browser.msie || $.browser.opera)
            io.src = 'javascript:false;document.write("");';
        $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });

        var xhr = { // mock object
            responseText: null,
            responseXML: null,
            status: 0,
            statusText: 'n/a',
            getAllResponseHeaders: function() {},
            getResponseHeader: function() {},
            setRequestHeader: function() {}
        };

        var g = opts.global;
        // trigger ajax global events so that activity/block indicators work like normal
        if (g && ! $.active++) $.event.trigger("ajaxStart");
        if (g) $.event.trigger("ajaxSend", [xhr, opts]);

        var cbInvoked = 0;
        var timedOut = 0;

        // add submitting element to data if we know it
        var sub = form.clk;
        if (sub) {
            var n = sub.name;
            if (n && !sub.disabled) {
                options.extraData = options.extraData || {};
                options.extraData[n] = sub.value;
                if (sub.type == "image") {
                    options.extraData[name+'.x'] = form.clk_x;
                    options.extraData[name+'.y'] = form.clk_y;
                }
            }
        }

        // take a breath so that pending repaints get some cpu time before the upload starts
        setTimeout(function() {
            // make sure form attrs are set
            var t = $form.attr('target'), a = $form.attr('action');
            $form.attr({
                target:   id,
                encoding: 'multipart/form-data',
                enctype:  'multipart/form-data',
                method:   'POST',
                action:   opts.url
            });

            // support timout
            if (opts.timeout)
                setTimeout(function() { timedOut = true; cb(); }, opts.timeout);

            // add "extra" data to form if provided in options
            var extraInputs = [];
            try {
                if (options.extraData)
                    for (var n in options.extraData)
                        extraInputs.push(
                            $('<input type="hidden" name="'+n+'" value="'+options.extraData[n]+'" />')
                                .appendTo(form)[0]);

                // add iframe to doc and submit the form
                $io.appendTo('body');
                io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
                form.submit();
            }
            finally {
                // reset attrs and remove "extra" input elements
                $form.attr('action', a);
                t ? $form.attr('target', t) : $form.removeAttr('target');
                $(extraInputs).remove();
            }
        }, 10);

        function cb() {
            if (cbInvoked++) return;

            io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);

            var operaHack = 0;
            var ok = true;
            try {
                if (timedOut) throw 'timeout';
                // extract the server response from the iframe
                var data, doc;

                doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;

                if (doc.body == null && !operaHack && $.browser.opera) {
                    // In Opera 9.2.x the iframe DOM is not always traversable when
                    // the onload callback fires so we give Opera 100ms to right itself
                    operaHack = 1;
                    cbInvoked--;
                    setTimeout(cb, 100);
                    return;
                }

                xhr.responseText = doc.body ? doc.body.innerHTML : null;
                xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
                xhr.getResponseHeader = function(header){
                    var headers = {'content-type': opts.dataType};
                    return headers[header];
                };

                if (opts.dataType == 'json' || opts.dataType == 'script') {
                    var ta = doc.getElementsByTagName('textarea')[0];
                    xhr.responseText = ta ? ta.value : xhr.responseText;
                }
                else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
                    xhr.responseXML = toXml(xhr.responseText);
                }
                data = $.httpData(xhr, opts.dataType);
            }
            catch(e){
                ok = false;
                $.handleError(opts, xhr, 'error', e);
            }

            // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
            if (ok) {
                opts.success(data, 'success');
                if (g) $.event.trigger("ajaxSuccess", [xhr, opts]);
            }
            if (g) $.event.trigger("ajaxComplete", [xhr, opts]);
            if (g && ! --$.active) $.event.trigger("ajaxStop");
            if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error');

            // clean up
            setTimeout(function() {
                $io.remove();
                xhr.responseXML = null;
            }, 100);
        };

        function toXml(s, doc) {
            if (window.ActiveXObject) {
                doc = new ActiveXObject('Microsoft.XMLDOM');
                doc.async = 'false';
                doc.loadXML(s);
            }
            else
                doc = (new DOMParser()).parseFromString(s, 'text/xml');
            return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
        };
    };
};

/**
 * ajaxForm() provides a mechanism for fully automating form submission.
 *
 * The advantages of using this method instead of ajaxSubmit() are:
 *
 * 1: This method will include coordinates for <input type="image" /> elements (if the element
 *    is used to submit the form).
 * 2. This method will include the submit element's name/value data (for the element that was
 *    used to submit the form).
 * 3. This method binds the submit() method to the form for you.
 *
 * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
 * passes the options argument along after properly binding events for submit elements and
 * the form itself.
 */
$.fn.ajaxForm = function(options) {
    return this.ajaxFormUnbind().bind('submit.form-plugin',function() {
        $(this).ajaxSubmit(options);
        return false;
    }).each(function() {
        // store options in hash
        $(":submit,input:image", this).bind('click.form-plugin',function(e) {
            var $form = this.form;
            $form.clk = this;
            if (this.type == 'image') {
                if (e.offsetX != undefined) {
                    $form.clk_x = e.offsetX;
                    $form.clk_y = e.offsetY;
                } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
                    var offset = $(this).offset();
                    $form.clk_x = e.pageX - offset.left;
                    $form.clk_y = e.pageY - offset.top;
                } else {
                    $form.clk_x = e.pageX - this.offsetLeft;
                    $form.clk_y = e.pageY - this.offsetTop;
                }
            }
            // clear form vars
            setTimeout(function() { $form.clk = $form.clk_x = $form.clk_y = null; }, 10);
        });
    });
};

// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
$.fn.ajaxFormUnbind = function() {
    this.unbind('submit.form-plugin');
    return this.each(function() {
        $(":submit,input:image", this).unbind('click.form-plugin');
    });

};

/**
 * formToArray() gathers form element data into an array of objects that can
 * be passed to any of the following ajax functions: $.get, $.post, or load.
 * Each object in the array has both a 'name' and 'value' property.  An example of
 * an array for a simple login form might be:
 *
 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
 *
 * It is this array that is passed to pre-submit callback functions provided to the
 * ajaxSubmit() and ajaxForm() methods.
 */
$.fn.formToArray = function(semantic) {
    var a = [];
    if (this.length == 0) return a;

    var form = this[0];
    var els = semantic ? form.getElementsByTagName('*') : form.elements;
    if (!els) return a;
    for(var i=0, max=els.length; i < max; i++) {
        var el = els[i];
        var n = el.name;
        if (!n) continue;

        if (semantic && form.clk && el.type == "image") {
            // handle image inputs on the fly when semantic == true
            if(!el.disabled && form.clk == el)
                a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
            continue;
        }

        var v = $.fieldValue(el, true);
        if (v && v.constructor == Array) {
            for(var j=0, jmax=v.length; j < jmax; j++)
                a.push({name: n, value: v[j]});
        }
        else if (v !== null && typeof v != 'undefined')
            a.push({name: n, value: v});
    }

    if (!semantic && form.clk) {
        // input type=='image' are not found in elements array! handle them here
        var inputs = form.getElementsByTagName("input");
        for(var i=0, max=inputs.length; i < max; i++) {
            var input = inputs[i];
            var n = input.name;
            if(n && !input.disabled && input.type == "image" && form.clk == input)
                a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
        }
    }
    return a;
};

/**
 * Serializes form data into a 'submittable' string. This method will return a string
 * in the format: name1=value1&amp;name2=value2
 */
$.fn.formSerialize = function(semantic) {
    //hand off to jQuery.param for proper encoding
    return $.param(this.formToArray(semantic));
};

/**
 * Serializes all field elements in the jQuery object into a query string.
 * This method will return a string in the format: name1=value1&amp;name2=value2
 */
$.fn.fieldSerialize = function(successful) {
    var a = [];
    this.each(function() {
        var n = this.name;
        if (!n) return;
        var v = $.fieldValue(this, successful);
        if (v && v.constructor == Array) {
            for (var i=0,max=v.length; i < max; i++)
                a.push({name: n, value: v[i]});
        }
        else if (v !== null && typeof v != 'undefined')
            a.push({name: this.name, value: v});
    });
    //hand off to jQuery.param for proper encoding
    return $.param(a);
};

/**
 * Returns the value(s) of the element in the matched set.  For example, consider the following form:
 *
 *  <form><fieldset>
 *      <input name="A" type="text" />
 *      <input name="A" type="text" />
 *      <input name="B" type="checkbox" value="B1" />
 *      <input name="B" type="checkbox" value="B2"/>
 *      <input name="C" type="radio" value="C1" />
 *      <input name="C" type="radio" value="C2" />
 *  </fieldset></form>
 *
 *  var v = $(':text').fieldValue();
 *  // if no values are entered into the text inputs
 *  v == ['','']
 *  // if values entered into the text inputs are 'foo' and 'bar'
 *  v == ['foo','bar']
 *
 *  var v = $(':checkbox').fieldValue();
 *  // if neither checkbox is checked
 *  v === undefined
 *  // if both checkboxes are checked
 *  v == ['B1', 'B2']
 *
 *  var v = $(':radio').fieldValue();
 *  // if neither radio is checked
 *  v === undefined
 *  // if first radio is checked
 *  v == ['C1']
 *
 * The successful argument controls whether or not the field element must be 'successful'
 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
 * The default value of the successful argument is true.  If this value is false the value(s)
 * for each element is returned.
 *
 * Note: This method *always* returns an array.  If no valid value can be determined the
 *       array will be empty, otherwise it will contain one or more values.
 */
$.fn.fieldValue = function(successful) {
    for (var val=[], i=0, max=this.length; i < max; i++) {
        var el = this[i];
        var v = $.fieldValue(el, successful);
        if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length))
            continue;
        v.constructor == Array ? $.merge(val, v) : val.push(v);
    }
    return val;
};

/**
 * Returns the value of the field element.
 */
$.fieldValue = function(el, successful) {
    var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
    if (typeof successful == 'undefined') successful = true;

    if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
        (t == 'checkbox' || t == 'radio') && !el.checked ||
        (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
        tag == 'select' && el.selectedIndex == -1))
            return null;

    if (tag == 'select') {
        var index = el.selectedIndex;
        if (index < 0) return null;
        var a = [], ops = el.options;
        var one = (t == 'select-one');
        var max = (one ? index+1 : ops.length);
        for(var i=(one ? index : 0); i < max; i++) {
            var op = ops[i];
            if (op.selected) {
                // extra pain for IE...
                var v = $.browser.msie && !(op.attributes['value'].specified) ? op.text : op.value;
                if (one) return v;
                a.push(v);
            }
        }
        return a;
    }
    return el.value;
};

/**
 * Clears the form data.  Takes the following actions on the form's input fields:
 *  - input text fields will have their 'value' property set to the empty string
 *  - select elements will have their 'selectedIndex' property set to -1
 *  - checkbox and radio inputs will have their 'checked' property set to false
 *  - inputs of type submit, button, reset, and hidden will *not* be effected
 *  - button elements will *not* be effected
 */
$.fn.clearForm = function() {
    return this.each(function() {
        $('input,select,textarea', this).clearFields();
    });
};

/**
 * Clears the selected form elements.
 */
$.fn.clearFields = $.fn.clearInputs = function() {
    return this.each(function() {
        var t = this.type, tag = this.tagName.toLowerCase();
        if (t == 'text' || t == 'password' || tag == 'textarea')
            this.value = '';
        else if (t == 'checkbox' || t == 'radio')
            this.checked = false;
        else if (tag == 'select')
            this.selectedIndex = -1;
    });
};

/**
 * Resets the form data.  Causes all form elements to be reset to their original value.
 */
$.fn.resetForm = function() {
    return this.each(function() {
        // guard against an input with the name of 'reset'
        // note that IE reports the reset function as an 'object'
        if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType))
            this.reset();
    });
};

/**
 * Enables or disables any matching elements.
 */
$.fn.enable = function(b) {
    if (b == undefined) b = true;
    return this.each(function() {
        this.disabled = !b
    });
};

/**
 * Checks/unchecks any matching checkboxes or radio buttons and
 * selects/deselects and matching option elements.
 */
$.fn.select = function(select) {
    if (select == undefined) select = true;
    return this.each(function() {
        var t = this.type;
        if (t == 'checkbox' || t == 'radio')
            this.checked = select;
        else if (this.tagName.toLowerCase() == 'option') {
            var $sel = $(this).parent('select');
            if (select && $sel[0] && $sel[0].type == 'select-one') {
                // deselect all other options
                $sel.find('option').select(false);
            }
            this.selected = select;
        }
    });
};

// helper fn for console logging
// set $.fn.ajaxSubmit.debug to true to enable debug logging
function log() {
    if ($.fn.ajaxSubmit.debug && window.console && window.console.log)
        window.console.log('[jquery.form] ' + Array.prototype.join.call(arguments,''));
};

})(jQuery);


/**********************************************************************************/
/*                   /scripts/lib/jquery/jquery.hoverIntent.js                    */
/**********************************************************************************/

/**
* hoverIntent is similar to jQuery's built-in "hover" function except that
* instead of firing the onMouseOver event immediately, hoverIntent checks
* to see if the user's mouse has slowed down (beneath the sensitivity
* threshold) before firing the onMouseOver event.
*
* hoverIntent r5 // 2007.03.27 // jQuery 1.1.2+
* <http://cherne.net/brian/resources/jquery.hoverIntent.html>
*
* hoverIntent is currently available for use in all personal or commercial
* projects under both MIT and GPL licenses. This means that you can choose
* the license that best suits your project, and use it accordingly.
*
* // basic usage (just like .hover) receives onMouseOver and onMouseOut functions
* $("ul li").hoverIntent( showNav , hideNav );
*
* // advanced usage receives configuration object only
* $("ul li").hoverIntent({
*	sensitivity: 7, // number = sensitivity threshold (must be 1 or higher)
*	interval: 100,   // number = milliseconds of polling interval
*	over: showNav,  // function = onMouseOver callback (required)
*	timeout: 0,   // number = milliseconds delay before onMouseOut function call
*	out: hideNav    // function = onMouseOut callback (required)
* });
*
* @param  f  onMouseOver function || An object with configuration options
* @param  g  onMouseOut function  || Nothing (use configuration options object)
* @author    Brian Cherne <brian@cherne.net>
*/
(function($) {
	$.fn.hoverIntent = function(f,g) {
		// default configuration options
		var cfg = {
			sensitivity: 7,
			interval: 100,
			timeout: 0
		};
		// override configuration options with user supplied object
		cfg = $.extend(cfg, g ? { over: f, out: g } : f );

		// instantiate variables
		// cX, cY = current X and Y position of mouse, updated by mousemove event
		// pX, pY = previous X and Y position of mouse, set by mouseover and polling interval
		var cX, cY, pX, pY;

		// A private function for getting mouse position
		var track = function(ev) {
			cX = ev.pageX;
			cY = ev.pageY;
		};

		// A private function for comparing current and previous mouse position
		var compare = function(ev,ob) {
			ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
			// compare mouse positions to see if they've crossed the threshold
			if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) {
				$(ob).unbind("mousemove",track);
				// set hoverIntent state to true (so mouseOut can be called)
				ob.hoverIntent_s = 1;
				return cfg.over.apply(ob,[ev]);
			} else {
				// set previous coordinates for next time
				pX = cX; pY = cY;
				// use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
				ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval );
			}
		};

		// A private function for delaying the mouseOut function
		var delay = function(ev,ob) {
			ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
			ob.hoverIntent_s = 0;
			return cfg.out.apply(ob,[ev]);
		};

		// A private function for handling mouse 'hovering'
		var handleHover = function(e) {
			// next three lines copied from jQuery.hover, ignore children onMouseOver/onMouseOut
			var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
			while ( p && p != this ) { try { p = p.parentNode; } catch(e) { p = this; } }
			if ( p == this ) { return false; }

			// copy objects to be passed into t (required for event object to be passed in IE)
			var ev = jQuery.extend({},e);
			var ob = this;

			// cancel hoverIntent timer if it exists
			if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }

			// else e.type == "onmouseover"
			if (e.type == "mouseover") {
				// set "previous" X and Y position based on initial entry point
				pX = ev.pageX; pY = ev.pageY;
				// update "current" X and Y position based on mousemove
				$(ob).bind("mousemove",track);
				// start polling interval (self-calling timeout) to compare mouse coordinates over time
				if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}

			// else e.type == "onmouseout"
			} else {
				// unbind expensive mousemove event
				$(ob).unbind("mousemove",track);
				// if hoverIntent state is true, then call the mouseOut function after the specified delay
				if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
			}
		};

		// bind the function to the two event listeners
		return this.mouseover(handleHover).mouseout(handleHover);
	};
})(jQuery);

/**********************************************************************************/
/*                     /scripts/lib/jquery/jquery.levitip.js                      */
/**********************************************************************************/

/*
 * jQuery leviTip plugin
 * Version: 0.1.1
 *
 * Copyright (c) 2007 Roman Weich
 * http://p.sohei.org
 *
 * Dual licensed under the MIT and GPL licenses
 * (This means that you can choose the license that best suits your project, and use it accordingly):
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Changelog:
 * v 0.1.1 - 2007-28-01
 *	-fix: error while unbinding events on jquery 1.2.2
 *	-fix: spelling error
 * v 0.1.0 - 2007-12-01
 */

/**
 * Yet another tooltip plugin.
 *
 * Features:
 * Displays a user defined tooltip on hover/click over/on a target element.
 * The way the content for the tooltip is retrieved can be configured through the source handlers / source types (see way below).
 *
 * @requires jquery.dimensions plugin
 * @requires {optional} jquery.bgiframe plugin (only for IE <= 6 to prevent a bug when showing the tooltip over a <select>-element)
 * @requires {optional} jquery.hoverintent plugin
 *
 * @param {map} options			An object for optional settings (options described below).
 *
 * @option {string} sourceType		The source type or source handler used to retrieve the contents for the levitip ('attribute', 'element', 'firstchild' and whatever you create yourself).
 *							Default value: 'attribute'
 * @option {string} source			The source as additional parameter to the sourceType. (see the description for the source handlers below)
 *							Default value: 'title'
 * @option {string} activateOn		What kind of user input will activate the levitip?
 *							Possible values: 'hover' and 'click'
 *							Default value: 'hover'
 * @option {string} insertInto		Where to insert the levitip div element into the DOM.
 *							Possible values: 'body' (inserted into the <body>-tag) and 'target' (inserted as child of the target element)
 *							Default value: 'body'
 * @option {string} addClass			A CSS class to add to the levitip div container.
 *							Default value: 'levitip'
 * @option {integer} leftOffset		The number of pixels to offset the position of the levitip to the left.
 *							Default value: 10
 * @option {integer} topOffset		The number of pixels to offset the position of the levitip to the top.
 *							Default value: 10
 * @option {integer} closeDelay		The number of milliseconds to wait before closing the levitip after the user hovered out of the target area.
 *							Default value: 100
 * @option {boolean} dropShadow		To drop or not to drop a shadow.
 *							The shadowbox CSS styles and the myshadow.png are required for this! (see the demo)
 *							Default value: true
 * @option {boolean} useHoverIntent	Use the hoverIntent plugin if found.
 *							Default value: true
 * @option {integer} hiSensitivity		Used by the hoverIntent plugin (sensitivity threshold).
 *							Default value: 7
 * @option {integer} hiInterval		Used by the hoverIntent plugin (milliseconds of polling interval).
 *							Default value: 50
 * @option {function} onOpen		Callback function which is triggered when the levitip is displayed.
 *							The passed parameters are: the levitip element (as jQuery object) and the target element.
 *							Default value: null
 * @option {function} onClose		Callback function which is triggered when the levitip is closed.
 *							The passed parameters are: the levitip element (as jQuery object) and the target element.
 *							Default value: null
 *
 * The examples are below, in the source handler description..
 *
 * @type jQuery
 *
 * @name leviTip
 * @cat Plugins/leviTip
 * @author Roman Weich (http://p.sohei.org)
 */

(function($)
{
	var activeTip = null,
		sourceHandlers = {},
		$tip = null,
		$innerTip = null,
		defaults = {
			sourceType: 'attribute',
			source: 'title',
			activateOn: 'hover',
			insertInto: 'body',
			addClass: 'levitip',
			topOffset: 10,
			leftOffset: 10,
			closeDelay: 100,
			dropShadow: true,
			useHoverIntent: true,
			hiSensitivity: 7,
			hiInterval: 50,
			onOpen: function(){},
			onClose: function(){}
		},
		mouseOver = $.fn.jquery <= "1.2.1" ? 'mouseover' : 'mouseenter',
		mouseOut = $.fn.jquery <= "1.2.1" ? 'mouseout' : 'mouseleave';

	$.extend({
		LeviTip: function(target, options)
		{
			this.init(target, options);
		}
	});
	$.extend($.LeviTip, {
		/**
		 * Adds a source handler object to the list of available handlers.
		 * A source handler is used to retrieve the contents, which will be displayed inside the levitip.
		 * The handler must have the following properties defined:
		 *	{string} type				The name of the handler. You have to use the same name in the sourceType levitip-option.
		 * 	{function} get(levitip-instance)	Searches and returns the element (or string) to be displayed in the levitip.
		 * The following properties are optional:
		 * 	{function} prepare(levitip-instance)	Is called, when the levitip for an element is initialized (e.g. on page load).
		 * 	{function} end(levitip-instance)	Is called, after the levitip was closed.
		 * @param {object} handler	The new source handler.
		 */
		addSourceHandler : function(handler)
		{
			if ( handler.get && handler.type )
				sourceHandlers[handler.type] = handler;
		},
		/**
		 * Closes the currently open levitip.
		 */
		closeLeviTip : function()
		{
			if ( activeTip )
				activeTip.close();
		},
		/**
		 * Sets the global default settings.
		 * @param {object} d	The object holding the settings.
		 */
		setDefaults : function(d)
		{
			$.extend(defaults, d);
		},
		prototype: {
			/**
			 * Initializes the new levitip instance.
			 * @param {element} target	The levitip target element (the one which activates the levitip).
			 * @param {object} o		The object holding the settings.
			 */
			init : function(target, o)
			{
				if ( !target )
				{
					return;
				}

				this.settings = $.extend({}, defaults, o);
				this.target = target;
				this.timer = this.tipHover = false;
				this.handler = sourceHandlers[this.settings.sourceType] || 0;
				this.pos = {cx:0, cy:0, px: 0, py: 0};

				var self = this,
					onHover = (this.settings.activateOn == 'hover' ? function(e)
					{
						if ( self.settings.activateOn == 'hover' )
							self.hoverIn(e);
					} : function(){});

				if ( !$tip )
				{
					$innerTip = $('<div class="innerbox"></div>');
					$tip = $('<div><div class="shadowbox1"></div><div class="shadowbox2"></div><div class="shadowbox3"></div></div>')
								.append($innerTip);
					$tip.css({position:'absolute', display: 'none'}).addClass('levitipouter').appendTo('body');
					//add iframe on ie 6
					if ( $.browser.msie && (!$.browser.version || parseInt($.browser.version) <= 6) && $.fn.bgiframe )
					{
						$tip.bgiframe();
					}
				}

				//no matching source handler found?
				if ( !this.handler )
				{
					return;
				}
				if ( this.handler.prepare )
				{
					this.handler.prepare(this);
				}

				if ( $.fn.hoverIntent && this.settings.useHoverIntent )
				{
					$(target).hoverIntent({
						interval: this.settings.hiInterval,
						sensitivity: this.settings.hiSensitivity,
						over: onHover,
						out: function()
						{
							self.hoverOut();
						},
						timeout: 0
					});
				}
				else
				{
					$(target).hover(onHover, function()
					{
						self.hoverOut();
					});
				}
				if ( this.settings.activateOn == 'click' )
				{
					$(target).click(function(e){
						self.hoverIn(e);
					});
				}
			},
			/**
			 * Displays the levitip.
			 * Calls the user defined onOpen callback function at the end.
			 * @param {object} e		The event object.
			 */
			hoverIn: function(e)
			{
				if ( activeTip ) //already a levitip open?
				{
					if ( activeTip == this ) //the current one is already open?
					{
						if ( this.timer ) //is there a closetimer running?
						{
							clearTimeout(this.timer);
						}
						return;
					}
					else //close the other levitip
					{
						activeTip.close();
						activeTip = null;
					}
				}
				//where to insert?
				var into = ( this.settings.insertInto == 'target' ) ? this.target :
								( this.settings.insertInto == 'body' ) ? 'body' : this.settings.insertInto;

				//insert levitip into document
				$tip.appendTo(into).css({visibility: 'hidden', display:'block'});
				//insert stuff into levitip
				var ins = this.handler.get(this);
				if ( !ins )
					return;
				$innerTip.html(ins).children().show();

				if ( this.settings.addClass )
				{
					$innerTip.addClass(this.settings.addClass);
				}
				//add the css styles to the outer div containers to display the shadow
				if ( this.settings.dropShadow )
				{
					$tip.addClass('outerbox');
				}

				//save mouse position
				this.pos = {cx: e.clientX, cy: e.clientY, px: e.pageX, py: e.pageY};
				//calc/set position
				this.setPosition();

				//show levitip
				$tip.css({display:'none', visibility: ''}).show();
				activeTip = this;
				if ( this.settings.insertInto == 'body' ) //make sure, the levitip wont be closed when moving the cursor out of the target -> over the levitip
				{
					var self = this;
					$tip.hover(function(e){
						self.tipHoverIn(e);
					},
					function(){
						self.tipHoverOut();
					});
				}

				//call user defined callback func
				if ( this.settings.onOpen )
				{
					this.settings.onOpen($tip, this.target);
				}
			},
			/**
			 * HoverOut function (called when hovering out of the target element)
			 */
			hoverOut: function()
			{
				var self = this;
				//close levitip after delay
				this.timer = setTimeout(function(){
					if ( !self.tipHover )
					{
						self.close();
					}
				}, this.settings.closeDelay);
			},
			/**
			 * HoverIn function of the levitip
			 */
			tipHoverIn: function()
			{
				this.tipHover = true;
			},
			/**
			 * HoverOut function of the levitip
			 */
			tipHoverOut: function()
			{
				this.tipHover = false;
				this.hoverOut();
			},
			/**
			 * Calculates and sets the position of the levitip.
			 * Displays the levitip on the lower right position of the cursor. When there is not enough space on the right side, the levitip is displayed on the left side of the cursor.  When there is not enough space
			 * below the cursor, the levitip is displayed on top of the cursor. If the levitip is again outside of the visible area on the left or on top, it will be shifted into view.
			 * Make sure the levitip is set to display: block before calling this function!
			 */
			setPosition: function()
			{
				var posX, posY, ww = $(window).width(), wh = $(window).height(), $op, opo;

				//calc position
				$op = $tip.offsetParent();
				opo = ( this.settings.insertInto == 'body' ) ? {left:0,top:0,scrollLeft:0,scrollTop:0} : $op.offset();
				if ( this.settings.insertInto == 'target' && $op.css('position') == 'fixed' )
				{
					posX = this.pos.cx;
					posY = this.pos.cy;
				}
				else
				{
					posX = this.pos.px;
					posY = this.pos.py;
				}
				posX += this.settings.leftOffset - opo.left - opo.scrollLeft;
				posY += this.settings.topOffset - opo.top - opo.scrollTop;
				//make sure, the tip wont get displayed outside the visible screen
				if ( ww < this.pos.cx + $tip[0].clientWidth + this.settings.leftOffset ) //outside on the right side?
				{
					var wsl = $(window).scrollLeft();
					posX -= $tip[0].clientWidth + this.settings.leftOffset * 2;
					if ( opo.left - wsl + posX < 0 ) //and now outside on the left? :)
					{
						posX -= opo.left - wsl + posX;
					}
				}
				if ( wh < this.pos.cy + $tip[0].clientHeight + this.settings.topOffset ) //outside on the bottom?
				{
					var wst = $(window).scrollTop();
					posY -= $tip[0].clientHeight + this.settings.topOffset * 2;
					if ( opo.top - wst + posY < 0 ) //outside on the top?
					{
						posY -= opo.top - wst + posY;
					}
				}
				//set position
				$tip.css({left: posX, top: posY});
			},
			/**
			 * Closes/hides the levitip.
			 * Calls the user defined onClose callback function at the end.
			 */
			close: function()
			{
				if ( this.timer )
				{
					clearTimeout(this.timer);
				}
				//hide levitip - unbind events - reset position to prevent flickering on next show() - remove dropshadow css class
				$tip.hide().unbind(mouseOver).unbind(mouseOut).css({left:0, top:0}).removeClass('outerbox');

				if ( this.settings.addClass )
				{
					$innerTip.removeClass(this.settings.addClass);
				}

				activeTip = false;
				if ( this.handler.end )
				{
					this.handler.end(this);
				}
				//call user defined callback func
				if ( this.settings.onClose )
				{
					this.settings.onClose($tip, this.target);
				}
			}
		}
	});

	/**
	 * "create" the jquery plugin
	 * the description is at the top of this file..
	 */
	$.fn.extend({
		leviTip: function(options)
		{
			return this.each(function(){
				new $.LeviTip(this, options);
			});
		}
	});

	//add default source handlers

	/**
	 * attribute source handler
	 * displays the attribute string as levitip content. the attribute has to be passed in the source-option.
	 *
	 * @example $('#someid').leviTip({sourceType: 'attribute', source: 'myattr'});
	 * @on <div id="someid" myattr="hello world!">...</div>
	 * @desc Will display "hello world!" as content of the levitip, when hovering over the div with the id #someid.
	 */
	$.LeviTip.addSourceHandler({
		type: 'attribute',
		get: function(levitip)
		{
			var attr = $(levitip.target).attr(levitip.settings.source);
			//remove title attribute (we dont want the default tooltip displayed)
			if ( levitip.settings.source == 'title' )
			{
				levitip.titleAttr = attr;
				$(levitip.target).attr('title', '');
			}
			return attr;
		},
		end: function(levitip)
		{
			//put the title attribute back again
			if ( levitip.settings.source == 'title' && levitip.titleAttr )
			{
				$(levitip.target).attr('title', levitip.titleAttr);
			}
		}
	});
	/**
	 * element source handler
	 * clones the element, which is found through the source-selector, and displays it as levitip content
	 *
	 * @example $('#someid').leviTip({sourceType: 'element', source: '#tooltip'});
	 * @on <div id="someid">...</div>
	 * @on <div id="tooltip">some stuff..<p>hello world!</p></div>
	 * @desc Will display a clone of the #tooltip div with all its contents as levitip, when hovering over the div with the id #someid.
	 *
	 * @example $('#someid').leviTip({sourceType: 'element', source: '#tooltip > p'});
	 * @on <div id="someid">...</div>
	 * @on <div id="tooltip">some stuff..<p>hello world!</p></div>
	 * @desc Will display a clone of the p-element inside the #tooltip div as levitip, when hovering over the div with the id #someid.
	 */
	$.LeviTip.addSourceHandler({
		type: 'element',
		prepare: function(levitip)
		{
			if ( levitip.settings.hideSourceElement )
			{
				$(levitip.settings.source).hide();
			}
		},
		get: function(levitip)
		{
			var $e = [];
			if ( levitip.settings.source )
			{
				$e = $(levitip.settings.source);
				if ( $e.length )
					$e = $e.clone(true).show();
			}
			return $e;
		}
	});
	/**
	 * firstchild source handler
	 * clones the element, which is the first child of the target element, and displays it as levitip content.
	 * the source-option will be ignored
	 *
	 * @example $('#someid').leviTip({sourceType: 'firstchild'});
	 * @on <div id="someid"><p>jquery rocks!</p><p>hello world!</p></div>
	 * @desc Will display a clone of the p-element ("jquery rocks!") inside the #someid div as levitip, when hovering over the div with the id #someid.
	 */
	$.LeviTip.addSourceHandler({
		type: 'firstchild',
		prepare: function(levitip)
		{
			if ( levitip.settings.hideSourceElement )
			{
				$(levitip.target.firstChild).hide();
			}
		},
		get: function(levitip)
		{
			var $e = $(levitip.target.firstChild);
			if ( $e.length )
				$e = $e.clone(true).show();
			return $e;
		}
	});
})(jQuery);

/**********************************************************************************/
/*                   /scripts/lib/jquery/jquery.lightbox-0.5.js                   */
/**********************************************************************************/

/**
 * jQuery lightBox plugin
 * This jQuery plugin was inspired and based on Lightbox 2 by Lokesh Dhakar (http://www.huddletogether.com/projects/lightbox2/)
 * and adapted to me for use like a plugin from jQuery.
 * @name jquery-lightbox-0.5.js
 * @author Leandro Vieira Pinho - http://leandrovieira.com
 * @version 0.5
 * @date April 11, 2008
 * @category jQuery plugin
 * @copyright (c) 2008 Leandro Vieira Pinho (leandrovieira.com)
 * @license CC Attribution-No Derivative Works 2.5 Brazil - http://creativecommons.org/licenses/by-nd/2.5/br/deed.en_US
 * @example Visit http://leandrovieira.com/projects/jquery/lightbox/ for more informations about this jQuery plugin
 */

// Offering a Custom Alias suport - More info: http://docs.jquery.com/Plugins/Authoring#Custom_Alias
(function($) {
	/**
	 * $ is an alias to jQuery object
	 *
	 */
	$.fn.lightBox = function(settings) {
		// Settings to configure the jQuery lightBox plugin how you like
		settings = jQuery.extend({
			// Configuration related to overlay
			overlayBgColor: 		'#000',		// (string) Background color to overlay; inform a hexadecimal value like: #RRGGBB. Where RR, GG, and BB are the hexadecimal values for the red, green, and blue values of the color.
			overlayOpacity:			0.8,		// (integer) Opacity value to overlay; inform: 0.X. Where X are number from 0 to 9
			// Configuration related to navigation
			fixedNavigation:		false,		// (boolean) Boolean that informs if the navigation (next and prev button) will be fixed or not in the interface.
			// Configuration related to images
			imageLoading:			'images/lightbox-ico-loading.gif',		// (string) Path and the name of the loading icon
			imageBtnPrev:			'images/lightbox-btn-prev.gif',			// (string) Path and the name of the prev button image
			imageBtnNext:			'images/lightbox-btn-next.gif',			// (string) Path and the name of the next button image
			imageBtnClose:			'images/lightbox-btn-close.gif',		// (string) Path and the name of the close btn
			imageBlank:				'images/lightbox-blank.gif',			// (string) Path and the name of a blank image (one pixel)
			// Configuration related to container image box
			containerBorderSize:	10,			// (integer) If you adjust the padding in the CSS for the container, #lightbox-container-image-box, you will need to update this value
			containerResizeSpeed:	400,		// (integer) Specify the resize duration of container image. These number are miliseconds. 400 is default.
			// Configuration related to texts in caption. For example: Image 2 of 8. You can alter either "Image" and "of" texts.
			txtImage:				'Image',	// (string) Specify text "Image"
			txtOf:					'of',		// (string) Specify text "of"
			// Configuration related to keyboard navigation
			keyToClose:				'c',		// (string) (c = close) Letter to close the jQuery lightBox interface. Beyond this letter, the letter X and the SCAPE key is used to.
			keyToPrev:				'p',		// (string) (p = previous) Letter to show the previous image
			keyToNext:				'n',		// (string) (n = next) Letter to show the next image.
			// Donґt alter these variables in any way
			imageArray:				[],
			activeImage:			0
		},settings);
		// Caching the jQuery object with all elements matched
		var jQueryMatchedObj = this; // This, in this context, refer to jQuery object
		/**
		 * Initializing the plugin calling the start function
		 *
		 * @return boolean false
		 */
		function _initialize() {
			_start(this,jQueryMatchedObj); // This, in this context, refer to object (link) which the user have clicked
			return false; // Avoid the browser following the link
		}
		/**
		 * Start the jQuery lightBox plugin
		 *
		 * @param object objClicked The object (link) whick the user have clicked
		 * @param object jQueryMatchedObj The jQuery object with all elements matched
		 */
		function _start(objClicked,jQueryMatchedObj) {
			// Hime some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
			$('embed, object, select').css({ 'visibility' : 'hidden' });
			// Call the function to create the markup structure; style some elements; assign events in some elements.
			_set_interface();
			// Unset total images in imageArray
			settings.imageArray.length = 0;
			// Unset image active information
			settings.activeImage = 0;
			// We have an image set? Or just an image? Letґs see it.
			if ( jQueryMatchedObj.length == 1 ) {
				settings.imageArray.push(new Array(objClicked.getAttribute('href'),objClicked.getAttribute('title')));
			} else {
				// Add an Array (as many as we have), with href and title atributes, inside the Array that storage the images references
				for ( var i = 0; i < jQueryMatchedObj.length; i++ ) {
					settings.imageArray.push(new Array(jQueryMatchedObj[i].getAttribute('href'),jQueryMatchedObj[i].getAttribute('title')));
				}
			}
			while ( settings.imageArray[settings.activeImage][0] != objClicked.getAttribute('href') ) {
				settings.activeImage++;
			}
			// Call the function that prepares image exibition
			_set_image_to_view();
		}
		/**
		 * Create the jQuery lightBox plugin interface
		 *
		 * The HTML markup will be like that:
			<div id="jquery-overlay"></div>
			<div id="jquery-lightbox">
				<div id="lightbox-container-image-box">
					<div id="lightbox-container-image">
						<img src="../fotos/XX.jpg" id="lightbox-image">
						<div id="lightbox-nav">
							<a href="#" id="lightbox-nav-btnPrev"></a>
							<a href="#" id="lightbox-nav-btnNext"></a>
						</div>
						<div id="lightbox-loading">
							<a href="#" id="lightbox-loading-link">
								<img src="../images/lightbox-ico-loading.gif">
							</a>
						</div>
					</div>
				</div>
				<div id="lightbox-container-image-data-box">
					<div id="lightbox-container-image-data">
						<div id="lightbox-image-details">
							<span id="lightbox-image-details-caption"></span>
							<span id="lightbox-image-details-currentNumber"></span>
						</div>
						<div id="lightbox-secNav">
							<a href="#" id="lightbox-secNav-btnClose">
								<img src="../images/lightbox-btn-close.gif">
							</a>
						</div>
					</div>
				</div>
			</div>
		 *
		 */
		function _set_interface() {
			// Apply the HTML markup into body tag
			$('body').append('<div id="jquery-overlay"></div><div id="jquery-lightbox"><div id="lightbox-container-image-box"><div id="lightbox-container-image"><img id="lightbox-image"><div style="" id="lightbox-nav"><a href="#" id="lightbox-nav-btnPrev"></a><a href="#" id="lightbox-nav-btnNext"></a></div><div id="lightbox-loading"><a href="#" id="lightbox-loading-link"><img src="' + settings.imageLoading + '"></a></div></div></div><div id="lightbox-container-image-data-box"><div id="lightbox-container-image-data"><div id="lightbox-image-details"><span id="lightbox-image-details-caption"></span><span id="lightbox-image-details-currentNumber"></span></div><div id="lightbox-secNav"><a href="#" id="lightbox-secNav-btnClose"><img src="' + settings.imageBtnClose + '"></a></div></div></div></div>');
			// Get page sizes
			var arrPageSizes = ___getPageSize();
			// Style overlay and show it
			$('#jquery-overlay').css({
				backgroundColor:	settings.overlayBgColor,
				opacity:			settings.overlayOpacity,
				width:				arrPageSizes[0],
				height:				arrPageSizes[1]
			}).fadeIn();
			// Get page scroll
			var arrPageScroll = ___getPageScroll();
			// Calculate top and left offset for the jquery-lightbox div object and show it
			$('#jquery-lightbox').css({
				top:	arrPageScroll[1] + (arrPageSizes[3] / 10),
				left:	arrPageScroll[0]
			}).show();
			// Assigning click events in elements to close overlay
			$('#jquery-overlay,#jquery-lightbox').click(function() {
				_finish();
			});
			// Assign the _finish function to lightbox-loading-link and lightbox-secNav-btnClose objects
			$('#lightbox-loading-link,#lightbox-secNav-btnClose').click(function() {
				_finish();
				return false;
			});
			// If window was resized, calculate the new overlay dimensions
			$(window).resize(function() {
				// Get page sizes
				var arrPageSizes = ___getPageSize();
				// Style overlay and show it
				$('#jquery-overlay').css({
					width:		arrPageSizes[0],
					height:		arrPageSizes[1]
				});
				// Get page scroll
				var arrPageScroll = ___getPageScroll();
				// Calculate top and left offset for the jquery-lightbox div object and show it
				$('#jquery-lightbox').css({
					top:	arrPageScroll[1] + (arrPageSizes[3] / 10),
					left:	arrPageScroll[0]
				});
			});
		}
		/**
		 * Prepares image exibition; doing a imageґs preloader to calculate itґs size
		 *
		 */
		function _set_image_to_view() { // show the loading
			// Show the loading
			$('#lightbox-loading').show();
			if ( settings.fixedNavigation ) {
				$('#lightbox-image,#lightbox-container-image-data-box,#lightbox-image-details-currentNumber').hide();
			} else {
				// Hide some elements
				$('#lightbox-image,#lightbox-nav,#lightbox-nav-btnPrev,#lightbox-nav-btnNext,#lightbox-container-image-data-box,#lightbox-image-details-currentNumber').hide();
			}
			// Image preload process
			var objImagePreloader = new Image();
			objImagePreloader.onload = function() {
				$('#lightbox-image').attr('src',settings.imageArray[settings.activeImage][0]);
				// Perfomance an effect in the image container resizing it
				_resize_container_image_box(objImagePreloader.width,objImagePreloader.height);
				//	clear onLoad, IE behaves irratically with animated gifs otherwise
				objImagePreloader.onload=function(){};
			};
			objImagePreloader.src = settings.imageArray[settings.activeImage][0];
		};
		/**
		 * Perfomance an effect in the image container resizing it
		 *
		 * @param integer intImageWidth The imageґs width that will be showed
		 * @param integer intImageHeight The imageґs height that will be showed
		 */
		function _resize_container_image_box(intImageWidth,intImageHeight) {
			// Get current width and height
			var intCurrentWidth = $('#lightbox-container-image-box').width();
			var intCurrentHeight = $('#lightbox-container-image-box').height();
			// Get the width and height of the selected image plus the padding
			var intWidth = (intImageWidth + (settings.containerBorderSize * 2)); // Plus the imageґs width and the left and right padding value
			var intHeight = (intImageHeight + (settings.containerBorderSize * 2)); // Plus the imageґs height and the left and right padding value
			// Diferences
			var intDiffW = intCurrentWidth - intWidth;
			var intDiffH = intCurrentHeight - intHeight;
			// Perfomance the effect
			$('#lightbox-container-image-box').animate({ width: intWidth, height: intHeight },settings.containerResizeSpeed,function() { _show_image(); });
			if ( ( intDiffW == 0 ) && ( intDiffH == 0 ) ) {
				if ( $.browser.msie ) {
					___pause(250);
				} else {
					___pause(100);
				}
			}
			$('#lightbox-container-image-data-box').css({ width: intImageWidth });
			$('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({ height: intImageHeight + (settings.containerBorderSize * 2) });
		};
		/**
		 * Show the prepared image
		 *
		 */
		function _show_image() {
			$('#lightbox-loading').hide();
			$('#lightbox-image').fadeIn(function() {
				_show_image_data();
				_set_navigation();
			});
			_preload_neighbor_images();
		};
		/**
		 * Show the image information
		 *
		 */
		function _show_image_data() {
			$('#lightbox-container-image-data-box').slideDown('fast');
			$('#lightbox-image-details-caption').hide();
			if ( settings.imageArray[settings.activeImage][1] ) {
				$('#lightbox-image-details-caption').html(settings.imageArray[settings.activeImage][1]).show();
			}
			// If we have a image set, display 'Image X of X'
			if ( settings.imageArray.length > 1 ) {
				$('#lightbox-image-details-currentNumber').html(settings.txtImage + ' ' + ( settings.activeImage + 1 ) + ' ' + settings.txtOf + ' ' + settings.imageArray.length).show();
			}
		}
		/**
		 * Display the button navigations
		 *
		 */
		function _set_navigation() {
			$('#lightbox-nav').show();

			// Instead to define this configuration in CSS file, we define here. And itґs need to IE. Just.
			$('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({ 'background' : 'transparent url(' + settings.imageBlank + ') no-repeat' });

			// Show the prev button, if not the first image in set
			if ( settings.activeImage != 0 ) {
				if ( settings.fixedNavigation ) {
					$('#lightbox-nav-btnPrev').css({ 'background' : 'url(' + settings.imageBtnPrev + ') left 15% no-repeat' })
						.unbind()
						.bind('click',function() {
							settings.activeImage = settings.activeImage - 1;
							_set_image_to_view();
							return false;
						});
				} else {
					// Show the images button for Next buttons
					$('#lightbox-nav-btnPrev').unbind().hover(function() {
						$(this).css({ 'background' : 'url(' + settings.imageBtnPrev + ') left 15% no-repeat' });
					},function() {
						$(this).css({ 'background' : 'transparent url(' + settings.imageBlank + ') no-repeat' });
					}).show().bind('click',function() {
						settings.activeImage = settings.activeImage - 1;
						_set_image_to_view();
						return false;
					});
				}
			}

			// Show the next button, if not the last image in set
			if ( settings.activeImage != ( settings.imageArray.length -1 ) ) {
				if ( settings.fixedNavigation ) {
					$('#lightbox-nav-btnNext').css({ 'background' : 'url(' + settings.imageBtnNext + ') right 15% no-repeat' })
						.unbind()
						.bind('click',function() {
							settings.activeImage = settings.activeImage + 1;
							_set_image_to_view();
							return false;
						});
				} else {
					// Show the images button for Next buttons
					$('#lightbox-nav-btnNext').unbind().hover(function() {
						$(this).css({ 'background' : 'url(' + settings.imageBtnNext + ') right 15% no-repeat' });
					},function() {
						$(this).css({ 'background' : 'transparent url(' + settings.imageBlank + ') no-repeat' });
					}).show().bind('click',function() {
						settings.activeImage = settings.activeImage + 1;
						_set_image_to_view();
						return false;
					});
				}
			}
			// Enable keyboard navigation
			_enable_keyboard_navigation();
		}
		/**
		 * Enable a support to keyboard navigation
		 *
		 */
		function _enable_keyboard_navigation() {
			$(document).keydown(function(objEvent) {
				_keyboard_action(objEvent);
			});
		}
		/**
		 * Disable the support to keyboard navigation
		 *
		 */
		function _disable_keyboard_navigation() {
			$(document).unbind();
		}
		/**
		 * Perform the keyboard actions
		 *
		 */
		function _keyboard_action(objEvent) {
			// To ie
			if ( objEvent == null ) {
				keycode = event.keyCode;
				escapeKey = 27;
			// To Mozilla
			} else {
				keycode = objEvent.keyCode;
				escapeKey = objEvent.DOM_VK_ESCAPE;
			}
			// Get the key in lower case form
			key = String.fromCharCode(keycode).toLowerCase();
			// Verify the keys to close the ligthBox
			if ( ( key == settings.keyToClose ) || ( key == 'x' ) || ( keycode == escapeKey ) ) {
				_finish();
			}
			// Verify the key to show the previous image
			if ( ( key == settings.keyToPrev ) || ( keycode == 37 ) ) {
				// If weґre not showing the first image, call the previous
				if ( settings.activeImage != 0 ) {
					settings.activeImage = settings.activeImage - 1;
					_set_image_to_view();
					_disable_keyboard_navigation();
				}
			}
			// Verify the key to show the next image
			if ( ( key == settings.keyToNext ) || ( keycode == 39 ) ) {
				// If weґre not showing the last image, call the next
				if ( settings.activeImage != ( settings.imageArray.length - 1 ) ) {
					settings.activeImage = settings.activeImage + 1;
					_set_image_to_view();
					_disable_keyboard_navigation();
				}
			}
		}
		/**
		 * Preload prev and next images being showed
		 *
		 */
		function _preload_neighbor_images() {
			if ( (settings.imageArray.length -1) > settings.activeImage ) {
				objNext = new Image();
				objNext.src = settings.imageArray[settings.activeImage + 1][0];
			}
			if ( settings.activeImage > 0 ) {
				objPrev = new Image();
				objPrev.src = settings.imageArray[settings.activeImage -1][0];
			}
		}
		/**
		 * Remove jQuery lightBox plugin HTML markup
		 *
		 */
		function _finish() {
			$('#jquery-lightbox').remove();
			$('#jquery-overlay').fadeOut(function() { $('#jquery-overlay').remove(); });
			// Show some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
			$('embed, object, select').css({ 'visibility' : 'visible' });
		}
		/**
		 / THIRD FUNCTION
		 * getPageSize() by quirksmode.com
		 *
		 * @return Array Return an array with page width, height and window width, height
		 */
		function ___getPageSize() {
			var xScroll, yScroll;
			if (window.innerHeight && window.scrollMaxY) {
				xScroll = window.innerWidth + window.scrollMaxX;
				yScroll = window.innerHeight + window.scrollMaxY;
			} else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
				xScroll = document.body.scrollWidth;
				yScroll = document.body.scrollHeight;
			} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
				xScroll = document.body.offsetWidth;
				yScroll = document.body.offsetHeight;
			}
			var windowWidth, windowHeight;
			if (self.innerHeight) {	// all except Explorer
				if(document.documentElement.clientWidth){
					windowWidth = document.documentElement.clientWidth;
				} else {
					windowWidth = self.innerWidth;
				}
				windowHeight = self.innerHeight;
			} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
				windowWidth = document.documentElement.clientWidth;
				windowHeight = document.documentElement.clientHeight;
			} else if (document.body) { // other Explorers
				windowWidth = document.body.clientWidth;
				windowHeight = document.body.clientHeight;
			}
			// for small pages with total height less then height of the viewport
			if(yScroll < windowHeight){
				pageHeight = windowHeight;
			} else {
				pageHeight = yScroll;
			}
			// for small pages with total width less then width of the viewport
			if(xScroll < windowWidth){
				pageWidth = xScroll;
			} else {
				pageWidth = windowWidth;
			}
			arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight);
			return arrayPageSize;
		};
		/**
		 / THIRD FUNCTION
		 * getPageScroll() by quirksmode.com
		 *
		 * @return Array Return an array with x,y page scroll values.
		 */
		function ___getPageScroll() {
			var xScroll, yScroll;
			if (self.pageYOffset) {
				yScroll = self.pageYOffset;
				xScroll = self.pageXOffset;
			} else if (document.documentElement && document.documentElement.scrollTop) {	 // Explorer 6 Strict
				yScroll = document.documentElement.scrollTop;
				xScroll = document.documentElement.scrollLeft;
			} else if (document.body) {// all other Explorers
				yScroll = document.body.scrollTop;
				xScroll = document.body.scrollLeft;
			}
			arrayPageScroll = new Array(xScroll,yScroll);
			return arrayPageScroll;
		};
		 /**
		  * Stop the code execution from a escified time in milisecond
		  *
		  */
		 function ___pause(ms) {
			var date = new Date();
			curDate = null;
			do { var curDate = new Date(); }
			while ( curDate - date < ms);
		 };
		// Return the jQuery object for chaining. The unbind method is used to avoid click conflict when the plugin is called more than once
		return this.unbind('click').click(_initialize);
	};
})(jQuery); // Call and execute the function immediately passing the jQuery object

/**********************************************************************************/
/*                    /scripts/lib/jquery/jquery.clipboard.js                     */
/**********************************************************************************/

/*

Clipboard - Copy utility for jQuery
Version 2.0
November 24, 2007

Project page:

	http://bradleysepos.com/projects/jquery/clipboard/

Files:

	Source:            jquery.clipboard.js
	Source (packed):   jquery.clipboard.pack.js
	Flash helper:      jquery.clipboard.swf

Usage examples:

	// Basic usage:
	$.clipboardReady(function(){
		$( "a" ).click(function(){
			$.clipboard( "You clicked on a link and copied this text!" );
			return false;
		});
	});

	// With options:
	$.clipboardReady(function(){
		$( "a" ).click(function(){
			$.clipboard( "You clicked on a link and copied this text!" );
			return false;
		});
	}, { swfpath: "path/to/jquery.clipboard.swf", debug: true } );

Compatibility:

	IE 6+, FF 2+, Safari 2+, Opera 9+
	Requires jQuery 1.2+
	Non-IE browsers require Flash 8 or higher.


Released under an MIT-style license

LICENSE
------------------------------------------------------------------------

Copyright (c) 2007 Bradley Sepos

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.

------------------------------------------------------------------------

*/

(function($){

// Some variables that need scope
var flashMinVersion = [8,0,0];
var flashDetectedVersion = [0,0,0];
var swfpath;
var debugging;

var flashdetect = function( minVersion ){
	// Flash detection
	// Based on swfObject 2.0: http://code.google.com/p/swfobject/
	var d = null;
	if (typeof navigator.plugins != "undefined" && typeof navigator.plugins["Shockwave Flash"] == "object") {
		d = navigator.plugins["Shockwave Flash"].description;
		if (d) {
			// Got Flash, parse version
			d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
			flashDetectedVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
			flashDetectedVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
			if ( /r/.test(d) ) {
				flashDetectedVersion[2] = parseInt(d.replace(/^.*r(.*)$/, "$1"), 10);
			} else {
				flashDetectedVersion[2] = 0;
			}
			if (flashDetectedVersion[0] > minVersion[0] || (flashDetectedVersion[0] == minVersion[0] && flashDetectedVersion[1] > minVersion[1]) || (flashDetectedVersion[0] == minVersion[0] && flashDetectedVersion[1] == minVersion[1] && flashDetectedVersion[2] >= minVersion[2])){
				// Version ok
				return true;
			} else {
				// Version too old
				return false;
			}
		}
	}
	// No Flash detected
	return false;
};

var iecopydetect = function(){
	// Check for IE method
	if ( typeof window.clipboardData != "undefined" ){
		return true;
	}
};

var debug = function( string ){
	if ( debugging && typeof console != "undefined" && typeof console.log == "function" ){
		console.log( string );
	}
};

var swfready = function(){

	// The swf is already loaded, ignore
	if ( $.clipboardReady.done ) {
		return false;
	}

	// Count how many times swfready() has been called
	if ( typeof $.clipboardReady.counter == 'undefined' ){
		// Init counter
		$.clipboardReady.counter = 0;
	}
	// Increment counter
	$.clipboardReady.counter++;
	if ( $.clipboardReady.counter > 599 ){
		// Terminate process after 600 executions to avoid calling indefinitely and crashing some
		// browsers (observed in Firefox 2.x). At 100ms interval, this should be plenty of time for
		// the swf to load on even the slowest connections.
		clearInterval( $.clipboardReady.timer );
		// Debug
		debug("Waited "+$.clipboardReady.counter/10+" seconds for Flash object to load, terminating.");
		return false;
	}
	if ( ($.clipboardReady.counter % 100) == 0 ){
		// Debug
		debug("Waited "+$.clipboardReady.counter/10+" seconds for Flash object to load so far...");
	}

	// Check to see if the swf's external interface is ready
	var swf = $("#jquery_clipboard_swf:first");
	var swfdom = $(swf).get(0);
	if ( typeof swfdom.jqueryClipboardCopy == "function" && swfdom.jqueryClipboardAvailable ){

		// Swf is ready, stop checking
		clearInterval( $.clipboardReady.timer );
		$.clipboardReady.timer = null;

		// Set copy method
		$.clipboard.method = 'flash';

		// Execute queued functions
		for ( var i = 0; i < $.clipboardReady.ready.length; i++ ){
			$.clipboardReady.ready[i]();
		}

		// Remember that the swf is ready
		$.clipboardReady.ready = null;
		$.clipboardReady.done = true;

		// Everything is totally ready now
		debug( "jQuery.clipboard: OK. Initialized and ready to copy using Flash method." );
	}
};

$.clipboardReady = function( f, options ){

	// Options
	options = jQuery.extend({
		swfpath: "jquery.clipboard.swf",
		debug: false
	}, options);
	swfpath = options.swfpath;
	debugging = options.debug;

	// Run immediately if IE method available
	if ( iecopydetect() ){
		$.clipboard.method = 'ie';
		debug( "jQuery.clipboard: OK. Initialized and ready to copy using native IE method." );
		return f();
	}

	// Run immediately if Flash 8 is available and loaded
	if ( $.clipboardReady.done ){
		return f();
	}

	// If we've already added a function
	if ( $.clipboardReady.timer ){

		// Add to the existing array
		$.clipboardReady.ready.push( f );

	} else {

		// Check for Flash and Flash version
		if ( flashdetect( flashMinVersion ) ){

			// Flash detected OK

			// Destroy any existing elements
			$( "#jquery_clipboard_swf" ).remove();
			$( "#jquery_clipboard_div" ).remove();

			// Create the wrapper div
			var div;
			div = $( "<div/>" )
				.attr( "id", "jquery_clipboard_div" )
				.css( "width", "0" )
				.css( "height", "0" )
				.appendTo( "body" )
				.html( "" );
			// Create the helper swf
			// Use embed method since we're only targeting non-IE browsers anyway
			var swf;
			swf = $( '<embed id="jquery_clipboard_swf" name="jquery_clipboard_swf" src="'+swfpath+'" type="application/x-shockwave-flash"></embed>' );
			$( swf )
				.css( "width", "0" )
				.css( "height", "0" )
				.appendTo( div );

			// Init the functions array
			$.clipboardReady.ready = [ f ];

			// Continually check to see if the swf is loaded
			$.clipboardReady.timer = setInterval( swfready, 100 );

			// Debug
			debug( "jQuery.clipboard: INFO. Waiting for Flash object to become ready. Detected Flash version: "+flashDetectedVersion[0]+"."+flashDetectedVersion[1]+"."+flashDetectedVersion[2] );

		} else if ( flashDetectedVersion[0] === 0 ){

			// Flash not detected
			debug( "jQuery.clipboard: ERROR. Flash plugin not detected." );
			return false;

		} else {

			// Flash version too old
			debug( "jQuery.clipboard: ERROR. Minimum Flash version: "+flashMinVersion[0]+"."+flashMinVersion[1]+"."+flashMinVersion[2]+" Detected Flash version: "+flashDetectedVersion[0]+"."+flashDetectedVersion[1]+"."+flashDetectedVersion[2] );
			return false;

		}
	}
};

$.clipboard = function( text ){

	// Check arguments
	if ( arguments.length < 1 || typeof text != "string" ){
		// First argument is not text
		debug( "jQuery.clipboard: ERROR. Nothing to copy. You must specify a string as the first parameter." );
		return false;
	}

	// Looks good, perform copy

	// Internet Explorer's built-in method
	if ( $.clipboard.method == 'ie' ){
		try {
			window.clipboardData.setData( "Text", text );
			debug( "jQuery.clipboard: OK. Copied "+text.length+" bytes to clipboard using native IE method." );
			return true;
		} catch (e) {
			debug( "jQuery.clipboard: ERROR. Tried to copy using native IE method but an unknown error occurred." );
			return false;
		}
	}

	// Flash method
	if ( $.clipboard.method == 'flash'){
		var swf = $("#jquery_clipboard_swf:first");
		var swfdom = $(swf).get(0);
		if ( swfdom.jqueryClipboardCopy( text ) ){
			// Copy succeeded
			debug( "jQuery.clipboard: OK. Copied "+text.length+" bytes to clipboard using Flash method." );
			return true;
		} else {
			// Copy failed
			debug( "jQuery.clipboard: ERROR. Tried to copy using Flash method but an unknown error occurred." );
			return false;
		}
	}

	// Uh-oh. Somebody called $.clipboard() without $.clipboardReady()
	debug( "jQuery.clipboard: ERROR. You must use $.clipboardReady() in conjunction with $.clipboard()." );
	return false;

};

})(jQuery); /* jQuery.clipboard */


/**********************************************************************************/
/*                              /scripts/lib/Date.js                              */
/**********************************************************************************/

/*
  Date.js, an Date class extension, adding localized format, parse and week numbering.
  Copyright (C) 2008 Henrik Lindqvist <henrik.lindqvist@llamalab.com>

  This library is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published
  by the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/**
 * Extends the native <code>Date</code> class with additional functionality.
 * <p>Many of the date arithmetics are based on the information from
 * <a href="http://http://www.merlyn.demon.co.uk/frames-1.htm" target="_blank">http://www.merlyn.demon.co.uk/frames-1.htm</a>.</p>
 * @class Date
 * @author Henrik Lindqvist &lt;<a href="mailto:henrik.lindqvist@llamalab.com">henrik.lindqvist@llamalab.com</a>&gt;
 */
(function (d, dp) {

/**
 * Get the date localization data for a specific language.
 * <p>Standard localizations included:</p>
 * <ul>
 *  <li><code>en-US</code></li>
 *  <li><code>iso</code> - Use for <a href="http://en.wikipedia.org/wiki/ISO_8601"
 *    target="_blank">ISO8601</a> and Schema <a href="http://www.w3.org/TR/xmlschema11-2/#dateTime"
 *    target="_blank"><code>dateTime</code></a></li>
 * </ul>
 * <p>A good source of localization data are
 * <a href="http://www.unicode.org/cldr/" target="_blank">http://www.unicode.org/cldr/</a>.</p>
 * @function {static object} i18n
 * @param {optional string} l - language, or user-agent language if omitted.
 * @returns localization data
 */
d.i18n = function (l) {
  return (typeof l == 'string')
       ? (l in Date.i18n ? Date.i18n[l] : Date.i18n(l.substr(0, l.lastIndexOf('-'))))
       : (l || Date.i18n(navigator.language || navigator.browserLanguage || ''));
};
d.i18n.inherit = function (l, o) {
  l = Date.i18n(l);
  for (var k in l) if (typeof o[k] == 'undefined') o[k] = l[k];
  return o;
};
d.i18n[''] = // default
d.i18n['en'] =
d.i18n['en-US'] = {
  months: {
    abbr: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep' ],
    full: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]
  },
  days: {
    abbr: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],
    full: [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ]
  },
  week: {   // Used by date pickers
    abbr: 'Wk',
    full: 'Week'
  },
  ad: 'AD',
  am: 'AM',
  pm: 'PM',
  gmt: 'GMT',
  z: ':',   // Hour - minute separator
  Z: '',    // Hour - minute separator
  fdow: 0,  // First day of week
  mdifw: 1  // Minimum days in first week
};
d.i18n['iso'] = d.i18n.inherit('en', {
  Z: ':',
  fdow: 1,
  mdifw: 4
});
/**
 * Milliseconds in a week.
 * @property {static read number} WEEK
 */
d.WEEK = 6048e5;
/**
 * Milliseconds in a day.
 * @property {static read number} DAY
 */
d.DAY = 864e5;
/**
 * Milliseconds in an hour.
 * @property {static read number} HOUR
 */
d.HOUR = 36e5;
/**
 * Milliseconds in a minute.
 * @property {static read number} MINUTE
 */
d.MINUTE = 6e4
/**
 * Milliseconds in a second.
 * @property {static read number} SECOND
 */
d.SECOND = 1000;
/**
 * New <code>Date</code> instance for todays date, with time at midnight.
 * @function {static Date} today
 * @returns todays date at midnight.
 * @see datePart
 */
d.today = function () {
  return new Date().datePart();
};
/**
 * Clone <code>this</code> date, creating a new instance.
 * @function {Date} clone
 * @returns clone of <code>this</code> date.
 */
dp.clone = function() {
  return new Date(+this);
};
/**
 * Create a new instance with only the date part from <code>this</code> date.
 * <p>The time for the new date will be midnight.</p>
 * @function {Date} datePart
 * @returns the date at midnight.
 * @see timePart
 */
dp.datePart = function () {
  with (this) return new Date(getFullYear(), getMonth(), getDate());
};
/**
 * Create a new instance with only the time part from <code>this</code> date.
 * <p>The date will be the JavaScript epoc (1970-01-01).</p>
 * @function {Date} timePart
 * @returns the time of <code>this</code> date.
 * @see datePart
 */
dp.timePart = function () {
  with (this) return new Date(1970, 0, 1, getHours(), getMinutes(), getSeconds(), getMilliseconds());
};
/**
 * Set the "raw" unlocalized weekday.
 * @function setDay
 * @param {number} d - the weekday (0-6).
 * @see getDay (<a href="http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Date:getDay" target="_blank">native</a>)
 */
dp.setDay = function (d) {
  with (this) setDate((getDate() - getDay()) + d);
};
/**
 * Get the localized day of week.
 * @function {number} getDayOfWeek
 * @param {optional} o - first day of week number, language string or localization object.
 * @returns the weekday (1-7).
 * @see setDayOfWeek
 */
dp.getDayOfWeek = function (o) {
  if (typeof o != 'number') o = Date.i18n(o).fdow;
  var d = this.getDay() - o;
  if (d < 0) d += 7;
  return d + 1;
};
/**
 * Set the localized day of week.
 * @function setDayOfWeek
 * @param {number} d - the weekday (1-7).
 * @param {optional} o - first day of week number, language string or localization object.
 * @see getDayOfWeek
 */
dp.setDayOfWeek = function (d, o) {
  with (this) setDate((getDate() - getDayOfWeek(o)) + d);
};
/**
 * Get the maximum days in the month for <code>this</code> date.
 * @function {number} getDaysInMonth
 * @returns the number of days in this month, 1-31.
 */
dp.getDaysInMonth = function () {
  with (this.clone()) {
    setDate(32);
    return 32 - getDate();
  }
};
/**
 * Get the maximum days in the year for <code>this</code> date.
 * @function {number} getDaysInYear
 * @returns the number of days in this year (1-366).
 */
dp.getDaysInYear = function () {
  var y = this.getFullYear();
  return Math.floor((Date.UTC(y+1, 0, 1) - Date.UTC(y, 0, 1)) / Date.DAY);
};
/**
 * Get the day of the year for <code>this</code> date.
 * @function {number} getDayOfYear
 * @returns the day of this year (1-366).
 * @see setDayOfYear
 */
dp.getDayOfYear = function () {
  return Math.floor((this - new Date(this.getFullYear(), 0, 1)) / Date.DAY) + 1;
};
/**
 * Set the day in the year for <code>this</code> date.
 * @function setDayOfYear
 * @param {number} d - the day of year (1-366).
 * @see getDayOfYear
 */
dp.setDayOfYear = function (d) {
  this.setMonth(0, d);
};
/**
 * Get the week of month for <code>this</code> date.
 * @function {number} getWeekOfMonth
 * @param {optional} l - language string or localization object.
 * @returns the week number of this month (0-6).
 * @see setWeekOfMonth
 */
dp.getWeekOfMonth = function (l) {
  l = Date.i18n(l);
  with (this.clone()) {
    setDate(1);
    var d = (7 - (getDay() - l.fdow)) % 7;
    d = (d < l.mdifw) ? -d : (7 - d);
    return Math.ceil((this.getDate() + d) / 7);
  }
};
/**
 * Set the week of month for <code>this</code> date.
 * @function setWeekOfMonth
 * @param {number} w - the week number of this month (1-6).
 * @param {optional} l - language string or localization object.
 * @see setWeekOfMonth
 */
dp.setWeekOfMonth = function (w, l) {
  l = Date.i18n(l);
  with (this.clone()) {
    setDate(1);
    var d = (7 - (getDay() - l.fdow)) % 7;
    d = (d < l.mdifw) ? -d : (7 - d);
    setDate(d);
  }
};
/**
 * Get the week of year for <code>this</code> date.
 * @function {number} getWeekOfYear
 * @param {optional} l - language string or localization object.
 * @returns the week number of this year, 1-53.
 * @see setWeekOfMonth
 */
dp.getWeekOfYear = function (l) {
  l = Date.i18n(l);
  with (this.clone()) {
    setMonth(0, 1);
    var d = (7 - (getDay() - l.fdow)) % 7;
    if (l.mdifw < d) d -= 7;
    setDate(d);
    var w = Math.ceil((+this - valueOf()) / Date.WEEK);
    return (w <= getWeeksInYear()) ? w : 1;
  }
};
/**
 * Set the week of year for <code>this</code> date.
 * @function setWeekOfYear
 * @param {number} w - the week number in this year, 1-53.
 * @param {optional} l - language string or localization object.
 * @see setWeekOfMonth
 */
dp.setWeekOfYear = function (w, l) {
  l = Date.i18n(l);
  with (this) {
    setMonth(0, 1);
    var d = (7 - (getDay() - l.fdow)) % 7;
    if (l.mdifw < d) d -= 7;
    d += w * 7;
    setDate(d);
  }
};
/**
 * Get the maximum weeks in the year of <code>this</code> date.
 * @function {number} getWeeksInYear
 * @returns the number of weeks in this year, 1-53.
 */
dp.getWeeksInYear = function () {
  var y = this.getFullYear();
  return 52 + (new Date(y, 0, 1).getDay() == 4 || new Date(y, 11, 31).getDay() == 4);
};
/**
 * Set the timezone offset for <code>this</code> date.
 * <p>This function only adjusts the date to the supplied offset,
 * it doesn&rsquo;t actually set the timezone.</p>
 * @function setTimezoneOffset
 * @param {number} o - offset in minutes.
 * @see getTimezoneOffset (<a href="http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Date:getTimezoneOffset" traget="_blank">native</a>)
 */
dp.setTimezoneOffset = function (o) {
  with (this) setTime(valueOf() + ((getTimezoneOffset() + -o) * Date.MINUTE));
};
/**
 * Format <code>this</code> date into a string.
 * <p>For pattern syntax, see <a href="http://java.sun.com/javase/6/docs/api/java/text/SimpleDateFormat.html" target="_blank">java.text.SimpleDateFormat</a>.</p>
 * @function format
 * @param {string} p - the pattern string of format
 * @param l - the language (string) or locationzation data used
 * @see i18n
 */
dp.format = function (p, l) {
  var i18n = Date.i18n(l);
  var d = this;
  var pad = function (n, l) {
    for (n = String(n), l -= n.length; --l >= 0; n = '0'+n);
    return n;
  };
  var tz = function (n, s) {
    return ((n<0)?'+':'-')+pad(Math.abs(n/60),2)+s+pad(Math.abs(n%60),2);
  };
  return p.replace(/([aDdEFGHhKkMmSsWwyZz])\1*|'[^']*'|"[^"]*"/g, function (m) {
    l = m.length;
    switch (m.charAt(0)) {
      case 'a': return (d.getHours() < 12) ? i18n.am : i18n.pm;
      case 'D': return pad(d.getDayOfYear(), l);
      case 'd': return pad(d.getDate(), l);
      case 'E': return i18n.days[(l > 3)?'full':'abbr'][d.getDay()];
      case 'F': return pad(d.getDayOfWeek(i18n), l);
      case 'G': return i18n.ad;
      case 'H': return pad(d.getHours(), l);
      case 'h': return pad(d.getHours() % 12 || 12, l);
      case 'K': return pad(d.getHours() % 12, l);
      case 'k': return pad(d.getHours() || 24, l);
      case 'M': return (l < 3)
                     ? pad(d.getMonth() + 1, l)
                     : i18n.months[(l > 3)?'full':'abbr'][d.getMonth()];
      case 'm': return pad(d.getMinutes(), l);
      case 'S': return pad(d.getMilliseconds(), l);
      case 's': return pad(d.getSeconds(), l);
      case 'W': return pad(d.getWeekOfMonth(i18n), l);
      case 'w': return pad(d.getWeekOfYear(i18n), l);
      case 'y': return (l == 2)
                     ? String(d.getFullYear()).substr(2)
                     : pad(d.getFullYear(), l);
      case 'Z': return tz(d.getTimezoneOffset(), i18n.Z);
      case 'z': return i18n.gmt+tz(d.getTimezoneOffset(), i18n.z);
      case "'":
      case '"': return m.substr(1, l - 2);
      default:  throw new Error('Illegal pattern');
    }
  });
}
/**
 * Parse a date string.
 * This function replaces the built-in <code>parse</code> but
 * reverts back to the original if <code>p</code> is omitted.
 * <p>For pattern syntax, see <a href="http://java.sun.com/javase/6/docs/api/java/text/SimpleDateFormat.html" target="_blank">java.text.SimpleDateFormat</a>.</p>
 * @function {static Date} parse
 * @param {optional string} p - the pattern string of format.
 * @param {optional} l - the language (string) or locationzation data used.
 * @returns the parsed {@link Date}, or <code>NaN</code> on failure.
 * @see i18n
 */
d.parse = function (s, p, l) {
  if (!p) return arguments.callee.original.call(this);
  var i18n = Date.i18n(l), d = new Date(1970,0,1,0,0,0,0);
  var pi = 0, si = 0, i, j, k, c;
  var num = function (x) {
    if (x) l = x;
    else if (!/[DdFHhKkMmSsWwy]/.test(p.charAt(pi))) l = Number.MAX_VALUE;
    for (i = si; --l >= 0 && /[0-9]/.test(s.charAt(si)); si++);
    if (i == si) throw 1;
    return parseInt(s.substring(i, si), 10);
  };
  var cmp = function (x) {
    if (s.substr(si, x.length).toLowerCase() != x.toLowerCase()) return false;
    si += x.length;
    return true;
  };
  var idx = function (x) {
    for (i = x.length; --i >= 0;) if (cmp(x[i])) return i+1;
    return 0;
  };
  try {
    while (pi < p.length) {
      c = p.charAt(l = pi);
      if (/[aDdEFGHhKkMmSsWwyZz]/.test(c)) {
        while (p.charAt(++pi) == c);
        l = pi - l;
        switch (c) {
          case 'a': if (cmp(i18n.pm)) d.setHours(12 + d.getHours());
                    else if (!cmp(i18n.am)) throw 2;
                    break;
          case 'D': d.setDayOfYear(num()); break;
          case 'd': d.setDate(num()); break;
          case 'E': if (i = idx(i18n.days.full)) d.setDay(i - 1);
                    else if (i = idx(i18n.days.abbr)) d.setDay(i - 1);
                    else throw 3;
                    break;
          case 'F': d.setDayOfWeek(num(), i18n); break;
          case 'G': if (!cmp(i18n.ad)) throw 4;
                    break;
          case 'H':
          case 'k': d.setHours((i = num()) < 24 ? i : 0); break;
          case 'K':
          case 'h': d.setHours((i = num()) < 12 ? i : 0); break;
          case 'M': if (l < 3) d.setMonth(num() - 1);
                    else if (i = idx(i18n.months.full)) d.setMonth(i - 1);
                    else if (i = idx(i18n.months.abbr)) d.setMonth(i - 1);
                    else throw 5;
                    break;
          case 'm': d.setMinutes(num()); break;
          case 'S': d.setMilliseconds(num()); break;
          case 's': d.setSeconds(num()); break;
          case 'W': d.setWeekOfMonth(num(), i18n); break;
          case 'w': d.setWeekOfYear(num(), i18n); break;
          case 'y': d.setFullYear((l == 2) ? 2000 + num() : num()); break;
          case 'z': if (!cmp(i18n.gmt)) throw 6;
          case 'Z': if (!/[+-]/.test(j = s.charAt(si++))) throw 6;
                    k = num(2) * 60;
                    if (!cmp(i18n[c])) throw 7;
                    k += num(2);
                    d.setTimezoneOffset((j == '+') ? -k : k);
        }
      }
      else if (/["']/.test(c)) {
        while (++pi < p.length && p.charAt(pi) != c);
        if (!cmp(p.substring(l+1, pi++))) throw 8;
      }
      else {
        while (pi < p.length && !/[aDdEFGHhKkMmSsWwyZz"']/.test(p.charAt(pi))) pi++;
        if (!cmp(p.substring(l, pi))) throw 9;
      }
    }
    return d;
  }
  catch (e) {
    if (e > 0) return Number.NaN;
    throw e;
  }
};
d.parse.original = d.parse;

})(Date, Date.prototype);

/**********************************************************************************/
/*                           /scripts/lib/Date_ru-RU.js                           */
/**********************************************************************************/

(function (d) {

d.i18n['ru'] =
d.i18n['ru-RU'] = {
  months: {
    abbr: [ 'янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек' ],
    full: [ 'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря' ]
  },
  days: {
    abbr: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],
    full: [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ]
  },
  week: {
    abbr: 'Нед',
    full: 'Неделя'
  },
  ad: 'AD',
  am: 'AM',
  pm: 'PM',
  gmt: 'GMT',
  z: ':',
  Z: '',
  fdow: 0,
  mdifw: 1
};

})(window.Date);

/**********************************************************************************/
/*                           /scripts/lib/swfobject.js                            */
/**********************************************************************************/

/*! SWFObject v2.1 <http://code.google.com/p/swfobject/>
	Copyright (c) 2007-2008 Geoff Stearns, Michael Williams, and Bobby van der Sluis
	This software is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/

var swfobject = function() {

	var UNDEF = "undefined",
		OBJECT = "object",
		SHOCKWAVE_FLASH = "Shockwave Flash",
		SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
		FLASH_MIME_TYPE = "application/x-shockwave-flash",
		EXPRESS_INSTALL_ID = "SWFObjectExprInst",

		win = window,
		doc = document,
		nav = navigator,

		domLoadFnArr = [],
		regObjArr = [],
		objIdArr = [],
		listenersArr = [],
		script,
		timer = null,
		storedAltContent = null,
		storedAltContentId = null,
		isDomLoaded = false,
		isExpressInstallActive = false;

	/* Centralized function for browser feature detection
		- Proprietary feature detection (conditional compiling) is used to detect Internet Explorer's features
		- User agent string detection is only used when no alternative is possible
		- Is executed directly for optimal performance
	*/
	var ua = function() {
		var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
			playerVersion = [0,0,0],
			d = null;
		if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
			d = nav.plugins[SHOCKWAVE_FLASH].description;
			if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
				d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
				playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
				playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
				playerVersion[2] = /r/.test(d) ? parseInt(d.replace(/^.*r(.*)$/, "$1"), 10) : 0;
			}
		}
		else if (typeof win.ActiveXObject != UNDEF) {
			var a = null, fp6Crash = false;
			try {
				a = new ActiveXObject(SHOCKWAVE_FLASH_AX + ".7");
			}
			catch(e) {
				try {
					a = new ActiveXObject(SHOCKWAVE_FLASH_AX + ".6");
					playerVersion = [6,0,21];
					a.AllowScriptAccess = "always";	 // Introduced in fp6.0.47
				}
				catch(e) {
					if (playerVersion[0] == 6) {
						fp6Crash = true;
					}
				}
				if (!fp6Crash) {
					try {
						a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
					}
					catch(e) {}
				}
			}
			if (!fp6Crash && a) { // a will return null when ActiveX is disabled
				try {
					d = a.GetVariable("$version");	// Will crash fp6.0.21/23/29
					if (d) {
						d = d.split(" ")[1].split(",");
						playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
					}
				}
				catch(e) {}
			}
		}
		var u = nav.userAgent.toLowerCase(),
			p = nav.platform.toLowerCase(),
			webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
			ie = false,
			windows = p ? /win/.test(p) : /win/.test(u),
			mac = p ? /mac/.test(p) : /mac/.test(u);
		/*@cc_on
			ie = true;
			@if (@_win32)
				windows = true;
			@elif (@_mac)
				mac = true;
			@end
		@*/
		return { w3cdom:w3cdom, pv:playerVersion, webkit:webkit, ie:ie, win:windows, mac:mac };
	}();

	/* Cross-browser onDomLoad
		- Based on Dean Edwards' solution: http://dean.edwards.name/weblog/2006/06/again/
		- Will fire an event as soon as the DOM of a page is loaded (supported by Gecko based browsers - like Firefox -, IE, Opera9+, Safari)
	*/
	var onDomLoad = function() {
		if (!ua.w3cdom) {
			return;
		}
		addDomLoadEvent(main);
		if (ua.ie && ua.win) {
			try {	 // Avoid a possible Operation Aborted error
				doc.write("<scr" + "ipt id=__ie_ondomload defer=true src=//:></scr" + "ipt>"); // String is split into pieces to avoid Norton AV to add code that can cause errors
				script = getElementById("__ie_ondomload");
				if (script) {
					addListener(script, "onreadystatechange", checkReadyState);
				}
			}
			catch(e) {}
		}
		if (ua.webkit && typeof doc.readyState != UNDEF) {
			timer = setInterval(function() { if (/loaded|complete/.test(doc.readyState)) { callDomLoadFunctions(); }}, 10);
		}
		if (typeof doc.addEventListener != UNDEF) {
			doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, null);
		}
		addLoadEvent(callDomLoadFunctions);
	}();

	function checkReadyState() {
		if (script.readyState == "complete") {
			script.parentNode.removeChild(script);
			callDomLoadFunctions();
		}
	}

	function callDomLoadFunctions() {
		if (isDomLoaded) {
			return;
		}
		if (ua.ie && ua.win) { // Test if we can really add elements to the DOM; we don't want to fire it too early
			var s = createElement("span");
			try { // Avoid a possible Operation Aborted error
				var t = doc.getElementsByTagName("body")[0].appendChild(s);
				t.parentNode.removeChild(t);
			}
			catch (e) {
				return;
			}
		}
		isDomLoaded = true;
		if (timer) {
			clearInterval(timer);
			timer = null;
		}
		var dl = domLoadFnArr.length;
		for (var i = 0; i < dl; i++) {
			domLoadFnArr[i]();
		}
	}

	function addDomLoadEvent(fn) {
		if (isDomLoaded) {
			fn();
		}
		else {
			domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
		}
	}

	/* Cross-browser onload
		- Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
		- Will fire an event as soon as a web page including all of its assets are loaded
	 */
	function addLoadEvent(fn) {
		if (typeof win.addEventListener != UNDEF) {
			win.addEventListener("load", fn, false);
		}
		else if (typeof doc.addEventListener != UNDEF) {
			doc.addEventListener("load", fn, false);
		}
		else if (typeof win.attachEvent != UNDEF) {
			addListener(win, "onload", fn);
		}
		else if (typeof win.onload == "function") {
			var fnOld = win.onload;
			win.onload = function() {
				fnOld();
				fn();
			};
		}
		else {
			win.onload = fn;
		}
	}

	/* Main function
		- Will preferably execute onDomLoad, otherwise onload (as a fallback)
	*/
	function main() { // Static publishing only
		var rl = regObjArr.length;
		for (var i = 0; i < rl; i++) { // For each registered object element
			var id = regObjArr[i].id;
			if (ua.pv[0] > 0) {
				var obj = getElementById(id);
				if (obj) {
					regObjArr[i].width = obj.getAttribute("width") ? obj.getAttribute("width") : "0";
					regObjArr[i].height = obj.getAttribute("height") ? obj.getAttribute("height") : "0";
					if (hasPlayerVersion(regObjArr[i].swfVersion)) { // Flash plug-in version >= Flash content version: Houston, we have a match!
						if (ua.webkit && ua.webkit < 312) { // Older webkit engines ignore the object element's nested param elements
							fixParams(obj);
						}
						setVisibility(id, true);
					}
					else if (regObjArr[i].expressInstall && !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac)) { // Show the Adobe Express Install dialog if set by the web page author and if supported (fp6.0.65+ on Win/Mac OS only)
						showExpressInstall(regObjArr[i]);
					}
					else { // Flash plug-in and Flash content version mismatch: display alternative content instead of Flash content
						displayAltContent(obj);
					}
				}
			}
			else {	// If no fp is installed, we let the object element do its job (show alternative content)
				setVisibility(id, true);
			}
		}
	}

	/* Fix nested param elements, which are ignored by older webkit engines
		- This includes Safari up to and including version 1.2.2 on Mac OS 10.3
		- Fall back to the proprietary embed element
	*/
	function fixParams(obj) {
		var nestedObj = obj.getElementsByTagName(OBJECT)[0];
		if (nestedObj) {
			var e = createElement("embed"), a = nestedObj.attributes;
			if (a) {
				var al = a.length;
				for (var i = 0; i < al; i++) {
					if (a[i].nodeName == "DATA") {
						e.setAttribute("src", a[i].nodeValue);
					}
					else {
						e.setAttribute(a[i].nodeName, a[i].nodeValue);
					}
				}
			}
			var c = nestedObj.childNodes;
			if (c) {
				var cl = c.length;
				for (var j = 0; j < cl; j++) {
					if (c[j].nodeType == 1 && c[j].nodeName == "PARAM") {
						e.setAttribute(c[j].getAttribute("name"), c[j].getAttribute("value"));
					}
				}
			}
			obj.parentNode.replaceChild(e, obj);
		}
	}

	/* Show the Adobe Express Install dialog
		- Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
	*/
	function showExpressInstall(regObj) {
		isExpressInstallActive = true;
		var obj = getElementById(regObj.id);
		if (obj) {
			if (regObj.altContentId) {
				var ac = getElementById(regObj.altContentId);
				if (ac) {
					storedAltContent = ac;
					storedAltContentId = regObj.altContentId;
				}
			}
			else {
				storedAltContent = abstractAltContent(obj);
			}
			if (!(/%$/.test(regObj.width)) && parseInt(regObj.width, 10) < 310) {
				regObj.width = "310";
			}
			if (!(/%$/.test(regObj.height)) && parseInt(regObj.height, 10) < 137) {
				regObj.height = "137";
			}
			doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
			var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
				dt = doc.title,
				fv = "MMredirectURL=" + win.location + "&MMplayerType=" + pt + "&MMdoctitle=" + dt,
				replaceId = regObj.id;
			// For IE when a SWF is loading (AND: not available in cache) wait for the onload event to fire to remove the original object element
			// In IE you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
			if (ua.ie && ua.win && obj.readyState != 4) {
				var newObj = createElement("div");
				replaceId += "SWFObjectNew";
				newObj.setAttribute("id", replaceId);
				obj.parentNode.insertBefore(newObj, obj); // Insert placeholder div that will be replaced by the object element that loads expressinstall.swf
				obj.style.display = "none";
				var fn = function() {
					obj.parentNode.removeChild(obj);
				};
				addListener(win, "onload", fn);
			}
			createSWF({ data:regObj.expressInstall, id:EXPRESS_INSTALL_ID, width:regObj.width, height:regObj.height }, { flashvars:fv }, replaceId);
		}
	}

	/* Functions to abstract and display alternative content
	*/
	function displayAltContent(obj) {
		if (ua.ie && ua.win && obj.readyState != 4) {
			// For IE when a SWF is loading (AND: not available in cache) wait for the onload event to fire to remove the original object element
			// In IE you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
			var el = createElement("div");
			obj.parentNode.insertBefore(el, obj); // Insert placeholder div that will be replaced by the alternative content
			el.parentNode.replaceChild(abstractAltContent(obj), el);
			obj.style.display = "none";
			var fn = function() {
				obj.parentNode.removeChild(obj);
			};
			addListener(win, "onload", fn);
		}
		else {
			obj.parentNode.replaceChild(abstractAltContent(obj), obj);
		}
	}

	function abstractAltContent(obj) {
		var ac = createElement("div");
		if (ua.win && ua.ie) {
			ac.innerHTML = obj.innerHTML;
		}
		else {
			var nestedObj = obj.getElementsByTagName(OBJECT)[0];
			if (nestedObj) {
				var c = nestedObj.childNodes;
				if (c) {
					var cl = c.length;
					for (var i = 0; i < cl; i++) {
						if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
							ac.appendChild(c[i].cloneNode(true));
						}
					}
				}
			}
		}
		return ac;
	}

	/* Cross-browser dynamic SWF creation
	*/
	function createSWF(attObj, parObj, id) {
		var r, el = getElementById(id);
		if (el) {
			if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
				attObj.id = id;
			}
			if (ua.ie && ua.win) { // IE, the object element and W3C DOM methods do not combine: fall back to outerHTML
				var att = "";
				for (var i in attObj) {
					if (attObj[i] != Object.prototype[i]) { // Filter out prototype additions from other potential libraries, like Object.prototype.toJSONString = function() {}
						if (i.toLowerCase() == "data") {
							parObj.movie = attObj[i];
						}
						else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
							att += ' class="' + attObj[i] + '"';
						}
						else if (i.toLowerCase() != "classid") {
							att += ' ' + i + '="' + attObj[i] + '"';
						}
					}
				}
				var par = "";
				for (var j in parObj) {
					if (parObj[j] != Object.prototype[j]) { // Filter out prototype additions from other potential libraries
						par += '<param name="' + j + '" value="' + parObj[j] + '" />';
					}
				}
				el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
				objIdArr[objIdArr.length] = attObj.id; // Stored to fix object 'leaks' on unload (dynamic publishing only)
				r = getElementById(attObj.id);
			}
			else if (ua.webkit && ua.webkit < 312) { // Older webkit engines ignore the object element's nested param elements: fall back to the proprietary embed element
				var e = createElement("embed");
				e.setAttribute("type", FLASH_MIME_TYPE);
				for (var k in attObj) {
					if (attObj[k] != Object.prototype[k]) { // Filter out prototype additions from other potential libraries
						if (k.toLowerCase() == "data") {
							e.setAttribute("src", attObj[k]);
						}
						else if (k.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
							e.setAttribute("class", attObj[k]);
						}
						else if (k.toLowerCase() != "classid") { // Filter out IE specific attribute
							e.setAttribute(k, attObj[k]);
						}
					}
				}
				for (var l in parObj) {
					if (parObj[l] != Object.prototype[l]) { // Filter out prototype additions from other potential libraries
						if (l.toLowerCase() != "movie") { // Filter out IE specific param element
							e.setAttribute(l, parObj[l]);
						}
					}
				}
				el.parentNode.replaceChild(e, el);
				r = e;
			}
			else { // Well-behaving browsers
				var o = createElement(OBJECT);
				o.setAttribute("type", FLASH_MIME_TYPE);
				for (var m in attObj) {
					if (attObj[m] != Object.prototype[m]) { // Filter out prototype additions from other potential libraries
						if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
							o.setAttribute("class", attObj[m]);
						}
						else if (m.toLowerCase() != "classid") { // Filter out IE specific attribute
							o.setAttribute(m, attObj[m]);
						}
					}
				}
				for (var n in parObj) {
					if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // Filter out prototype additions from other potential libraries and IE specific param element
						createObjParam(o, n, parObj[n]);
					}
				}
				el.parentNode.replaceChild(o, el);
				r = o;
			}
		}
		return r;
	}

	function createObjParam(el, pName, pValue) {
		var p = createElement("param");
		p.setAttribute("name", pName);
		p.setAttribute("value", pValue);
		el.appendChild(p);
	}

	/* Cross-browser SWF removal
		- Especially needed to safely and completely remove a SWF in Internet Explorer
	*/
	function removeSWF(id) {
		var obj = getElementById(id);
		if (obj && (obj.nodeName == "OBJECT" || obj.nodeName == "EMBED")) {
			if (ua.ie && ua.win) {
				if (obj.readyState == 4) {
					removeObjectInIE(id);
				}
				else {
					win.attachEvent("onload", function() {
						removeObjectInIE(id);
					});
				}
			}
			else {
				obj.parentNode.removeChild(obj);
			}
		}
	}

	function removeObjectInIE(id) {
		var obj = getElementById(id);
		if (obj) {
			for (var i in obj) {
				if (typeof obj[i] == "function") {
					obj[i] = null;
				}
			}
			obj.parentNode.removeChild(obj);
		}
	}

	/* Functions to optimize JavaScript compression
	*/
	function getElementById(id) {
		var el = null;
		try {
			el = doc.getElementById(id);
		}
		catch (e) {}
		return el;
	}

	function createElement(el) {
		return doc.createElement(el);
	}

	/* Updated attachEvent function for Internet Explorer
		- Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
	*/
	function addListener(target, eventType, fn) {
		target.attachEvent(eventType, fn);
		listenersArr[listenersArr.length] = [target, eventType, fn];
	}

	/* Flash Player and SWF content version matching
	*/
	function hasPlayerVersion(rv) {
		var pv = ua.pv, v = rv.split(".");
		v[0] = parseInt(v[0], 10);
		v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
		v[2] = parseInt(v[2], 10) || 0;
		return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
	}

	/* Cross-browser dynamic CSS creation
		- Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
	*/
	function createCSS(sel, decl) {
		if (ua.ie && ua.mac) {
			return;
		}
		var h = doc.getElementsByTagName("head")[0], s = createElement("style");
		s.setAttribute("type", "text/css");
		s.setAttribute("media", "screen");
		if (!(ua.ie && ua.win) && typeof doc.createTextNode != UNDEF) {
			s.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
		}
		h.appendChild(s);
		if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
			var ls = doc.styleSheets[doc.styleSheets.length - 1];
			if (typeof ls.addRule == OBJECT) {
				ls.addRule(sel, decl);
			}
		}
	}

	function setVisibility(id, isVisible) {
		var v = isVisible ? "visible" : "hidden";
		if (isDomLoaded && getElementById(id)) {
			getElementById(id).style.visibility = v;
		}
		else {
			createCSS("#" + id, "visibility:" + v);
		}
	}

	/* Filter to avoid XSS attacks
	*/
	function urlEncodeIfNecessary(s) {
		var regex = /[\\\"<>\.;]/;
		var hasBadChars = regex.exec(s) != null;
		return hasBadChars ? encodeURIComponent(s) : s;
	}

	/* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
	*/
	var cleanup = function() {
		if (ua.ie && ua.win) {
			window.attachEvent("onunload", function() {
				// remove listeners to avoid memory leaks
				var ll = listenersArr.length;
				for (var i = 0; i < ll; i++) {
					listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
				}
				// cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
				var il = objIdArr.length;
				for (var j = 0; j < il; j++) {
					removeSWF(objIdArr[j]);
				}
				// cleanup library's main closures to avoid memory leaks
				for (var k in ua) {
					ua[k] = null;
				}
				ua = null;
				for (var l in swfobject) {
					swfobject[l] = null;
				}
				swfobject = null;
			});
		}
	}();


	return {
		/* Public API
			- Reference: http://code.google.com/p/swfobject/wiki/SWFObject_2_0_documentation
		*/
		registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr) {
			if (!ua.w3cdom || !objectIdStr || !swfVersionStr) {
				return;
			}
			var regObj = {};
			regObj.id = objectIdStr;
			regObj.swfVersion = swfVersionStr;
			regObj.expressInstall = xiSwfUrlStr ? xiSwfUrlStr : false;
			regObjArr[regObjArr.length] = regObj;
			setVisibility(objectIdStr, false);
		},

		getObjectById: function(objectIdStr) {
			var r = null;
			if (ua.w3cdom) {
				var o = getElementById(objectIdStr);
				if (o) {
					var n = o.getElementsByTagName(OBJECT)[0];
					if (!n || (n && typeof o.SetVariable != UNDEF)) {
							r = o;
					}
					else if (typeof n.SetVariable != UNDEF) {
						r = n;
					}
				}
			}
			return r;
		},

		embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj) {
			if (!ua.w3cdom || !swfUrlStr || !replaceElemIdStr || !widthStr || !heightStr || !swfVersionStr) {
				return;
			}
			widthStr += ""; // Auto-convert to string
			heightStr += "";
			if (hasPlayerVersion(swfVersionStr)) {
				setVisibility(replaceElemIdStr, false);
				var att = {};
				if (attObj && typeof attObj === OBJECT) {
					for (var i in attObj) {
						if (attObj[i] != Object.prototype[i]) { // Filter out prototype additions from other potential libraries
							att[i] = attObj[i];
						}
					}
				}
				att.data = swfUrlStr;
				att.width = widthStr;
				att.height = heightStr;
				var par = {};
				if (parObj && typeof parObj === OBJECT) {
					for (var j in parObj) {
						if (parObj[j] != Object.prototype[j]) { // Filter out prototype additions from other potential libraries
							par[j] = parObj[j];
						}
					}
				}
				if (flashvarsObj && typeof flashvarsObj === OBJECT) {
					for (var k in flashvarsObj) {
						if (flashvarsObj[k] != Object.prototype[k]) { // Filter out prototype additions from other potential libraries
							if (typeof par.flashvars != UNDEF) {
								par.flashvars += "&" + k + "=" + flashvarsObj[k];
							}
							else {
								par.flashvars = k + "=" + flashvarsObj[k];
							}
						}
					}
				}
				addDomLoadEvent(function() {
					createSWF(att, par, replaceElemIdStr);
					if (att.id == replaceElemIdStr) {
						setVisibility(replaceElemIdStr, true);
					}
				});
			}
			else if (xiSwfUrlStr && !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac)) {
				isExpressInstallActive = true; // deferred execution
				setVisibility(replaceElemIdStr, false);
				addDomLoadEvent(function() {
					var regObj = {};
					regObj.id = regObj.altContentId = replaceElemIdStr;
					regObj.width = widthStr;
					regObj.height = heightStr;
					regObj.expressInstall = xiSwfUrlStr;
					showExpressInstall(regObj);
				});
			}
		},

		getFlashPlayerVersion: function() {
			return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
		},

		hasFlashPlayerVersion: hasPlayerVersion,

		createSWF: function(attObj, parObj, replaceElemIdStr) {
			if (ua.w3cdom) {
				return createSWF(attObj, parObj, replaceElemIdStr);
			}
			else {
				return undefined;
			}
		},

		removeSWF: function(objElemIdStr) {
			if (ua.w3cdom) {
				removeSWF(objElemIdStr);
			}
		},

		createCSS: function(sel, decl) {
			if (ua.w3cdom) {
				createCSS(sel, decl);
			}
		},

		addDomLoadEvent: addDomLoadEvent,

		addLoadEvent: addLoadEvent,

		getQueryParamValue: function(param) {
			var q = doc.location.search || doc.location.hash;
			if (param == null) {
				return urlEncodeIfNecessary(q);
			}
			if (q) {
				var pairs = q.substring(1).split("&");
				for (var i = 0; i < pairs.length; i++) {
					if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
						return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
					}
				}
			}
			return "";
		},

		// For internal usage only
		expressInstallCallback: function() {
			if (isExpressInstallActive && storedAltContent) {
				var obj = getElementById(EXPRESS_INSTALL_ID);
				if (obj) {
					obj.parentNode.replaceChild(storedAltContent, obj);
					if (storedAltContentId) {
						setVisibility(storedAltContentId, true);
						if (ua.ie && ua.win) {
							storedAltContent.style.display = "block";
						}
					}
					storedAltContent = null;
					storedAltContentId = null;
					isExpressInstallActive = false;
				}
			}
		}
	};
}();



// код БФМ
/**********************************************************************************/
/*                             /scripts/news/list.js                              */
/**********************************************************************************/

/**
 * Список новостей.
 */
$.fn.bfm_news_list = function() {
	var container = this;
	$.ieready(function() {
		$(container).find('li').each(function() {
			var container = $(this).find('.b-tooltip-container');
			$(this).children('a').leviTip({
				sourceType: 'element',
				source: container,
				addClass: 'b-tooltip',
				dropShadow: false
			});
		});
	});
};


/**********************************************************************************/
/*                           /scripts/news/recommend.js                           */
/**********************************************************************************/

/**
 * Коментарии.
 */
$.fn.bfm_recommend = function(id) {

	var self = this;

	$(self).find('.b-btn-grey').click(function() {
		$.ajax({
			url: '/news/recommend.html',
			type: 'post',
			data: {
				id: id
			},
			dataType: 'json',
			success: function(response) {
				if (response.status) {
					$(self).find('.number').text(response.count);
				} else {
					alert(response.message);
				}
			}
		});
	});
}


/**********************************************************************************/
/*                         /scripts/news/tell-a-friend.js                         */
/**********************************************************************************/

/**
 * Отправить ссылку другу.
 *
 * @version	$Id: tell-a-friend.js 6266 2008-12-03 16:14:00Z morozov $
 */
function bfm_tell_a_friend() {
	$('#tell-a-friend').toggle();
};


/**********************************************************************************/
/*                              /scripts/news/all.js                              */
/**********************************************************************************/

function bfm_all_news() {
	var button = $('#all-news-button'),
		list = $('#all-news-list').bfm_autohide(button);
		list.find('.close').click(function() {
			list.hide();
		});
		button.click(function() {
			list.show();
		});
}


/**********************************************************************************/
/*                           /scripts/news/comments.js                            */
/**********************************************************************************/

/**
 * Показывает форму и устанавливает фокус на первом поле ввода.
 *
 */
$.fn.bfm_form_show = function() {
	$(this).show().find('input:first').focus();
	return this;
};

/**
 * Инициализирует CAPTCHA в форме.
 *
 */
$.fn.bfm_form_captcha = function() {
	var captcha = $(this).find('dl.captcha'),
		image = captcha.find('img');

	captcha.find('.b-captcha-changeimg').click(function() {
		var src = image.attr('src').split('?').shift();
		image.attr('src', src + '?' + (new Date).getTime());
	});

	return this;
};

/**
 * Комментарии.
 */
$.fn.bfm_comments = function() {
	var form = $(this).find('form').bfm_form_captcha().hide();

	if ('#comments' == location.hash) {
		form.bfm_form_show();
	}

	$(this).find('.b-comment-add').each(function() {
		var container = $(this).find('.b-comment-form');
		$(this).find('.b-btn-comment-display').click(function() {
			form.appendTo(container).bfm_form_show();
		});
	});
};


/**********************************************************************************/
/*                           /scripts/news/favorites.js                           */
/**********************************************************************************/

/**
 * Панель добавления новости в избранное.
 */
$.fn.bfm_favorites = function() {

	this.each(function() {
		var self = this,
			id = $(self).attr('id').match(/\d+/)[0];

		function status(value) {
			$.ajax({
				url: '/news/favorites.html',
				type: 'post',
				data: {
					id: id,
					status: value
				},
				success: function(added) {
					if (added > 0) {
						$(self).addClass('in-favorites');
					} else {
						$(self).removeClass('in-favorites');
					}
				}
			});
		}

		$.each({'.add': 1, '.remove': 0}, function(selector, value) {
			$(self).find(selector).click(function() {
				status(value);
			});
		});
	});
}


/**********************************************************************************/
/*                              /scripts/news/top.js                              */
/**********************************************************************************/

/**
 * Горячие новости.
 *
 * @version	$Id: top.js 6266 2008-12-03 16:14:00Z morozov $
 */
$.fn.bfm_top_news = function() {
	$(this).each(function() {
		var items = $(this).find('li').mouseover(function() {
			items.removeClass('selected');
			$(this).addClass('selected');
		});
	});
};



/**********************************************************************************/
/*                            /scripts/preferences.js                             */
/**********************************************************************************/

/**
 * Интерфейс работы с настройками пользователя.
 *
 * @version $Id: preferences.js 6675 2009-03-31 13:38:46Z stepanuyk $
 */
$.preferences = (function(preferences) {

	/**
	 * Абстрактный класс адаптеров настроек.
	 *
	 */
	var _abstract = function() {};

	/**
	 * Возвращает значение указанного параметра.
	 *
	 * @param {String} name
	 */
	_abstract.prototype.get = function(name) {
		return name in this.preferences ? this.preferences[name] : null;
	};

	/**
	 * Устанавливает значение указанного параметра
	 *
	 * @param {String} name
	 * @param {Object} value
	 */
	_abstract.prototype.set = function(name, value) {
		this.preferences[name] = value;
	};

	/**
	 * Удаляет указанный параметр.
	 *
	 * @param {String} name
	 */
	_abstract.prototype.unset = function(name) {
		delete this.preferences[name];
	};

	/**
	 * Сбрасывает значения настроек.
	 *
	 */
	_abstract.prototype.reset = function() {
		this.preferences = {};
	};

	/**
	 * Адаптер хранения настроек в cookie.
	 *
	 */
	var cookie = function() {
		this.cookie_name = 'preferences';
		try {
			var preferences = $.evalJSON($.cookie(this.cookie_name));
		} catch (e) {
			var preferences = {};
		}
		this.preferences = $.extend({}, preferences);
	};

	cookie.prototype = new _abstract();

	/**
	 * Устанавливает значение указанного параметра
	 *
	 * @param {String} name
	 * @param {Object} value
	 */
	cookie.prototype.set = function(name, value) {
		_abstract.prototype.set.apply(this, arguments);
		this.commit();
	};

	/**
	 * Сбрасывает значения настроек.
	 *
	 */
	cookie.prototype.reset = function() {
		_abstract.prototype.reset.apply(this, arguments);
		this.commit();
	};

	/**
	 * Сохраняет настройки.
	 *
	 */
	cookie.prototype.commit = function() {
		$.cookie(this.cookie_name, $.toJSON(this.preferences), $.cookie.defaults);
	};

	/**
	 * Адаптер хранения настроек в профайле пользователя.
	 *
	 */
	var profile = function() {
		this.preferences = preferences;
	};
	profile.prototype = new _abstract();

	/**
	 * Устанавливает значение указанного параметра
	 *
	 * @param {String} name
	 * @param {Object} value
	 */
	profile.prototype.set = function(name, value) {
		_abstract.prototype.set.apply(this, arguments);
		var data = {};
		data[name] = value;
		this.submit({action: 'update', data: $.toJSON(data)});
	};

	/**
	 * Сбрасывает значения настроек.
	 *
	 */
	profile.prototype.reset = function() {
		_abstract.prototype.reset.apply(this, arguments);
		this.submit({action: 'reset'});
	};

	/**
	 * Отправляет настройки на сервер.
	 *
	 * @param {Object} request
	 */
	profile.prototype.submit = function(request) {
		$.post('/users/save-preferences/', request);
	};

	// создаем адаптер для текущего пользователя
	var adapter = !!preferences ? new profile() : new cookie();

	return function(name, value) {
		if (arguments.length < 2) {
			if (null === name) {
				adapter.reset();
			} else {
				return adapter.get(name);
			}
		} else {
			if (null !== value) {
				// предотвращаем сохранение параметров, которые не изменились,
				// правда, только для скаларных значений
				if (value != adapter.get(name) || value instanceof Object) {
					adapter.set(name, value);
				}
			} else {
				adapter.unset(name);
			}
		}
	};

})(window.preferences);


/**********************************************************************************/
/*                              /scripts/ieready.js                               */
/**********************************************************************************/

(function() {
	var loaded = false;
	$(document).ready(function() {
		loaded = true;
	});

	/**
	 * Вызывает указанную функцию:
	 * - для IE после загрузки документа (чтобы избежать Operation Aborted)
	 * - для остальных браузеров — немедленно
	 */
	$.ieready = function(fn) {
		if ($.browser.msie && !loaded) {
			$(document).ready(fn);
		} else {
			fn();
		}
	}
})();


/**********************************************************************************/
/*                                 /scripts/ui.js                                 */
/**********************************************************************************/

/**********************************************************************************/
/*                            /scripts/ui/autohide.js                             */
/**********************************************************************************/

/**
 * Автоскрытие при клике по документу для выпадающих элементов.
 */
$.fn.bfm_autohide = function(button) {
	$(this).each(function() {
		var self = this;
		$(document).click(function(e) {
			var branch = $(e.target).parents().andSelf();
			if ($.inArray($(button).get(0), branch) < 0 && $.inArray(self, branch) < 0) {
				$(self).hide().trigger('hide');
			}
		});

		/*$(window).scroll(function() {
			body.hide();
		});*/
	});
	return this;
};


/**********************************************************************************/
/*                            /scripts/ui/pulldown.js                             */
/**********************************************************************************/

/**
 * Выпадающие списки.
 */
$.fn.bfm_pulldown = function() {
	$(this).each(function() {
		var self = this,
			body = $(this).find('.b-pulldown-body'),
			arrow = $(this).find('.b-pulldown-arrow');

		$(this).click(function(e) {
			var parents = $(e.target).parents().andSelf();
			if ($.inArray(arrow.get(0), parents) < 0) {
				body.show();
			}
		});

		arrow.click(function() {
			body.hide();
		})

		// :NOTE: morozov 19062008: в IE при сворачивании блока в момент,
		// когда попап открыт, он как-то криво скрывается.
		$(this).parents('.b-minimizeable').bind('minimize', function() {
			body.hide();
		});

		body.bfm_autohide(this);
	});
};


/**********************************************************************************/
/*                             /scripts/ui/select.js                              */
/**********************************************************************************/

/**
 * Cпискок в выпадающем меню.
 *
 * @version	$Id: select.js 6266 2008-12-03 16:14:00Z morozov $
 */
 $.fn.bfm_ui_select = function(values, value) {

	var VALUE_SELECTOR = 'span.click';

	// преобразуем массив в объект
	if (values instanceof Array) {
		var object = new Object();
		for (var i = 0, length = values.length; i < length; i++) {
			object[values[i]] = values[i];
		}
		values = object;
	}

	// определяем значение по умолчанию, если не указано
	if (arguments.length < 2) {
		for (var i in values) {
			value = i;
			break;
		}
	}

	var component = this;
	$(this).find('ul').each(function() {
		$(this).empty();
		var list = this;
		$.each(values, function(id, name) {
			(function(index) {
				var item = $('<li>' + name + '</li>');
				item.click(function(e) {
					if (!$(this).hasClass('selected')) {
						$(this).addClass('selected').siblings().removeClass('selected');
						$(component).find(VALUE_SELECTOR).text($(this).text());

						// сохраняем текущее значение
						value = index;
						$(component).trigger('change', index).find('.b-pulldown-body:first').hide();

						e.stopPropagation();
					}
				});
				$(list).append(item);
				if (index == value) {
					item.click();
				}
			})(id);
		});
	});
	return this;
};


/**********************************************************************************/
/*                             /scripts/ui/options.js                             */
/**********************************************************************************/

/**
 * Заполняет выпадающий список массивом значений.
 */
$.fn.bfm_ui_options = function(values) {
	var self = this,
		// исходное значение элемента
		current = $(this).val();
	$(this).empty();
	$.each(values, function() {
		$(self).append('<option>' + this + '</option>');
	});
	$(this).val(current);
	return this;
};


/**********************************************************************************/
/*                          /scripts/ui/input-filter.js                           */
/**********************************************************************************/

/**
 * Фильтр ввода в текстовом поле.
 *
 */
$.fn.bfm_ui_input_filter = function(filter) {

	var self = this;

	/**
	 * Вызывает событие "change" с задержкой.
	 *
	 */
	function change() {
		window.setTimeout(function() {
			$(self).trigger('change');
		});
	};

	$(this).keypress(function(e) {
		if (e.which < 0x20 || filter.indexOf(String.fromCharCode(e.which)) >= 0) {
			change();
			return true;
		}
		return false;
	});

	$(this).keydown(function(e) {
		if (8 == e.which || 46 == e.which) {
			change();
		}
	});
	return this;
};


/**********************************************************************************/
/*                           /scripts/ui/datepicker.js                            */
/**********************************************************************************/

/**
 * Календарик.
 *
 * @param {Object} options
 */
$.fn.bfm_ui_datepicker = function(options) {

	var self = this,
		iDay = $('.day', this).bfm_ui_input_filter('1234657890'),
		iMonth = $('.month', this).bfm_ui_input_filter('1234657890'),
		iYear = $('.year', this).bfm_ui_input_filter('1234657890'),
		dYear = $('.b-year .value', this),
		dMonth = $('.b-month .value', this);

	/**
	 * Грязный хак. Часть 1: выносим индикаторы календарика вовне.
	 */
	var old = $.datetimepicker._updateDatepicker;

	$.datetimepicker._updateDatepicker = function(inst) {
		old.apply(this, arguments);
		dYear.text(inst._drawYear);
		dMonth.text(inst._get('monthNames')[inst._drawMonth]);
	}

	/**
	 * Синхронизирует значение в текстовых полях со значением в календарике.
	 *
	 * @param {String} value
	 */
	function picker2text(value) {
		var date = new Date(value);
		iDay.val(date.format('dd'));
		iMonth.val(date.format('MM'));
		iYear.val(date.format('yyyy'));
	}

	// рисуем календарик
	var picker = $(this).find('.calendar').datetimepicker($.extend(options, {
		onSelect: function(value) {
			var date = new Date(value);
			self.trigger('change', date.format('yyyy-MM-dd'));
			picker2text(value);
		}
	}));

	// и сразу же синхронизируем значение
	picker2text(picker.datetimepicker('getDate'));

	/**
	 * Грязный хак. Часть 2: выносим кнопки управления календариком вовне.
	 */
	var inst = $.datetimepicker._getInst(picker.get(0)._calId);

	$.each({'month': 'M', 'year': 'Y'}, function(period, value) {
		$.each({'previous': -1, 'next': 1}, function(direction, offset) {
			$(self).find('.b-' + period + ' .' + direction).click(function() {
				$.datetimepicker._adjustDate(inst, offset, value);
			});
		});
	});

	$(this).find('form').submit(function() {
		var day = iDay.val(),
			month = iMonth.val() - 1,
			year = iYear.val();

		if (Date.isValid(year, month, day)) {
			var date = new Date(year, month, day);
			picker.datetimepicker('setDate', date);
			self.trigger('change', date.format('yyyy-MM-dd'));
		} else {
			alert('Введена неверная дата');
		}

		return false;
	});
	return this;
};




/**********************************************************************************/
/*                              /scripts/carousel.js                              */
/**********************************************************************************/

/**
 * Перелистывание новостей.
 *
 * @version	$Id: carousel.js 6266 2008-12-03 16:14:00Z morozov $
 */
$.fn.bfm_carousel = function() {

	var container = this,
		list = $(container).find('.b-runningline-news'),
		items = new Array(),
		api;

	$(list).children().each(function() {
		items.push($(this).html());
	});

	list.jcarousel({
		wrap: 'circular',
		animation: false,
		scroll: 1,
		initCallback: function(carousel) {

			carousel.funcResize = function(){};

			var interval, enabled = true;

			function start() {
				if (!interval) {
					interval = window.setInterval(function() {
						if (enabled) {
							carousel.next();
						}
					}, 5000);
				}
			}

			function stop() {
				window.clearInterval(interval);
				interval = null;
			}

			api = {
				enable: function() {
					enabled = true;
				},
				disable: function() {
					enabled = false;
				}
			};

			$(container).find('.b-button-turn li').click(function() {
				var action = $(this).attr('class');
				if (action in carousel) {
					carousel[action]();
				}
			});

			$(container).find('.b-button-turn, .b-runningline-news').mouseover(function() {
				stop();
			}).mouseout(function() {
				start();
			});

			start();
		},
		itemVisibleInCallback: {
			onBeforeAnimation: function(carousel, item, i, state, evt) {
				var idx = carousel.index(i, items.length);
				carousel.add(i, items[idx - 1]);
			}
		},
		itemVisibleOutCallback: {
			onAfterAnimation: function(carousel, item, i, state, evt) {
				carousel.remove(i);
			}
		},
		buttonNextHTML: null,
		buttonPrevHTML: null
	});

	return api;
};


/**********************************************************************************/
/*                              /scripts/scroller.js                              */
/**********************************************************************************/

/**
 * Бегущая строка.
 *
 * @version	$Id: scroller.js 6266 2008-12-03 16:14:00Z morozov $
 */
$.fn.bfm_scroller = function() {

	var speed = 2,
		refresh = 40;

	var component = this,
		interval,
		element,
		width,
		disabled = $.preferences('stop-scrolling') > 0;

	/**
	 * Инициализирует бегущую строку.
	 */
	function initialize() {
		var contents = $(component).find('.b-scroller ul'),
			container = $(contents).parents(':first');

			element = container.get(0);

			// ширина исходного содержимого
			width = element.scrollWidth;

			var containerWidth = element.offsetWidth,

			// ширина, необходимая для обеспечения непрерывности прокрутки
			needed = containerWidth + width,

			current = $(contents).children(':first');

		// дублируем содержимое бегущей строки для непрерывности
		// прокручивания
		while (element.scrollWidth < needed) {
			var before = element.scrollWidth;
			current.clone().appendTo(contents);
			current = current.next() || current.siblings(':first');
			var after = element.scrollWidth;
			if (after <= before) {
				// если ширина не увеличилась, значит что-то с версткой,
				// и дальнейшее выполнение цикла бессмысленно
				// :TODO: morozov 03112008: бросить бы здесь исключение,
				// да нет времени разбираться с версией для печати
				break;
			}
		}

		$(contents).mouseover(stop).mouseout(function() {
			if (!disabled) {
				start();
			}
		});

		if (!disabled) {
			start();
		} else {
			component.addClass('disabled');
		}
	}

	function start() {
		if (!interval) {
			interval = window.setInterval(function() {
				element.scrollLeft = (element.scrollLeft + speed) % width;
			}, refresh);
		}
	}

	function stop() {
		window.clearInterval(interval);
		interval = null;
	}

	function enable() {
		$.preferences('stop-scrolling', 0);
		component.removeClass('disabled');
		disabled = false;
		start();
	}

	function disable() {
		$.preferences('stop-scrolling', 1);
		component.addClass('disabled');
		disabled = true;
		stop();
	}

	$(this).find('.b-button-play li').click(function() {
		switch ($(this).attr('class')) {
			case 'start':
				enable();
				break;
			case 'stop':
				disable();
				break;
		}
	});

	window.setInterval(function() {
		$.get('/stock/stock_runline.html', {}, function(data) {
			stop();
			$(component).find('.b-scroller').html(data + '<span class="clr"></span>');
			initialize();
		});
	}, 1000 * 60 * 15);

	initialize();
};


/**********************************************************************************/
/*                            /scripts/minimizeable.js                            */
/**********************************************************************************/

(function() {

	/** @type {Array} */
	var status = $.preferences('minimized') || [];

	/**
	 * "Сворачиваемое" поведение блоков.
	 *
	 * @version	$Id: minimizeable.js 6266 2008-12-03 16:14:00Z morozov $
	 */
	$.fn.minimizeable = function() {

		$(this).each(function() {

			var block = this;

			// сворачивание по кнопке "свернуть"
			$(this).find('.minimize').click(function() {
				if (!$(block).hasClass('b-minimized')) {
					$(block).minimize();
				} else {
					$(block).restore();
				}
			});

			// восстанавливаем статус блока из настроек
			if ($(this).minimized()) {
				$(this).addClass('b-minimized');
			}
		});

		return this;
	};

	/**
	 * Свернуть блок.
	 *
	 */
	$.fn.minimize = function() {
		if (!$(this).minimized()) {
			$(this).trigger('minimize').addClass('b-minimized').minimized(1);
		}
	};

	/**
	 * Развернуть блок.
	 *
	 */
	$.fn.restore = function() {
		if ($(this).minimized()) {
			$(this).trigger('restore').removeClass('b-minimized').minimized(0);
		}
	};

	/**
	 * Сохраняет и возвращает состояние блока.
	 *
	 * @param	{Boolean}	status
	 */
	$.fn.minimized = function(value) {

		if ($(this).length != 1) {
			throw new Error('minimized() must be applied for a single element');
		}

		// не понятно, где точки заменяются на подчеркивания. меняем назад.
		var id = $(this).attr('id').replace(/\./g, '_');

		if ('' == id) {
			throw new Error('element ID not specified');
		}

		var index = status.indexOf(id);

		if (0 == arguments.length) {
			return index >= 0;
		} else {
			if (value && index < 0) {
				status.push(id);
			} else if (!value && index >= 0) {
				status.splice(index, 1);
			}

			$.preferences('minimized', status);
		}
	};

})();


/**********************************************************************************/
/*                               /scripts/ticker.js                               */
/**********************************************************************************/

	$.fn.zebra = function() {
		this.find('tr').removeClass('odd').each(function(i) {
			if (i % 2) {
				$(this).addClass('odd');
			}
		});
	};

	var Tickers = (function() {
		var instruments = [];

		function _findAll() {
			instruments = [];
			$('tr.instrument').each(function() {
				instruments.push(this.id.substring(11));
			});
		}

		function _save() {
			$.preferences('instruments', instruments);
		}

		function _remove(id) {
			var pos = instruments.indexOf(id);
			if (pos > -1) {
				instruments.splice(pos, 1);
				var table = $('#instrument_' + id).parents('table');
				$('#instrument_' + id).remove();
				table.zebra();
				$.getJSON('/stock/blocks/get_row.json', {id: id});
				_save();
			}
		}

		function render(row) {
			var c_change = c_arrow = 'zero';

			if (Number(row.change) > 0) {
				c_arrow = 'up';
				c_change = 'plus';
			} else if (Number(row.change) < 0) {
				c_arrow = 'down';
				c_change = 'minus';
			}

			var c_change_year = Number(row.change_year) > 0 ? 'plus' : (Number(row.change_year) < 0 ? 'minus' : 'zero');

			$('#instrument_' + row.id).children().each(function(i) {
				var td = $(this);
				switch(i) {
					case 0:
						td.html('<div class="arrow-' + c_arrow + '"></div>');
						break;
					case 2:
						td.html(row.date);
						break;
					case 3:
						td.html(row.close);
						break;
					case 4:
						td.html('<span class="' + c_change + '">' + row.change + '</span>');
						break;
					case 5:
						td.html('<span class="' + c_change_year + '">' + row.change_year + '</span>');
						break;
				}
			});
		}

		return {
			add: function(id) {
				if ('' == id) {
					alert('Инструмент не существует!');
					return;
				}
				if (instruments.indexOf(id) >= 0) {
					alert('Инструмент уже добавлен!');
					return;
				}

				$.getJSON('/stock/blocks/get_row.json', {id: id}, function(data) {
					if (data) {
						var tr = $(data.html);
						var alias = data.instrument.group_alias + '-' + data.instrument.type_alias;

						tr.find('.ticker-delete').click(function() {
							_remove(data.instrument.id);
							return false;
						});

						var section = $('#gt-' + alias);
						section.parent().find('input:text').val('');
						section.find('table').append(tr).zebra();
						if (section.parent().find('.b-add-ticker').is(':visible')) {
							tr.find('.ticker-delete').show();
						};

						instruments.push(id);
						_save();
					}
				});
			},
			getAll: function() {
				return instruments;
			},
			defaults: function(name) {
				$.get('/stock/blocks/default_' + name + '.html', function(data) {
					if (data != '') {
						$(data).appendTo($('#gt-' + name).empty()).find('.ticker-delete').each(function() {
							$(this).show().click(function() {
								_remove(this.rel);
								return false;
							});
						});
						_findAll();
						_save();
					}
				});
			},
			reload: function() {
				$.getJSON("/stock/blocks/data.json", {'id': instruments.join('_')}, function(data) {
					if (data) {
						for (var i = 0; i < data.length; i++) {
							render(data[i]);
						}
					}
				});
			},
			remove: function(id) {
				_remove(id);
			},
			init: function() {
				_findAll();
			}
		};
	})();

	$.fn.bmf_stock_settings = function() {
		Tickers.init();

		$(this).each(function() {
			$(this).find('.stock-tune').click(function() {
				if ($(this).text() == 'Настроить') {
					$(this).text('Закончить');
				} else {
					$(this).text('Настроить');
				}
				var section = $(this).parents('.b-section');
				section.find('.b-add-ticker,.ticker-delete').toggle();
				section.parents('.b-tabs').toggleClass('edit');

				return false;
			});

			$(this).find('.reload-defaults').click(function() {
				var alias = $(this).parents('.b-section').children(':first').attr('id').substring(3);
				Tickers.defaults(alias);

				return false;
			});

			$(this).find('.ticker-delete').click(function() {
				var id = $(this).parents('tr').attr('id').substring(11);
				Tickers.remove(id);
				return false;
			});
		});
	};

	$(document).ready(function() {
		$('.b-stockbar form').each(function() {
			var group_type = $(this).find('input[name=ticker_group_type]').val();
			var id = $(this).find('input[name=id]');

			var inp = $(this).find('input:text').change(function() {
				id.val('');
			});

			$(this).submit(function() {
				Tickers.add(id.val());
				return false;
			});

			inp.autocomplete('/stock/blocks/autocomplete_' + group_type + '.txt', {
				selectFirst: true,
				delay: 150,
				cacheLength: 0,
				max: 10,
				width: 163,
				onItemSelect: function(item) {
					id.val(item.extra[0]);
					inp.focus();
				}
			});
		});
		window.setInterval(function() {
			Tickers.reload();
		}, 1000 * 60 * 15);
		Tickers.reload();
	});


/**********************************************************************************/
/*                               /scripts/popup.js                                */
/**********************************************************************************/

(function() {

	var params = {
		'radio': 'width=296,height=183'
	};

	$(document).click(function(e) {
		var element = e.target || e.srcElement;
		if ($(element).is('a[rel=popup]')) {
			var name = element.className;
				window.open(element.href, name, name in params ? params[name] : '');
			return false;
		}
	});
})();


/**********************************************************************************/
/*                             /scripts/font-size.js                              */
/**********************************************************************************/

/**
 * Размер шрифта на странице.
 *
 * @version	$Id: font-size.js 6266 2008-12-03 16:14:00Z morozov $
 */
$.fn.bfm_font_size = function() {

	var size = $.preferences('font-size'),
		sizes = new Array();

	function apply(size) {
		var body = $(document.body);
		$.each(sizes, function() {
			body.removeClass(this);
		});
		body.addClass(size);
	}

	$(this).find('span').hover(function() {
		$(this).addClass('hover');
	}, function() {
		$(this).removeClass('hover');
	});

	$(this).find('li').click(function() {
		size = $(this).attr('class');
		$.preferences('font-size', size);
		apply(size);
	}).each(function() {
		sizes.push($(this).attr('class'));
	});

	if (size) {
		apply(size);
	} else {
		$(this).find('li:first').click();
	}

	return this;
};


/**********************************************************************************/
/*                             /scripts/user-panel.js                             */
/**********************************************************************************/

/**
 * Панель пользователя.
 *
 * @version	$Id: user-panel.js 6266 2008-12-03 16:14:00Z morozov $
 */
$.fn.bfm_user_panel = function() {
	var form = $(this).find('form.login'),
		buttons = $(this).find('.buttons'),
		menu = $(this).find('.menu');

	$(this).find('a.login').click(function() {
		menu.hide();
		buttons.hide();
		form.show().find('input:first').focus();
		return false;
	});

	form.find('.login-cancel span').click(function() {
		form.hide();
		menu.show();
		buttons.show();
	});
};


/**********************************************************************************/
/*                              /scripts/lightbox.js                              */
/**********************************************************************************/

/**
 * Адаптер LightBox.
 *
 */
$.fn.bfm_lightbox = function(settings) {

	var host = document.location.protocol + '//static.' + document.location.host + '/';

	return $(this).lightBox($.extend({
		imageLoading:  host + 'images/lightbox/ico-loading.gif',
		imageBtnPrev:  host + 'images/lightbox/btn-prev.gif',
		imageBtnNext:  host + 'images/lightbox/btn-next.gif',
		imageBtnClose: host + 'images/lightbox/btn-close.gif',
		imageBlank:    host + 'images/blank.gif',
		txtImage:     'Изображение',
		txtOf:        'из'
	}, settings));
};


/**********************************************************************************/
/*                               /scripts/media.js                                */
/**********************************************************************************/

/**
 * Отображает медиапроигрыватель.
 */
var bfm_media = (function() {

	function flashplayer(id, src, width, height) {
		swfobject.embedSWF('/flash/mediaplayer.swf', id, width, height, "7", false, {
			usefullscreen: 'false',
			showdigits: 'false',
			frontcolor: '0x576990',
			backcolor: '0xF0F0F0',
			lightcolor: '0x8999B7',
			screencolor: '0x009999',
			file: src
		}, {
			allowfullscreen: 'false',
			wmode: 'transparent'
		});
	}

	var types = {
		audio: function(id, src) {
			flashplayer(id, src, 320, 20);
		},
		video: function(id, src) {
			flashplayer(id, src, 640, 480);
		}
	};

	/**
	 * Отображает медиапроигрыватель.
	 *
	 * @param {String} id
	 * @param {String} src
	 * @param {String} type
	 */
	return function(id, src, type) {

		if (!type in types) {
			throw new Error('Unsupported type "' + type + '"');
		}

		types[type](id, src);
	}
})();


/**********************************************************************************/
/*                               /scripts/print.js                                */
/**********************************************************************************/

function bfm_printview(href) {

	var sheet = document.getElementById('screenview'),
		initial = sheet.href;

	function printview(enabled) {

		// :KLUDGE: morozov 27102008: грязный хак — принудительно отключем
		// карусель, иначе в IE она начинает беситься
		if (window.carousel && enabled) {
			window.carousel.disable();
		}

		var value = enabled ? href : initial;

		// предотвращаем повторную загрузку исходной таблицы стилей
		if (sheet.href != value) {
			sheet.href = value;
		}

		if (window.carousel && !enabled) {
			window.carousel.enable();
		}
	}

	(function() {
		var hash;
		window.setInterval(function() {
			if (location.hash != hash) {
				hash = location.hash;
				printview('#print' == hash);
			}
		}, 100);
	})();
};


/**********************************************************************************/
/*                              /scripts/sitemap.js                               */
/**********************************************************************************/

function bfm_sitemap() {
	var button = $('#sitemap-button')
		contents = $('#sitemap-content')
			.bfm_autohide(button)
			.bind('hide', function() {
				button.removeClass('b-sitemap-button-active')
			});

	button.click(function() {
		contents.toggle();
		if (contents.is(':visible')) {
			$(this).addClass('b-sitemap-button-active');
		} else {
			$(this).removeClass('b-sitemap-button-active');
		}
	});
}



// Формы
/**********************************************************************************/
/*                           /scripts/table_manager.js                            */
/**********************************************************************************/

function checkAll() {
	$('#table_manager tbody input:checkbox').attr('checked', true).each(function() {
		$(this).parents('tr').addClass('selected');
	});
}

function unCheckAll() {
	$('#table_manager tbody input:checkbox').attr('checked', false).each(function() {
		$(this).parents('tr').removeClass('selected');
	});;
}

function invertCheck() {
	$('#table_manager tbody input:checkbox').each(function() {
		$(this).attr('checked', !$(this).attr('checked'));
		if ($(this).is(':checked')) {
			$(this).parents('tr').addClass('selected');
		} else {
			$(this).parents('tr').removeClass('selected');
		}
	});
}

function act(action) {
	$('#table_manager').attr('action', action).submit();
}

function actOne(action, id) {
	$('#table_manager tbody tr').each(function() {
		if ($(this).attr('id') != id) {
			$(this).removeClass('selected').find('input:checkbox').attr('checked', false);
		} else{
			$(this).addClass('selected').find('input:checkbox').attr('checked', true);
		}
	});
	$('#table_manager').attr('action', action).submit();
}

$(document).ready(function() {
	$('#table_manager tbody input:checkbox').change(function() {
		if ($(this).is(':checked')) {
			$(this).parents('tr').addClass('selected');
		} else {
			$(this).parents('tr').removeClass('selected');
		}
	});
});


/**********************************************************************************/
/*                               /scripts/pager.js                                */
/**********************************************************************************/

document.onkeydown = function(e) {
	e = e || window.event;
	var rels = {37: 'previous', 39: 'next'}, code = e.keyCode;
	if (e.ctrlKey && code in rels) {
		var links = document.getElementsByTagName('link');
		for (var i = 0, count = links.length, link; i < count; i++) {
			link = links[i];
			if (link.rel == rels[code]) {
				location.href = link.href;
				return;
			}
		}
	}
};

/**********************************************************************************/
/*                             /scripts/textalias.js                              */
/**********************************************************************************/

(function() {

	var clearChars = 'ьъ';
	var rus = '0 1 2 3 4 5 6 7 8 9 Щ Ё Ж Ч Ш Э Ю Я А Б В Г Д Е З И Й К Л М Н О П Р С Т У Ф Х Ц Ы щ ё ж ч ш э ю я а б в г д е з и й к л м н о п р с т у ф х ц ы'.split(' ');
	var lat = '0 1 2 3 4 5 6 7 8 9 sch jo zh ch sh e ju ja a b v g d e z i j k l m n o p r s t u f h c y shh jo zh ch sh e ju ja a b v g d e z i j k l m n o p r s t u f h c y'.split(' ');

	var table = {};

	String.prototype.to_alias = function() {
		var ret = '';
		var str = this;

		for (var i = 0; i < this.length; i++) {
			var c = str.charAt(i);

			if (clearChars.indexOf(c) > -1) {
				continue;
			}

			if (typeof(table[c]) != 'undefined') {
				ret += table[c];
			} else {
				var lower = String(c).toLowerCase();
				ret += ((lower >= 'a' && lower <= 'z') || (lower >= '0' && lower <= '9')) ? c : '-';
			}
		}

		return ret.toLowerCase().replace(/-+/g, '-').replace(/^-/, '').replace(/-$/, '');
	};

	String.prototype.to_alias_words = function(count) {
		return this.to_alias().split('-').slice(0, count).join('-');
	}

	$.fn.textalias = function() {
		$(this).each(function() {
			var element = $(this);
			var id = element.attr('id').substring(0, element.attr('id').length - '_alias'.length);
			var alias_state = $('#' + id + '_state');
			var alias_element = $('#' + id);

			if (element.val() == '') {
				alias_state.attr('checked', true);
			}

			alias_state.click(function() {
				if ($(this).attr('checked')) {
					element.val(alias_element.val().to_alias());
				}
			});

			alias_element.keyup(function() {
				var flag = alias_state.attr('checked');
				if (flag) {
					element.val($(this).val().to_alias());
				}
			});
		});
	};

	for (var i = 0; i < rus.length; i++) {
		table[rus[i]] = lat[i];
	}
}) ();



// Блоки главной страницы
/**********************************************************************************/
/*                               /scripts/block.js                                */
/**********************************************************************************/

/**
 * Пространство имен функций работы с блоками.
 */
var Block = new Object();

/**
 * Возвращает URI указанного блока. Временное решение.
 * Дублирует код в cmsBlockUtils.
 */
Block.uri = function(id, params) {
	var uri = '/block/' + id.replace(/\./g, '/') + '.html';
	if (arguments.length > 1) {
		var query = new Array();
		for (var param in params) {
			query.push(param + '=' + params[param]);
		}
		uri += '?' + query.join('&');
	}
	return uri;
};

/**
 * Возвращает наименование указанного блока.
 */
Block.caption = function(element) {
	return $(element).find('h2').text();
};

/**********************************************************************************/
/*                            /scripts/block/filter.js                            */
/**********************************************************************************/

if (!window.Block) {
	var Block = new Object();
}

/**
 * Фильтрует пользовательские настройки.
 *
 */
Block.Filter = (function() {

	/**
	 * Фильтрует данные домашней страницы.
	 *
	 * @param	{Object}	value
	 * @return	{Object}
	 */
	function home(value) {
		return {
			main:  place(_get(value, 'main')),
			right: place(_get(value, 'right'))
		};
	}

	/**
	 * Фильтрует данные места.
	 *
	 * @param	{Array}	value
	 * @return	{Array}
	 */
	function place(value) {
		return value instanceof Array
			? value.map(block).filter(function(x) {
				return x;
			}) : new Array();
	}

	/**
	 * Фильтрует данные набора параметров блоков.
	 *
	 * @param	{Object}	value
	 * @return	mixed
	 */
	function settings(value) {
		var result = new Object();
		if (value instanceof Object) {
			for (var i in value) {
				if (value[i] instanceof Object) {
					result[i] = value[i];
				}
			}
		}
		return result;
	}

	/**
	 * Фильтрует данные блока.
	 *
	 * @param	{String}	value
	 * @return	mixed
	 */
	function block(value) {
		return 'string' == typeof value ? value : null;
	}

	/**
	 * Возвращает элемент массива с указанным индексом.
	 *
	 * @param	{Object}	object
	 * @param	mixed	index
	 * @return	mixed
	 */
	function _get(object, index) {
		return object instanceof Object && index in object ? object[index] : null;
	}

	return {

		/**
		 * Фильтрует основной набор данных.
		 *
		 * @param	{Object}	value
		 * @return	{Object}
		 */
		main: function(value) {
			return {
				home:     home(_get(value, 'home')),
				deleted:  place(_get(value, 'deleted')),
				settings: settings(_get(value, 'settings'))
			};
		}
	};
})();


/**********************************************************************************/
/*                            /scripts/block/layout.js                            */
/**********************************************************************************/

if (!window.Block) {
	var Block = new Object();
}

/**
 * Пространство имен методов работы с пользовательскими настройками.
 */
Block.Layout = (function() {

	/**
	 * Наименование переменной настроек, в которой хранятся настройки блоков для
	 * пользователя.
	 */
	var PARAM_NAME = 'blocks';

	/**
	 * Текущие настройки пользователя.
	 */
	var data = Block.Filter.main($.preferences(PARAM_NAME));

	/**
	 * Этот флаг определяет, определены ли у пользователя его персональные
	 * настройки.
	 *
	 * :KLUDGE: morozov 01082008: временное решение — если на главной есть хоть
	 * один блок, считаем, что настройки поднялись.
	 */
	var defined = data.home.lenght > 0;

	/**
	 * Инициализирует пользовательские настройки тем, что творится сейчас на
	 * экране, или админскими настройками, если находимся на внутренней странице.
	 *
	 */
	function initialize() {
		data.home = current();
		defined = true;
	}

	/**
	 * Сохраняет настройки блоков в профайле пользователя.
	 */
	function commit() {
		$.preferences(PARAM_NAME, data);
	}

	function current() {
		try {
			return Block.Desktop.current();
		} catch (e) {
			return window.desktop;
		}
	}

	return {

		/**
		 * Добавляет указанный блок в список удаленных.
		 *
		 * @param	{String}	name
		 */
		remove: function(name) {
			if (!data.deleted.indexOf(name)) {
				data.deleted.push(name);
			}
			commit();
		},

		/**
		 * Возвращает параметры блока.
		 *
		 * @param	{String}	name
		 * @param	{String}	param
		 * @param	{Object}	_default
		 * @return	{Object}
		 */
		getParam: function(name, param, _default) {
			var params = data.settings[name] || new Object();
			if (arguments.length > 1) {
				return param in params
					? params[param] : (arguments.length > 2
						? _default : null);
			} else {
				return params;
			}
		},

		/**
		 * Устанавливает параметры блока.
		 *
		 * @param	{String}	name
		 * @param	{Object}	value
		 * @return	{Object}
		 */
		setParam: function(name, value) {
			if (!defined) {
				initialize();
			}

			data.settings[name] = value;
			commit();
		},

		initialize: function() {
			initialize();
			commit();
		},

		/**
		 * Определяет, добавлен ли указанный блок на главную страницу.
		 *
		 * @param {String} name
		 */
		added: function(name) {
			initialize();
			for (var i = 0, lengthi = data.home.main.length; i < lengthi; i++) {
				for (var j = 0, lengthj = data.home.main[i].length; j < lengthj; j++) {
					if (name == data.home.main[i][j]) {
						return true;
					}
				}
			}
			return false;
		},

		/**
		 * Удаленно добавляет блок с указанным именем.
		 *
		 * @param {String} name
		 */
		addRemote: function(name) {
			initialize();

			var least;

			for (var i = data.home.main.length - 1; i >= 0; i--) {
				var column = data.home.main[i],
					length = column.length;
				if (undefined === least || least.length >= length) {
					least = column;
				}
			}

			least.push(name);
			commit();
		},

		/**
		 * Удаленно удаляет блок с указанным именем.
		 *
		 * @param {String} name
		 */
		removeRemote: function(name) {
			initialize();
			for (var i = 0, lengthi = data.home.main.length; i < lengthi; i++) {
				for (var j = 0, lengthj = data.home.main[i]; j < lengthj; j++) {
					if (name == data.home.main[i][j]) {
						data.home.main[i].splice(j, 1);
						commit();
						return;
					}
				}
			}
		}
	}
})();


/**********************************************************************************/
/*                           /scripts/block/desktop.js                            */
/**********************************************************************************/

if (!window.Block) {
	var Block = new Object();
}

/**
 * Перетаскиваемые блоки главной страницы.
 *
 * @version	$Id: desktop.js 6266 2008-12-03 16:14:00Z morozov $
 */
Block.Desktop = (function() {

	/**
	 * Удаленные блоки для отображения в каталоге информации
	 */
	var removed = new Object();

	/**
	 * Возвращает колонку, в которую нужно добавить новый блок.
	 */
	function getColumn(id) {

		if (/^widget\./.test(id)) {
			return $('#widgets-bar').get(0);
		}

		var columns = $('.b-desktop .column'), min;
		for (var i = 0; i < columns.length; i++) {
			var column = columns.get(i),
				count = $(column).children().length;
			if (undefined == min || count < min) {
				min = count;
				result = column;
			}
		}
		return result;
	}

	function minimizeable(selector) {
		$(selector).minimizeable().each(function() {
			var block = this, id = this.id;
			$(this).find('.close').click(function() {
				// предварительно раскрываем блок, чтобы при последующем
				// добавлении он был открыт
				$(block).restore();
				remove(id);
			});
		});
	}

	/**
	 * Перетаскиваемые блоки главной страницы.
	 *
	 * @version	$Id: desktop.js 6266 2008-12-03 16:14:00Z morozov $
	 */
	function bind(selector) {

		minimizeable($('.b-block-move'));

		var attribute = $.browser.msie && $.browser.version <= 6
			? 'height' : 'min-height';

		/**
		 * Выравнивает высоту колонок.
		 *
		 */
		function valign(columns) {
			var max = 0;
			columns.each(function() {
				var height = $(this).height();
				if (height > max) {
					max = height;
				}
			}).each(function() {
				$(this).css(attribute, max);
			});
		}

		/**
		 * Восстанавливает высоту колонок.
		 *
		 */
		function restore(columns) {
			columns.each(function() {
				$(this).css(attribute, '');
			});
		}

		$.each([
			$(selector).find('.column')/*,
			$('#widgets-bar')*/
		], function() {
			var self = this;
			this.Sortable({
				accept: 'b-block-move',
				helperclass: 'b-block-move-helper',
				handle:'.head',
				tolerance: 'pointer',
				onChange: function(ser) {
					Block.Layout.initialize();
				},
				onStart: function() {
					valign(self);
				},
				onStop: function() {
					restore(self);
				}
			});
		});
	}

	function blocks(selector) {
		var blocks = new Array();
		$(selector).find('.b-block-move').each(function() {
			blocks.push(this.id);
		});
		return blocks;
	}

	function current(plain) {

		if (0 == $('.b-desktop').length) {
			throw new Error('Desktop is not available');
		}

		var main  = new Array(),
			right = new Array(),
			linear = new Array();
		$('.b-desktop .column').each(function() {
			var column = blocks(this);
			linear = linear.concat(column)
			main.push(column);
		});

		right = blocks('#widgets-bar');
		linear = linear.concat(right);

		return plain ? linear : {
			main: main,
			right: right
		};
	}

	/**
	 * Добавляет блок на страницу
	 */
	function add(id) {
		if (!added(id)) {
			$.ajax({
				url: Block.uri(id),
				success: function(contents) {

					// определяем колонку, куда нужно добавить блок
					var column = getColumn(id),

					// создаем элемент блока
						block = $(contents);

					minimizeable(block);

					// добавляем блок в колонку и в набор перетаскиваемых блоков
					try {
						$(column).append(block).SortableAddItem(block.get(0));
					} catch (e) {
						// пока что в правой колонке перетаскивания не будет
					}

					// удаляем блок из списка удаленных
					delete removed[id];

					// обновляем расположение блоков
					Block.Layout.initialize();
				},
				error: function(XMLHttpRequest, textStatus, errorThrown) {
					alert(errorThrown || textStatus);
				}
			});
		} else {
			alert('Блок уже добавлен');
		}
	}

	/**
	 * Удаляет блок со страницы
	 */
	function remove(id) {
		// получаем элемент блока (идентификатор содержит точки, поэтому достаем
		// по старинке)
		var element = document.getElementById(id);

		// сохраняем блок в удаленных для текущей страницы
		removed[id] = Block.caption(element);

		$(element).remove();

		// сохраняем блок в удаленных для настроек пользователя
		// :TODO: morozov 13102008 сделать, чтобы выполнялось автоматически
		Block.Layout.remove(id);
		Block.Layout.initialize();
	}

	function added(id) {
		return current(true).indexOf(id) >= 0;
	}

	return {
		bind: bind,
		current: current,
		add: add,
		remove: remove,
		removed: function() {
			return removed;
		}
	};
})();


/**********************************************************************************/
/*                          /scripts/block/catalogue.js                           */
/**********************************************************************************/

if (!window.Block) {
	var Block = new Object();
}

/**
 * Пространство имен методов работы с каталогом информации.
 *
 * @version	$Id: catalogue.js 6266 2008-12-03 16:14:00Z morozov $
 */
Block.Catalogue = (function() {

	/**
	 * Селектор основного элемента каталога информации.
	 */
	var catalogue;

	/**
	 * Элементы списков, соответствующие блокам на главной странице.
	 */
	var items;

	/**
	 * Исходное состояние главной страницы на момент открытия каталога.
	 */
	var initial;

	/**
	 * Массив идентификаторов блоков, представленных в каталоге.
	 */
	var available = new Array();

	// инициализирует поведение списка
	function initialize_list(list) {
		$(list).find('li').click(function() {
			$(this).toggleClass('selected');
		}).each(function() {
			available.push($(this).attr('name'));
		});

		$(list).find('.preview').click(function(e) {
			e.preventDefault();
			e.stopPropagation();
			alert($(this).parents('li:first').attr('name'));
		});
	}

	/**
	 * Привязывает каталог информации к указанному селектору.
	 *
	 */
	function bind(selector) {

		catalogue = selector;

		// применяем к каталогу интерфейс вкладок
		$(catalogue).find('.b-left-column ul').tabs({
			// здесь открытую вкладку запоминать не надо
			cookie: null
		});

		available = new Array();

		$(catalogue).find('.b-right-column ul').each(function() {
			initialize_list(this);
		});

		$(catalogue).find('.b-popup-btn input').click(function() {
			commit();
			close();
		});

		$(catalogue).find('.popup-close').click(function() {
			close();
		});

		// приделываем поведение к подсказке RSS
		$(catalogue).find('.b-rss-example a').click(function() {
			$(catalogue).find('.rss-inp').val($(this).attr('href'));
			return false;
		});

		// форма добавления потока
		$('#rss').submit(function() {
			var preview = $(this).find('.b-feed-preview').hide(),
				error = $(this).find('.b-misstake').hide();
			$(this).ajaxSubmit({
				success: function(id) {
					$.get(Block.uri('feed.' + id), function(data) {
						var block = $(data).removeAttr('id');
						preview.find('.contents').html(block);
						preview.show();
						preview.find('.add').click(function() {
							Block.Desktop.add('feed.' + id);
							preview.hide();
						});
					});
				},
				error: function(request) {
					error/*.text(request.responseText)*/.show();
				}
			});
			return false;
		});

		// форма сброса настроек
		$('#reset-blocks').submit(function() {
			$.preferences(null);

			// делаем перезагрузку по таймауту, чтобы запрос на сброс данных
			// смог хотя бы уйти
			window.setTimeout(function() {
				window.location.reload(true);
			}, 100);

			close();
			return false;
		});
	};

	/**
	 * Открывает каталог.
	 */
	function open() {

		// собираем состояние каталога на момент открытия
		initial = Block.Desktop.current(true);

		// строим вкладку "удаленные блоки"
		var list = $('#deleted-blocks').empty().append('<ul></ul>');
		$.each(Block.Desktop.removed(), function(id, caption) {
			list.append('<li name="' + id + '">' + caption + '</li>'); // "<span class="preview">Предпосмотр</span> "
		});
		initialize_list(list);

		// элементы списков, соответствующие блокам главной страницы
		items = $(catalogue).find('.b-right-column li');

		// сбрасываем состояние "отмечено"
		items.removeClass('selected');

		// помечаем пункты блоков, которые есть на странице
		$.each(initial, function() {
			$(items).filter('[name=' + this + ']').addClass('selected');
		});

		$(catalogue).show().trigger('open');
	}

	/**
	 * Сохраняет изменения, выполненные в каталоге.
	 */
	function commit() {

		// собираем текущее состояние каталога
		var current = new Array();
		items.each(function() {
			if ($(this).hasClass('selected')) {
				current.push($(this).attr('name'));
			}
		});

		// блоки, которые нужно удалить. исключаем из удаления блоки, которые
		// добавлены на страницу, но не представлены в каталоге (например,
		// RSS-поток, не одобренный редакцией)
		$.each(initial.diff(current).intersect(available), function() {
			Block.Desktop.remove(this);
		});

		// блоки, которые нужно добавить — добавляем после удаления, чтобы
		// расположение по колонкам было более-менее равномерным (блоки
		// добавляются в колонку, содержащую наименьшее число блоков на момент
		// добавления)
		$.each(current.diff(initial), function() {
			Block.Desktop.add(this);
		});
	}

	/**
	 * Закрывает каталог.
	 */
	function close() {
		$(catalogue).hide();
	}

	return {
		bind: bind,
		open: open,
		close: close,
		status: status
	}
})();


/**********************************************************************************/
/*                            /scripts/block/remote.js                            */
/**********************************************************************************/

(function() {

	var actions = {
		'add': function(name) {
			return Block.Layout.addRemote(name);
		},

		'remove': function(name) {
			return Block.Layout.removeRemote(name);
		}
	};

	/**
	 * Блок удаленного добвления/удаления блока на главную страницу.
	 *
	 * @param {String} name
	 */
	$.fn.bfm_block_remote = function(name) {
		var forms = $(this).find('form').submit(function() {
			var action = $(this).attr('name');
			if (action in actions) {
				actions[action](name);
				forms.show();
				$(this).hide();
			}
			return false;
		});

		if (Block.Layout.added(name)) {
			forms.filter('[name=add]').hide();
			forms.filter('[name=remove]').show();
		} else {
			forms.filter('[name=add]').show();
			forms.filter('[name=remove]').hide();
		}
	};

})();





// Виджеты
// :TODO: morozov 18102008: вынести в динамическую подгрузку с кодом виджета
/**********************************************************************************/
/*                           /scripts/widget/weather.js                           */
/**********************************************************************************/

/**********************************************************************************/
/*                         /scripts/widget/controller.js                          */
/**********************************************************************************/

var Widget = new Object();

/**
 * Кэш данных виджета.
 *
 */
Widget.Cache = (function() {

	/**
	 * Данные кэша.
	 *
	 */
	var data = new Object();

	return {

		/**
		 * Возвращает, сохранены ли данные для указанного URI.
		 *
		 * @param	{String}	uri
		 */
		isset: function(uri) {
			return uri in data;
		},

		/**
		 * Возвращает данные, соответствующие указанному URI.
		 *
		 * @param	{String}	uri
		 */
		get: function(uri) {
			return data[uri];
		},

		/**
		 * Сохраняет данные, соответствующие указанному URI.
		 *
		 * @param	{String}	uri
		 * @param	{Object}	value
		 */
		set: function(uri, value) {
			data[uri] = value;
		}
	};
})();

/**
 * Фабрика контроллеров виджетов, работающих со внешними данными.
 *
 * @param {String}		path
 * @param {Object}		params
 * @param {Function}	callback
 */
Widget.Controller = function(path, params, callback) {

	/**
	 * Текущее состояние контроллера.
	 */
	var state = new Object(),

	/**
	 * Интерфейс контроллера.
	 */
		api = new Object();

	/**
	 * Собирает URI данных, соответствующих текущему состоянию.
	 *
	 */
	function get_uri() {
		var query = new Array();
		for (var param in state) {
			query.push(param + '=' + state[param]);
		}
		return path + '?' + query.join('&');
	}

	/**
	 * Собирает массив аргументов для функции обтатного вызова,
	 * соответствующий текущему состоянию.
	 *
	 */
	function get_args(data) {
		var args = [data];
		for (var param in state) {
			args.push(state[param]);
		}
		return args;
	}

	/**
	 * Проверяет готовность элементов пользовательского интерфейса.
	 *
	 */
	function ready() {
		for (var param in state) {
			if (null === state[param]) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Вызывает функцию обратного вызова, передавая запрошенные данные и текущие
	 * значения параметров.
	 *
	 * @param {Object} data
	 */
	function __callback(data) {
		callback.apply(callback, get_args(data));
	}

	/**
	 * Обработчик события изменения входных параметров.
	 *
	 */
	function onchange() {
		if (ready()) {
			var uri = get_uri();
			if (Widget.Cache.isset(uri)) {
				var cached = Widget.Cache.get(uri);
				__callback(cached)
			} else {
				$.getJSON(uri, function(data) {
					Widget.Cache.set(uri, data);
					__callback(data);
				});
			}
		}
	};

	for (var i = 0, length = params.length; i < length; i++) {
		(function(param) {
			state[param] = null;
			api[param] = function(value) {
				state[param] = value;
				onchange();
			};
		})(params[i]);
	}

	return api;
};



/**
 * Виджет погоды.
 *
 * @version $Id: weather.js 6266 2008-12-03 16:14:00Z morozov $
 */
$.fn.weather = function(towns, town) {

	var self = this;

	// создаем контроллер виджета
	var controller = Widget.Controller(
		'/weather/index.html',
		['town'],
		function(data, town) {

			// сохраняем город в настройках
			Block.Layout.setParam('widget.weather', {town: town});

			var container = $(self).find('table'),
				row1 = $(container).find('tr:first').empty(),
				row2 = $(container).find('tr:last').empty();

			$.each(data, function() {
				row1.append('<th>' + this.day + ', ' + this.time + '</th>');
				row2.append('<td class="' + this.tod + '"><div class="icon ' + this.weather + '"><!-- --></div><div class="temp">' + this.min + '..' + this.max + '</div></td>');
			});

			row1.find('th:first').addClass('today');
		}
	);

	// список городов
	$(this).find('.setting-select').change(function(e, value) {
		controller.town(value);
	}).bfm_ui_select(towns, town);

	$(this).find('.b-pulldown-click').bfm_pulldown();

	return this;
};


/**********************************************************************************/
/*                          /scripts/widget/converter.js                          */
/**********************************************************************************/



/**
 * Виджет конвертера валют.
 *
 * @version $Id: converter.js 6266 2008-12-03 16:14:00Z morozov $
 */
(function() {

	/**
	 * Контейнер курсов.
	 */
	var rates = (function() {

		/**
		 * Данные курсов для тещих параметров (пока что только дата).
		 */
		var data = new Object();

		return {

			/**
			 * Устанавливает указанные курсы.
			 *
			 * @param {Object} values
			 */
			initialize: function(values) {
				data = values;
			},

			/**
			 * Возвращает массив валют, _из_ которых можно конвертировать.
			 *
			 * @return {String[]}
			 */
			cur1: function() {
				var currencies = new Array();
				$.each(data, function(currency, rates) {
					currencies.push(currency);
				});
				return currencies;
			},

			/**
			 * Возвращает массив валют, _в_ которые можно конвертировать.
			 *
			 * @return {String[]}
			 */
			cur2: function(date) {
				var currencies = new Array(), done = new Object;
				$.each(data, function(currency, rates) {
					$.each(rates, function(currency, rate) {
						// что-то не получается с $.unique
						if (!done[currency]) {
							currencies.push(currency);
							done[currency] = true;
						}
					});
				});
				return $.unique(currencies);
			},

			/**
			 * Возвращает значение курса.
			 *
			 * @param {String} cur1
			 * @param {String} cur2
			 * @return {Number}
			 */
			get: function(cur1, cur2) {
				return data[cur1][cur2];
			}
		};
	})();

	$.fn.converter = function(date) {

		/**
		 * Контроллер элементов интерфейса.
		 */
		$(this).each(function() {

			/**
			 * Контроллер виджета
			 */
			var controller = Widget.Controller(
				'/stock/currencies.json',
				['date'],
				function(data, date) {
					// форматируем дату по-русски
					var text = Date.parse(date, 'yyyy-MM-dd').format('d MMMMM yyyy', 'ru');
					$(self).find('.b-pulldown-click span.click').text(text);
					rates.initialize(data);
					_switch();
				}
			);

			var maxDate = Date.parse(date, 'yyyy-MM-dd');

			// рисуем календарик
			$(this).find('.b-calendar-wrap').bfm_ui_datepicker({
				defaultDate: maxDate,
				maxDate: maxDate
			}).change(function(e, value) {
				// :KLUDGE: morozov 22102008: в FF откуда-то валятся ложные
				// срабатывания при вводе даты через форму календарика.
				// может быть он сам их и бросает
				if (value) {
					controller.date(value);
				}
				$(this).parents('.b-pulldown-body:first').hide();
			}).trigger('change', date);

			// инициализируем элементы интерфейса
			var self = this,

				/**
				 * Элементы интерфейса конвертера
				 */
				oControls = $('form input[name], form select', self),
				oCur1 = $('[name=cur1]', self),
				oCur2 = $('[name=cur2]', self),
				oVal1 = $('[name=value1]', self).bfm_ui_input_filter('1234657890.'),
				oVal2 = $('[name=value2]', self);

				var update = function() {

					enable();

					var cur1 = oCur1.val(),
						cur2 = oCur2.val(),
						val1 = oVal1.val(),
						val2,
						error = false;

					switch (true) {
						case isNaN(val1):
						case '' == val1:
							val2 = '';
							break;
						default:
							// округляем до двух знаков после запятой
							val2 = Math.round(val1 * rates.get(cur1, cur2) * 100) / 100;
							break;
					}

					oVal2.val(val2);

					if (false !== error) {
						disable(error);
					}
				},

				/**
				 * Переключает конвертер на указанные параметры.
				 */
				_switch = function(date) {
					var cur1 = rates.cur1(),
						cur2 = rates.cur2();
					if (cur1.length > 0 && cur2.length) {
						oCur1.bfm_ui_options(cur1);
						oCur2.bfm_ui_options(cur2);
						update();
					} else {
						disable('.b-no-rates');
					}
				},

				/**
				 * Включает элементы интерфейса.
				 */
				enable = function() {
					$('form', self).removeClass('error');
					$('.b-error', self).hide();
					oControls.removeAttr('disabled');
				},

				/**
				 * Отключает элементы интерфейса.
				 */
				disable = function(selector) {

					$('form', self).addClass('error');

					// очищаем поле результата
					oVal2.val('');

					// показываем сообщение об ошибке
					$(selector, self).show();

					// отключаем элементы интерфейса
					oControls.attr('disabled', 'disabled');
				};

			oControls.change(update);
		});

		$(this).find('.b-pulldown-click').bfm_pulldown();

		return this;
	};
})();



// :TODO: morozov 18102008: разнести по коду страницы, чтобы не дожидаться
// загрузки документа

$(function() {
	// если поставить выше, падает в IE. надо разобраться
	Block.Desktop.bind('.b-desktop');

	$('.b-user-panel-btn span').click(function() {
		Block.Catalogue.open();
	});
});

// настройки cookies по умолчанию. запихиваем в саму функцию, хоть
// изначально в ней такого не реализовано
$.cookie.defaults = {
	expires: 30,
	path: '/'
};

// выставляем в настройках табов автоматическое сохранение состояния в куки
$.extend($.ui.tabs.defaults, {
	cookie: $.cookie.defaults
});

