/*
Copyright (c) 2007, Caridy Patiño. All rights reserved.
Portions Copyright (c) 2007, Yahoo!, Inc. All rights reserved.
Code licensed under the BSD License:
http://www.bubbling-library.com/eng/licence
*/
YAHOO.namespace("CMS");
YAHOO.namespace("CMS.widget","CMS.behaviors", "CMS.plugin");
(function() {
  var $Y = YAHOO.util,
	  $E = YAHOO.util.Event,
	  $D = YAHOO.util.Dom,
	  $L = YAHOO.lang,
	  $  = YAHOO.util.Dom.get;

  /**
  * @class Bubble
  */
  YAHOO.CMS.Bubble = function () {
  	var obj = {},
		ua = navigator.userAgent.toLowerCase(),
        isOpera = (ua.indexOf('opera') > -1);
	// private stuff
	var navRelExternal = function (layer, args) {
		  var el = obj.getAncestorByTagName( args[1].target, 'A' );
		  if (!args[1].decrepitate && el) {
			  var r = el.getAttribute("rel"),
			  	  t = el.getAttribute("target");
		  	  if ((!t || (t === '')) && (r == 'external')) {
				el.setAttribute("target", "blank");
		      }
		  }
	};
    var defaultActionsControl = function (layer, args) {
	  obj.processingAction (layer, args, obj.defaultActions);
    };
	var _searchYUIButton = function (t) {
		var el = obj.getAncestorByClassName( t, 'yui-button' ), bt = null, id = null;
		if ($L.isObject(el)) {
			id = $E.generateId (el);
			if (obj.buttons.hasOwnProperty(id)) {
				bt = obj.buttons[id]; // if we have a wrapper to the YUI button
			} else {
				bt = el; // optional return the YUI Button DOM reference
			}
		}
		return bt;
	};

	// public vars
	obj.ready = false;
	obj.moments = {};
	obj.bubble = { // CustomEvent Handles
		          navigate: new $Y.CustomEvent('navigate'),
		          property: new $Y.CustomEvent('property'),
				  submit:   new $Y.CustomEvent('submit'),
				  key:      new $Y.CustomEvent('key'),
		          repaint:  new $Y.CustomEvent('repaint'),
		          rollover: new $Y.CustomEvent('rollover'),
		          rollout:  new $Y.CustomEvent('rollout')
				};
	// public methods
	obj.onKeyPressedTrigger = function(args, e, m){
	  var b = 'key';
	  e = e || $E.getEvent();
	  m = m || {};
	  m.action = b;
	  m.target = args.target;
	  m.decrepitate = false;
	  m.event = e;
	  m.stop = false;
	  m.type = args.type;
	  m.keyCode = args.keyCode;
	  m.charCode = args.charCode;
	  m.ctrlKey = args.ctrlKey;
	  m.shiftKey = args.shiftKey;
	  m.altKey = args.altKey;
	  obj.moments[b] = false;
	  this.bubble.key.fire(e, m);
	  if (m.stop) {
	  	$E.stopEvent(e);
		obj.moments[b] = true;
	  }
	};
	obj.onEventTrigger = function(b, e, m){
	  e = e || $E.getEvent();
	  m = m || {};
	  m.action = b;
	  m.target = (e?$E.getTarget(e):null);
	  m.decrepitate = false;
	  m.event = e;
	  m.stop = false;
	  obj.moments[b] = false;
	  this.bubble[b].fire(e, m);
	  if (m.stop) {
	  	$E.stopEvent(e);
		obj.moments[b] = true;
	  }
	};
	obj.onNavigate = function(e){
	  var conf = {
	  	anchor: this.getAncestorByTagName( $E.getTarget(e), 'A' ),
		button: _searchYUIButton($E.getTarget(e))
	  };
	  if (!conf.anchor && !conf.button) {
	  	conf.input = this.getAncestorByTagName( $E.getTarget(e), 'INPUT' );
	  }
	  if (conf.button) {
            // hack: in some case, we can't get the button reference...
	        // official line: conf.value = conf.button.get('value');
	        conf.value = (!$D.inDocument(conf.button)?conf.button.get('value'):null);
	  } else if (conf.input) {
	        conf.value = conf.input.getAttribute('value');
	  }
	  this.onEventTrigger ('navigate', e, conf);
	};
	obj.onProperty = function(e){
	  this.onEventTrigger ('property', e, {
	  	anchor: this.getAncestorByTagName( $E.getTarget(e), 'A' ),
		button: _searchYUIButton($E.getTarget(e))
	  });
	};
	obj.onRepaint = function(e){
	  e = e || {target:null};
	  this.onEventTrigger ('repaint', e);
	};
	obj.onSubmit = function(e){
	  this.onEventTrigger ('submit', e);
	};
	obj.onRollOver = function(e){
	  this.onEventTrigger ('rollover', e);
	};
	obj.onRollOut = function(e){
	  this.onEventTrigger ('rollout', e);
	};
	obj.onKeyPressed = function(args){
	  this.onKeyPressedTrigger(args);
	};
	obj.onKeyListener = function(ev, args){
	  this.onKeyPressedTrigger(args[1]);
	};
	/**
	* * Este método representa la eliminación de un evento...
	* @public
	* @param {object} e Referencia al evento
	* @return void
	*/
	obj.eventGarbage = function (e) {
	  if ($L.isObject(e)) {
	    $E.stopEvent(e);
	  }
	  return false;
	};
	/**
	* * Este método determina el Ancestro del elemento basado en la clase
	* @public
	* @param {object} el Child element reference
	* @param {object} c  ClassName of the Ancestor
	* @return void
	*/
	obj.getAncestorByClassName = function (el, c) {
	  if (el && ($L.isObject(el) || (el = $( el ))) && c) {
	      while(el.parentNode) {
		  	if ($D.hasClass(el, c)) {
		  		return el;
		  	}
		    if (el.tagName == "BODY") {
		    	return null;
		    }
			el = el.parentNode;
		  }
	  }
	  return null;
	};
	/**
	* * Este método determina el ancestro del elemento basado en el tagName
	* @public
	* @param {object} el Child element reference
	* @param {object} c  ClassName of the Ancestor
	* @return void
	*/
	obj.getAncestorByTagName = function (el, t) {
	  if (el && ($L.isObject(el) || (el = $( el ))) && t) {
	  	  while(el.parentNode) {
		  	if (el.tagName == t) {
		  		return el;
		  	}
			if (el.tagName == "BODY") {
				return null;
			}
		    el = el.parentNode;
		  }
	  }
	  return null;
	};
	/**
	* * Este método determina la acción por defecto para un elemento
	* @public
	* @param {object} el        element reference
	* @param {object} actions   object with the list of posibles actions
	* @return void
	*/
	obj.getActionName = function (el, depot) {
	  depot = depot || {};
	  var b = null, r = null,
	      f = ($D.inDocument(el)?function(b){return $D.hasClass(el, b)}:function(b){return el.hasClass(b);}); // f: check is certain object has a classname
	  if (el && ($L.isObject(el) || (el = $( el )))) {
	  	try{
			r = el.getAttribute("rel"); // if rel is available...
		}catch(e){};
		for (b in depot) { // behaviors in the depot...
			if ((depot.hasOwnProperty(b)) && (f(b) || (b === r))) {
				return b;
			}
		}
	  }
	  return null;
	};
	/**
	* * Este método determina el primer tab hijo basado en el tabName
	* @public
	* @param {object} el Child element reference
	* @param {object} c  ClassName of the Ancestor
	* @return void
	*/
	obj.getFirstChildByTagName = function (el, t) {
	  if (el && ($L.isObject(el) || (el = $( el ))) && t) {
	  	var l = el.getElementsByTagName(t);
		if (l.length > 0) {
		  return l[0];
		}
	  }
	  return null;
	};
	/**
	* * Este método determina si un evento es interno o no a un contenedor...
	* @public
	* @param {object} e  Referencia al evento
	* @param {object} el Referencia al contendor
	* @return void
	*/
	obj.virtualTarget = function (e, el) {
	  if (el && ($L.isObject(el) || (el = $( el ))) && e) {
	    var t = $E.getRelatedTarget ( e );  // target element
	    if ($L.isObject(t)) {
	      while((t.parentNode) && $L.isObject(t.parentNode) &&  (t.parentNode.tagName !== "BODY")) {
		    if (t.parentNode === el) {
		      return true;
		    }
		    t = t.parentNode;
		  }
		}
	  }
	  return false;
	};

	/*****************************************************************************************
	 * Ajax Custom Navigation Events
	 *****************************************************************************************/
    obj.buttons = {};
    obj.dispatchButton = function (bt, userConfig) {
  	  if (bt) {
	    if (userConfig) {
	    	if (userConfig.action) {
	    		bt.addClass (userConfig.action);
	    	}
	    	if (userConfig.className) {
	    		bt.addClass (userConfig.className);
	    	}
	    }
	  	// storing a reference to the button...
	    this.buttons[bt.get('id')] = bt;
	  }
    };

	obj.processingAction = function (layer, args, actions) {
      var behavior = null, t;
	  if (!args[1].decrepitate) {
	  	  // checking for anchor, input or button
		  t = args[1].anchor || args[1].input || args[1].button;
		  if (t) {
			behavior = this.getActionName ( t, actions );
		  }
		  if (behavior && (actions[behavior].apply(args[1], [layer, args]))) {
			this.eventGarbage(args[0]);
		    args[1].decrepitate = true;
		    args[1].stop = true;
		  }
	  }
	};
	obj.defaultActions = {
	};
    obj.addDefaultAction = function (n, f) {
		if (n && f && (!this.defaultActions.hasOwnProperty(n))) {
		  	this.defaultActions[n] = f;
		}
    };

	// default behaviors
	$E.addListener(window, "resize", obj.onRepaint, obj, true);

	// default Suscriptions
	obj.bubble.navigate.subscribe(navRelExternal);
	obj.bubble.navigate.subscribe(defaultActionsControl);

	// initialization inside the selfconstructor
	obj.init = function () {
	  if (!this.ready) {
	    var el = document.body;
	    $E.addListener(
			el,
			"click",
			obj.onNavigate,
			obj,
			true
		);
	    /*
	        Listen for the "mousedown" event in Opera b/c it does not
	        support the "contextmenu" event
	    */
	    $E.addListener(
	        el,
	        (isOpera ? "mousedown" : "contextmenu"),
	        obj.onProperty,
	        obj,
	        true
	    );
	    /*
	        Assign a "click" event handler to the trigger element(s) for
	        Opera to prevent default browser behaviors.
	    */
	    if(isOpera) {
	        $E.addListener(
	            el,
	            "click",
	            obj.onProperty,
	            obj,
	            true
	        );
	    }
		$E.addListener(el, "mouseover", obj.onRollOver, obj, true);
	    $E.addListener(el, "mouseout", obj.onRollOut, obj, true);

		// keys...
		$E.addListener(document, "keydown",  obj.onKeyPressed, obj, true);
        $E.addListener(document, "keyup",  obj.onKeyPressed, obj, true);

		// submit
		$E.addListener(document, "submit", obj.onSubmit, obj, true);

		this.ready = true;
	  }
	};
	$E.onDOMReady(obj.init, obj, true);
	return obj;
  }();
})();
YAHOO.register("bubble", YAHOO.CMS.Bubble, {version: "2.3.0", build: "204"});