/** section: libraries
 * class DropDown
 *
 *  v0.0.2-dev, modified for adhdonline.pl
 *  for dropdown menus, based on dropline code
 *  (c) 2010 Maciej Taranienko <matt@prayam.com>
 *  All rights reserved
 *
 *  TODO:
 *  - opened items handling
 *
**/
DropDown = Class.create({

	// globals
	menu: undefined,

	switchers: [],
	targets: [],

	active: null,

	timerObj: undefined,
	timerRunning: false,

	timerPeriod: 1.5,
	switchDuration: 0.3,

	isSwitching: false,

	// init
	initialize: function(menu) {

		if($(menu)) {

			// set
			this.menu = $(menu);

			// switchers
			this.switchers = this.menu.select('div.button');
			//this.switchers = Element.select(this.menu, 'div.button');

			 //alert(this.switchers.inspect() + '\nlength: ' + this.switchers.length);
			 

			// targets
			//this.switchers.each(function(switcher) { this.targets.push(switcher.select('ul')[0]) }.bind(this));
			this.switchers.each(function(switcher) { this.targets.push(switcher.select('div')[0]) }.bind(this));
			 //alert(this.targets.inspect() + '\nlength: ' + this.targets.length);

			// register behaviours
			if(this.switchers.length == this.targets.length) { this.register(); }
			else { throw('DropDown.js: incorrect switchers/targets amount for #' + menu); }
		}
	},

	register: function() {

		this.applyEvents();
	},

	applyEvents: function() {

		// switchers hovers
		applySwitchers = function(switcher, index) {

			switcher.observe('mouseover', this.showTarget.bindAsEventListener(this, index));
			switcher.observe('mouseout', this.timerStart.bindAsEventListener(this));
		}
		this.switchers.each(applySwitchers.bind(this));

		// target link hovers
		applyTargetLinks = function(link) {

			link.observe('mouseover', this.timerStop.bindAsEventListener(this));
			link.observe('mouseout', this.timerStart.bindAsEventListener(this));
		}
		this.targets.each( function(target) { target.select('li a').each( applyTargetLinks.bind(this) ) }.bind(this) );	// ** over-binded structure:S
	},

	// helper method
	/*
	clearHovers: function() {

		this.switchers.invoke('removeClassName', 'selected');
	},
	*/

	// events
	showTarget: function(e, index) {

		// kill timer
		this.timerStop();

		// ** add more rules here
		if(index != this.active && !this.isSwitching) {

			// set that we're switching now
			this.isSwitching = true;

			// do highlights
			/*
			this.clearHovers();
			Event.element(e).addClassName('selected');
			*/

			// fast switch if anything is active, or effect-switch if not
			if(this.active != null) {

				this.targets[this.active].hide();
				this.targets[index].show();

				this.active = index;
				this.isSwitching = false;
			}
			else {

				switchingDone = function() {

					this.active = index;
					this.isSwitching = false;
				}
				new Effect.Appear(this.targets[index], {
				
					duration: this.switchDuration,
					afterFinish: switchingDone.bind(this)
				});
			}
		}
	},

	hideTarget: function() {

		if(this.active != null && !this.isSwitching) {

			this.isSwitching = true;

			hidingDone = function() {

				this.active = null;
				//this.clearHovers();

				this.isSwitching = false;
			}

			new Effect.Fade(this.targets[this.active], {

				duration: this.switchDuration,
				afterFinish: hidingDone.bind(this)
			});
		}
	},

        /**
	 * timer methods
	**/
	timerStart: function() {

		if(!this.timerRunning) {

			this.timerRunning = true;
			this.timerObj = new PeriodicalExecuter(this.hideTarget.bind(this), this.timerPeriod);
		}
	},
	timerStop: function() {

		if(this.timerRunning) {

			this.timerRunning = false;
			this.timerObj.stop();
		}
	}
});

/**
 * class Slideable
 *
 *  v0.1.6-beta
 *
 *  for defining all the slideable objects
**/
Slideable = Class.create({

	// config
	duration: undefined,

	// elements
	handle: undefined,
	target: undefined,

	// controllers
	isAnimating: false,
	isOpened: undefined,	// why not false? check :>

	// callbacks
	beforeOpen: undefined,
	afterClose: undefined,
	afterOpen: undefined,

	/* base methods */
	initialize: function(params) {

		// required params
		if(!params.handle) throw('Slideable: No handler attribute defined');
		if(!params.target) throw('Slideable: No target attribute defined');

		// assing elements
		this.handle = (Object.isElement(params.handle)) ? params.handle : $(params.handle);
		this.target = (Object.isElement(params.target)) ? params.target : $(params.target);
		if(!this.handle) throw('Slideable: No handle element found at #' + params.handle);
		if(!this.target) throw('Slideable: No target element found at #' + params.target);

		// assign callbacks
		if(Object.isFunction(params.beforeOpen)) this.beforeOpen = params.beforeOpen;
		if(Object.isFunction(params.afterOpen)) this.afterOpen = params.afterOpen;
		if(Object.isFunction(params.afterClose)) this.afterClose = params.afterClose;

		// duration
		this.duration = (params.duration) ? params.duration : 1.0;

		// register
		this.register();
	},

	register: function() {

		this.getState();
		this.handle.observe('click', this.toggle.bindAsEventListener(this));
	},

	getState: function() {

		$(this.target).visible() ? this.isOpened = true : this.isOpened = false;
	},

	/* open/close */
	toggle: function(e) {

		if(!this.isAnimating) {

			this.isAnimating = true;
			this.isOpened ? this.close() : this.open();
		}
		e.stop();
	},

	open: function() {

		if(this.beforeOpen) this.beforeOpen();

		Effect.SlideDown(this.target, {

			duration: this.duration,
			afterFinish: function() {

				// update state
				this.isAnimating = false;
				this.isOpened = true;

				// bind callbacks
				if(this.afterOpen) this.afterOpen.call();
			}.bind(this)
		});
	},

	close: function() {

		Effect.SlideUp(this.target, {

			duration: this.duration,
			afterFinish: function() {

				// update state
				this.isAnimating = false;
				this.isOpened = false;

				/// bind callbacks
				if(this.afterClose) this.afterClose.call();
			}.bind(this)
		});
	}

});

/**
 * class Slideable.Menu
 *
 * v0.2.0-dev
 *
 * KNOWN ISSUES:
 *  It's possible to mess things up on rapid clicking. Most likely cause of no local switching/notswitching controller.
 *  It looks like a small design flaw in Slideable/Slideable.Menu interaction. Don't give a shit anyway.. :)
**/
Slideable.Menu = Class.create({

	// elms
	elmWrapper: undefined,

	// helpers
	handles: [],
	targets: [],
	slideables: [],

	// controllers
	slideOpen: undefined,

	// init
	initialize: function(wrapper) {

		if($(wrapper)) {

			this.elmWrapper = $(wrapper);

			// store handlers/targets elements
			this.handles = this.elmWrapper.select('a.dropdown_handle');
			this.targets = this.elmWrapper.select('div.dropdown_target');

			// iterate and create in a hacky way;
			// WARNING, improperly built HTML will crash eveyrhting up
			this.handles.each(function(handle, index) {

				// assign target
				var target = this.targets[index];	// ** very experimental and possibly failable on bad HTML

				// idendify elements
				[handle, target].invoke('identify');

				// and create a new object for them
				this.slideables[index] = new Slideable({
					handle: handle.id,
					target: target.id,
					duration: 0.3,

					// callbacks
					beforeOpen: function() {

						// close previous slide
						if(this.slideOpen !== undefined) this.slideables[this.slideOpen].close();

					}.bind(this),

					afterOpen: function() {

						// assign new open slide
						this.slideOpen = index;

					}.bind(this),

					// ** consider if it's really neccessary here
					afterClose: function() {

						// remove opened if somehow we close the only opened slide
						if(this.slideOpen == index) this.slideOpen = undefined;
					}.bind(this)
				});

			}.bind(this));

		}
		else throw('Slideable.Menu: Wrapper id not found at #' + wrapper);
	}
});

/**
 * class inputHovers
 *
 * v0.2-bugged
 *
 * TODO:
 * - improve it with plugin input fields handling
 *   (like datepicekr fields, swfupload fields and tinymce/fck fields)
 *
**/
InputHovers = Class.create({

	// elements
	elmTds: [],
	elmInputs: [],

	// init
	initialize: function() {

		// get valid inputs (recurrently)
		this.elmTds = $$('td.input');
		this.elmTds.each(function(td) {

			// fix: actually catch both textareas and inputs ;)
			td.adjacent('input, textarea').each(function(input) {
				this.elmInputs.push(input);
			}.bind(this));

		}.bind(this));

		// apply blurs
		this.elmInputs.invoke('observe', 'focus', function(e) {

			e.stop();
			elmParent = Event.element(e).up(0);
			if(elmParent.nodeName == "TD" && !elmParent.hasClassName('inputFocused')) elmParent.addClassName('inputFocused');

		}.bindAsEventListener(this));

		this.elmInputs.invoke('observe', 'blur', function(e) {

			e.stop();
			elmParent = Event.element(e).up(0);
			if(elmParent.nodeName == "TD" && elmParent.hasClassName('inputFocused')) {

				elmParent.removeClassName('inputFocused');
				elmParent.removeClassName('inputHovered');
			}


		}.bindAsEventListener(this));

		// apply hovers
		this.elmInputs.invoke('observe', 'mouseover', function(e) {

			e.stop();
			elmParent = Event.element(e).up(0);
			if(elmParent.nodeName == "TD" && !elmParent.hasClassName('inputFocused')) elmParent.addClassName('inputHovered');

		}.bindAsEventListener(this));

		this.elmInputs.invoke('observe', 'mouseout', function(e) {

			e.stop();
			elmParent = Event.element(e).up(0);
			if(elmParent.nodeName == "TD" && !elmParent.hasClassName('inputFocused')) elmParent.removeClassName('inputHovered');


		}.bindAsEventListener(this));
	}


});


// Global functions
function reloadBrowserWindow() {

	Try.these(
		window.location.reload(),       // 'true' tells the browser not to use their cache (?)
		history.go(0),
		window.location.href=window.location.href
	);
}
function unixTime() {

	return parseInt(new Date().getTime().toString().substring(0, 10))
}
function fixUrlDecode(str) {

	[['+', ' '], ['&nbsp;', ' '], ['&amp;', '&'], ['&#039;', "'"]].each(function(pattern) { str = str.gsub(pattern[0], pattern[1]); });
	return str;
}
function randString(length) {

	// define our charracter set for randomizing
	var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz';

	// prepare the output var
	var randomstring = '';

	// run a loop and build it
	for (var i=0; i<length; i++) {
		var rnum = Math.floor(Math.random() * chars.length);
		randomstring += chars.substring(rnum,rnum+1);
	}
	return randomstring;
}

// Common runtime (mostly layout elements)
Event.observe(window, "load", function() {

	// Top menu
	new DropDown('top_menu');
});

