/**
 * @class tkid
 * @version 0.3.1
 * @author biophonc
 *
 * licenced under: http://creativecommons.org/licenses/by-sa/3.0/
 */
var tkid={};


/**
 * static methods
 */
tkid.Helper={
	/**
	 * checks if Scriptaculous is loaded.
	 * @return bool
	 */
	ready: function() {
		return (typeof Scriptaculous=='object') ? true : false;
	},
	
	/**
	 * checks if the givin value is negative
	 * @param int number
	 * @return bool
	 */
	isNegative: function(number) {
		return (number < 0) ? true : false;
	},
	
	/**
	 * checks if the givin value is an int
	 * @param mixed number
	 * @return bool
	 */
	isInteger: function(number) {
		return (number.toString().search(/^-?[0-9]+$/) === 0);
	},
	
	/**
	 * converts a negative int to a positive value
	 * @param int number
	 * @return int
	 */
	negativeToPositive: function(number) {
		return (this.isNegative(number)) ? number*(-1) : number;
	},
	

	/**
	 * square a quantity
	 * @param int number
	 * @return {}
	 */
	toSquare: function(number) {
		return number*number;
	},
	
	/**
	 * 
	 * @return {}
	 */
	isFF2: function() {
		var reFF2 = /Firefox\/2/i;
		return reFF2.match(navigator.userAgent);
	},	
	
	/**
	 * 
	 * @return {}
	 */
	isFF3: function() {
		var reFF3 = /Firefox\/3/i;
		return reFF3.match(navigator.userAgent);
	},
	
	/**
	 * 
	 * @return {}
	 */
	isIE6: function() {
		var reIE6 = /MSIE 6/i;
		return reIE6.match(navigator.userAgent);
	},
	
	/**
	 * 
	 * @return {}
	 */
	isIE7: function() {
		var reIE7 = /MSIE 7/i;
		return reIE7.match(navigator.userAgent);
	},

	isIE: function() {
		return (tkid.Helper.isIE6() || tkid.Helper.isIE7());
	}
};



/**
 * @class Carousel
 * @version 0.2
 */
tkid.Carousel = Class.create(tkid, {
	
	// add description here
	params:
	{
		axis: false, 				// x or y
		autoPlay: true,			//
		autoPlayDelay: 20000, 		// time in MS			
		cssWrapper: false,			// if null => abort
		cssButtonBack: "SC-back",	// s.o.
		cssButtonNext: "SC-next",	// s.o.
		cssActive: false,			// @deprecated
		highlightActive: false,		// default: false
		seamless: true,				// @todo: add this feature (a real carousel and or a faked one)
		widthView: 0,				// width of the visible area
		widthItem: 0,				// only if item is not full size
		mode: 'default',			// [default, center]
		amountItems: 0,				// amount of items - regadkless of what type
		id: false,					// id of carousel
		duration: 0.3,
		transition: Effect.Transitions.sinoidal // @see: http://wiki.github.com/madrobby/scriptaculous/effect-transitions
	},
	
	/**
	 * 
	 * @param {} id
	 * @return {Boolean}
	 */
	initialize: function(id) {
		
		// run only when all components are loaded
		if(!tkid.Helper.ready()) {
			return false;
		}
		
		// update params/prefs
		if(typeof arguments != 'undefined') {
			if(arguments.length==2 && typeof arguments[1] == 'object') {
				Object.extend(this.params, arguments[1]);
			}
		}
		
		if($(id)=='null') {
			return false;
		}
		
		// set element ID
		this.id = id;
		
		// fetch all ListItems
		this._ul = $(this.id).down('UL');
		this._listItems = this._ul.childElements();
		if(this._listItems.length===0) {
			throw new Error("no items available");
		}
		
		// set/get amount of items
		this.params.amountItems = this._listItems.length;
		
		// calculate the width of all existent items
		this._realWidth = 0;
		for(_itemCount=0;this._listItems.length>_itemCount;_itemCount++) {
			this._realWidth += this._listItems[_itemCount].getWidth();
		}
		
		// get+set width of wrapper
		this.params.widthView = this._ul.up('div').getWidth();
		
		// iterate over item list, to fetch the items which are not full size
		for(i=0;this._listItems.length>i;i++) {
			if(this._listItems[i].getWidth() != this.params.widthView) {
				this.params.widthItem = this._listItems.first().getWidth();
			}
		}

		// create "current view" dom storrage
		$$('body')[0].insert(new Element('span', { id: "storrage-"+this.id, title: this.params.amountItems, 'class': '' }));
		
		// create storrage container for width's of all items
		this._storrage={};
		this._storrage[this.id] = []; // new Array();

		// fill up storrage
		for(_itemCount=0;this._listItems.length>_itemCount;_itemCount++) {
			this._storrage[this.id].push(
				{
					width:this._listItems[_itemCount].getWidth()
				}
			);

		}
		
		// clone objects/elements
		this._clone();
		
		// create ItemCollection for later use
		this._buildStorrage();
		
		// set initial position
		this._switchMode();

		// make eventListner available to all members
		this.buttonEventBack = this._scroll.bindAsEventListener(this);
		this.buttonEventNext = this._scroll.bindAsEventListener(this);
		
		// add Events to buttons
		this._buttonEvent($(this.id).down("."+this.params.cssButtonBack).down("IMG"), 'back');
		this._buttonEvent($(this.id).down("."+this.params.cssButtonNext).down("IMG"), 'next');
		
		if(this.params.autoPlay) {
			this._autoPlay();
		}
	},
	
	/**
	 * gets called only once on init
	 */
	_buildStorrage: function() {

		// create storrage container
		this._storrageCnt = {};
		this._storrageCnt[this.id] = []; // new Array();
		
		// get/set internal used "constants"
		_allE = this._ul.childElements();
		_min = this._listItems.length;
		_max = this._listItems.length*2;

		// create basic itemcollection
		for(_i=0;_allE.length>_i;_i++) {
			_width = _allE[_i].getWidth();
			this._storrageCnt[this.id][_i] = {
				width: _width,
				isFullWidth: (_width==this.params.widthView) ? true : false,
				isView: ((_i>=_min) && (_i<_max)) ? true : false
			};	
		}
		
		org_length = _allE.length/3;

		
		// add(calculate) position for each element
		for(_i=0;_allE.length>_i;_i++) {
			
			// current width
			_width = _allE[_i].getWidth();
			_view = this.params.widthView;
			// previous and next index
			__idxPrev = _i-1;
			__idxNext = _i+1;
			
				/**
				 * 1: s s s 
				 * 2: s s l
				 * 3: s l l
				 * 4: l l l
				 * 5: l l s 
				 * 6: l s s
				 * 7: l s l
				 * 8: s l s
				 */
				if(_i===0) {
					// first
					if(this._storrageCnt[this.id][_i].isFullWidth) {
						__position = 0;
					} else {
						__position = (_view/4*1)*-1;
					}
					
				} else {

					// calculate position, in dependence to the previous and next item
					if((typeof this._storrageCnt[this.id][__idxNext] != 'undefined') && (typeof this._storrageCnt[this.id][__idxPrev] != 'undefined')) {
						
						if(!this._storrageCnt[this.id][__idxPrev].isFullWidth 
						&& !this._storrageCnt[this.id][_i].isFullWidth 
						&& !this._storrageCnt[this.id][__idxNext].isFullWidth) 
						{
							__position = (__position<=0) ? (_view/4*1) : __position+(_view/4*2);
						}
						
						if(!this._storrageCnt[this.id][__idxPrev].isFullWidth 
						&& !this._storrageCnt[this.id][_i].isFullWidth 
						&& this._storrageCnt[this.id][__idxNext].isFullWidth) 
						{
							__position = (__position<=0) ? (_view/4*1) : __position+(_view/4*2);
						}
						
						if(!this._storrageCnt[this.id][__idxPrev].isFullWidth 
						&& this._storrageCnt[this.id][_i].isFullWidth 
						&& this._storrageCnt[this.id][__idxNext].isFullWidth) 
						{
							__position = (__position+(_view/4*3));
						}
						
						if(this._storrageCnt[this.id][__idxPrev].isFullWidth 
						&& this._storrageCnt[this.id][_i].isFullWidth 
						&& this._storrageCnt[this.id][__idxNext].isFullWidth) 
						{
							__position = (__position+_view);
						}
						
						if(this._storrageCnt[this.id][__idxPrev].isFullWidth 
						&& this._storrageCnt[this.id][_i].isFullWidth 
						&& !this._storrageCnt[this.id][__idxNext].isFullWidth) 
						{
							__position = (__position+(_view));
						}
						
						if(this._storrageCnt[this.id][__idxPrev].isFullWidth 
						&& !this._storrageCnt[this.id][_i].isFullWidth 
						&& !this._storrageCnt[this.id][__idxNext].isFullWidth) 
						{
							__position = (__position+(_view/4*3));
						}
						
						if(this._storrageCnt[this.id][__idxPrev].isFullWidth 
						&& !this._storrageCnt[this.id][_i].isFullWidth 
						&& this._storrageCnt[this.id][__idxNext].isFullWidth) 
						{
							__position = (__position+(_view/4*3));
						}
						
						if(!this._storrageCnt[this.id][__idxPrev].isFullWidth 
						&& this._storrageCnt[this.id][_i].isFullWidth 
						&& !this._storrageCnt[this.id][__idxNext].isFullWidth) 
						{
							__position = (__position+(_view/4*3));
						}
					}
					
				}
			// add the position to the earlier created itemColletion
			Object.extend(this._storrageCnt[this.id][_i], {
				position: __position
			});
			
		}
		//console.dir(this._storrageCnt[this.id]);
	},
	
	_switchMode: function() {
		switch(this.params.mode) {
			case 'center':
				// position is centered 
				if(this.params.axis == 'x') {
					// is full width
					if(this._storrage[this.id][0].width==this.params.widthView) {
						position = parseInt(this._realWidth-this.params.widthView+this._storrage[this.id][0].width, 10)*-1;
						this._ul.setStyle("left: "+position+"px");
					} else { // is *not* full width
						position = parseInt(this._realWidth-(this.params.widthView/4*1), 10)*-1;
						this._ul.setStyle("left: "+position+"px");
					}
				} 
			break;
			default: // default mode is "default" (left)
				// position is left
				this._ul.setStyle("left: 0px");
			break;
		}
	},
	
	/**
	 * 
	 * @param {} obj
	 * @param {} times
	 * @param {} position
	 */
	_clone: function(/*obj, times, position*/) {
		// clone my clone to a cyclone.....
		_clone =  this._ul.select('li');
		
		for(i=0;2>i;i++) {
			for(var item in _clone) {
				if(typeof _clone[item] == 'object') {
					var _myClone = _clone[item].cloneNode(true);
					var _cyClone = this._ul.insert({'bottom':_myClone});
				}
			}
		}
		delete _clone;
	},
	
	/**
	 * 
	 * @return {}
	 * @version 0.2
	 */
	_currentPosition: function() {
		return (this.params.axis == 'x') ? parseInt(this._ul.positionedOffset()[0], 10) : parseInt(this._ul.positionedOffset()[1], 10);
	},
	
	/**
	 * 
	 * @param {} element
	 * @return {}
	 */
	_getDirection: function(element) {

		if(Prototype.Browser.IE) {
			res = element.srcElement.up().className.endsWith('next');
		} else if(Prototype.Browser.WebKit || Prototype.Browser.Opera) {
			res = element.srcElement.up().getAttribute('class').endsWith('next');
		} else if(Prototype.Browser.Gecko) {
			res = element.rangeParent.className.endsWith('next');
		} 

		return (res) ? 'next' : 'back';
	},
	
	/**
	 * 
	 * @param {} obj
	 */
	_scroll: function(obj) {
		
		// if the "obj" is a string, _scroll gets called from the autoplay 
		if(typeof obj == "string") {
			direction = obj;
		} else {
			direction = this._getDirection(obj);
		}

		// direction dependend values
		_dimensions = (this.params.axis == 'x') ? parseInt(this.params.widthItem, 10) : parseInt(this.params.widthHeight, 10);
		
		// defaults
		_scrollFx={};
		_scrollTo=0;
		
		// get current index from dom storrage
		___storrage = parseInt($('storrage-'+this.id).getAttribute('title'), 10);
		
		// override position and index in title if cond matches
		_revert = false;
		
		_startFrom = ___storrage;
		
		// set index level for previous, current and next position
		_idxPrev = (___storrage-1);
		_idxCurr = (___storrage);
		_idxNext = (___storrage+1);
		
		_min = this._listItems.length;
		_max = this._listItems.length*2;
		
		if(direction=='next') {
			_scrollTo = this._storrageCnt[this.id][_idxNext].position*-1;
			
			if(!this._storrageCnt[this.id][_idxNext].isView) {
				_startFrom = _min;
				_revert = true;
			} else {
				_startFrom = _idxNext;
			}

			_scrollFx.morph = {
				element: this._ul,
				options: {
					style: 'left: '+_scrollTo+'px',
					duration: this.params.duration,
					transition: this.params.transition,
					queue: 'end'
				}
			};
		} else {
			_scrollTo = this._storrageCnt[this.id][_idxPrev].position*-1;	
			
			if(!this._storrageCnt[this.id][_idxPrev].isView) {
				_startFrom = _max;
				_revert = true;
			} else {
				_startFrom = _idxPrev;
			}
			_scrollFx.morph = {
				element: this._ul,
				options: {
					style: 'left: '+_scrollTo+'px',
					duration: this.params.duration,
					transition: this.params.transition,
					queue: 'end'
				}
			};
		}

		// save data to storrage
		$('storrage-'+this.id).title = _startFrom;
		

		new Effect.Morph(_scrollFx.morph.element, _scrollFx.morph.options);

		
		// if we've reached the position of the defined index, jump back/forward to it's clone 
		if(_revert) {
			if(direction=='next') {
				$('storrage-'+this.id).title = _min;
				_goto = this._storrageCnt[this.id][_min].position;
			} else {
				$('storrage-'+this.id).title = _max-1;
				_goto = this._storrageCnt[this.id][_max-1].position;
			}
			new Effect.Morph(this._ul, {
					style: "left: -"+_goto+"px; color: red",
					duration: 0,
					queue: 'end'
			});
		} 
			
		
	},
	
	/**
	 * 
	 * @param {} element
	 * @param {} direction
	 */
	_buttonEvent: function(element, direction) {
		if(typeof element == 'object') {
			switch(direction) {
				case 'back':
					x = Event.observe(element, "click", this.buttonEventBack);
					break;
				case 'next':
					x = Event.observe(element, "click", this.buttonEventNext);
					break;
				default:
					throw new Error(element+" has no direction.");
					break;
			}
		} else {
			throw new Error("element is no object.");
		}
	},
	
	/**
	 * recursive timer!
	 */
	_autoPlayWrapper: function() {
		this._scroll('next');
		this._autoPlay();
	},
	
	/**
	 * 
	 */
	_autoPlay: function() {
		btn = $(this.id).down("."+this.params.cssButtonNext).down("IMG");
		setTimeout(this._autoPlayWrapper.bind(this), this.params.autoPlayDelay);
	}
	
});


/**
 * @class Feed
 * @version 0.1
 */
tkid.Feed = Class.create(tkid, {
	
	params:
	{
		duration: 0.3,
		transition: Effect.Transitions.sinoidal // @see: http://wiki.github.com/madrobby/scriptaculous/effect-transitions
	},
	
	initialize: function(id) {
		// run only when all components are loaded
		if(!tkid.Helper.ready()) {
			return false;
		}
		if($(id)=='null') {
			return false;
		}
		
		// set element ID
		this.id = id;
		this.map = $(id).select('.b90feedmap')[0]; 
		this.areas = this.map.descendants();
		this._ul = $(this.id).select('.b90feeds_list')[0]; 
		this._height = this._ul.getHeight();
		this._heightView = $(this.id).select('.b90feeds_wrapper').first().getHeight();
		
		// make eventListner available to all members
		this.buttonEventBack = this._scroll.bindAsEventListener(this);
		this.buttonEventNext = this._scroll.bindAsEventListener(this);
		
		// add Events to buttons
		this._buttonEvent(this.areas[0], 'back');
		this._buttonEvent(this.areas[1], 'next');
		
		return false;
	},
	
	_scroll: function(element) {
		
		
		if(Prototype.Browser.IE) {
			//res = element.srcElement.up().className.endsWith('next');
			res = element.target.className;
		} else if(Prototype.Browser.WebKit || Prototype.Browser.Opera) {
			//res = element.srcElement.up().getAttribute('class').endsWith('next');
			res = element.currentTarget.className;
		} else if(Prototype.Browser.Gecko) {
			res = element.currentTarget.className;
		}
		
		// this._height
		_currPosition = parseInt(this._ul.positionedOffset()[1]);
		var _scrollFx = {};
		
		
		if(res=="back") {
			_newPos = _currPosition+(this._heightView/4*3);
			_scrollFx.morph = {
				element: this._ul,
				options: {
					style: 'top: '+_newPos+'px',
					duration: this.params.duration,
					transition: this.params.transition
				}
			};
			if(_currPosition*-1 < (this._heightView/4*1)) {
				_scrollFx.morph.options = {style: "top: 0px" };
			}
		} else {
			_newPos = _currPosition-(this._heightView/4*3);
			_scrollFx.morph = {
				element: this._ul,
				options: {
					style: 'top: '+_newPos+'px',
					duration: this.params.duration,
					transition: this.params.transition
				}
			};
			if(_currPosition*-1 > (this._height-(this._heightView/4*6))) {
				_scrollFx.morph.options = {style: "top: -"+(this._height-(this._heightView/4*1))+"px" };
			}
		}

		new Effect.Morph(_scrollFx.morph.element, _scrollFx.morph.options);
	},
	
	_buttonEvent: function(element, direction) {
		if(typeof element == 'object') {
			switch(direction) {
				case 'back':
					x = Event.observe(element, "click", this.buttonEventBack);
					break;
				case 'next':
					x = Event.observe(element, "click", this.buttonEventNext);
					break;
				default:
					throw new Error(element+" has no direction.");
					break;
			}
		} else {
			throw new Error("element is no object.");
		}
	}
});

/**
 * @class Tab
 * @version 0.1
 */
tkid.Tab = Class.create(tkid, {
	params: 
	{
		axis: null, 			// x or y
		cssWrapper: null 		// 
	},
	initialize: function(id) {
		// run only when all components are loaded
		if(!tkid.Helper.ready() || typeof className == null) {
			return false;
		}
		
		return false;
	}
});

/**
 * @class Clearer
 * @description Fetches the title attribute and inserts it into the value from an input field. 
 * 
 * tested on: FF3, Opera 9, Safari3, IE6
 * 
 * @version 0.2
 * @example new tkid.Clearer('className');
 */

tkid.Clearer = Class.create(tkid, {
	params: 
	{
		elementAttrib: 'title', 	// the title value gets inserted as value 
		cssTrigger: null,			// the class name, by which the element gets triggered
		toggle: true				// if toggle is true and no changes are made to the input, the title value gets inserted again.
									// otherwise it will be left blank and the event gets removed. This is usefull, if 
									// you have a formular and don't want to double check the value for emptyness and a special value.
									// @todo: add an onsubmit handler to clear the values which are equal to it's title attribute
	},
	
	/**
	 * constructor
	 * @param {} className
	 * @return {Boolean}
	 */
	initialize: function(className) {
		// run only when all components are loaded
		if(!tkid.Helper.ready() || typeof className == null) {
			return false;
		}
		
		// copy className to member
		this.params.cssTrigger = className;
		
		// make eventListner available to all members
		this.eventFieldFocus = this.toggleValue.bindAsEventListener(this);
		this.eventFieldBlur = this.toggleValue.bindAsEventListener(this);
		this.eventFieldLoad = this.toggleValue.bindAsEventListener(this);
		
		// iterate  through docuemnt and assign event, if condition matches
		$$("."+this.params.cssTrigger).each(
			function(element) {
				this.addEvent(element);
			}.bind(this)
		);
	},

	/**
	 * attaches events to element
	 * @param {} element
	 */
	addEvent: function(element) {
		this.initElement(element);
		Event.observe(element, "focus", this.eventFieldFocus);
		Event.observe(element, "blur", this.eventFieldBlur);
	},
	
	/**
	 * preset an elements value
	 * @param {} element
	 */
	initElement: function(element) {
		if(typeof element.title == "string") {
			element.setValue(element.getAttribute(this.params.elementAttrib));
		}
	},
	
	/**
	 * switches/toggles the attributes of an element
	 * @param {} element
	 */
	toggleValue: function(element) {
		
		if(Prototype.Browser.IE) {
			res = element.target;
		} else if(Prototype.Browser.WebKit || Prototype.Browser.Opera) {
			res = element.currentTarget;
		} else if(Prototype.Browser.Gecko) {
			res = element.currentTarget;
		}
		
		// active element
		if(element.type == 'focus') {
			if(res.title == res.getValue()) {
				res.setValue('');
			}
		// release of focus
		} else {
			if(res.getValue() == "") {
				res.setValue(res.getAttribute(this.params.elementAttrib));
			}
		}
	}
});


tkid.Gal = Class.create(tkid, {
	
	/**
	 * constructor
	 * @param {} id
	 */
	initialize: function(id) {
		if(typeof $(id)!='object') {
			return false;
		}
		
		this.id = id;
		this.items = $(this.id).down('UL').childElements();
		
		// hide all items
		this.items.each(function(e){
			e.hide();
		});
		
		// make first visible
		this.items.first().show();
		
		// pager button one back
		$$('.b90gal_pager')[0].insert(new Element('a', {href:'javascript:tkid.Gal.go(\''+this.id+'-back\');', 'class': 'b90gal_pager_back'}).update('&lt;'));
		
		// create pager
		for(i=0; this.items.length>i; i++) {
			num = i+1;
			if(i===0) {
				e = new Element('a', {href:'javascript:tkid.Gal.go(\''+this.id+'-'+i+'\');', id: this.id+'-'+i, 'class': 'b90gal_pager_active'}).update(num);
			} else {
				e = new Element('a', {href:'javascript:tkid.Gal.go(\''+this.id+'-'+i+'\');', id: this.id+'-'+i}).update(num);
			}
			$$('.b90gal_pager')[0].insert(e);

			// get content from container
			content = (Prototype.Browser.IE) ? this.items[i].down('.b90gal_content').innerHTML : this.items[i].down('.b90gal_content').textContent;

			// remove all whitespaces:
			cleanContent = content.replace(/  |\t|\n/g, "");
			
			// if content exceeds a givin length - add "more" button
			if(cleanContent.length > 500) {
				a = new Element('a', {href:'javascript:tkid.Gal.more(\''+this.id+'-'+i+'\');', 'class': 'b90gal_more'}).update('mehr');
				$(this.items[i]).insert(a);
			}
		}
		
		// pager button one next
		$$('.b90gal_pager')[0].insert(new Element('a', {href:'javascript:tkid.Gal.go(\''+this.id+'-next\');', 'class': 'b90gal_pager_next'}).update('&gt;'));
	}
	
});

/**
 * singletn like method for tkid.Gal
 * @param {} elm
 */
tkid.Gal.go = function(elm) {

	tmp = elm.split('-');
	id = tmp[0];
	idx = tmp[1];
	pager = false;
	items = $(id).down('UL').childElements();
	
	if(idx.match('back')) {
		idx = parseInt($(id).down('.b90gal_pager_active').id.split('-')[1])-1;
		if(idx==-1) {
			return;
		}
		pager = true;
	} else if(idx.match('next')) {
		idx = parseInt($(id).down('.b90gal_pager_active').id.split('-')[1])+1;
		if(idx>=items.length) {
			return;
		}
		pager = true;
	}
	elmForPager = $(id).down('.b90gal_pager_active').id.split('-')[0]+"-"+idx;
	
	var cnt=0;
	items.each(function(e){
		if(cnt == idx && e.style.display == '') { 
			return false; 
		}
		if(e.style.display == '') { 
			e.fade({ duration: 0.3 }); 
		}
		cnt++;
	});

	$$('.b90gal_pager')[0].childElements().each(
		function(e) {
			e.removeClassName('b90gal_pager_active');
		}
	);
	items[idx].appear({ duration: 0.3 });
	
	if(pager) {
		$(elmForPager).addClassName('b90gal_pager_active');
	} else {
		$(elm).addClassName('b90gal_pager_active');
	}
};
	
tkid.Gal.more = function(elm) {
	tmp = elm.split('-');
	id = tmp[0];
	idx = tmp[1];
	
	items = $(id).down('UL').childElements();
	new Effect.Morph(items[idx], {style: 'height: 1000px'});
	new Effect.Morph(items[idx].down('.b90gal_content'), {style: 'height:490px'});
	
	// gently removin the "more" button
	items[idx].down('a.b90gal_more').fade();
};

