var SL = SL || {};

/**
 * == UI ==
 * The UserInterface Section
 * 
 * ### Utility Functions
 * - [[SL.ElementNotify]] : Displays a short notifcation over another element
 * 
**/

/** section: UI
 * SL
 * The SL Namespace
**/


/** section: UI
 * SL.ElementNotify(element, text, options) -> Element
 *     - element (Element | String): The Element, or an id for an element to display a notification for
 *     - text (String): The text content to use for the notification (alternatively you may pass some html via options)
 *     - options (Object): _(Optional)_ fx, styling and behavior options
 * 
 * Displays a short notifcation over another element by incecting a new notification element and [wink](http://mootools.net/docs/more/Fx/Fx.Reveal#Element:wink) it.
 * Returns the created notification element. Keep in mind, that this may be already gone, when `destroyOnHide` is set.
 * 
 * ##### Options
 * - destroyOnHide (boolean): _(Default: true)_ Whether to destroy the created notification element
 * - duration (Integer): millisecs, how long the notification shall be visible
 * - fxOptions (Object): All options that shall be passed to [Fx.Reveal](http://mootools.net/docs/more/Fx/Fx.Reveal#Fx-Reveal)
 * - element (Object): Options used to create the new notification element. All as passed to [Element](http://mootools.net/docs/core/Element/Element#Element:constructor)
 *   <br />___Defaults:___
 *   - class (String): _(default: 'wink-notification')_ the css class for the element
 *   - styles (Object): _(default: `{'display':'none'}`)_ aditional styles while generating. the `display:none` makes the "wink"-effekt.
 * 
 * ##### Examples
 *     myAjaxRequest.addEvent('success', function (responseTree, responseElements, responseHTML, responseJavaScript) {
 *         SL.ElementNotify('target-element-id', "", {element: {'html': responseHTML}});
 *     });
**/
SL.ElementNotify = function (element, text, options) {

	options = $merge({
		'destroyOnHide': true,
		'hideOnClick':true,
		'duration': 500,
		'fxOptions': {},
		'element': {
			'class': 'wink-notification',
			'styles': {
				'display': 'none'
			}
		},
		'position':'topCenter',
		'edge':'topCenter'
	}, options || {});
	
	var r = new Element('div', options.element).appendText(text || "").inject(document.id(document.body));
	r.position({'relativeTo': $(element) || document.body, 'position':options.position, 'edge':options.edge});
	if (options.destroyOnHide)
	{
		options.fxOptions.onHide = function () {
			r.destroy();
		};
	}
	r.wink(options.duration, options.fxOptions);
	
	r.addEvent('click', function(event){
		if(options.destroyOnHide) {
			this.destroy();
		} else {
			this.setStyle('display', 'none');
		}
	});
	
	return r;
};

/** section: UI
 * class SL.ModalDialog < Mask
 * includes Chain
 * 
 * Base Class for creating a Modal-Dialog-Layer
 * 
 * ##### Examples
 * A typical dialog looks like:
 *     <div class="modaldialog-container">
 *       <div class="modaldialog-content">
 *         <div class="modaldialog-message">The Message Text/Content goes in here</div>
 *         <div class="modaldialog-buttons">
 *           <a class="modaldialog-close modaldialog-button" href="#">Abbrechen</a>
 *           <a href="#" class="modaldialog-button">Los!</a>
 *           <a href="#" class="modaldialog-button"><em>Delete</em></a>
 *         </div>
 *       </div>
 *     </div>
**/

/**
 * new SL.ModalDialog([target = document.body, options = {}])
 *     - target (Element | String): _(Optional, default: document.body)_  A string of the id for an Element, to relative position the dialog to
 *     - options (Object): _(Optional)_ The options object [Mask](http://mootools.net/docs/more/Interface/Mask) uses to configure itself, plus a bunsh of confururing the displayed message, see below.
 * 
 * ##### Options
 * - buttons (Object): key/value pair of buttons to inject. See below for the default button config
 * - message (String): The text content to use for the message (alternatively you may pass some html via options.messageContainer.html)
 * - closeButtons (Array): array of the keys (see: options.buttons) of all buttons which shall close the Dialog
 * - maskTarget (Element | String): the id of an element or an Element to mask (cover with a modal layer) _(Default: document.body)_
 * - hideOnClick (Boolean): _(default: true)_ whether the Dialog shall close, when clicking on the Modal-Layer
 * - destroyOnHide (Boolean): _(default: true)_ whether the Dialog (and the modal layer) shall be destroyed after close
 * - fxOptions (Object): options passed to the effects used to transition the modal layer. See [Fx.Reveal](http://mootools.net/docs/more/Fx/Fx.Reveal#Fx-Reveal)
 * - dialogContainer (Object): the options object to create the dialog-elmenent (which is the outer most container, right after the modal layer)
 * - contentContainer (Object): the options object to create the content-elmenent (which is the container holting the message- and button-container)
 * - messageContainer (Object): the options object to create the message-elmenent (wrapper for the massage)
 * - buttonsContainer (Object): the options object to create the buttons-elmenent (wrapper for all buttons)
 *     
 * ##### Container Options
 * - Options used to create the new notification element. All as passed to [Element](http://mootools.net/docs/core/Element/Element#Element:constructor)
 * - By default each container gets an individual css class `modaldialog-container`, `modaldialog-content`, `modaldialog-message`, `modaldialog-buttons`
 * - The `messageContainer.html` is a good place to put random html content in
 * 
 * ##### Button Options
 * - Options used to create the button element. All as passed to [Element](http://mootools.net/docs/core/Element/Element#Element:constructor)
 * - The key will be used to fire an event. So clicking a button `"foo": {"html": "Click me"}` will fire a Event "foo"
 * 
 * ##### Default Button
 * `"close": {"html": "X", "class": "modaldialog-close"}`
 * - This button will allways close the Modal-Layer
 * - You may want to overwrite the `html` part for translation
 * 
 * ##### Examples
 * 
 *     	var options = {
 *     		"message": "Please choose...", 
 *     		"buttons": {
 *     			"close": {"html": "abort"},
 *     			"delete": {"html": "<em>Delete</em>"},
 *     			"append": {"html": "append"}
 *     		}
 *     	};
 *     
 *     	var dialog = new SL.ModalDialog('some-element', options); // make the dialog modal for the entire document
 *     
 *     	dialog.addEvents({
 *     		'append': function (e) {
 *     			new Event(e).stop(); // we have to stop the links click event
 *     			$('some-element').appendText(' appended ');
 *     		},
 *     		'delete': function (e) {
 *     			new Event(e).stop();
 *     			$('some-element').nix();
 *     		}
 *     	);
 *     
 *     	dialog.show(); // display the dialog
**/
SL.ModalDialog = new Class({
	
	Extends: Mask,
	
	options: {
		
		'buttons': {
			'close': {
				'html': '<span>X</span>',
				'class': 'modaldialog-close'
			}
		},
		
		'prompts': {},
		
		'message': '',
		
		'closeButtons': [],
		
		'dialogContainer': {
			'class': 'modaldialog-container'
		},
		
		'contentContainer': {
			'class': 'modaldialog-content'
		},
		
		'messageContainer': {
			'class': 'modaldialog-message'
		},
		
		'buttonsContainer': {
			'class': 'modaldialog-buttons'
		},
		
		'fxOptions': {
			'link': 'chain',
			'duration': '100'
		},
		
		'style': {
			'z-index': 2147483520
		},
		
		'animatedShow': true,
		'maskMargin': 100,
		'hideOnClick': true,
		'destroyOnHide': true,
		'maskTarget': null,
		'containerPosition': {'relativeTo': null} // will be overwritten by the target argument
	},
	
	initialize: function (target, options)
	{
		this.id = Math.random();
		
		this.setOptions(options);
		
		var positionTarget = this.options.containerPosition.relativeTo = this.options.containerPosition.relativeTo || target;
		var maskTarget = this.options.maskTarget || document.body;
		var args = [maskTarget, options];
		
		this.parent.apply(this, args);
		this.target.store('modaldialog', this);
		
		//add this to events for when noFx is true; parent methods handle hide/show
		var deactivate = function(){ this.active = false; }.bind(this);
		this.addEvents({
			hide: deactivate,
			show: deactivate
		});

		// destroy the dialog when options.destroyOnHide is set to true
		if (this.options.destroyOnHide)
		{
			this.addEvent('hide', function()
			{
				this.destroy();
			});
		}
	},
	
	render: function ()
	{
		this.parent();
		this.element.set('id', this.options.id || "modaldialog-" + $time());
		this.element.set('tween', this.options.fxOptions);
		
		this.container = document.id(this.options.dialogContainer) || new Element('div', this.options.dialogContainer);
		this.content = document.id(this.options.contentContainer) || new Element('div', this.options.contentContainer);
		this.message = document.id(this.options.messageContainer) || new Element('div', this.options.messageContainer).appendText(this.options.message);
		this.buttons = document.id(this.options.buttonsContainer) || new Element('div', this.options.buttonsContainer);
		
		this.content.adopt(this.message);
		this.buildPrompts(); // adopt the prompts to this.content, so we don't need a prompt container for this moment
		this.content.adopt(this.buttons);
		this.container.adopt(this.content);
		
		this.container.inject(document.body, 'bottom');
		this.element.inject(document.body, 'bottom');
		
		this.buildButtons();

		this.element.removeEvents('click');
		if (this.options.hideOnClick)
		{
			this.element.addEvent('click', function (evt)
			{
				if (evt.target === this.element)
				{
					this.hide();
				}
			}.bind(this));
		}
	},
	
	buildPrompts : function  () {
		$each(this.options.prompts, function (options, key) {
			var elem = new Element(options.element, options.elementOptions);
			elem.addClass('modaldialog-prompt');
			this.content.adopt(elem);
		}, this);
	},
	
	buildButtons: function ()
	{
		$each(this.options.buttons, function (options, key)
		{
			var btn = new Element('a', $merge(options, {
				'href': '#',
				'events': {
					'click': function (evt, args)
					{
						evt.stop();
						this.fireEvent(key, [evt].extend(args || []));
					}.bind(this)
				}
			}));
			btn.addClass('modaldialog-button');
			
			if (this.options.closeButtons.contains(key) || 'close' === key)
			{
				btn.addEvent('click', function (evt, args)
				{
					this.hide();
				}.bind(this));
			}
			
			this.buttons.adopt(btn);
			
		}, this);
	},

	/**
	 * SL.ModalDialog#show([noFx]) -> SL.ModalDialog
	 *     - noFx (Boolean): Whether to show this layer whithout any transitions
	 * 
	 * Display this dialog
	 * 
	 * returns this instance for a fluent api
	**/
	show: function (noFx) {
		
		if (this.active) {
			return this.chain(this.show.bind(this));
		}
		
		if (!this.hidden) {
			this.callChain.delay(20, this);
			return this;
		}
		this.setAdvertising(false);
		this.active = true;
		return this.parent(noFx);
	},

	/**
	 * SL.ModalDialog#showMask([noFx]) -> SL.ModalDialog
	 *      - noFx (Boolean): Whether to show this layer whithout any transitions
	 * 
	 * Shows the dialog with a transition.
	 * 
	 * returns this instance for a fluent api 
	**/
	showMask: function (noFx) {
		var pos = function () {
			this.container.position($merge({
				relativeTo: this.element
			}, this.options.containerPosition));
			this.maskHeight = this.container.getSize()['y'];
		}.bind(this);

		if (noFx) {
			this.parent();
			pos();
		} else {
			if (this.options.animatedShow) {
 				var H = this.element.measure(function(){ return this.getSize()['y']; });

				this.container.setStyle('display','none');

				this.element.setStyle('display', 'block');
				this.element.setStyle('top', window.getScroll()['y']);
				this.element.setStyle('height', 0);

				new Fx.Morph(this.element,
				{
					fps: 100,
					duration: 400,
					transition: Fx.Transitions.Linear,
					onComplete: function()
					{
						this.element.setStyles({
							top: 0,
							height: H
						});
						pos();
						this.container.setStyle('display','block');
						this.hidden = false;
						this.fireEvent('show');
						this.callChain();
					}.bind(this)
				}).start({
					height: window.getSize()['y']
				});
			} else {
				this.element.setStyles({
					display: 'block'
				});
				this.hidden = false;
				this.fireEvent('show');
				this.callChain();
			}

		}
	},

	/**
	 * SL.ModalDialog#hide([noFx]) -> SL.ModalDialog
	 *      - noFx (Boolean): Whether to hide this layer whithout any transitions
	 * 
	 * Hide/Close this dialog
	 * 
	 * returns this instance for a fluent api 
	**/
	hide: function (noFx)
	{
		if (this.active) {
			return this.chain(this.hide.bind(this));
		}
		
		if (this.hidden) {
			this.callChain.delay(20, this);
			return this;
		}
		
		this.setAdvertising(true);
		this.active = true;
 		// this.request.cancel() // deactivated, there are too many problems
		window.removeEvent('resize', this.position);
		this.hideMask.apply(this, arguments);
	},

	hideMask: function (noFx) {
		if (noFx) {
			this.parent();
			return;
		}

		this.container.setStyle('display','none');
		this.element.setStyles({
			top: window.getScroll()['y'],
			height: window.getSize()['y']
		});
		new Fx.Morph(this.element,
		{
			duration: 300,
			transition: Fx.Transitions.Linear
		}).start({
			'height': 0
		}).chain(function()
		{
			this.element.setStyle('display', 'none');
			this.hidden = true;
			this.fireEvent('hide');
			this.callChain();
			if (this.options.destroyOnHide) return this.destroy();
		}.bind(this));
	},

	/**
	 * SL.ModalDialog#destroy()
	 * 
	 * removes the layer from the DOM
	**/
	destroy: function () {
		this.container.destroy();
		this.parent();
		this.target.eliminate('modaldialog');
	},
	
	
	/**
	 * SL.ModalDialog#getContentHeight() ->int
	 * 
	 * returns this dialogs contents height
	**/
	getContentHeight: function() {
		// IE seems to have some problems to determine the correct height of modaldialog-content
		// when the container height is set to 'auto' (even temporarily) it works
		if (Browser.Engine.trident) {
 			var origHeight = this.container.getStyle('height');
			this.container.setStyle('height','auto');
		}
		var contentHeight = this.content.getSize()['y'];
		if (Browser.Engine.trident) {
			this.container.setStyle('height',origHeight);
		}

		// for firefox, calculate the height of flash objects
		if (Browser.Engine.gecko)
		{
			var flashObjects = this.content.getElements('object');
			if (flashObjects)
			{
				flashObjects.each(function(f)
				{
					if (!f.getParent('#flashVideoPlayerWrapper'))
						contentHeight += (f.getSize()['y'].toInt() != 0 ? f.getSize()['y'].toInt() : f.getStyle('height').toInt());
				});
			}
		}

		var containerPadding = this.container.getStyle('padding-top').toInt() + this.container.getStyle('padding-bottom').toInt();
		var contentInnerPadding = this.content.getStyle('padding-top').toInt() + this.content.getStyle('padding-bottom').toInt();
		var wholePadding = contentInnerPadding + containerPadding + this.options.maskMargin;
		var contentInnerMaxHeight = window.getSize()['y'].toInt() - wholePadding;

		if (contentHeight > contentInnerMaxHeight) {
			contentHeight = contentInnerMaxHeight;
			this.content.addClass('scrollable');
			if (Browser.Engine.trident && Browser.Engine.version == '4') this.content.setStyle('height', contentInnerMaxHeight);
		}
		this.content.setStyle('max-height', contentInnerMaxHeight);

		return contentHeight;
	},


	/**
	 * SL.ModalDialog#adaptSize() -> SL.ModalDialog
	 * 
	 * Adapts the size of this dialog to the contents height
	 * 
	 * returns this instance for a fluent api
	**/
	adaptSize: function(noRemoveOfLoadingClass) {
		
		if(!noRemoveOfLoadingClass) {
			this.container.removeClass('loading');
		}
		
		this.container.addClass('resizing');
		var contentHeight = this.getContentHeight();
		var dialogPosTop = window.getScroll()['y'] + (window.getSize()['y'].toInt() - contentHeight - this.options.maskMargin/2 )/2;

		new Fx.Morph(this.container,
		{
			duration: this.options.fxOptions.duration,
			transition: Fx.Transitions.Linear,
			onComplete: function()
			{
				this.container.removeClass('resizing');
				this.fireEvent('resized');
			}.bind(this)
		}).start({
			'height': contentHeight,
			'top': dialogPosTop
		});
		return this;
	},


	/**
	 * SL.ModalDialog.Ajax#postAndUpdateContent(url, options = {}) -> SL.ModalDialog.Ajax
	 *     - url (String): the url where to send the post request and from which retrieve the content to update the Overlay
	 *     - options (Object): all of [[SL.ModalDialog.Ajax]], plus:
	 *  	
	 * 
	 * send a post request to the url and updates the content of this modal dialog with the html retrieved from the given post request
	 * 
	 * returns this instance for a fluent api
	**/
	postAndUpdateContent: function(url, options) {
		options = $merge(this.options, options || {});

		if (options.message != '') {
			this.message.set('html', options.message);
		}
		this.container.addClass('loading');
		this.adaptSize();
		
		this.request = new Form.Request(options.formElement, this.message, {
				'onSuccess': function() {
					this.adaptSize();
					this.fireEvent('contentUpdated');
				}.bind(this),
				'resetForm':false
			}).send();
		return this;
	},
	
	
	injectElementToMessage: function(element, options) {

		var defaultOptions = {
			position:'bottom',
			overwrite:true,
			resize:true,
			resizeDuration:1000
		}
		options = $merge(defaultOptions, options || {});
		
		if(options.overwrite) {
			this.message.empty();
			this.message.set('html', '');
		}
		element.inject(this.message, options.position);

		this.container.removeClass('loading');
		
		var dialogLeft      = this.container.getCoordinates().left.toInt();
		var dialogWidth     = this.container.getCoordinates().width.toInt();
		var newContentWidth = element.getCoordinates().width.toInt();
		var diffValue       = (dialogWidth-newContentWidth)/2;

		new Fx.Morph(this.container, {'duration': options.resizeDuration, 'transition': Fx.Transitions.Sine.easeOut}).start({
		    'width': [dialogWidth, newContentWidth],
			'left': [dialogLeft,dialogLeft+diffValue]
		});

		var closeButton     = $(this.buttons).getElements('.modaldialog-close')[0];

		new Fx.Morph(closeButton, {'duration': options.resizeDuration, 'transition': Fx.Transitions.Sine.easeOut}).start({
			'left': [dialogWidth,newContentWidth]
		});

		this.adaptSize();
	},
	
	/**
	 * SL.ModalDialog#setAdvertisement(visible) -> undefined
	 *     - visible (Boolean): Whether to show advertisement banner or not
	 * 
	 * hides advertisement banners if there is an open overlay (because some browsers cannot hide flash movies behind an overlay)
	 * 
	**/
	setAdvertising : function(visible) {
		['Advertisement', 'skyscraper'].each(function(advElementId) {
			var advElement = $(advElementId);
			if(advElement) {
				advElement.setStyle('visibility', ((visible) ? 'visible' : 'hidden'));
			}
		});
	}

});
SL.ModalDialog.implement(new Chain);


/** section: UI
 * class SL.ModalDialog.Ajax < SL.ModalDialog
 * 
 * Like SL.ModalDialog, but get the message-content from an ajax request
**/

/**
 * new SL.ModalDialog.Ajax([target = document.body, options = {}])
 *     - target (Element | String): _(Optional, default: document.body)_  A string of the id for an Element or an Element reference to overlay
 *     - options (Object): all of [[SL.ModalDialog]], plus:
 * 
 * returns the Request used to get the message content
 * 
 * ##### Options
 * - request (Object): an options object passed the the [Request.HTML](http://mootools.net/docs/core/Request/Request.HTML) instance. Put the url in here, where to get the message content. _(Default: {'method': 'get'})_
 * 
 * ##### Examples
 * HTML:
 *     <a href="some/url" onclick="askWithRomoteMessage(event)">do stuff</a>
 * JS:
 *     var askWithRomoteMessage = function (evt, options) {
 *         evt = new Event(evt).stop();
 *         options = $merge({'buttons': {'close': {'html': 'Abbrechen'}}}, options || {});
 *         new SL.ModalDialog.Ajax(evt.target.href, 'The-Realted-Element', options).show();
 *     };
 * 
**/
SL.ModalDialog.Ajax = new Class({
	
	Extends: SL.ModalDialog,
	
	options: {
		'request': {
			'method': 'get'
		},
		'fxOptions': {
			'duration': 250
		}
	},
	
	initialize: function ()
	{
		var args = $A(arguments);
		
		var url = args.shift();
		this.parent.apply(this, args);
		this.container.addClass('loading');
	    
		this.addEvent('show', function() {
			this.request = new Request.HTML($merge(this.options.request, {
				'update': this.message,
				'url': url,
				'onSuccess': function() {
					this.adaptSize();
					this.fireEvent('contentUpdated');
				}.bind(this)
			})).send();	
		}.bind(this));

	},

	
	/**
	 * SL.ModalDialog.Ajax#adaptSize(url, options = {}) -> SL.ModalDialog.Ajax
	 *     - url (String): the url to retrieve the content from
	 *     - options (Object): all of [[SL.ModalDialog.Ajax]], plus:
	 *  	
	 * 
	 * updates the content of this modal dialog with the html retrieved from the given url
	 * 
	 * returns this instance for a fluent api
	**/
	updateContent: function(url, options) {
		var options = options || {}
		this.options = $merge(this.options, options);

		this.message.set('html', ($defined(options.message) ? options.message : ''));
		this.container.addClass('loading');
		this.adaptSize(true);

		this.request = new Request.HTML($merge(this.options.request, {
			'update': this.message,
			'url': url,
			'onSuccess': function() {
				this.adaptSize();
				this.fireEvent('contentUpdated');
			}.bind(this)
		})).send();
		
		return this;
	}
	


});







































/** section: UI
 * class SL.ModalDialog.Confirm < SL.ModalDialog
 * 
 * Ask before doing an Ajax/Json request.
 * This Dialog-Type inserts a second button 'ok', which, when 'click'ed, triggers the Request.
 * All Events fired by the Request will be refired by this Dialog.
 * 
**/

/**
 * new SL.ModalDialog.Confirm(url[, target = document.body, options = {}])
 *     - url (String): The url to send the request to
 *     - target (String): See [SL.ModalDialog]
 *     - options (Object): options object, all of [SL.ModalDialog], plus
 * 
 * ##### Options
 * - closeOnOk (Boolean): _(Default: true)_ Whether to close the Dialog, when the OK button got pressed
 * - requestType (String): _(Default: "HTML")_ One of "HTML" or "JSON". The Subtype of a Request to use. Either [Request.HTML] or [Request.JSON]
 * - request (Object): _(Default: {'method': 'get'})_ The options object to pass to the Request.HTML/JSON.
 * 
 * ##### Examples
 * HTML:
 *     <li id="some123">
 *       <a href="delete/some/content" onclick="askFirst(event, 'some123', {'buttons': {'close': {'html': 'Abbrechen'}, 'ok': {'html': 'Löschen!'}}, 'request': {'method': 'post'}})">Delete</a>
 *     </li>
 * JS:
 *     	var askFirst = function (evt, target, options)
 *     	{
 *     		evt = new Event(evt).stop();
 *     		options = $merge({'requestType': 'HTML'}, options || {});
 *     	
 *     		var dialog = new SL.ModalDialog.Confirm(evt.target.href, target, options);
 *     	
 *     		dialog.addEvent('success', function (responseTree, responseElements, responseHTML, responseJavaScript)
 *     		{
 *     			SL.ElementNotify($(target), "", {notify: {'html': responseHTML}});
 *     			$(target).nix();
 *     		});
 *     	
 *     		dialog.show();
 *     	}
**/
SL.ModalDialog.Confirm = new Class({
	
	Extends: SL.ModalDialog,
	
	options: {
		'buttons' : {
			'ok': {
				'html': 'OK'
			}
		},
		'dialogContainer': {
			'class': 'modaldialog-container confirm'
		},
		'closeOnOk': true,
		'request': {
			'method': 'get'
		},
		'requestType': 'HTML' //or JSON
	},
	
	initialize: function ()
	{
		var args = $A(arguments);
		this.requestUrl = args.shift();
		this.parent.apply(this, args);
		
		if(this.options.request.method == "none") {
			this.addEvent('ok', this.handle.bind(this));
		} else {
			this.addEvent('ok', this.send.bind(this));
		}
	},
	
	send: function (evt, options)
	{
		this.request = new Request[this.options.requestType.toUpperCase()]($merge(this.options.request, {
			'url': this.requestUrl
		}));
		
		this.request.addEvent('complete', function () {
			this.fireEvent('complete', arguments);
		}.bind(this));
		
		this.request.addEvent('success', function () {
			this.fireEvent('success', arguments);
		}.bind(this));
		
		this.request.addEvent('failure', function () {
			this.fireEvent('failure', arguments);
		}.bind(this));
		
		this.request.addEvent('request', function () {
			this.fireEvent('request', arguments);
		}.bind(this));
			
		this.request.addEvent('exception', function () {
			this.fireEvent('exception', arguments);
		}.bind(this));
		
		this.request.addEvent('cancel', function () {
			this.fireEvent('cancel', arguments);
		}.bind(this));

		this.request.send();
		
		if (this.options.closeOnOk)
		{
			this.hide();
		}
	},
	
	handle: function (evt, options) {
		
		this.fireEvent('success');
		
		if (this.options.closeOnOk)
		{
			this.hide();
		}	
	}
});



















SL.ModalDialog.Prompt = new Class({
	
	Extends: SL.ModalDialog,
	
	options: {
		'buttons' : {
			'ok': {
				'html': 'OK'
			}
		},
		'dialogContainer': {
			'class': 'modaldialog-container confirm'
		},
		'closeOnOk': true
	},
	
	initialize: function ()
	{
		var args = $A(arguments);
		this.parent.apply(this, args);

		this.addEvent('ok', this.handle.bind(this));

	},
	
	handle: function (evt, options) {
		
		this.fireEvent('success');
		
		if (this.options.closeOnOk)
		{
			this.hide();
		}	
	}
});


























/** section: UI
 * class SL.Rating
 *  includes Options
 * 
 * Handles the mouseover-effects and rate-request.
 * Each rating option has to be a single link-element, ordered by rating value, which links to an corresponding ajax url
 **/

/**
 * new SL.Rating(options)
 *     - options (Object): The config options object
 * 
 * ##### Options
 * - `target` (`Element` | `String`): The Element or ID of the "ratingContainer". This is the root of all later Selectors and the place, where the Rating-Ajax-Response will be updated.
 * - `ratingSelector` (`String`): The Selector to find the rating-links inside the target. (default: `"a.uoRatingLink"`)
 * - `highlightSelector` (`String`): The Selector to find the element to put the css-highlight class on (default: `"div.uoRate"`)
 * - `highlightClassPrefix` (`String`): The CSS-Class-Prefix which is used to set the active-highlight, without the rating-number (default: `"highlightClassPrefix"`)
 * - `hoverClassPrefix` (`string`): The CC-Class-Prefix which is used to set a highlight hover effect
 * - `requestOptions` (`Object`): Options Object to pass to [[Request.HTML]] (default: `{'useSpinner': true}`)
 * 
**/ 
SL.Rating = new Class({
	
	Implements: [Options],
	
	options: {
		'ratingSelector': 'a.uoRatingLink',
		'highlightSelector': 'div.uoRate',
		'highlightClassPrefix': 'ratingBig',
		'hoverClassPrefix': 'ratingBigHover',
		'requestOptions': {
			'useSpinner': true
		}
	},
	
	Binds: ['onRate', 'onOver', 'onResetRating', 'initOvers'],
	
	
	ratingContainer: null,
	highlightElement: null,
	oldRatingValue: null,
	
	initialize: function (options) {
		
		this.setOptions(options);
		
		this.ratingContainer = document.id(options.target);
		this.ratingContainer.getElements(this.options.ratingSelector).addEvent('click', this.onRate);
		
		this.initOvers();
	},
	
	initOvers: function () {
		
		this.highlightElement = this.ratingContainer.getElement(this.options.highlightSelector);
		var match = this.highlightElement.get('class').match(new RegExp(this.options.highlightClassPrefix + '(\\d+)'));
		this.oldRatingValue = (match ? match[1] : "1");
		this.ratingContainer.addEvent('mouseleave', this.onResetRating);
		this.attatch();
	},
	
	/**
	 * attach the over-events only.
	 */
	attatch: function () {
		
		this.ratingContainer.getElements(this.options.ratingSelector).each(function (el, index) {
			el.addEvent('mouseenter', this.onOver.bindWithEvent(this, [index]));
		}, this);
	},
	
	
	detatch: function () {
		this.highlightElement = null;
		this.ratingContainer.getElements(this.options.ratingSelector).each(function (el, index) {
			el.removeEvents('mouseenter');
		}, this);
	},
	
	
	onRate: function (evt, target) {
		evt = new Event(evt).stop();
		
		var url = SL._fetchEventUrl(evt);
		this.detatch();
		this.rateRequest = new Request.HTML($merge(this.options.requestOptions, {
			'url': url,
			'update': this.ratingContainer
		}));
		
		this.rateRequest.send();
	},
	
	onOver: function (evt, index) {
		this.highlight(index + 1);
		this.hoverHighlight(index + 1);
	},
	
	onResetRating: function (evt) {
		this.highlight(this.oldRatingValue);
		this.hoverHighlight(0);
	},
	
	highlight: function (rating_value) {
		// there is a small change, that we dropped that element while a rate is in progress
		if (this.highlightElement) {
			var newClassNames = this.highlightElement.get('class').replace(new RegExp(this.options.highlightClassPrefix + '(\\d+)'), this.options.highlightClassPrefix + rating_value);
			this.highlightElement.set('class', newClassNames);
		}
	},
	
	hoverHighlight: function (rating_value) {
		if (this.highlightElement) {
			var newClassNames = this.highlightElement.get('class').replace(new RegExp(this.options.hoverClassPrefix + '(\\d+)'), this.options.hoverClassPrefix + rating_value);
			this.highlightElement.set('class', newClassNames);
		}
	}
	
});

/** related: SL.Registry
 * SL.Rating.startup() -> undefined
 * 
 * Uses [[SL.Registry]] namespaces:
 * - `sl.startup.ratings` to create new SL.Rating instances
 * 
 * A single registry value is:
 * - setting (Object): options object as defined by [[SL.Rating]]. As a minimum the `target` has to be defined
**/
SL.Rating.startup = function () {
	SL.Registry.get('sl.startups.ratings').each(function (setting) {
		new SL.Rating(setting); 
	});
};
SL.Registry.add('sl.impl.startups', SL.Rating.startup);



/** section: UI
 * class SL.SlideShow
 * includes Options
 * 
 * Handles s slideshow with different effects.
 * A slideshow has slider elements and button elements. The slider can change from ONE slide to another with different effects.
 * It has only few effects, but you can add your own FX effects (see: http://mootools.net/forge/p/slideshow)
 * If you look for a solution with more than one slider elements, try the class EasySlider
 * 
 **/

/**
 * new SL.SlideShow(slidersId, buttonsId, options)
 *     - slidersId (String): The id of the element, which child elements are the different sliders
 *     - buttonsId (String): The id of the element, which child elements (matched to the given seperator in options object) are the buttons
 *     - options (Object): The config options object
 * 
 * ##### Options
 * - `slidersSelector` (`Element` | `String`): The selector to find the slide elements with the content you want to slide (default: `"li"`)
 * - `buttonsSelector` (`Element` | `String`): The selector to find the elements with which we can swapp between the different slider (default: `"li a"`)
 * - `activeElementSelector` (`String`): The Selector to find the elements which get the css class `activeCss` on active status (default: `"li"`)
 * - `activeCss` (`String`): The name of the css class which will be set for the `activeElement` elements (default: `"active"`)
 * - `slideShowOptions` (`Object`): Options Object with settings for the slide show (default: `{'delay':4000,'transition':'fade','duration':500,'autoplay':'true'}`)
 * 
 * ##### possible transition values in slideShowOptions
 * - `none` (String)
 * - `fade` (String)
 * - `crossFade` (String)
 * - `fadeThroughBackground` (String)
 * - `pushLeft` (String)
 * - `pushRight` (String)
 * - `pushUp` (String)
 * - `pushDown` (String)
 * - `blindLeft` (String)
 * - `blindRight` (String)
 * - `blindUp` (String)
 * - `blindDown` (String)
 * - `blindLeftFade` (String)
 * - `blindRightFade` (String)
 * - `blindUpFade` (String)
 * - `blindDownFade` (String)
 *
 *##### Examples
 *
 *	<div id="slider">
 *		<div>slider element 1</div>
 *		<div>slider element 2</div>
 *		<div>slider element 3</div>
 *	</div>
 *
 *	<ul id="buttons">
 *		<li>link to slider 1</li>
 *		<li>link to slider 2</li>
 *		<li>link to slider 3</li>
 *	</ul>
 *
 *	var slSlider = new SL.SlideShow('slider', 'buttons', {buttonsSelector: 'li'});
 *
 *	// you can use following optional commands
 *	slSlider.show(1);  			// show slider with the numeric index 1, in our example slider element 2
 *	slSlider.play();  			// play all slider elements in loop
 *	slSlider.pause(); 			// the sliders stopped, will be continue with the slSlider->play(); command
 *  slSlider.reverse(); 		// change the direction of the slider
 *  slSlider.showPrevious(); 	// show the previous slider
 *  slSlider.showNext(); 		// show the next slider
 *  slSlider.reset();  			// resets the slider
 *  
**/ 
SL.SlideShow = new Class({

	Implements: [Options],

	options: {
		'slidersSelector': 'li',
		'buttonsSelector': 'li a',
		'activeElementSelector': 'li',
		'activeCss': 'active',
		'slideShowOptions': {
			'delay': 4000,
			'transition': 'fade',
			'duration': 500,
			'autoplay': true
		}
	},

	buttons: null,
	activeElements: null, // 
	slideshow: null,
	currentButtonIndex: 0,
	activeAnimation: false,


	initialize: function (slidersId, buttonsId, options) {

		this.setOptions(options);

		var buttonsContainer = document.id(buttonsId);
		this.buttons = buttonsContainer.getElements(this.options.buttonsSelector);
		this.activeElements  = buttonsContainer.getElements(this.options.activeElementSelector);
		
		if(this.options['slidersSelector']) {
			this.options.slideShowOptions = $merge(this.options.slideShowOptions, {'slidersSelector':this.options['slidersSelector']});
		}

		this.slideshow = new SlideShow(slidersId, this.options.slideShowOptions);
		this.addEvents();
	},

	addEvents: function () {
		
		this.slideshow.addEvent('show', function (slide, index) {
			this.activeAnimation = true;
			this.activeElements[this.currentButtonIndex].removeClass(this.options.activeCss);
			this.activeElements[index].addClass(this.options.activeCss);
			this.currentButtonIndex = index;
			
		}.bind(this));

		this.slideshow.addEvent('showComplete', function (slide, index) {
			this.activeAnimation = false;
		}.bind(this));


		this.buttons.each(function (el, i) {
			el.addEvent('click', function(evt) {
				evt = new Event(evt).stop();
				if (this.activeAnimation) return;
				this.slideshow.show(i);
				if(this.options.slideShowOptions.autoplay === true) {
					this.slideshow.pause();
					this.slideshow.play();
				}
			}.bind(this));
		}.bind(this));

	}

});


/**
 * SL.EpisodePlaying -> undefined
 * 
 * - episodeId (string): the id of the episode which is currently playing on the video player
 * 
 * Mark the currently playing episode in the episode slider and slides to the currently playing episode teaser
 * 
**/
SL.EpisodePlaying = function (episodeId) {
	if($defined($('showEpisode-'+episodeId))) {
		var episodeTeaserElem = $('showEpisode-'+episodeId);
		episodeTeaserElem.addClass('nowPlaying');
		var relAttribute  = episodeTeaserElem.getElement('a[rel]').get('rel');
		var teaserNumber  = parseInt(relAttribute.substring(14, relAttribute.length));
		SL.Registry.get('sl.easysliders.slShowEpisodeSlider')[0].slideInRangeToIndex(teaserNumber-1);
	}
}


/**
 * SL.FormRefill -> undefined
 * 
 * - elementId (string): the id of the element which should be form refilled
 * 
 * Shows a standard value in an input field, if you click into it, this value will be hidden, otherwise you click out of the field, the value will be shown again
 * 
**/
SL.FormRefill = function (elementId) {
	if($defined($(elementId))) {
		var elementId = $(elementId);
		new OverText(elementId);
	}
}


/**
 * == Startups ==
 * The Startup Section
 * 
 * ### Utility Functions
 * - [[SL.EasySliderSetup]] : will be called after loading DOM, and creates an EasySlider
 * 
 * 
**/

/** section: Startups
 * SL
 * The SL Namespace
**/

/** section: Startups
 * SL.EasySliderSetup() -> undefined
 * 
 * Uses [[Startups]] namespaces:
 * - `sl.startups.easyslider` for the EasySlider settings. A single registry value is
 *   - setting (`Object`): options as defined by [[SL.EasySlider]]
 *
**/
SL.EasySliderSetup = function () {
	
	SL.Registry.get('sl.startups.easyslider').each(function (settings){
	
		var sliderList = document.getElement(settings.sliderList);
		var slider = new EasySlide(sliderList, settings.options);
				
		slider.bootStrap();

		SL.Registry.add('sl.easysliders.'+settings.sliderKey, slider);
	});
	
};
SL.Registry.add('sl.impl.startups', SL.EasySliderSetup);


/** section: Startups
 * SL.SlideShowSetup() -> undefined
 * 
 * Uses [[Startups]] namespaces:
 * - `sl.startups.slideshows` for the SlideShow settings. A single registry value is
 *   - setting (`Object`): options as defined by [[SL.SlideShow]]
 *
**/
SL.SlideShowSetup = function () {
	
	SL.Registry.get('sl.startups.slideshows').each(function (settings){
	
		var sliderKey = settings.sliderKey;
		var buttonKey = settings.buttonKey;
		
		delete settings['sliderKey'];
		delete settings['buttonKey'];
		
		var slider = new SL.SlideShow(sliderKey, buttonKey, settings);

		SL.Registry.add('sl.slideshows.'+sliderKey, slider);
	});
	
};
SL.Registry.add('sl.impl.startups', SL.SlideShowSetup);


/** section: Startups
 * SL.EpisodePlayingSetup() -> undefined
 * 
 * Uses [[Startups]] namespaces:
 * - `sl.startups.episodePlaying` for the EpisodePlaying settings. A single registry value is
 *   - settings (`Object`): options has only the episodeId
 *
**/
SL.EpisodePlayingSetup = function() {
	SL.Registry.get('sl.startups.episodePlaying').each(function (settings) {
		var episodeId = settings.episodeId;
		
		SL.EpisodePlaying(episodeId);
		
	});
};
SL.Registry.add('sl.impl.startups', SL.EpisodePlayingSetup);


/** section: Startups
 * SL.FormRefillSetup() -> undefined
 * 
 * Uses [[Startups]] namespaces:
 * - `sl.startups.formRefill` for the FormRefill settings. A single registry value is
 *   - settings (`Object`): options has only the elementId from the refill input element
 *
**/
SL.FormRefillSetup = function() {
	SL.Registry.get('sl.startups.formRefill').each(function (settings) {
		if($defined(settings.elementId)) {
			var elementIds = [settings.elementId];
		} else {
			if($defined(settings.elementIds)) {
				var elementIds = settings.elementIds;
			} else {
				var elementIds = [];
			}
		}
		elementIds.each(function(elementId){
			SL.FormRefill(elementId);
		});
	});
};
SL.Registry.add('sl.impl.startups', SL.FormRefillSetup);


