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


function PBSignalAccumulator(settings) {
	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 __targetValue;
	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 __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(__current = nextValue);
		}		
	};
	
	this.setTargetValue = function (targetValue) {
		__targetValue = targetValue;
		
		if (typeof __intervalId == 'undefined') {
			__intervalId = setInterval(__worker, __frameInterval);
		}
	};
}

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


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 () {
		__animator.setTargetValue(0);
	});
	
	$(__container).mouseenter(function () {
		__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.setAttribute('class', 'collapsed');
		
	var __label = document.createElement('label');
		__label.setAttribute('class', 'unchecked');
		__label.innerHTML = settings.label;
	
	var __childrenList = document.createElement('div');
	
	var __childrenContainer = document.createElement('div');
		__childrenContainer.setAttribute('class', 'children');
		__childrenContainer.appendChild(__childrenList);
	
	var __container = document.createElement('div');
		if (__icon) {
			__container.appendChild(__icon);			
		}
		
		if (__input) {
			__container.appendChild(__input);
		}
		
		__container.appendChild(__label);
		__container.appendChild(__expander);
		
		__container.appendChild(__childrenContainer);
		
		if (settings.className) {
			__container.setAttribute('class', 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.setAttribute('class', 'partial');
		return this;
	};
	
	this.check = function () {
		__setState(__inst.STATE_CHECKED);
		
		if (!__dontRecurse) {
			for (var i in __children) {
				__children [i].check();
			}
		}
		
		if (__parent) {
			__parent.refresh();
		}
		
		__label.setAttribute('class', 'checked');
		return this;
	};
	
	this.uncheck = function () {
		__setState(__inst.STATE_UNCHECKED);
		
		if (!__dontRecurse) {
			for (var i in __children) {
				__children [i].uncheck();
			}
		}
		
		if (__parent) {
			__parent.refresh();
		}
		
		__label.setAttribute('class', 'unchecked');
		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;
	};
	
	this.expand = function () {
		__animator.setTargetValue(__childrenListHeight);
		
		__expander.setAttribute('class', 'expanded');
		__expanded = true;
		return this;
	};
	
	this.collapse = function () {
		__animator.setTargetValue(0);
		
		__expander.setAttribute('class', 'collapsed');
		__expanded = false;
		
		return this;
	};
	
	$(__expander).click(function () {
		if (__expanded) {
			__inst.collapse();
			
		} else {
			__inst.expand();
		}
	});	
}


(
function ($){
	$.fn.pbRichEdit = function(opts) {
		opts = opts ? opts : {};
			
		this.each(function () {
			var _opts = opts;
			_opts.textArea = this;
			
			return new PBRichEdit(_opts);
		});
	};
}
) (jQuery)
	;


function PBRichEdit (opts) {
	var __editor = opts.textArea;
	
	var __container = document.createElement('div');
		__editor.parentNode.insertBefore(__container, __editor);
		__container.setAttribute('class', opts.className);
	
	var __toolbar = document.createElement('div');
		__toolbar.setAttribute('class', 'toolbar');
		__container.appendChild(__toolbar);
		
	var __buttons = document.createElement('ul');
		__toolbar.appendChild(__buttons);
		
	/*
	 * jQuery plugin: fieldSelection - v0.1.0 - last change: 2006-12-16
	 * (c) 2006 Alex Brem <alex@0xab.cd> - http://blog.0xab.cd
	 * 
	 */

	var __getSelection = function (e) {
		return (
			/* mozilla / dom 3.0 */
			('selectionStart' in e && function() {
				var l = e.selectionEnd - e.selectionStart;
				return { start: e.selectionStart, end: e.selectionEnd, length: l, text: e.value.substr(e.selectionStart, l) };
			}) ||

			/* exploder */
			(document.selection && function() {

				e.focus();

				var r = document.selection.createRange();
				if (r == null) {
					return { start: 0, end: e.value.length, length: 0 }
				}

				var re = e.createTextRange();
				var rc = re.duplicate();
				re.moveToBookmark(r.getBookmark());
				rc.setEndPoint('EndToStart', re);

				return { start: rc.text.length, end: rc.text.length + r.text.length, length: r.text.length, text: r.text };
			}) ||

			/* browser not supported */
			function() {
				return { start: 0, end: e.value.length, length: 0 };
			}

		)();
		};

	var __buttonClick = function (event) {
		var selection = __getSelection(__editor);
		var spec = this.__pbRichEditSpec;
		var content;
		
		var head = spec.head ?
			spec.head : 
			''
			;
		
		if (typeof spec.action == 'function') {
			content = spec.action(selection.text, this);
			
		} else {
			switch (spec.action) {
				case 'replace':
					content = '';
					break;
					
				case 'wrap':
				default:
					content = selection.text;
			}
		}
			
		var tail = spec.tail ?
			spec.tail : 
			''
			;
		
		var result = '' + head + content + tail;
		var orig = $(__editor).val();
		
		$(__editor).val(orig.substr(0, selection.start) + result + orig.substr(selection.end));
		__updater.signal();
	}
		
	var __addButtons = function (buttons) {
		for (var i in buttons) {
			var button = document.createElement('li');
				button.__pbRichEditSpec = buttons [i];
				button.title = buttons [i].label;
				button.setAttribute('class', i);
				
			$(button).click(__buttonClick);				
			__buttons.appendChild(button);
		}	
	};
	__addButtons (opts.buttons ? opts.buttons : []);
		
	var __body = document.createElement('div');
		__body.setAttribute('class', 'body');
		__container.appendChild(__body);
		__body.appendChild(__editor);
	
		
	var __updater = new PBSignalAccumulator()
		;
	
	if (opts.preview && opts.preview.url && opts.preview.resultContainer) {
		__updater.setCallback(function () {
			$.ajax({
				url: opts.preview.url,
				dataType: 'json',
				type: 'POST',
				
				data: {
					content: $(__editor).val()
					},
					
				success: function(response){
					opts.preview.resultContainer.innerHTML = response.html;
					},
	
				error: function (XMLHttpRequest, textStatus, errorThrown) {
					//console.log("%o", errorThrown);
				}
			});
		});
	}

	$(__editor).keypress(function () {
		__updater.signal();
	})
}


