/**
 * The YACS AJAX shared library.
 *
 * This file extends prototype, etc., to enhance interactions with the end-user
 *
 * @see users/heartbit.php
 * @see users/notifications.php
 * @see users/view.php
 *
 * @author Bernard Paques [email]bernard.paques@bigfoot.com[/email]
 * @reference
 * @license http://www.gnu.org/copyleft/lesser.txt GNU Lesser General Public License
 */

var Yacs = {

	/**
	 * create a Google marker
	 */
	googleCreateMarker: function(point, markerType, html) {
		var marker = new GMarker(point, Yacs.googleCustomIcons[markerType]);
		GEvent.addListener(marker, 'click', function() {
			marker.openInfoWindowHtml(html);
			});
		return marker;
	},

	/**
	 * the default icons used in YACS to interface with Google Maps
	 */
	googleCustomIcons: [],

	/**
	 * we have received an 'alert' notification
	 *
	 * @see users/notifications.php
	 */
	handleAlertNotification: function(response) {

		// build a message in English if no localized message has been provided by the server
		if(typeof response['dialog_text'] != 'string')
			response['dialog_text'] = 'New at ' + response['title'] + "\nby:" + response['nick_name'] + "\n" + 'Would you like to browse this page?';

		// switch to the offered address, if accepted by surfer
		if(typeof response['address'] == 'string') {
			if(confirm(response['dialog_text'])) {
				window.open(response['address']);
			}
		}
	},

	/**
	 * we have received a 'browse' notification
	 *
	 * @see users/notifications.php
	 */
	handleBrowseNotification: function(response) {

		// build a message in English if no localized message has been provided by the server
		if(typeof response['dialog_text'] != 'string')
			response['dialog_text'] = 'From ' + response['nick_name'] + ':' + "\n" + response['message'] + "\n" + 'Would you like to browse the provided link?';

		// switch to the offered address, if already in tracker, or if accepted by surfer
		if(typeof response['address'] == 'string') {
			if((window.name == 'yacs_tracker') || confirm(response['dialog_text'])) {
				window.open(response['address'], 'yacs_tracker');
			}
		}
	},

	/**
	 * we have received a 'hello' notification
	 *
	 * @see users/notifications.php
	 */
	handleHelloNotification: function(response) {

		// build a message in English if no localized message has been provided by the server
		if(typeof response['dialog_text'] != 'string')
			response['dialog_text'] = 'From ' + response['nick_name'] + ':' + "\n" + response['message'] + "\n" + 'Would you like to chat with this person?';

		// only show the message to the end-user
		if(typeof response['address'] != 'string') {
			alert(response['dialog_text']);

		// else switch to the offered address, if accepted by surfer
		} else {
			if(confirm(response['dialog_text'])) {
				window.open(response['address']);
			}
		}
	},

	/**
	 * load some opaque overlay during back-end processing
	 */
	isWorking: function() {

		// insert some html at the bottom of the page that looks similar to this:
		//
		// <div id="yacsOverlay">
		//	<div>
		//		<img src="/yacs/skins/_reference/ajax_working.gif"/>
		//	</div>
		// </div>

		var objWorkingImage = document.createElement("img");
		objWorkingImage.setAttribute('src', url_to_root + 'skins/_reference/ajax_working.gif');

		var objCentered = document.createElement("div");
		Element.setStyle(objCentered, { position: 'absolute', top: '30%', left: '0%', height: '25%', width: '100%', textAlign: 'center', lineHeight: '0' });
		objCentered.appendChild(objWorkingImage);

		Yacs.workingOverlay = document.createElement("div");
		Yacs.workingOverlay.setAttribute('id','yacsOverlay');
		Element.setStyle(Yacs.workingOverlay, { position: 'fixed', top: '0', left: '0', zIndex: '1000', width: '100%', height: '100%', minHeight: '100%', backgroundColor: '#000', filter: 'alpha(opacity=20)', opacity: '0.2', display: 'block' });
		Yacs.workingOverlay.onclick = function() { Element.setStyle(Yacs.workingOverlay, { display: 'none' });};
		Yacs.workingOverlay.appendChild(objCentered);

		var objBody = document.getElementsByTagName("body").item(0);
		objBody.appendChild(Yacs.workingOverlay);

	},

	/**
	 * general initialization on window successful load
	 */
	onWindowLoad: function() {

		// compute and record surfer time zone
		Yacs.surferTimeZone();

		// pre-load the spinning image used during ajax updates
		Yacs.spinningImage = new Image();
		Yacs.spinningImage.src = url_to_root + 'skins/_reference/ajax_spinner.gif';

		// pre-load the image used at the working overlay
		Yacs.workingImage = new Image();
		Yacs.workingImage.src = url_to_root + 'skins/_reference/ajax_working.gif';

		// change the behavior of buttons used for data submission, except those with style 'no_spin_on_click'
		var buttons = document.getElementsByTagName('button');
		for(var index = 0; index < buttons.length; index++) {
			var button = buttons[index];
			var buttonType = String(button.getAttribute('type'));
			if(buttonType.toLowerCase().match('submit') && !Element.hasClassName(button, 'no_spin_on_click')) {
				button.onclick = function () { Yacs.isWorking(); return true; };
			}
		}

		// pre-fetch Google icons if the library is available

		if(typeof GIcon == Object) {

			var iconBlue = new GIcon();
			iconBlue.image = 'http://labs.google.com/ridefinder/images/mm_20_blue.png';
			iconBlue.shadow = 'http://labs.google.com/ridefinder/images/mm_20_shadow.png';
			iconBlue.iconSize = new GSize(12, 20);
			iconBlue.shadowSize = new GSize(22, 20);
			iconBlue.iconAnchor = new GPoint(6, 20);
			iconBlue.infoWindowAnchor = new GPoint(5, 1);

			var iconRed = new GIcon();
			iconRed.image = 'http://labs.google.com/ridefinder/images/mm_20_red.png';
			iconRed.shadow = 'http://labs.google.com/ridefinder/images/mm_20_shadow.png';
			iconRed.iconSize = new GSize(12, 20);
			iconRed.shadowSize = new GSize(22, 20);
			iconRed.iconAnchor = new GPoint(6, 20);
			iconRed.infoWindowAnchor = new GPoint(5, 1);

			Yacs.googleCustomIcons["other"] = iconBlue;
			Yacs.googleCustomIcons["user"] = iconRed;

		}

		// subscribe to notifications
		Yacs.subscribe();

	},

	/**
	 * open a popup window
	 */
	popup: function(options) {

		// default options
		this.options = {
			url: '#',
			width: 600,
			height: 500,
			name:"_blank",
			location:"no",
			menubar:"no",
			toolbar:"no",
			status:"yes",
			scrollbars:"yes",
			resizable:"yes",
			left:"",
			top:"",
			normal:false
		}

		// use provided options, if any
    	Object.extend(this.options, options || {});

    	// sanity check
		if (this.options.normal){
			this.options.menubar = "yes";
			this.options.status = "yes";
			this.options.toolbar = "yes";
			this.options.location = "yes";
		}

		// some computations
		this.options.width = this.options.width < screen.availWidth?this.options.width:screen.availWidth;
		this.options.height=this.options.height < screen.availHeight?this.options.height:screen.availHeight;
		var openoptions = 'width='+this.options.width+',height='+this.options.height+',location='+this.options.location+',menubar='+this.options.menubar+',toolbar='+this.options.toolbar+',scrollbars='+this.options.scrollbars+',resizable='+this.options.resizable+',status='+this.options.status
		if (this.options.top!="")openoptions+=",top="+this.options.top;
		if (this.options.left!="")openoptions+=",left="+this.options.left;

		// open the popup
	    window.open(this.options.url, this.options.name, openoptions);
		return false;
	},

	/**
	 * subscribe to notifications sent by the back-end asynchronously
	 *
	 * @see users/heartbit.php
	 */
	subscribe: function() {
		Yacs.subscribeAjax = new Ajax.Request(url_to_root + 'users/heartbit.php', {
			method: 'get',
			parameters: { },
			onSuccess: function(transport) {
				Yacs.subscribeError = false;

				// dispatch received notification
				var response = transport.responseText.evalJSON();
				switch(response['type']) {
				case 'alert':
					Yacs.handleAlertNotification(response);
					break;
				case 'browse':
					Yacs.handleBrowseNotification(response);
					break;
				case 'hello':
					Yacs.handleHelloNotification(response);
					break;
				}

			},
			onFailure: function(transport) {
				// back-end will generate an HTTP error if no notification
				Yacs.subscribeError = true;
			},
			onComplete: function(transport) {
				// idle cycle
				if(Yacs.subscribeError)
					setTimeout(function(){ Yacs.subscribe() }, 15000);

				// minimum time between two successive notifications
				else
					setTimeout(function(){ Yacs.subscribe() }, 5000);
			}
		});
	},

	/**
	 * track errors on subscriptions
	 */
	subscribeError: false,

	/**
	 * record surfer time zone
	 *
	 * This function computes the surfer time zone, and save it in a cookie to
	 * share this with the back-end
	 */
	surferTimeZone: function () {

		// compute time shift
		var now = new Date();
		var gmtDate = new Date(now.getFullYear(), 0, 1, 0, 0, 0, 0);
		var gmtString = gmtDate.toGMTString();
		var localDate = new Date(gmtString.substring(0, gmtString.lastIndexOf(" ")-1));
		var timeZone = (gmtDate - localDate) / (1000 * 60 * 60);

		// remember this in a cookie
		document.cookie = "TimeZone=" + timeZone + "; path=/";

		// return the result, if useful
		return timeZone;

	},

	/**
	 * This handler adds code to tabbing items, to process further clicks.
	 *
	 * It has to be called once, after the construction of proper DOM elements.
	 * You can either build the DOM manually, then place a call to this function,
	 * or, alternatively, just call Skin::build_tabs() from within you PHP code
	 * to have everything done automatically.
	 *
	 * @see skins/skin_skeleton.php
	 *
	 * More information for those who would like to do it the hard way:
	 *
	 * @param tabs A list of tabs related to panels and URLs
	 * @param args This corresponds to the Ajax options supported in prototype.js
	 *
	 * A live example featured in users/view.php:
	 *
	 *	<script type="text/javascript"><!--
	 *	// animate tabs after page loading
	 *	Event.observe(window, 'load', function() { Yacs.tabs({
	 *		'contributions_tab': [ 'contributions_panel' ],
	 *		'activities_tab': [ 'activities_panel', '/yacs/user-element/2-activities' ],
	 *		'actions_tab': [ 'actions_panel', '/yacs/user-element/2-actions' ],
	 *		'contact_tab': [ 'contact_panel', '/yacs/user-element/2-contact' ],
	 *		'information_tab': [ 'information_panel', '/yacs/user-element/2-information' ],
	 *		'preferences_tab': [ 'preferences_panel', '/yacs/user-element/2-preferences' ]}, {})
	 *		});
	 *	// -->
	 *	</script>
	 *
	 * @see users/view.php
	 *
	 * @link http://actsasflinn.com/Ajax_Tabs/index.html AJAX Tabs (Rails redux)
	 * @link http://20bits.com/2007/05/23/dynamic-ajax-tabs-in-20-lines/
	 */
	tabs: function(tabs, args) {

		// attach behavior to each item
		for(id in tabs) {

			// react to clicks
			Event.observe($(id), 'click', function(e) {

				// target the clicked tab
				var clicked = Event.element(e);

				// if we click on a link, move upwards to list item
				if(clicked.tagName == 'A')
					clicked = clicked.parentNode;

				// trigger custom behavior, if any
				if(typeof(args.onClick) == 'function')
					args.onClick(cliked);

				// activate the clicked tab -- see skins/_reference/ajax.css
				for(iterator in tabs) {
					if(clicked.id == $(iterator).id) {
					 	$(iterator).className = 'tab-foreground';
				 	} else {
					 	$(iterator).className = 'tab-background';
				 	}
			 	}

				// activate the related panel -- see skins/_reference/ajax.css
				for(iterator in tabs) {
					if(clicked.id == $(iterator).id) {
					 	$(tabs[iterator][0]).className = 'panel-foreground';
				 	} else {
					 	$(tabs[iterator][0]).className = 'panel-background';
				 	}
			 	}

			 	// load panel content, if any
			 	if(tabs[clicked.id].length > 1) {
				 	Yacs.updateOnce(tabs[clicked.id][0], tabs[clicked.id][1], args);
			 	}

			 	// do not propagate event
				Event.stop(e);
			})

		}
	},

	/**
	 * toggle a folded box
	 *
	 * @param the box
	 * @param string URL of the extending icon
	 * @param string URL of the collapsing icon
	 */
	toggle_folder: function(node, plus_href, minus_href) {

		// unfold the branch if it is not visible
		if(node.nextSibling.style.display == 'none') {
			node.nextSibling.style.display = 'block';

			// change the image (if there is an image)
			if(node.childNodes.length > 0) {
				if(node.childNodes.item(0).nodeName == "IMG") {
					node.childNodes.item(0).src = minus_href;
				}
			}

		// collapse the branch if it is visible
		} else {
			node.nextSibling.style.display = 'none';

			// change the image (if there is an image)
			if(node.childNodes.length > 0) {
				if(node.childNodes.item(0).nodeName == "IMG") {
					node.childNodes.item(0).src = plus_href;
				}
			}

		};

	},

	/**
	 * update content asynchronously
	 *
	 * This function displays a nice spinning image while loading the page.
	 *
	 * @param string id of the target CSS container
	 * @param string web address to fetch new snippet
	 * @param mixed additional parameters to transmit to Ajax
	 *
	 */
	update: function(panel, address, args) {

		new Ajax.Updater(panel, address, $H({
			asynchronous: true,
			method: 'get',
			evalScripts: true,
	  		onLoading: function(request) { $(panel).innerHTML = '<img alt="*" src="' + Yacs.spinningImage.src + '" style="vertical-align:-3px" />' }
			}).merge(args)
		);

	},

	/**
	 * set content only once
	 *
	 * This function is similar to Yacs.update(), except it does nothing if the target
	 * item already contains something.
	 *
	 * Use this function for example to populate tabbed panels of a complex page.
	 * Look at Yacs.tabs() above for a practical example of use
	 *
	 * @param string id of the target CSS container
	 * @param string web address to fetch new snippet
	 * @param mixed additional parameters to transmit to Ajax
	 *
	 */
	updateOnce: function(panel, address, args) {

		// do nothing if the panel contains something
		if(!$(panel).innerHTML || ($(panel).innerHTML == '') || ($(panel).innerHTML == '<img alt="*" src="' + Yacs.spinningImage.src + '" style="vertical-align:-3px" />')) {
			Yacs.update(panel, address, args);
		}

	}

}

// ready to receive new notifications
Event.observe(window, 'load', function() { Yacs.onWindowLoad() });

