/*
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
version: 1.4.0
*/
YAHOO.namespace("plugin","behavior");
YAHOO.namespace("CMS","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 Bubbling
  */
  YAHOO.Bubbling = function () {
  	var obj = {},
		ua = navigator.userAgent.toLowerCase(),
        isOpera = (ua.indexOf('opera') > -1);
	// private stuff
	var navRelExternal = function (layer, args) {
		  var el = args[1].anchor;
		  if (!(args[1].flagged || 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.getOwnerByClassName( t, 'yui-button' ), bt = null, id = null;
		if ($L.isObject(el) && YAHOO.widget.Button) {
			bt = YAHOO.widget.Button.getButton(el.id);
		}
		return bt;
	};

	// public vars
	obj.ready = false;
	obj.force2alfa = false;
	obj.bubble = {}; // CustomEvent Handles
    obj.onReady = new $Y.CustomEvent('bubblingOnReady', obj, true);

	// mapping external methods...
	obj.getOwnerByClassName = function(node, className) {
		return ($D.hasClass(node, className)?node:$D.getAncestorByClassName (node, className));
    };
    obj.getOwnerByTagName = function(node, tagName) {
		node = $D.get(node);
		if (!node) {
			return null;
		}
		return (node.tagName && node.tagName.toUpperCase() == tagName.toUpperCase()?node:$D.getAncestorByTagName (node, tagName));
	};
	// Deprecated in favor of getOwnerByClassName and getOwnerByTagName
	obj.getAncestorByClassName = obj.getOwnerByClassName;
    obj.getAncestorByTagName = obj.getOwnerByTagName;

	// public methods
	obj.onKeyPressedTrigger = function(args, e, m){
	  var b = 'key';
	  e = e || $E.getEvent();
	  m = m || {};
	  m.action = b;
	  m.target = args.target || (e?$E.getTarget(e):null);
	  m.flagged = false; 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;
	  this.bubble.key.fire(e, m);
	  if (m.stop) {
	  	$E.stopEvent(e);
	  }
	  return m.stop;
	};
	obj.onEventTrigger = function(b, e, m){
	  e = e || $E.getEvent();
	  m = m || {};
	  m.action = b;
	  m.target = (e?$E.getTarget(e):null);
	  m.flagged = false; m.decrepitate = false;
	  m.event = e;
	  m.stop = false;
	  this.bubble[b].fire(e, m);
	  if (m.stop) {
	  	$E.stopEvent(e);
	  }
	  return m.stop;
	};
	obj.onNavigate = function(e){
	  var conf = {
	  	anchor: this.getOwnerByTagName( $E.getTarget(e), 'A' ),
		button: _searchYUIButton($E.getTarget(e))
	  };
	  if (!conf.anchor && !conf.button) {
	  	conf.input = this.getOwnerByTagName( $E.getTarget(e), 'INPUT' );
	  }
	  if (conf.button) {
            conf.value = conf.button.get('value');
	  } else if (conf.input) {
	        conf.value = conf.input.getAttribute('value');
	  }
	  if (!this.onEventTrigger ('navigate', e, conf)) {
	    this.onEventTrigger ('god', e, conf); // if nobody claim the event, god can handle it...
	  }
	};
	obj.onProperty = function(e){
	  this.onEventTrigger ('property', e, {
	  	anchor: this.getOwnerByTagName( $E.getTarget(e), 'A' ),
		button: _searchYUIButton($E.getTarget(e))
	  });
	};
	obj._timeoutId = 0;
	obj.onRepaint = function(e){
	  // Downshift Your Code (can’t let something happen multiple times in a second)
	  // http://yuiblog.com/blog/2007/07/09/downshift-your-code/
      clearTimeout(obj._timeoutId);
      obj._timeoutId = setTimeout(function(){
            var b = 'repaint',
                e = {target:document.body},
                m = {
            	    action: b,
            	    target: null,
            	    event: e,
	  				flagged: false,
            	    decrepitate: false,
            	    stop: false
            	};
        	obj.bubble[b].fire(e, m);
        	if (m.stop) {
        	  	$E.stopEvent(e);
        	}
        }, 150
      );
	};
	obj.onRollOver = function(e){
	  this.onEventTrigger ('rollover', e, {
	  	anchor: this.getOwnerByTagName( $E.getTarget(e), 'A' )
	  });
	};
	obj.onRollOut = function(e){
	  this.onEventTrigger ('rollout', e, {
	  	anchor: this.getOwnerByTagName( $E.getTarget(e), 'A' )
	  });
	};
	obj.onKeyPressed = function(args){
	  this.onKeyPressedTrigger(args);
	};
	/**
	* * 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 ))) && $L.isObject(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;
	};

	/**
	* * Creating a new behaviors layer...
	* @public
	* @param {string||array} layers  Behaviors layers GUID
	* @param {object} scope  Custom  Event default execution scope
	* @return boolean if not exists...
	*/
    obj.addLayer = function (layers, scope) {
		var result = false;
		layers = ($L.isArray(layers)?layers:[layers]);
        scope = scope || window;
		for (var i = 0; i < layers.length; ++i) {
          if (layers[i] && !this.bubble.hasOwnProperty(layers[i])) {
            this.bubble[layers[i]] = new $Y.CustomEvent(layers[i], scope, true);
            result = true;
          }
        }
		return result;
    };
	/**
	* * Subcribing an bahavior to certain bahaviors layer...
	* @public
	* @param {string} layer  Behavior layer GUID
	* @param {object} bh     The function that represent the behavior
	* @return boolean if it is the first listener
	*/
    obj.subscribe = function (layer, bh, scope) {
        var first = this.addLayer(layer); // return true if it's the first listener
        if (layer) {
			if ($L.isObject(scope)) {
			  this.bubble[layer].subscribe(bh, scope, true);  // correcting the default scope
			} else {
			  this.bubble[layer].subscribe(bh);  // use the default scope
			}
        }
        return first;
    };
    obj.on = obj.subscribe; // defining an alias...
	/**
	* * Broadcasting the message in the corresponding behavior layer...
	* @public
	* @param {string} layer  Behavior layer GUID
	* @param {object} obj    The function that represent the behavior
	* @return boolean if someone has claim the event
	*/
    obj.fire = function (layer, obj) {
	    obj = obj || {};
	    obj.action = layer;
	    obj.flagged = false; obj.decrepitate = false;
	    obj.stop = false;
	    if (this.bubble.hasOwnProperty(layer)) {
	        this.bubble[layer].fire(null, obj);
	    }
	    return obj.stop;
    };
	/**
	* * Processing an action based on the classname of the target element...
	* @public
	* @param {string} layer     Behavior layer GUID
	* @param {object} args      Event object (extended)
	* @param {object} actions   List of availables behaviors...
	* @param {boolean} force    Proccess the actions without worry about the flagged value...
	* @return void
	*/
	obj.processingAction = function (layer, args, actions, force) {
      var behavior = null, t;
	  if (!(args[1].flagged || args[1].decrepitate) || force) {
	  	  // checking for anchor, input or button
		  t = args[1].anchor || args[1].input || args[1].button;
		  if (t) {
			behavior = this.getActionName ( t, actions );
			args[1].el = t;
		  }
		  if (behavior && (actions[behavior].apply(args[1], [layer, args]))) {
			$E.stopEvent(args[0]);
			args[1].flagged = true;
		    args[1].decrepitate = true;
		    args[1].stop = true;
		  }
	  }
	};
	obj.defaultActions = {};
    obj.addDefaultAction = function (n, f, force) {
		if (n && f && (!this.defaultActions.hasOwnProperty(n) || force)) {
		  	this.defaultActions[n] = f;
		}
    };

	// default behaviors
	$E.addListener(window, "resize", obj.onRepaint, obj, true);

	// default Suscriptions
	obj.on('navigate', navRelExternal);
	obj.on('navigate', defaultActionsControl);

	// initialization of the font and scroll monitors
	obj.initMonitors = function ( config ) {
	    var fMonitors = function () {
            var oMonitors = new YAHOO.widget.Module('yui-cms-font-monitor', {
                monitorresize:true,
                visible:false
            });
            oMonitors.render(document.body);
            // monitoring font-size...
            YAHOO.widget.Module.textResizeEvent.subscribe(obj.onRepaint, obj, true);
            // monitoring scroll actions...
            YAHOO.widget.Overlay.windowScrollEvent.subscribe(obj.onRepaint, obj, true);
	    };
	    if ($L.isFunction(YAHOO.widget.Module)) {
	      $E.onDOMReady (fMonitors, obj, true);
	    }
    };
	// 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, "keyup",  obj.onKeyPressed, obj, true);
		$E.addListener(document, "keydown",  obj.onKeyPressed, obj, true);

		this.ready = true;
		obj.onReady.fire();
	  }
	};
	$E.onDOMReady(obj.init, obj, true);

	// creating the default layers...
	obj.addLayer (['navigate','god','property','key','repaint','rollover', 'rollout']); // god layer - hack: the layer after the common navigate layer...

	return obj;
  }();
})();
YAHOO.CMS.Bubble = YAHOO.Bubbling; // deprecated: backward compatibility issue...
YAHOO.register("bubbling", YAHOO.Bubbling, {version: "1.4.0", build: "219"});
