/**
 * $Id: discovery-auth-ui.js 24716 2009-06-03 10:07:59Z alucas $
 * 
 * Project:		DNI User Authentication: UI Javascript Component
 * Version:		2.0.0
 * Author:		alucas
 * 
 */


if (typeof(discovery) == "undefined") var discovery = {};
if (typeof(discovery.authentication) == "undefined") discovery.authentication = {};
if (typeof(discovery.authentication.ui) == "undefined") discovery.authentication.ui = {};
if (typeof(discovery.authentication.ui.util) == "undefined") discovery.authentication.ui.util = {};
if (typeof(discovery.authentication.ui.lib) == "undefined") discovery.authentication.ui.lib = {};
function augment(obj, args, func) { func.apply(obj, args) }






//#FORM CONTROLS FRAMEWORK

discovery.authentication.ui.core = (function() {
	
	
		//EXTERNALS
	
		var utils = {
			getElementsByClassName:		discovery.common.dom.getElementsByClassName,
			addEventListener:			discovery.common.dom.addEventListener,
			each:						discovery.common.object.each			
		}
	
	
		//PRIVATE VARS	
		
		var _typeMap = {};	// contains the HTML class to Control type mappings
		var _regex = {};		//collection of compiled regular expressions
		var _instances = [];
		
		
		//PUBLIC INTERFACE
		
		return {
			
			/**
			 * associates a control type with a HTML class
			 * 
			 * @param	{String}	className
			 * @param	{Function}	controlConstructor
			 * @return	{String}
			 */
			registerType: function(className, controlConstructor) {
	
				_instances[className] = [];
				_typeMap[className] = controlConstructor;
			},
			
			
			/**
			 * @void
			 * processes child elements and initialises any controls 
			 * based on the <code>_typeMap</code> object
			 * 
			 * @param	{HTMLElement}	targetElement
			 */
			processElement: function(targetElement) {

				utils.each(_typeMap, function(index){
					
					var elements = utils.getElementsByClassName
							.call(document, index);
					
					if (elements.length > 0)
						utils.each(elements, function(){

							if (this.tagName.toLowerCase() !== "label")
								_instances.push(_typeMap[index](this));
						})
				})
			},
			
			
			/**
			 * returns a type to object map of all objects instantiated
			 * 
			 * @return	{Object}
			 */
			getInstances: function() {
				return Array.prototype.slice.apply(_instances);
			}
	
		}	
})();



//#FORM CONTROLS

discovery.authentication.ui.lib = {}; //library of control types

augment(discovery.authentication.ui.lib,
		[discovery.authentication.ui.lib,
		 discovery.authentication],
		function(module, namespace){

	
	//EXTERNALS
	
	var utils = {
		getElementsByClassName:		discovery.common.dom.getElementsByClassName,
		addEventListener:			discovery.common.dom.addEventListener,
		each:						discovery.common.object.each			
	}
	
	var userInfo = discovery.USER.info;
	var userAuth = discovery.USER.auth;
	var page = discovery.PAGE;
	
	
	//STUBS
	
	var _isTrue = function() { return true }
	var _isFalse = function() { return false }
	var _isNull = function() { return null }

	
	//PRIVATE METHODS
	
	utils.attachHandlers = function(elem, events) {
		
		utils.each(events, function(index) {
			utils.addEventListener.call(elem, index, this);
		});
	}
	

	//PICTURE PICKER FACTORY #

	/**
	 * initialises a <code>PicturePicker</code> instance
	 * for a corresponding form control
	 * 
	 * @param	{HTMLElement}	element
	 */
	module.PicturePicker = function(element) {
		
		
			//PRIVATE VARS
			
			var _DEFAULT_SETTINGS = {	//default settings applied to PicturePicker instances
				'HOVER_CLASS': "hover", 'SELECTED_CLASS': "selected",
				'KEY_IMAGE_ID': "handle", 'REMOVE_BTN_CLASS': "remove-avatar-button",
				'HIDE_AVATAR_CLASS': "removed", 'CURRENT_AVATAR_CLASS': "avatar"
			}
			
			var _getRegex = function (picker){	//collection of compiled regular expressions
				
				return {
					'IMAGE_ID_EXTRACT': { //extracts KEY_IMAGE_ID value
						match:		RegExp('(^|.*? )' + picker.settings["KEY_IMAGE_ID"]
											+ '-(.*?)($| .*)'),
						index:		2
					},
					'HOVER_REMOVE': {	//removes HOVER_CLASS class
						match:		RegExp('(^| )' + picker.settings["HOVER_CLASS"]
											+ '($| )', "g"),
						replace:	"$1$2"
					},
					'SELECT_REMOVE': {	//removes SELECTED_CLASS class
						match:		RegExp('(^| )' + picker.settings["SELECTED_CLASS"]
						      				+ '($| )', "g"),
						replace:	"$1$2"
					},
					'HIDE_AVATAR_REMOVE': {	//removes SELECTED_CLASS class
						match:		RegExp('(^| )' + picker.settings["HIDE_AVATAR_CLASS"]
						      				+ '($| )', "g"),
						replace:	"$1$2"
					}
				}
			}
				
			var _hoverInactive = {}; //keeps track of hover state
			
			
			//CONSTRUCTOR
			
			/**
			 * @constructor
			 * 
			 * Initialises a <code>PicturePicker</code> form control
			 */
			function PicturePicker(input) {
				
				this.settings = _DEFAULT_SETTINGS;
				this.settings._REGEX = _getRegex(this);
				
				this.selectedImage = { id: null, element: null };
				
						//CUSTOM METHODS

						/**
						 * returns the input associated with this
						 * <code>PicturePicker</code> instance
						 * 
						 * @return	{HTMLElement}
						 */
						this.getInput = function() { return input }
				
						/**
						 * returns the container associated with
						 * this <code>PicturePicker</code> instance
						 * 
						 * @return	{HTMLElement}
						 */
						this.getContainer = function() {
							
							var container = this.getInput()
									.parentNode.parentNode;
							
							this.getContainer = function() { return container }
							return container;
						};
						
						/**
						 * returns the avatar form icon associated
						 * this this <code>PicturePicker</code>
						 * instance
						 * 
						 * @return	{HTMLElement}
						 */
						this.getCurrentAvatar = function() {
							
							var avatar = utils.getElementsByClassName.call(
									this.getContainer(),
									this.settings['CURRENT_AVATAR_CLASS']);
							
							if (avatar.length > 0) avatar = avatar[0];
							else avatar = null
							
							this.getCurrentAvatar = function() {
								return avatar;
							}
							return avatar;
						}
						
						/**
						 * @void
						 * toggles the remove status of this
						 * <code>PicturePicker</code> instance
						 */
						this.toggleRemove = function() {
							
							var removed = false;
							this.toggleRemove = function() {
							
								if (!removed) {
									removed = true;
									setRemove(this);
								} else {
									removed = false;
									unsetRemove(this);
								}
							}
							this.toggleRemove();
						}
						
						/**
						 * returns an image's identifier for submission
						 * 
						 * @param	{HTMLElement}	image
						 * @return	{String}
						 */
						this.getImageId = function(image) {
							
							var returnValue = null;
							
							if (!image.imageId) {
								var regex = this.settings._REGEX['IMAGE_ID_EXTRACT'];
								returnValue = getImageId(image, regex);
								
							} else {
								returnValue = image.imageId;
							}
							
							return returnValue;
						}
						
						//END CUSTOM METHODS
						
				initRemoveLink(this);
				var images = [];
				
				utils.each(this.getInput().parentNode
						.getElementsByTagName("img"), function(){
					if (this.tagName)
						images.push(this);
				})
				
				this.images = images;
				this.processImages();			
			}
			
			
			//PROTOTYPED METHODS
			
			
			/**
			 * @void
			 * overwrites the settings for this <code>PicturePicker</code>
			 * instance
			 * 
			 * @param	{String}	name
			 * @param	{Object}	value
			 */
			PicturePicker.prototype.changeSetting = function(name, value) {
				this.settings[name] = value;
				this.settings._REGEX = _getRegex();
			}
			
			
			/**
			 * @void
			 * processes all images associated with this
			 * <code>PicturePicker</code> instance
			 * 
			 * @param	{PicturePicker}	picker
			 */			
			PicturePicker.prototype.processImages = function() {
				
				var self = this;
				utils.each(this.images, function(){
					
					var image = this;
					_hoverInactive[self.getImageId(image)] = true;
					
					utils.attachHandlers(image, {
						'mouseOver': 	function() { onImageOver(self, image) },
						'mouseOut':		function() { onImageOut(self, image) },
						'click':		function() { onImageClick(self, image) }
					})
				})
			}			
		
			
			//PRIVATE METHODS
			
			
			function getImageId(image, regex) {
				
				var val = image.className.match(regex.match);
				if (val) return val[regex.index];
				else return null;
			}
			
			function initRemoveLink(picker) {
				
				var input = utils.getElementsByClassName
						.call(picker.getContainer(),
								"removeAvatar");
				
				if (input.length > 0) {
					
					input = input[0];
					picker.getRemoveInput = function() {
						return input;
					}
				
					var link = utils.getElementsByClassName.call(
						picker.getContainer(),
						picker.settings["REMOVE_BTN_CLASS"]);
					
					if (link.length > 0) {
						link = link[0];
						utils.addEventListener.call(link, "click", function(){
							picker.toggleRemove();
						});
					}
				}
			}
			
					//EVENT LISTENERS
			
					function setRemove(picker) {
						
						picker.getRemoveInput().value = "1";
						
						var avatar = picker.getCurrentAvatar();
						avatar.className = avatar.className
								+ " " + picker.settings["HIDE_AVATAR_CLASS"];
					}
					
					function unsetRemove(picker) {
						
						picker.getRemoveInput().value = "0";
						
						var avatar = picker.getCurrentAvatar();
						var regex = picker.settings._REGEX["HIDE_AVATAR_REMOVE"];
						avatar.className = avatar.className
								.replace( regex.match, regex.replace);
					}
			
					function onImageOver(picker, image) {
						
						var imageId = picker.getImageId(image);
						if ((imageId != null)&&(_hoverInactive[imageId])) {
							
							image.className = image.className
									+ " " + picker.settings["HOVER_CLASS"];
							
							_hoverInactive[imageId] = false;
						}
					}
					
					function onImageOut(picker, image) {
						
						var imageId = picker.getImageId(image);
						_hoverInactive[imageId] = true;
						
						var regex = picker.settings._REGEX["HOVER_REMOVE"];
						image.className = image.className
								.replace( regex.match, regex.replace);
					}
					
					function onImageClick(picker, image) {
						
						var regex = picker.settings._REGEX["SELECT_REMOVE"];
						
						var imageId = picker.getImageId(image);
						var selectedImage = picker.selectedImage;
						var selectedImageElement = selectedImage.element;
						
						//check image is not already selected
						if (selectedImage.id != imageId) {
							
							//set the value of the hidden input
							picker.getInput().value = imageId;
						
							if (selectedImage.id != null) {
								
								//deselect the previously selected image
								selectedImageElement.className = selectedImageElement
										.className.replace( regex.match, regex.replace);
							}
							
							//update selectedImage
							selectedImage.element = image;
							selectedImage.id = imageId;
							
							image.className = image.className
									+ " " + picker.settings["SELECTED_CLASS"];
						}
					}
					
			
			//CLOSE FACTORY
			module.PicturePicker = function(element) {
				return new PicturePicker(element);
			}
			return module.PicturePicker(element);
		
	}
	//END PICTURE PICKER FACTORY #
	
	
	
	
	//NEWSLETTER SELECT FACTORY #

	/**
	 * initialises a <code>NewsletterSelect</code> instance
	 * for a corresponding form control
	 * 
	 * @param	{HTMLElement}	element
	 */
	module.NewsletterSelect = function(element) {
			
			
			//PRIVATE VARS
			
			var _DEFAULT_SETTINGS = {	//default settings applied to PicturePicker instances
				'DISABLED_CLASS': "disabled", 'KEY_CHECKBOX_ID': "checkbox",
				'ENABLED_VALUE_SUFFIX': "n"
			}
			
			var _getRegex = function (nSelect){	//collection of compiled regular expressions
				
				return {
					'CHECKBOX_ID_EXTRACT': { //extracts KEY_CHECKBOX_ID value
						match: RegExp('(^|.*? )' + nSelect.settings["KEY_CHECKBOX_ID"] + '-(.*?)($| .*)'),
						index: 2
					},
					'DISABLED_REMOVE': {	//removes HOVER_CLASS class
						match:		RegExp('(^| )' + nSelect.settings["DISABLED_CLASS"] + '($| )', "g"),
						replace:	"$1$2"
					},
					'HAS_NEWSLETTER': {
						match: RegExp('(^.*)' + nSelect.settings["ENABLED_VALUE_SUFFIX"] + "$"),
						index: 1
					}
				}
			}
			
			
			//CONSTRUCTOR
				
			/**
			 * @constructor
			 * 
			 * initialises a corresponding <code>NewsletterSelect</code>
			 * form control
			 */
			function NewsletterSelect(select) {

				this.settings = _DEFAULT_SETTINGS;
				this.settings._REGEX = _getRegex(this);
				
						
						//CUSTOM METHODS
				
						/**
						 * returns the select control associated with this
						 * <code>NewsletterSelect</code> instance
						 * 
						 *  @return {HTMLElement}
						 */
						this.getElement = function() { return select }
							
						/**
						 * [STUB] returns the checkbox associated with this
						 * <code>NewsletterSelect</code> instance
						 * 
						 * @return	{HTMLElement}
						 */
						this.getCheckbox = function() { }
						
						/**
						 * returns the current state of this <code>NewsletterSelect</code>
						 * instance
						 * 
						 * @return	{Boolean}
						 */
						this.hasNewsletter = function () { return true }
						
				
						
				this.refresh();
				
				if (this.getCheckbox() != null) {
					this.attachListeners();
				} else {
					//if there is no associated checkbox, prevent instantiation
					return null;
				}
			}
			
			
			//PROTOTYPED METHODS

			
			/**
			 * attaches event listeners to this <code>NewsletterSelect</code>'s
			 * associated inputs
			 */
			NewsletterSelect.prototype.attachListeners = function() {
				var self = this;
				var listener = createListener(self);
				utils.attachHandlers(this.getElement(), {
					'change':	listener
				})
				listener();
			}
	
	
			/**
			 * overwrites the settings for this <code>NewsletterSelect</code>
			 * instance
			 * 
			 * @param	{String}	name
			 * @param	{Object}	value
			 */
			NewsletterSelect.prototype.changeSetting = function(name, value) {
				this.settings[name] = value;
				this.refresh();
			}
			
			
			/**
			 * causes this <code>NewsletterSelect</code> instance to
			 * update after a change to a vital property
			 */
			NewsletterSelect.prototype.refresh = function() {
				var regex = _getRegex(this);
				
				this.settings._REGEX = regex;
				var checkbox = getCheckbox(
						this.getElement(), regex['CHECKBOX_ID_EXTRACT']);
				
				if (!checkbox != null)
					this.getCheckbox = function() { return checkbox }
			}
		
			
			
			//PRIVATE METHODS
			
			function createListener(nSelect) {
				
				return function() {
					if (hasNewsletter(nSelect)) {
						
						if(!nSelect.hasNewsletter()){
							
							var checkboxParent = nSelect.getCheckbox().parentNode;
							var regex = nSelect.settings._REGEX['DISABLED_REMOVE'];
							checkboxParent.className = checkboxParent.className
									.replace( regex.match, regex.replace );
							
							nSelect.hasNewsletter = function() { return true }
						}
					} else {
						if (nSelect.hasNewsletter()) {
							
							var checkboxParent = nSelect.getCheckbox().parentNode;
							checkboxParent.className = checkboxParent.className
									+ " " + nSelect.settings['DISABLED_CLASS'];
							
							nSelect.hasNewsletter = function() { return false }
						}
					}
				}
			}
			
			function getCheckbox(select, regex) {
				
				var val = select.className.match(regex.match);
				if (val) return document.getElementById(val[regex.index]);
				else return null;
			}
			
			function hasNewsletter(nSelect) {
				var regex = nSelect.settings._REGEX['HAS_NEWSLETTER'];
				return !(!nSelect.getElement().value.match(regex.match));
			}
			

					
			
			//CLOSE FACTORY
			module.NewsletterSelect = function(element) {
				return new NewsletterSelect(element);
			}
			return module.NewsletterSelect(element);
		
	}
	
	//END NEWSLETTER SELECT FACTORY #
	
	
	
	//FB CONNECT BUTTON FACTORY #
	
	module.FBConnectButton = function(e) {
		return module.FBConnectBaseButton(e, "connect");		
	}
	
	module.FBDisconnectButton = function(e) {
		return module.FBConnectBaseButton(e, "disconnect");		
	}

	/**
	 * initialises a <code>FBConnectButton</code> instance
	 * for a corresponding form control
	 * 
	 * @param	{HTMLElement}	element
	 */
	module.FBConnectBaseButton = function(_element, _isConnect) {
		
			
			//PRIVATE VARS
		
			var _regex = {
				'QUERY_STRING': {
					match:	/^.*?\?([^#]*).*/,
					index: 1
				}
			}
			
			//CONSTRUCTOR
				
			/**
			 * @constructor
			 * 
			 * initialises a corresponding <code>FBConnectButton</code>
			 * form control
			 */
			function FBConnectButton(element, type) {
				
				this.getType = function() { return type }
				
				var container2, container1 = document
						.getElementById("update-profile-form-facebook-account");
				
				if ((container1)&&(container2 = document
						.getElementById("update-profile-form-facebook-account-d")))
					this.getContainers = function() { return [container1, container2] }
				else
					this.getContainers = _isNull;
				
				utils.attachHandlers(element, {
					'click':	getOnClick(this)
				});
			}
			
			
			//PRIVATE METHODS
			
			
			function getOnClick(instance) {
				
				var type = instance.getType();
				
				return function() {
					if (userInfo.isFacebookUser() != null) {
					
						switch(type) {
							case "connect":
								namespace.fb.connectUser(function() {
									connected(instance);
								});
								break;
							case "disconnect":
								namespace.fb.disconnectUser(function(xhr) {
									
									var codeRange = xhr.status.toString()[0];
									if ((codeRange != 5)&&(codeRange != 3)) {
										userAuth.setAuthToken(xhr.responseText);
										page.reloadHat();
										disconnected(instance);
									}
								});
								break;
							default:
								namespace.fb.logUserIn(returnUrl());
						}
					} else {
					
						alert("There has been an error communicating with"
								+" Facebook.\nPlease reload or try again later.");
					}
				}
			}
			
			function returnUrl() {
				
				var url = getQueryParams(document.location.href)
						.returnUrl;
				
				if (!url) {
					var inputs = utils
							.getElementsByClassName.call(document,
									"ua-profile-management-returnUrl");
					if (inputs.length > 0)
						url = inputs[0].value;
				} else {
					url = unescape(url);
				}
					
				return url;
			}
			
			
			function connected(instance) {
				
				var containers = instance.getContainers();
				if (containers)
					utils.each(containers, function(){
						this.className = this.className.replace(/(fb-enabled|)/, "fb-enabled");
					})
			}
			
			
			function disconnected(instance) {
				
				var containers = instance.getContainers();
				if (containers)
					utils.each(containers, function(){
						this.className = this.className.replace(/(^| )fb-enabled( |$)/g, "$1$2");
					})
			}
			
			
			function getQueryParams(url) {
				
				var params = {};
				var regex = _regex['QUERY_STRING'];
				var qString = url.match(regex.match);
				
				if ((qString)&&(qString.length > regex.index)) {
					
					var frags = qString[regex.index].split("&");
					for (var i=frags.length-1,k; k=frags[i]; i--) {
						
						var frag = k.split("=");
						var val = null;
						
						if (frag.length > 1) val = frag[1];
						params[frag[0]] = val;
					}
				}
				
				return params;
			}
			
			
			
			
			//CLOSE FACTORY
			module.FBConnectBaseButton = function(element, isConnect) {
				return new FBConnectButton(element, isConnect);
			}
			return module.FBConnectBaseButton(_element, _isConnect);
		
	}
	
	//END FB CONNECT BUTTON FACTORY #
	
});
	




