/*
ModalBox - The pop-up window thingie with AJAX, based on prototype and script.aculo.us.

Copyright Andrey Okonetchnikov (andrej.okonetschnikow@gmail.com), 2006-2007
All rights reserved.

VERSION 1.5.4
Last Modified: 07/16/2007

Copyright (c) 2006-2007 Andrey Okonetchnikov

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

if (!window.Modalbox)
	var Modalbox = new Object();

Modalbox.Methods = {
	focusableElements: new Array,
	options: {
		title: "VKF Renzel", // Title of the ModalBox window
		overlayClose: true, // Close modal box by clicking on overlay
		width: 500, // Default width in px
		height: 90, // Default height in px
		closedelay: 0,
		overlayOpacity: .40, // Default overlay opacity
		overlayDuration: 0, // Default overlay fade in/out duration in seconds
		slideDownDuration: 0, // Default Modalbox appear slide down effect in seconds
		slideUpDuration: 0, // Default Modalbox hiding slide up effect in seconds
		resizeDuration: 0, // Default resize duration seconds
		inactiveFade: true, // Fades MB window on inactive state
		loadingString: "Please wait. Loading...", // Default loading string message
		closeString: "Close window", // Default title attribute for close window link
		params: {},
		method: 'get' // Default Ajax request method
	},
	_options: new Object,

	setOptions: function(options) {
		Object.extend(this.options, options || {});
	},

	_init: function(options) {
		// Setting up original options with default options
		Object.extend(this._options, this.options);
		this.setOptions(options);
		//Create the overlay
		this.MBoverlay = Builder.node("div", { id: "MB_overlay", opacity: "0" });
		//Create the window
		this.MBwindow = Builder.node("div", {id: "MB_window", style: "display: none"}, [
			this.MBframe = Builder.node("div", {id: "MB_frame"}, [
				this.MBheader = Builder.node("div", {id: "MB_header"}, [
					this.MBcaption = Builder.node("div", {id: "MB_caption"}),
					this.MBclose = Builder.node("a", {id: "MB_close", title: this.options.closeString, href: "#"}, [
						Builder.build("<img border='0' style='' alt='' src='http://images.vkf-renzel.com/img/btn/cancel.png'/>"),
					]),
				]),
				this.MBcontent = Builder.node("div", {id: "MB_content"}, [
					this.MBloading = Builder.node("div", {id: "MB_loading"}, this.options.loadingString),
				]),
			]),
		]);
		// Inserting into DOM
		document.body.insertBefore(this.MBwindow, document.body.childNodes[0]);
		document.body.insertBefore(this.MBoverlay, document.body.childNodes[0]);

		// Initial scrolling position of the window. To be used for remove scrolling effect during ModalBox appearing
		this.initScrollX = window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft;
		this.initScrollY = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;

		//Adding event observers
		this.hide = this.hide.bindAsEventListener(this);
		this.close = this._hide.bindAsEventListener(this);
		this.kbdHandler = this.kbdHandler.bindAsEventListener(this);
		this._initObservers();
		this.initialized = true; // Mark as initialized
		this.active = true; // Mark as active
		new Draggable('MB_window');
	},

	show: function(content, options) {
		if(!this.initialized) this._init(options); // Check for is already initialized

		this.content = content;
		this.setOptions(options);

		Element.update(this.MBcaption, this.options.title); // Updating title of the MB

		if(this.MBwindow.style.display == "none") { // First modal box appearing
			this._appear();
			this.event("onShow"); // Passing onShow callback
		}
		else { // If MB already on the screen, update it
			this._update();
			this.event("onUpdate"); // Passing onUpdate callback
		}
		if(this.options.closedelay != 0)
		{
			this.hide.delay(this.options.closedelay).bind(this);
		}
	},

	hide: function(options) { // External hide method to use from external HTML and JS
		if(this.initialized) {
			if(options) Object.extend(this.options, options); // Passing callbacks
			Effect.SlideUp(this.MBwindow, { duration: this.options.slideUpDuration, afterFinish: this._deinit.bind(this) } );
		} else throw("Modalbox isn't initialized");
	},

	_hide: function(event) { // Internal hide method to use inside MB class
		if(event) Event.stop(event);
		this.hide();
	},

	fastOrder: function() {
		var action = '/xhr/basket.php';
		new Ajax.Request(action,{
			parameters: Form.serializeElements($('BasketFormID').getInputs()),
			method: "post",
			onSuccess : function (transport)
			{
				this._insertContent(transport.responseText);
			}.bind(this),
			//update Basket summary
			onComplete: function() {
				new Ajax.Updater('BasketSummary',action,{method:'post',parameters: {bact: 'bskupdate'}});
				$('BasketSummary').setStyle({ backgroundColor: null });
				this.hide.delay(2.5).bind(this);
			}.bind(this)
		});

		return false;
	},

	_appear: function() { // First appearing of MB
		this._toggleSelects();
		this._setOverlay();
		this._setWidth();
		this._setPosition();
		this.MBoverlay.show();
		new Effect.Fade(this.MBoverlay, {
				from: 0,
				to: this.options.overlayOpacity,
				duration: 0,
				afterFinish: function() {
					new Effect.Appear(this.MBwindow, {
						duration:0,
						afterFinish: function(){
							this._setPosition();
							this.loadContent();
						}.bind(this)
					});
				}.bind(this)
		});

		this._setWidthAndPosition = this._setWidthAndPosition.bindAsEventListener(this);
		Event.observe(window, "resize", this._setWidthAndPosition);
	},

	resize: function(byWidth, byHeight, options) { // Change size of MB without loading content
		var wHeight = Element.getHeight(this.MBwindow);
		var hHeight = Element.getHeight(this.MBheader);
		var cHeight = Element.getHeight(this.MBcontent);
		var newHeight = ((wHeight - hHeight + byHeight) < cHeight) ? (cHeight + hHeight - wHeight) : byHeight;
		this.setOptions(options); // Passing callbacks
		new Effect.ScaleBy(this.MBwindow, byWidth, newHeight, {
				duration: this.options.resizeDuration,
			  	afterFinish: function() { this.event("afterResize") }.bind(this) // Passing callback
			});
	},

	_update: function() { // Updating MB in case of wizards
		this.currentDims = [this.MBwindow.offsetWidth, this.MBwindow.offsetHeight];
		if((this.options.width + 10 != this.currentDims[0]) || (this.options.height + 5 != this.currentDims[1]))
			new Effect.ScaleBy(this.MBwindow,
				(this.options.width - this.currentDims[0]), //New width calculation
				(this.options.height - this.currentDims[1]), //New height calculation
			{
				duration: this.options.resizeDuration,
				afterFinish: this._loadAfterResize.bind(this),
				beforeStart: function(effect) {
					Element.update(this.MBcontent, "");
					this.MBcontent.appendChild(this.MBloading);
					Element.update(this.MBloading, this.options.loadingString);
				}.bind(this)
			});
		else {
			Element.update(this.MBcontent, "");
			this.MBcontent.appendChild(this.MBloading);
			Element.update(this.MBloading, this.options.loadingString);
			this._loadAfterResize();
		}
	},

	loadContent: function () {
		if(this.event("beforeLoad") != false) { // If callback passed false, skip loading of the content
			if(typeof this.content == 'string') {

				var htmlRegExp = new RegExp(/<\/?[^>]+>/gi);
				if(htmlRegExp.test(this.content)) // Plain HTML given as a parameter
					this._insertContent(this.content);

				else new Ajax.Request( this.content, { method: this.options.method.toLowerCase(), parameters: this.options.params,
						onComplete: function(transport) {
							var response = new String(transport.responseText);
							response.extractScripts().map(function(script) {
								return eval(script.replace("<!--", "").replace("// -->", ""));
							}.bind(window));
							this._insertContent(transport.responseText.stripScripts());
						}.bind(this)
					});

			} else if (typeof this.content == 'object') {// HTML Object is given
				this._insertContent(this.content);
			} else {
				Modalbox.hide();
				throw('Please specify correct URL or HTML element (plain HTML or object)');
			}
		}
	},

	_insertContent: function(content){
		Element.extend(this.MBcontent);
		this.MBcontent.update("");
		if(typeof content == 'string') {
			//this.MBcontent.hide()
			//if(console.log){console.log(this.MBcontent.getHeight())};
			this.MBcontent.hide().update(content);
			//if(console.log){console.log(this.MBcontent.getHeight())};
			//this.MBcontent.show();
		}
		else if (typeof this.content == 'object') { // HTML Object is given
			var _htmlObj = content.cloneNode(true); // If node already a part of DOM we'll clone it
			if(this.content.id) _htmlObj.id = "MB_" + _htmlObj.id; // If clonable element has ID attribute defined, modifying it to prevent duplicates
			this.MBcontent.hide().appendChild(_htmlObj);
			this.MBcontent.down().show(); // Toggle visibility for hidden nodes
		}
		// Prepare and resize modal box for content
		if(this.options.height == this._options.height)
			Modalbox.resize(0, this.MBcontent.getHeight() - Element.getHeight(this.MBwindow) + Element.getHeight(this.MBheader), {
				afterResize: function(){
					this.MBcontent.show();
					this.focusableElements = this._findFocusableElements();
					//this._moveFocus(); // Setting focus on first 'focusable' element in content (input, select, textarea, link or button)
					this.event("afterLoad"); // Passing callback
				}.bind(this)
			});
		else { // Height is defined. Creating a scrollable window
			this._setWidth();
			this.MBcontent.setStyle({overflow: 'auto', height: Element.getHeight(this.MBwindow) - Element.getHeight(this.MBheader) - 13 + 'px'});
			this.MBcontent.show();
			this.focusableElements = this._findFocusableElements();
			//this._moveFocus(); // Setting focus on first 'focusable' element in content (input, select, textarea, link or button)
			this.event("afterLoad"); // Passing callback
		}

	},

	activate: function(options){
		this.setOptions(options);
		this.active = true;
		Event.observe(this.MBclose, "click", this.close);
		if(this.options.overlayClose) Event.observe(this.MBoverlay, "click", this.hide);
		Element.show(this.MBclose);
		if(this.options.inactiveFade) new Effect.Appear(this.MBwindow, {duration: this.options.slideUpDuration});
	},

	deactivate: function(options) {
		this.setOptions(options);
		this.active = false;
		Event.stopObserving(this.MBclose, "click", this.close);
		if(this.options.overlayClose) Event.stopObserving(this.MBoverlay, "click", this.hide);
		Element.hide(this.MBclose);
		if(this.options.inactiveFade) new Effect.Fade(this.MBwindow, {duration: this.options.slideUpDuration, to: .75});
	},

	_initObservers: function(){
		Event.observe(this.MBclose, "click", this.close);
		if(this.options.overlayClose) Event.observe(this.MBoverlay, "click", this.hide);
		Event.observe(document, "keypress", Modalbox.kbdHandler );
	},

	_removeObservers: function(){
		Event.stopObserving(this.MBclose, "click", this.close);
		if(this.options.overlayClose) Event.stopObserving(this.MBoverlay, "click", this.hide);
		Event.stopObserving(document, "keypress", Modalbox.kbdHandler );
	},

	_loadAfterResize: function() {
		this._setWidth();
		this._setPosition();
		this.loadContent();
	},

	_moveFocus: function() { // Setting focus to be looped inside current MB
		if(this.focusableElements.length > 0)
			this.focusableElements.first().focus(); // Focus on first focusable element except close button
		//else
			//$("MB_close").focus(); // If no focusable elements exist focus on close button
	},

	_findFocusableElements: function(){ // Collect form elements or links from MB content
		return $A($("MB_content").descendants()).findAll(function(node){
			return (["INPUT", "TEXTAREA", "SELECT", "BUTTON"].include(node.tagName));
		});
	},

	kbdHandler: function(e) {
		var node = Event.element(e);
		switch(e.keyCode) {
			case Event.KEY_TAB:
				if(Event.element(e) == this.focusableElements.last()) {
					Event.stop(e);
					//this._moveFocus();  // Find last element in MB to handle event on it. If no elements found, uses close ModalBox button
				}
			break;
			case Event.KEY_ESC:
				if(this.active) this._hide(e);
			break;
			case 32:
				this._preventScroll(e);
			break;
			case 0: // For Gecko browsers compatibility
				if(e.which == 32) this._preventScroll(e);
			break;
			case Event.KEY_UP:
			case Event.KEY_DOWN:
			case Event.KEY_PAGEDOWN:
			case Event.KEY_PAGEUP:
			case Event.KEY_HOME:
			case Event.KEY_END:
				// Safari operates in slightly different way. This realization is still buggy in Safari.
				if(/Safari|KHTML/.test(navigator.userAgent) && !["textarea", "select"].include(node.tagName.toLowerCase()))
					Event.stop(e);
				else if( (node.tagName.toLowerCase() == "input" && ["submit", "button"].include(node.type)) || (node.tagName.toLowerCase() == "a") )
					Event.stop(e);
			break;
		}
	},

	_preventScroll: function(event) { // Disabling scrolling by "space" key
		if(!["input", "textarea", "select", "button"].include(Event.element(event).tagName.toLowerCase()))
			Event.stop(event);
	},

	_deinit: function()
	{
		this._toggleSelects(); // Toggle back 'select' elements in IE
		this._removeObservers();
		Event.stopObserving(window, "resize", this._setWidthAndPosition );
		Effect.toggle(this.MBoverlay, 'appear', {duration: this.options.overlayDuration, afterFinish: this._removeElements.bind(this) });
		Element.setStyle(this.MBcontent, {overflow: '', height: ''});
	},

	_removeElements: function () {
		if (navigator.appVersion.match(/\bMSIE\b/)) {
			this._prepareIE("", ""); // If set to auto MSIE will show horizontal scrolling
			window.scrollTo(this.initScrollX, this.initScrollY);
		}
		Element.remove(this.MBoverlay);
		Element.remove(this.MBwindow);
		this.initialized = false;
		this.event("afterHide"); // Passing afterHide callback
		this.setOptions(this._options); //Settings options object into intial state
	},

	_setOverlay: function () {
		if (navigator.appVersion.match(/\bMSIE\b/)) {
			this._prepareIE("100%", "hidden");
			if (!navigator.appVersion.match(/\b7.0\b/)) window.scrollTo(0,0); // Disable scrolling on top for IE7
		}
	},

	_setWidth: function () { //Set size
		Element.setStyle(this.MBwindow, {width: this.options.width + "px", height: this.options.height + "px"});
	},

	_setPosition: function () {
		this.MBwindow.style.left = Math.round((Element.getWidth(document.body) - Element.getWidth(this.MBwindow)) / 2 ) + "px";
	},

	_setWidthAndPosition: function () {
		this._setWidth();
		this._setPosition();
	},

	_getScrollTop: function () { //From: http://www.quirksmode.org/js/doctypes.html
		var theTop;
		if (document.documentElement && document.documentElement.scrollTop)
			theTop = document.documentElement.scrollTop;
		else if (document.body)
			theTop = document.body.scrollTop;
		return theTop;
	},
	// For IE browsers -- IE requires height to 100% and overflow hidden (taken from lightbox)
	_prepareIE: function(height, overflow){
		var body = document.getElementsByTagName('body')[0];
		body.style.height = height;
		body.style.overflow = overflow;

		var html = document.getElementsByTagName('html')[0];
		html.style.height = height;
		html.style.overflow = overflow;
	},
	// For IE browsers -- hiding all SELECT elements
	_toggleSelects: function() {
		if (navigator.appVersion.match(/\bMSIE\b/))
			$$("select").each( function(select) {
				select.style.visibility = (select.style.visibility == "") ? "hidden" : "";
			});
	},
	event: function(eventName) {
		if(this.options[eventName]) {
			var returnValue = this.options[eventName](); // Executing callback
			this.options[eventName] = null; // Removing callback after execution
			if(returnValue != undefined)
				return returnValue;
			else
				return true;
		}
		return true;
	}
}

Object.extend(Modalbox, Modalbox.Methods);

Effect.ScaleBy = Class.create();
Object.extend(Object.extend(Effect.ScaleBy.prototype, Effect.Base.prototype), {
	initialize: function(element, byWidth, byHeight, options) {
			this.element = $(element)
			var options = Object.extend({
			scaleFromTop: true,
			scaleMode: 'box',        // 'box' or 'contents' or {} with provided values
			scaleByWidth: byWidth,
			scaleByHeight: byHeight
		}, arguments[3] || {});
		this.start(options);
	},

	setup: function() {
		this.elementPositioning = this.element.getStyle('position');

		this.originalTop  = this.element.offsetTop;
		this.originalLeft = this.element.offsetLeft;

		this.dims = null;
		if(this.options.scaleMode=='box')
			this.dims = [this.element.offsetHeight, this.element.offsetWidth];
		if(/^content/.test(this.options.scaleMode))
			this.dims = [this.element.scrollHeight, this.element.scrollWidth];
		if(!this.dims)
			this.dims = [this.options.scaleMode.originalHeight,
						 this.options.scaleMode.originalWidth];

		this.deltaY = this.options.scaleByHeight;
		this.deltaX = this.options.scaleByWidth;
	},

	update: function(position) {
		var currentHeight = this.dims[0] + (this.deltaY * position);
		var currentWidth = this.dims[1] + (this.deltaX * position);
		this.setDimensions(currentHeight, currentWidth);
	},

	setDimensions: function(height, width) {
		var d = {};
		d.width = width + 'px';
		d.height = height + 'px';

		var topd  = Math.round((height - this.dims[0])/2);
		var leftd = Math.round((width  - this.dims[1])/2);
		if(this.elementPositioning == 'absolute' || this.elementPositioning == 'fixed') {
			if(!this.options.scaleFromTop) d.top = this.originalTop-topd + 'px';
			d.left = this.originalLeft-leftd + 'px';
		} else {
			if(!this.options.scaleFromTop) d.top = -topd + 'px';
				d.left = -leftd + 'px';
		}
		this.element.setStyle(d);
	}
});

