/**
 * jQuery Picobello addon
 * @author Marcin
 */


function PBSignalAccumulator(settings) {
	settings || (settings = {});
	var __inst = this;
	var __timeOutId;
	
	
	var __execute = (typeof settings.callback == 'function') ?
		settings.callback :
		function () {}
		;
		
	this.setCallback = function (callback) {
		__execute = callback;
		
		return this;
	};
	
	var __signal = function () {
		__execute.call(__inst);
	};
		
	
	var __timeout = settings.timeout ?
		settings.timeout :
		700
		;
	
	this.setTimeout = function (timeout) {
		__timeout = timeout;
		
		return this;
	};
	
	
	this.signal = function () {
		if (__timeOutId) {
			clearTimeout(__timeOutId);
		}
		__timeOutId = setTimeout(__signal, __timeout);
		
		return this;
	};
}


/**
 * settings.frameInterval: animation frame delay [smooth, performance, int] default: smooth
 * settings.speed: animation time [fast, slow, int] defalt: fast
 * settings.callback: callback to recive next value.
 * settings.current: float startup value default: 0
 * settings.min: float default: 0
 * settings.max: float default: 1
 * settings.calcFunction: optional function(currentValue, targetValue) retuns nextValue [optional]
 */
function PBValueAdjuster(settings) {
	var __inst = this;
	var __intervalId;
	
	var __min = (typeof settings.min != 'undefined') ?
		settings.min :
		0
		;

	this.setMin = function (value) {
		__min = value;
		
		return this;
	};
	
	var __max = (typeof settings.max != 'undefined') ?
		settings.max :
		1
		;
	
	this.setMax = function (value) {
		__max = value;
		
		return this;
	};

	var __current = (typeof settings.current != 'undefined') ?
		settings.current :
		0
		;
	
	var __targetValue = __current;
	
	
	var __frameInterval = settings.frameInterval ?
		settings.frameInterval :
		'smooth'
		;
		switch (__frameInterval) {
			case 'smooth':
				__frameInterval = 20;
				break;
				
			case 'performance':
				__frameInterval = 60;
				break;
		}
	
	var __speed = settings.speed ?
		settings.speed :
		'fast'
		;
		switch (__speed) {
			case 'fast':
				__speed = 300;
				break;
				
			case 'slow':
				__speed = 600;
				break;
		}
	
	var __linearStep = __frameInterval/__speed;
	var __linearNextValueCalc = function(currentValue, targetValue) {
		return (currentValue > targetValue) ?
			currentValue - __linearStep :
			currentValue + __linearStep
			;
	};

	var __sqrtNextValueCalc = function(currentValue, targetValue) {
		var spd = __speed / 3;
		
		return (currentValue > targetValue) ?
			currentValue - Math.sqrt((currentValue - targetValue) * spd) / spd:
			currentValue + Math.sqrt((targetValue - currentValue) * spd) / spd
			;
	};

	var __nextValueCalc;
	if (typeof settings.calcFunction == 'function') {
		__nextValueCalc = settings.calcFunction;
		
	} else {
		switch (settings.calcFunction) {
			case 'linear':
				__nextValueCalc = __linearNextValueCalc;
				break;
				
			case 'sqrt':
				__nextValueCalc = __sqrtNextValueCalc;
				break;
				
			default:
				__nextValueCalc = __sqrtNextValueCalc;
		};
	}

	var __valueCallback = settings.callback;
	var __worker = function () {
		var nextValue = __nextValueCalc(__current, __targetValue);
			if (nextValue < __min) {
				nextValue = __min;
				
			} else if (nextValue > __max) {
				nextValue = __max;
			}
			
		switch (nextValue) {
			case __min: case __max:
				/* no break - call the callback */
				clearInterval(__intervalId);
				__intervalId = undefined;
				
			default:
				__valueCallback.call(__inst, __current = nextValue);
		}		
	};
	
	this.setTargetValue = function (targetValue) {
		if (targetValue > __max) {
			targetValue = __max;
			
		} else if (targetValue < __min) {
			targetValue = __min;
		}
		
		__targetValue = targetValue;
		
		if (typeof __intervalId == 'undefined') {
			__intervalId = setInterval(__worker, __frameInterval);
		}
		
		return this;
	};
	
	this.inc = function (offset) {
		return this.setTargetValue(__targetValue + offset);
	}
	
	this.dec = function (offset) {
		return this.setTargetValue(__targetValue - offset);
	}
}

(
	function ($) {
		$.fn.activatePBMenu = function (opts) {
			opts = opts ? opts : {};
			
			this.each(function () {
				var settings = opts;
				
				settings.canvas = this;
				new PBMenu(settings);
			});
			
			return this;
		};
	}
) (jQuery);

function PBSlider(settings) {
	var __isCollapsed = settings.startupState != 'expanded';
	var __instance = this;
	
	var __callback = function (value) {
		$(settings.container).css('height', value);
	};
	
	var __adjuster = new PBValueAdjuster({
		min: settings.collapsedHeight,
		max: settings.expandedHeight,
		current: __isCollapsed ?
			settings.collapsedHeight : 
			settings.expandedHeight
			,
		callback: __callback,
		speed: 5
	});
	
	var __actionCallback = settings.actionCallback;
	this.setActionCallback = function (callback) {
		__actionCallback = callback;
	};
	
	this.expand = function () {
		if (!$(settings.container).hasClass('expanded')) {
			$(settings.container).removeClass('collapsed').addClass('expanded');
			$(settings.activator).removeClass('collapsed').addClass('expanded');
			
			__adjuster.setTargetValue(settings.expandedHeight);
			__isCollapsed = false;
			
			if (__actionCallback) {
				__actionCallback.call(__instance, {action: 'expand'});
			}
		}
		
		return this;
	};
	
	this.collapse = function () {
		if (!$(settings.container).hasClass('collapsed')) {
			$(settings.container).removeClass('expanded').addClass('collapsed');
			$(settings.activator).removeClass('expanded').addClass('collapsed');
			
			__adjuster.setTargetValue(settings.collapsedHeight);
			__isCollapsed = true;
			
			if (__actionCallback) {
				__actionCallback.call(__instance, {action: 'collapse'});
			}
		}
		
		return this;
	};
	
	$(settings.activator).click(function () {
		return !__isCollapsed ?
			__instance.collapse() :
			__instance.expand()
			;
	});
}

function PBMenu(settings) {
	var __animatorSettings = {
		frameInterval: settings.frameInterval,
		speed: settings.speed,
		min: 0,
		max: 1
		};
	
	var __canvas = settings.canvas;
	switch ($(__canvas).css('visibility')) {
		case 'visible': case '':
			$(__canvas).css('opacity', '1');
			__animatorSettings.current = 1;
			break;
			
		case 'hidden': case 'collapse':
			$(__canvas).css('opacity', '0');
			__animatorSettings.current = 0;
			break;
	}
	
	__animatorSettings.callback = function (v) {
		switch (v) {
			case 0:
				$(__canvas).css('visibility', 'hidden');
				break;
				
			case 1:
				break;
				
			default:
				$(__canvas).css('visibility', 'visible');
		}
		
		$(__canvas).css('opacity', v);
	};
	
	var __animator = new PBValueAdjuster(__animatorSettings);
	
	var __container = settings.container ?
		settings.container :
		__canvas.parentNode
		;
	
	$(__container).mouseleave(function (event) {
		__animator.setTargetValue(0);
	});
	
	$(__container).mouseenter(function (event) {
		for (var node = event.target; node; node = node.parentNode) {
			if (node === __canvas) {
				return;
			}
		}
		
		__animator.setTargetValue(1);
	});
}


(
	function ($) {
		$.fn.appendPBCheckbox = function (opts) {
			opts = opts ? opts : {};
			
			this.each(function () {
				var settings = opts;
				
				settings.container = this;
				new PBCheckbox(settings);
			});
			
			return this;
		};
	}
) (jQuery);



PBCheckbox.prototype.STATE_UNCHECKED = 1;
PBCheckbox.prototype.STATE_PARTIAL = 2;
PBCheckbox.prototype.STATE_CHECKED = 3;

function PBCheckbox(settings) {
	var __state = this.STATE_UNCHECKED;
	var __expanded = false;
	var __inst = this;
	
	if (settings.icon) {
		var __icon = document.createElement('img');
			__icon.src = settings.icon;
	}
	
	if (settings.name) {
		var __input = document.createElement('input');
			__input.setAttribute('name', settings.name);
			__input.setAttribute('value', settings.value);
			__input.setAttribute('type', 'hidden');
	}
	
	var __expander = document.createElement('span');
		$(__expander).addClass('collapsed');
		
	var __label = document.createElement('label');
		$(__label).addClass('unchecked');
		__label.innerHTML = settings.label;
	
	var __childrenList = document.createElement('div');
	
	var __childrenContainer = document.createElement('div');
		$(__childrenContainer).addClass('children');
		__childrenContainer.appendChild(__childrenList);
	
	var __container = document.createElement('div');
		if (__icon) {
			__container.appendChild(__icon);			
		}
		
		if (__input) {
			__container.appendChild(__input);
			__input.__pbCheckbox = __inst;
		}
		
		__container.appendChild(__label);
		__container.appendChild(__expander);
		
		__container.appendChild(__childrenContainer);
		
		if (settings.className) {
			$(__container).addClass(settings.className);
		}
		
	var __parent = settings.parent;
	var __children = [];

	this.getChildrenContainer = function () {
		return __childrenList;
	};
	
	if (__parent) {
		__parent.getChildrenContainer().appendChild(__container);
		
	} else {
		settings.container.appendChild(__container);
	}
	
	this.getState = function () {
		return __state;
	};
	
	var __dontRecurse = false;	
	this.refresh = function () {
		var c = 0;
		
		for (var i in __children) {
			if (__children [i].getState() == this.STATE_CHECKED) {
				c++;
			};
		}
		
		__dontRecurse = true;
		switch (c) {
			case 0:
				this.uncheck();
				break;
				
			case __children.length:
				this.check();
				break;
				
			default:
				__setPartial();
		}
		__dontRecurse = false;
	};
	
	var __setState = function (state) {
		if (__input) {
			switch (__state = state) {
			case __inst.STATE_CHECKED:
				__input.checked = true;
				break;
				
			case __inst.STATE_PARTIAL:
				__input.checked = false;
				break;
				
			case __inst.STATE_UNCHECKED:
				__input.checked = false;
				break;
			}
			
		} else {
			__state = state;
		}
	};
	
	var __setPartial = function () {
		__setState(__inst.STATE_PARTIAL);
		
		if (__parent) {
			__parent.refresh();
		}
		
		$(__label).addClass('partial').removeClass('unchecked').removeClass('checked');
		return this;
	};
	
	this.check = function () {
		__setState(__inst.STATE_CHECKED);
		
		if (!__dontRecurse) {
			for (var i in __children) {
				__children [i].check();
			}
		}
		
		if (__parent) {
			__parent.refresh();
		}
		
		$(__label).addClass('checked').removeClass('unchecked').removeClass('partial');
		return this;
	};
	
	this.uncheck = function () {
		__setState(__inst.STATE_UNCHECKED);
		
		if (!__dontRecurse) {
			for (var i in __children) {
				__children [i].uncheck();
			}
		}
		
		if (__parent) {
			__parent.refresh();
		}
		
		$(__label).addClass('unchecked').removeClass('checked').removeClass('partial');
		return this;
	};
	
	$(__label).click(function () {
		switch (__state) {
			case __inst.STATE_CHECKED:
				__inst.uncheck();
				break;
				
			case __inst.STATE_PARTIAL:				
				__inst.check();
				break;
				
			case __inst.STATE_UNCHECKED:
				__inst.check();
				break;
				
			default:
				//console.log(__state);
		}
	});

	this.addChild = function (params) {
		this.createChild(params);
		
		return this;
	};
	
	var __animator = new PBValueAdjuster({
		callback: function (v) {
			$(__childrenContainer).height(v + 'px');
		},
		speed: 3
		});
	
	var __childrenListHeight = 0;
	this.createChild = function (params) {
		params.parent = this;
		
		var newChild = new PBCheckbox(params);
		__children.push(newChild);
		
		__animator.setMax(__childrenListHeight = $(__childrenList).height());
		
		return newChild;
	};
	
	
	var __resizeCallback;
	this.setResizeCallback = function (callback) {
		__resizeCallback = callback;
	};
	
	this.expand = function (opts) {
		if ($(__expander).hasClass('expanded')) {
			return this;
		}
		
		opts || (opts = {});
		
		if (opts.instant) {
			$(__childrenContainer).height(__childrenListHeight + 'px');
			
		} else {
			__animator.setTargetValue(__childrenListHeight);
		}
			
		$(__expander).removeClass('collapsed').addClass('expanded');
		__expanded = true;
		
		if (typeof __resizeCallback == 'function') {
			__resizeCallback.call(__inst, {type: 'expanding'});
		}
		
		return this;
	};
	
	this.collapse = function (opts) {
		if ($(__expander).hasClass('collapsed')) {
			return this;
		}
		
		opts || (opts = {});
		
		if (opts.instant) {
			$(__childrenContainer).height(0 + 'px');

		} else {
			__animator.setTargetValue(0);
		}
		
		$(__expander).removeClass('expanded').addClass('collapsed');
		__expanded = false;
			
		if (typeof __resizeCallback == 'function') {
			__resizeCallback.call(__inst, {type: 'collapsing'});
		}
		
		return this;
	};
	
	$(__expander).click(function () {
		if (__expanded) {
			__inst.collapse();
			
		} else {
			__inst.expand();
		}
	});	
}

function PBFileUploadBox(opts) {
	var __instance = this;
	
	var __input = document.createElement('input');
		__input.setAttribute('name', opts.name);
		__input.setAttribute('type', 'file');
	
	var __label = document.createElement('label');
		
	var __cancelBtn = document.createElement('input');
		__cancelBtn.setAttribute('type', 'button');
		__cancelBtn.setAttribute('value', 'X');
		
	var __container = document.createElement('div');
		__container.appendChild(__input);
		__container.appendChild(__label);
		
	opts.container.appendChild(__container);
	
	if (opts.container.__fileUploadBoxCount) {
		opts.container.__fileUploadBoxCount++;
		
	} else {
		opts.container.__fileUploadBoxSelectedCount = 0;
		opts.container.__fileUploadBoxCount = 1;
	}
	
	var __isFileSelected = false;
	$(__input).change(function (event) {
		if (!__isFileSelected) {
			opts.container.__fileUploadBoxSelectedCount++;
			__container.appendChild(__cancelBtn);
			__isFileSelected = true;
			
			if (opts.autoIncrement && (!opts.maxFiles || (opts.maxFiles > opts.container.__fileUploadBoxCount))) {
				new PBFileUploadBox(opts);
			}
		}
		__label.innerHTML = this.value.replace(/^.*\/(.*)/, '\1');
	});
	
	$(__cancelBtn).click(function () {
		if (__isFileSelected && (opts.container.__fileUploadBoxCount > 1)) {
			if ((opts.container.__fileUploadBoxSelectedCount == opts.maxFiles) && opts.autoIncrement){
				new PBFileUploadBox(opts);
			}
			
			opts.container.__fileUploadBoxSelectedCount--;
			opts.container.removeChild(__container);
			opts.container.__fileUploadBoxCount--;
		}
	});
}

function PBPager(opts) {
	var __itemsPerPage = opts.itemsPerPage ?
		opts.itemsPerPage :
		10
		;
	var __pagerWidth = opts.pagerWidth ?
		opts.pagerWidth :
		5
		;
	var __itemsCount = opts.itemsCount;
	
	var __totalPages = Math.ceil(__itemsCount / __itemsPerPage);
	var __pager = {pages: []};
	
	var __currentPage;
	this.setPage = function (pageNo) {
		__currentPage = pageNo;
		__pager = {pages: []};
		
		for (var i = -__pagerWidth; i <= __pagerWidth; i++) {
			var p = pageNo + i;
			
			if ((p >= 1) && (p <= __totalPages)) {
				__pager.pages.push (p);
			}
		}
		
		if (__pager.pages.length > __pagerWidth) {
			var start = Math.floor((__pager.pages.length - __pagerWidth) / 2);
			__pager.pages = __pager.pages.slice(start, start + __pagerWidth);
		}
		
		if (pageNo > 1) {
			__pager.prev = pageNo - 1;
		}
		
		if (pageNo < __totalPages) {
			__pager.next = pageNo + 1;
		}
		
		if (__pager.pages [0] > 1) {
			__pager.first = 1;
		}
		
		if (__pager.pages [__pager.pages.length - 1] < __totalPages) {
			__pager.last = __totalPages;
		}
		
		if (__pager.pages.length > 0) {
			switch (__pager.last - __pager.pages [__pager.pages.length - 1]) {
				case NaN: case 1:
					__pager.rightDots = false;
					break;
				
				case 2: 
					__pager.pages.push (__pager.last - 1);
					__pager.rightDots = false;
					break;
					
				default:
					__pager.rightDots = true;
			}
			
			switch (__pager.pages [0] - __pager.first) {
				case NaN: case 1:
					__pager.leftDots = false;
					break;
					
				case 2: 
					__pager.pages.unshift(__pager.first + 1);
					__pager.leftDots = false;
					break;
					
				default:
					__pager.leftDots = true;
			}
			
		} else {
			__pager.rightDots = false;
			__pager.leftDots = false;
		}
		
		return this;
	};
	
	if (opts.page) {
		this.setPage(opts.page);
	}
	
	this.getFirst = function () {
		return __pager.first;
	};
	
	this.getPrev = function () {
		return __pager.prev;
	};
	
	this.getPages = function () {
		return __pager.pages;
	};
	
	this.getNext = function () {
		return __pager.next;
	};
	
	this.getLast = function () {
		return __pager.last;
	};
	
	this.getPagesCount = function () {
		return __totalPages;
	};
	
	this.getLeftDots = function (dots) {
		return __pager.leftDots ?
			dots :
			''
			;
	};
	
	this.getRightDots = function (dots) {
		return __pager.rightDots ?
			dots :
			''
			;
	};
	
	this.getCurrentPage = function () {
		return __currentPage;
	};
}


(
function ($) {
	$.fn.pbChange = function (handler, opts) {
		opts || (opts = {});
		
		opts.timeout || (opts.timeout = 700);
		
		this.each(function () {
			new (function (input, handler, opts) {
				var __inst = this;
				
				var __accu = new PBSignalAccumulator({timeout: opts.timeout});
				__accu.setCallback(function (event) {
					handler.call(input, {
						data: opts.data
					});
				});
				
				$(input).bind(opts.events || 'keypress paste change', function () {
					__accu.signal();
				});
				
			})
				(this, handler, opts)
				;
		});
		return this;
	};
}
) (jQuery);


function PBFormBuilder(form, fields, options) {
	var __opts = options ? options : {};
	var __inst = this;
	
	__opts.invalidValueClassName || (__opts.invalidValueClassName = 'invalid');
	__opts.validValueClassName || (__opts.validValueClassName = 'valid');
			
	var __containers = [];
	var __currentGroup;

	var __setValid = function (isValid) {
		this.__pb.valid = isValid;
		
		if (isValid) {
			$(this)
				.removeClass(__opts.invalidValueClassName)
				.addClass(__opts.validValueClassName)
				;
			
		} else {
			$(this)
				.removeClass(__opts.validValueClassName)
				.addClass(__opts.invalidValueClassName)
				;			
		}
	};
	
	var __validate = function (opts) {
		opts || (opts = {});
		
		var pb = this.parentNode.__pb;
		var noAjax = opts.noAjax;
		
		var valid;
		if (pb.groups) {
			//console.log(!pb.groups [__currentGroup]);
		}
		
		if ((this.value.length == 0) && ((pb.required === false) || (__currentGroup && pb.groups && !pb.groups [__currentGroup]))) {
			valid = true;
			
		} else {
			switch (pb.type) {
				case 'text': case 'password': case 'textarea':
					if (pb.compareTo) {
						var ref = form [pb.compareTo];
						valid = (ref && (ref.value === this.value));
						
					} else if (pb.pattern) {
						valid = this.value.match(pb.pattern) !== null;
						
					} else if (pb.blankMsg) {
						valid = this.value.length > 0;
					}
					
					if (valid && pb.ajaxVerify) {
						valid = undefined;
						
						if (noAjax && (pb.valid !== undefined)) {
							return pb.valid;
						};
						
						new (function (container, value) {
							var opts = container.__pb.ajaxVerify;
							
							var data = {};
							data [opts.postFieldName] = value;
							
							$.ajax({
								dataType:	'json',
								type:		'POST',
								url: 		opts.url,
								data: 		data,
								success:	function (response, textStatus) {
									if (response [opts.responseFieldName]) {
										__setValid.call(container, response [opts.responseFieldName]);
										
									} else {
										container.__pb.msgContainer.innerHTML = __msgWrapper(response [opts.responseMsgFieldName]);
										__setValid.call(container, false);
									}
								}
							});
						}) 
							(this.parentNode, this.value)
							;
					}
					break;
					
				case 'select':
					valid = this.item(this.selectedIndex).value !== '';
					break;
					
				case 'checkbox':
					valid =
						(pb.requiredState === undefined)
							||
						((pb.requiredState == 'checked') && this.checked)
							||
						((pb.requiredState == 'unchecked') && !this.checked)
						;
					break;
					
				default:
					throw 'can never happen, right?';
			}
		}
		
		if (valid !== undefined) {
			if (!valid) {
				pb.msgContainer.innerHTML = ((this.value.length == 0) && (pb.blankMsg)) ?
					pb.blankMsg :
					pb.errorMsg
					;					
			}
				
			__setValid.call(this.parentNode, valid);
			
		} else {
			this.parentNode.__pb.valid = undefined;
		}

		if (typeof this.parentNode.__pb.onchange == 'function') {
			this.parentNode.__pb.onchange.call(__inst, {
				target: this
				});
		}
		
		return valid;
	};
	
	$(form).submit(function (event) {
		var isFormValid = true;
		
		for (var i in __containers) {
			var v = __validate.call(__containers [i].__pb.editor, {noAjax: true});
			isFormValid = v && isFormValid;
		}
		
		return isFormValid;
	});
	
	var __prevChangeValue;
	var __changeHandler = function (event) {
		if (this.value === __prevChangeValue) {
			return;
			
		} else {
			__prevChangeValue = this.value;
		}
		
		__validate.call(this, false);
		
	};
	
	var __msgWrapper = (typeof __opts.msgWrapper !== 'function') ?
		function (msg) { return msg; } :
		__opts.msgWrapper
		;			
	
	for (var i in fields) {
		var field = fields [i];
		
		var container = document.createElement('div');
			$(container).addClass(field.type);
			
		container.__pb = {
			errorMsg: field.errorMsg && __msgWrapper(field.errorMsg),
			blankMsg: field.blankMsg && __msgWrapper(field.blankMsg),
			
			ajaxVerify: field.ajaxVerify,

			requiredState: field.requiredState,
			compareTo: field.compareTo,
			pattern: field.pattern,
			
			required: field.required,
			type: field.type,
			
			onchange: field.onChange
			};
		
		if (field.groups && field.groups.length !== undefined) {
			container.__pb.groups = [];
			
			for (var i in field.groups) {
				container.__pb.groups [field.groups [i]] = true;
			}
		}
			
		switch (field.type) {
			case 'text': case 'password': case 'textarea':
				var label = document.createElement('label');
					field.label && (label.innerHTML = field.label);
					
				var editor = (field.type == 'textarea') ? 
					document.createElement('textarea') :
					document.createElement('input')
					;
					
					if (field.type != 'textarea') {
						field.value && editor.setAttribute('value', field.value);
						editor.setAttribute('type', field.type);
						
					} else {
						field.value && (editor.innerHTML = field.value);
					}
					
					field.name && editor.setAttribute('name', field.name);
					
				var p = document.createElement('p');
					p.innerHTML = field.errorMsg;
					
				container.__pb.msgContainer = p;
				container.__pb.editor= editor;
					
				container.appendChild(label);
				container.appendChild(editor);
				container.appendChild(p);
				
				$(editor).pbChange(__changeHandler, {
					data: {
						container: container
						}
				});
				
				__containers.push(container);
				break;
				
			case 'select':
				var label = document.createElement('label');
					field.label && (label.innerHTML = field.label);
					
				var editor = document.createElement('select');
					field.name && editor.setAttribute('name', field.name);
					
					for (var idx in field.items) {
						var opt = document.createElement('option');
							opt.value = field.items [idx].value || '';
							opt.innerHTML = field.items [idx].label;
							
							if (opt.value === field.value) {
								opt.setAttribute('selected', 'selected');
							}						
							
							editor.appendChild(opt);
					}
					
					
				var p = document.createElement('p');
					p.innerHTML = field.errorMsg;
					
				container.__pb.msgContainer = p;
				container.__pb.editor= editor;
				
				container.appendChild(label);
				container.appendChild(editor);
				container.appendChild(p);

				$(editor).change(__changeHandler);
				
				__containers.push(container);
				break;
				
			case 'checkbox':
				var label = document.createElement('label');
					field.label && (label.innerHTML = field.label);
					
				var editor = document.createElement('input');
					field.checked && editor.setAttribute('checked', 'checked');
					field.value && editor.setAttribute('value', field.value);
					field.name && editor.setAttribute('name', field.name);
					editor.setAttribute('type', field.type);
					
				var p = document.createElement('p');
					p.innerHTML = field.errorMsg;
					
				container.__pb.msgContainer = p;
				container.__pb.editor= editor;
				
				container.appendChild(editor);
				container.appendChild(label);
				container.appendChild(p);
				
				new (function (label, editor) {
					$(label).click(function () {
						editor.click();
					});					
				}) 
					(label, editor)
					;
				
				$(editor).change(__changeHandler);
				
				__containers.push(container);
				break;
				
			case 'submit':
				var editor = document.createElement('input');
					(field.value !== undefined) && editor.setAttribute('value', field.value);
					field.className && $(editor).addClass(field.className);
					editor.setAttribute('type', field.type);
					
				container.__pb = {
					type: field.type
				};
				
				container.appendChild(editor);
				break;
				
			default:
				throw 'unknown field type';
		}
	
		if (field.disabled) {
			editor.setAttribute('disabled', 'disabled');
		}
		
		form.appendChild(container);
	}
	
	this.switchToGroup = function (groupNo) {
		__currentGroup = groupNo;
		
		for (var i in __containers) {
			var groups = __containers [i].__pb.groups;
			
			if (!groups || groups [groupNo]) {
				$(__containers [i]).show();
				
			} else {
				$(__containers [i])
					.removeClass(__opts.invalidValueClassName)
					.removeClass(__opts.validValueClassName)
					.hide()
					;
				
				switch (__containers [i].__pb.type) {
					case 'text': case 'password': case 'textarea':
						__containers [i].__pb.editor.value = '';
						__containers [i].__pb.valid = undefined;
						break;
				}
			}
		}
		
		return this;
	};
}



(function ($) {
	$.fn.slider = function (opts) {
		opts || (opts = {});
		
		this.each(function () {
			new (function (originalContainer, opts) {
				var __width = opts.width ? 
					opts.width : 
					10000
					;
				
				var __step = opts.step ?
					opts.step :
					100
					;
				
				var __slider = document.createElement('div');
					originalContainer.parentNode.insertBefore(__slider, originalContainer);
					$(__slider).addClass('slider');
					
				var __viewport = document.createElement('div');
					originalContainer.parentNode.insertBefore(__viewport, originalContainer);
					$(__viewport).addClass('viewport');
					__slider.appendChild(__viewport);
					
				var __wideContainer = document.createElement('div');
					$(__wideContainer).addClass('container');
					__wideContainer.appendChild(originalContainer);
					__viewport.appendChild(__wideContainer);
					
					$(__wideContainer).css('width', __width + 'px');
					
				var __adjuster = new PBValueAdjuster({
					min: - __width + $(__viewport).width(),
					max: 0,
					speed: 2,
					callback: function (value) {
						$(__wideContainer).css('left', value + "px");
					}
					});

				if ($(__wideContainer).width() > $(__viewport).width()) {
					var __scrollLeft = document.createElement('div');
						$(__scrollLeft).addClass('left-scroller');
						__slider.appendChild(__scrollLeft);
				
					var __scrollRight = document.createElement('div');
						$(__scrollRight).addClass('right-scroller');
						__slider.appendChild(__scrollRight);
				
					$(__scrollLeft).mousedown(function () {
						__adjuster.inc(__step);
					});
					
					$(__scrollRight).mousedown(function () {
						__adjuster.dec(__step);
					});
				}
			})
				(this, opts)
				;
		});
		return this;
	};
})
	(jQuery)
	;

