/**
 * @author Damiano Seno
 * Unobtrusive logic for pages 
 */

Object.extend( Object, {
	isPlain: function( variable ) { // lets you know if an object is simply a hash
		return Object.isHash( variable ) || (variable.constructor.toString() == {}.constructor.toString());
	},
	dump: function( variable ) {	
		if ( typeof( variable ) == 'undefined' || Object.isUndefined( variable ) ) return 'undefined';
		if ( Object.isArray( variable ) || Object.isString( variable ) ) return variable.inspect();
		if ( Object.isElement( $(variable) ) ) return Object.toHTML( $(variable) );
		if ( Object.isPlain( variable ) ) { // if plain object, json format is more readable 
			return Object.toJSON( variable );
		}
		// if object can be instantiated, call prototype's inspect
		return Object.inspect( variable );
	},
	parse: function( str, delimeter ) { // lets you dump an object
		var result = {};
		var params = str.split( delimeter );
		params.each(
			function( param ) {
				var paramArr = param.split( '=' );
				if ( paramArr.length > 1 ) {
					result[ paramArr[ 0 ] ] = paramArr[ 1 ];
				}
			}
		);
		return result;
	},
	defined: function( variable ) {
		if ( Object.isString( variable ) ) {
			var def = true;
			var val = null;
			try {
				def = (val = eval( variable )) && !Object.isUndefined( val );
			} catch ( e ) {
				def = false;
			}
			return def;
		} else {
			return !Object.isUndefined( variable );
		}
	}
  }
);
 
var Unobtrusive = {
	params: {
		lang: 'it' // default language
	},
	init: function() {
		var js = /unobtrusive\.js\?(.*)$/;
		$$('head script[src]').findAll( function(s) {
		     return s.src.match( js );
		 }
		).each( function( s ) {
		    var params = s.src.match( js );
		    if ( params && params.length ) {
		    	Unobtrusive.params = Object.extend( Unobtrusive.params || {}, Object.parse( params[ 1 ], '&' ) );
		    }
		  }
		);
	},
	errors: {
		it: {
			xhr: 'C\'è stato un problema durante il caricamento di #{link}. Risolveremo il problema il prima possibile',
			forbidden: 'Sessione scaduta. Effettuare nuovamente il login.'
		}
	},
	_handleThumbs: function( isOnLoading ) {
		if ( isOnLoading ) {
			Unobtrusive.Modal.init( {
			   language: Unobtrusive.params.lang,
			   players: [ 'html', 'img' ],
			   useSizzle: false // we don't need sizzle
			 }
			);		
		}
		
		var links = $$( 'a.image-thumb' ).nonObserved();
		links.each(
			function( linkToThumb ) {
				var galleryTitle = 'Gallery', container = null, titleElement = null;
				if ( container = linkToThumb.up( '.thumb-gallery' ) ) {
					if ( titleElement = container.down( '.gallery-title' ) ) {
						switch( titleElement.tagName.toLowerCase() ) {
							case 'a':
								galleryTitle = titleElement.readAttribute( 'title' );
								break;
							case 'img':
								galleryTitle = titleElement.readAttribute( 'alt' );
								break;
							case 'div':
							case 'span':
							case 'h6':							
							case 'h5':
							case 'h4':							
							case 'h3':
							case 'h2':
							case 'h1':
							case 'td':
							case 'th':
								galleryTitle = titleElement.innerHTML;
								break;			
						}
					} else {
						galleryTitle = 'Gallery_' + container.identify();
					}
				}
				Unobtrusive.Modal.markAsTrigger( linkToThumb, { gallery: galleryTitle } );
				// if no modal window engine available, ensure at least that original image will open in another tab
				if ( !Unobtrusive.Modal.available() ) {
					linkToThumb.writeAttribute( 'target', '_blank' );	
				}
			}
		);

		var sbOpts = {
			players: [ 'img' ],
		       onOpen: function( obj ) {
				   	var gallery = null, galleryInfo = null;
				    if ( !(galleryInfo = $( 'sb-gallery-info' )) ) {
				    	galleryInfo = new Element( 'div', { id: 'sb-gallery-info' } );
				    	galleryInfo.setStyle( { textAlign: 'center' } );
						$( 'sb-wrapper' ).insert( galleryInfo );
					}
				   
				   if ( !(gallery = Url.decode( obj.gallery ).strip()).match( new RegExp( '^Gallery' ) ) ) { // if no default gallery
					   galleryInfo.update( Url.decode( gallery ) ); // decode gallery name (shadowbox encodes it)
				   } else { // if default gallery, do not display gallery info
					   galleryInfo.update( '' );
				   }
			   }
		}
		
		if ( Unobtrusive.Modal.available() ) {
			if ( isOnLoading ) {
				Event.observe( window, 'load', function() {
					links.length && Unobtrusive.Modal.setup( links, sbOpts );				
				  }
				);
			} else {
				Unobtrusive.Modal.clearCache(); // clear cache
				links.length && Unobtrusive.Modal.setup( links, sbOpts );				
			}
		}
	},
	addExtensions: function() {
		Element.addMethods( {
  			isObserved: function( element ){
     			return ( '_prototypeEventID' in $(element) );
			},
			toggleLoading: function( element, cssClass ) {
				var element = $( element );
				var overlay = null; 
				if ( !(overlay = element.down( 'div.loading-overlay' )) ) {
					overlay = new Element( 'div' );
					if ( !Object.isUndefined( cssClass ) ) {
						overlay.addClassName( cssClass );
					}
					overlay.addClassName( 'loading-overlay' );
					overlay.setStyle( { position: 'absolute', opacity: 0.7, width: '100%', height: '100%', display: 'none', top: '0px', left: '0px' } );
					element.setStyle( { position: 'relative' } );
					element.insert( { top: overlay } );
				}
				if ( overlay.visible() ) {
					overlay.hide();
				} else {
					overlay.show();
				}
			},
			getPage: function( element ) {
				var page = element.up( 'div.page' ); // gets closest page container 
				if ( rel = element.getAttribute( 'rel' ) ) {
					page = $( rel ) || page;
				}
				return page;
			},
			notifyInvalid: Unobtrusive.Form.notifyInvalid,
			add: function( element, callback ) {
				if ( element.tagName.toLowerCase() != 'form' ) return; // only for forms
				Unobtrusive.Form.Queue.add( callback );
			},
			dispatch: function( element, callback ) {
				if ( element.tagName.toLowerCase() != 'form' ) return; // only for forms
				document.observe( 'queue:dispatched', callback ); 
				Unobtrusive.Form.Queue.next();
			}
	      }
		);
		Array.prototype.nonObserved = function() {
			return this.findAll(
				function( el ) {
					return !el.isObserved();
				} 
			);	
		}
	},
	handleGoUps: function() {
		$$( 'a.go-up' ).nonObserved().invoke( 'observe', 'click', function( ev ) { 
			 var href = null;
			 var element = document.body;
			 if ( href = ev.element().getAttribute( 'href' ) ) {
			 	if ( (href = href.split( '#' )).length ) {
			 		element = $( href[ 1 ] ) || element;
			 	}	
			 }
			 ev.stop();
			 Effect.ScrollTo( element );
		  }  
		);
	},
	handleGoBacks: function() {
		$$( 'a.go-back' ).nonObserved().invoke( 'observe', 'click', function( ev ) { 
			ev.stop();
			history.back();
		  }  
		);		
	},
	handlePrint: function() {
		$$( 'a.printer' ).nonObserved().invoke( 'observe', 'click', function( ev ) { 
			ev.stop();
			window.print();
		  }
		);
	},
	handleExternalLinks: function() {
		$$( 'a.external' ).nonObserved().invoke( 'observe', 'click', function( ev ) {
			ev.stop();
			window.open( ev.element().getAttribute( 'href' ) );
		  }
		);
	},
	handlePageUpdaters: function() {
		$$( 'a.page-updater', 'div.page-updaters-container a' ).nonObserved().invoke( 'observe', 'click', function( ev ) {			
			var link = ev.element().up( 'a' ) || ev.element();
			ev.stop();			  
			var page = link.getPage();
			if ( link.hasClassName( 'if-empty' ) ) {
				if ( page.innerHTML.strip() ) {
					page.show();
					return;
				}
			}
			
			page.up().toggleLoading(); // ,let's make page's parentNode loading
			new Ajax.Updater( // updates page with content from server
				page,
				link.getAttribute( 'href' ), {
					 method: 'get',
			  		 evalScripts: true,
			  		 onFailure: function() {
			  		 	Unobtrusive.alert500( link );
			  		 },
			  		 onComplete: function( transport ) {
						switch ( transport.status ) {
							case 403: 
								Unobtrusive.alert403();
								window.setTimeout( function() {
									document.location = (transport.getHeader( 'X-Message' )? Url.decode( transport.getHeader( 'X-Message' ) ) : '/');
								  }, 5000
								);
								break;
						}
			  		 	this.up().toggleLoading();
			  		 	Effect.ScrollTo( this );									  		 	
			  		 	window.setTimeout( function() { document.fire( 'dom:updated' ) }, 1000 );
			  		 }.bind( page )
			  	}
			  );
		   }
		);
	},
	handlePageDisposers: function() {
		$$( 'a.page-disposer' ).nonObserved().invoke( 'observe', 'click', function ( ev ) {
			 ev.stop();
			 ev.element().getPage().hide();
		  } 
		);
	},
	handleSubmitters: function() {
		$$( 'a.submitter' ).nonObserved().invoke( 'observe', 'click', function( ev ) {
			ev.stop();
			var link = ev.element().up( 'a' ) || ev.element();
			var page = link.getPage();
			var form = page.down( 'form' );
			// is this not an ajax form, just do submit
			if ( !form.hasClassName( 'ajaxable' ) ) {
				form.writeAttribute( 'action', link.readAttribute( 'href' ) );
				form.simulate( 'submit' );	// dispatch onsubmit observers and do submit
				form.submit();
			} else { // else do an ajax submit
				page.up().toggleLoading();
				form.simulate( 'submit' ); // dispatch onsubmit observers
				new Ajax.Request(
					link.readAttribute( 'href' ), {
						method: form.readAttribute( 'method' ),
						parameters: Object.extend( form.serialize( { hash: true } ), { format: 'json' } ),
						onError: function() { Unobtrusive.alert500( form.action ) },
						onComplete: function( transport ) {
							// forbidden page? return to homepage
							if ( transport.status == 403 ) {
								Unobtrusive.alert403();
								document.location = '/';
								return;	
							}
							this.up().toggleLoading();
							if ( transport.responseJSON ) { // errors occured
								form.notifyInvalid( transport.responseJSON );
							} else { // it's ok
								this.update( transport.responseText );
								document.fire( 'dom:updated' );
								window.onbeforeunload = function() {}; // reset beforeunload callback
							}
						}.bind( page )
					}
				);				
			}
		 }
		);
	},
	handleAjaxableForms: function() {
		$$( 'form.ajaxable input[type=submit]' ).invoke( 'hide' );
		$$( 'form.ajaxable' ).invoke( 'observe', 'submit', function( ev ) {
			ev.stop(); // do not let ajaxable form submit (we submit via xhr post)
		  } 
		);
	},
	handleAutoLoaders: function() {
		$$( 'a.auto-loader' ).each(
			function( link ) {
				if ( link.hasClassName( 'done' ) ) return;
				var rel = null;
				if ( (rel = link.getAttribute( 'rel' )) && $( rel ) ) {
					link.hide();
					new Ajax.Updater(
						$( rel ),
						link.getAttribute( 'href' ), {
							method: 'get',
							evalScripts: true,
							onComplete: function( transport ) {
								if ( this.hasClassName( 'use-editor' ) ) {
									Object.defined( 'tinyMCE' ) && tinymce_force_parsing();
								}
								link.addClassName( 'done' );
								document.fire( 'dom:updated' );
							}.bind( link )
					}
					);
				}
			}
		);		
	},
	handleMapLinks: function() {
		$$( 'a.map-link' ).nonObserved().invoke( 'observe', 'click', function( ev ) {
			 ev.stop();
			 var link = ev.element().up( 'a' ) || ev.element(), rel = null;
			 if ( (rel = link.getAttribute( 'rel' ))  ) {
			 	// rel must be a callback
			 	var isFun = false;
			 	try {
			 		var callback = eval( rel );
			 		isFun = Object.isFunction( callback );
			 		isFun && Unobtrusive.Map.display( callback.call( window ) );
			 	} catch ( e ) {
			 		// in case callback is undefined
			 	}
			 } 
		  }
		);
	},
	handleAutoCompletePrevention: function() {
		var forms = document.getElementsByTagName( 'form' );
		if ( !forms || forms.length == 0 ) return;
		
		for ( var i = 0; i < forms.length; i++ ) {
			var iform = forms[ i ];
			iform.setAttribute( 'autocomplete', 'off' );
		}		
	},
	alert500: function( link ) {
		var l = this.params.lang;
		this.alert( { title: 'Ooops', content: '<div>' + (new Template( this.errors[ l ].xhr ).evaluate( { link: link } )) + '</div>' } );
	},
	alert403: function( link ) {
		var l = this.params.lang;
		this.alert( { title: 'Attenzione', content: '<div>' + (this.errors[ l ].forbidden) + '</div>' } );
	},
	alert: function( data ) {
		Unobtrusive.Modal.open( Object.extend( {
			player: 'html',
			width: 300,
			height: 100
		  }, data )
		);		
	},
	Form: { // what about using js templates here?
		notifyInvalid: function( form, errors ) {
			$A( form.elements ).each( function( el ) {
				var field = el.up( '.field' );
				var elementKey = el.readAttribute( 'name' );
				var errorsLayer = field.down( '.errors' );

				if ( errors[ elementKey ] ) { // element with errors
					el.addClassName( 'error' );
				} else {
					el.removeClassName( 'error' );
					if ( errorsLayer ) {
						errorsLayer.remove();
					}
					return; // skip to next element
				}
				
				if ( !errorsLayer ) {
					var errorsLayer = new Element( 'div' );
					errorsLayer.addClassName( 'errors' );
					errorsLayer.setStyle( { display: 'none' } );
					errorsLayer.insert( new Element( 'ul' ) );
					field.insert( errorsLayer );
				}
				
				var ul = errorsLayer.down();
				ul.update( '' );
				
				Object.values( errors[ elementKey ] ).each( function( message ) {
					var li = new Element( 'li' );
					li.update( message );
					ul.insert( li );
				  }
				);
				errorsLayer.appear( { duration: .5 } );
			  }
			);
		},
		Queue: { // queue of async callbacks to load before submitting a form
			current: 0,
			callbacks: [],
			add: function( callback ) {
				this.callbacks.push( callback );
			},
			next: function() { // dispatches next element in the queue
				if ( this.current == this.callbacks.length ) {
					document.fire( 'queue:dispatched' );
				} else {
					this.callbacks[ this.current ].call( window ); // execute current callback
					this.current++; // go to the next callback
				}
			}
		}
	},
	Modal: { // modal adapter
		init: function( params ) { Shadowbox.init( params ); },
		open: function( params ) { Shadowbox.open( params ) },
		close: function() { Shadowbox.close(); },
		setup: function( links, params ) { Shadowbox.setup( links, params ); },
		clearCache: function() { Shadowbox.clearCache() },
		getContent: function() {
			return $( Shadowbox.contentId() );
		},
		markAsTrigger: function( el, opts ) {
			el.writeAttribute( 'rel', 'shadowbox[ ' + opts.gallery + ' ]' );
		},
		available: function() {
			return !!typeof( Shadowbox );
		}
	},
	Map: {
		key: null, /* get it from querystring */
		display: function( params ) {
			if ( typeof GMap2 == 'undefined' ) {
				document.observe( 'gmaps:loaded', Unobtrusive.Map._display.bind( window, params ) );
				Unobtrusive.Map._include();
			} else {
				Unobtrusive.Map._display( params );
			}
		},
		_display: function( params ) {
			if ( !GBrowserIsCompatible() ) {
				return;
			}
			
			if ( params.geoCode ) { // if address info is avaiable, do geo-coding
				var geoCoder = new GClientGeocoder();
				geoCoder.getLatLng( params.geoCode.strip(), function( point ) {
					Unobtrusive.Map._draw( point, this );
				  }.bind( params )
				);
			} else { // otherwise draw at specified coordinates
				Unobtrusive.Map._draw( new GLatLng( params.lat, params.lng ), params );
			}
		},
		_draw: function( point, params ) {
			Unobtrusive.Modal.open( {
				content: '',
				player: 'html',
				width: 500,
				height: 400,
				options: {
					onFinish: function( item ) {
						var map = new GMap2( Unobtrusive.Modal.getContent() );
						map.setCenter( point, 14 );					
						map.addControl(new GSmallMapControl());
	                    map.addControl(new GMapTypeControl());
	                    
						var place = new GMarker( point );
						map.addOverlay( place );
						
						if ( params.icon ) { // if an icon is available
							place.setImage( params.icon );
						}
						
						if ( params.content ) { // if a content is available
							place.bindInfoWindowHtml( params.content ); // bind info window
							GEvent.trigger( place, 'click' ); // show info window
						}
					}
				  }
			   }
			);
		},
		_include: function() { // lets you include google api main script
			Includer.include( 'http://www.google.com/jsapi?key=' + Unobtrusive.params.key + '&callback=Unobtrusive.Map._include2' );
		},
		_include2: function() {
			google.load( "maps", "2", { "callback" : "Unobtrusive.Map._loaded" } );
		},
		_loaded: function() {
			document.fire( 'gmaps:loaded' );
		}
	},
	start: function() {
		Unobtrusive.init();
		Unobtrusive.addExtensions();
		document.observe( 
			'dom:loaded', 
			function() { with( Unobtrusive ) { Try.anyway( [ _handleThumbs.bind( window, 1 ) ].concat( findHandlers() ) ); } } 
		);
		document.observe( 
			'dom:updated', 
			function() { with( Unobtrusive ) { Try.anyway( [ _handleThumbs.bind( window, 0 ) ].concat( findHandlers() ) ); } },
			Prototype.Browser.WebKit // use-capture if webkit (only god knows why)
		);
	},
	findHandlers: function() {
		return Object.keys( Unobtrusive ).findAll(
			function( key ) {
				return !!key.match( new RegExp( '^handle' ) ) && Object.isFunction( Unobtrusive[ key ] );
			}
		).collect(
			function( k ) { return Unobtrusive[ k ]; }
		) ;
	}
}

// let's start the unobtrusive engine
Unobtrusive.start();

// lets you execute a list of functions even if an error occures
Try.anyway = function( funs ) {
	funs.each(
		function( fun ) {
			try { fun(); } catch ( e ) {/* avoid error propagation */ }
		}
	);
}

var Includer = {
	include: function( urls ) {
		if ( !Object.isArray( urls ) ) {
			urls = [ urls ];
		}
		if ( document.body ) { // page has already loaded
			// append to body element.. IE 6 crashes otherwise
			for ( var i = 0; i < urls.length; i++ ) {
				var script = document.createElement( 'script' );
				script.setAttribute( 'type', 'text/javascript' );
				script.setAttribute( 'src', urls[ i ] );
				document.body.appendChild( script );				
			}	
		} else {
			// if no document.body (page is loading) writes tag directly
			for ( var i = 0; i < urls.length; i++ ) {
				document.write( '<script type="text/javascript" src="' + urls[ i ] + '"></script>' );
			}
		}
	},
	require: function( url, callback ) {
		new Ajax.Request(
			url, {
				method: 'get',
				evalScripts: true,
				onComplete: function( transport ) {
					callback.call( window );
				}
			}
		);
	},
	includeCSS: function( file ) {
		try {
			var link = document.createElement( 'link' );
			link.setAttribute( 'href', file );
			link.setAttribute( 'type', 'text/css' );
			link.setAttribute( 'rel', 'stylesheet' );
			document.getElementsByTagName( 'head' )[ 0 ].appendChild( link );
			return link;
		} catch( e ) {
			return false;
		}
	}
};

/**
*
*  URL encode / decode
*  http://www.webtoolkit.info/
*
**/
var Url = {
	// public method for url encoding
	encode : function (string) {
		return escape(this._utf8_encode(string));
	},
 
	// public method for url decoding
	decode : function (string) {
		return this._utf8_decode(unescape(string));
	},
 
	// private method for UTF-8 encoding
	_utf8_encode : function (string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";
 
		for (var n = 0; n < string.length; n++) {
 
			var c = string.charCodeAt(n);
 
			if (c < 128) {
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}
 
		}
 
		return utftext;
	},
 
	// private method for UTF-8 decoding
	_utf8_decode : function (utftext) {
		var string = "";
		var i = 0;
		var c = c1 = c2 = 0;
		while ( i < utftext.length ) {
			c = utftext.charCodeAt(i);
			if (c < 128) {
				string += String.fromCharCode(c);
				i++;
			}
			else if((c > 191) && (c < 224)) {
				c2 = utftext.charCodeAt(i+1);
				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
				i += 2;
			}
			else {
				c2 = utftext.charCodeAt(i+1);
				c3 = utftext.charCodeAt(i+2);
				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
				i += 3;
			}
 
		}
 
		return string;
	}
}

