/*
Some rights reserved (CC) 2007, GoNow Ltda.
Code licensed under the Creative Commons license:
http://creativecommons.org/licenses/by-sa/3.0/

@reference: YUI
@adapted by: Tiago Canzian
*/


/**
 * The GNW global namespace
 * @constructor
 */

window.GNW = window.GNW || {};
GNW.util = {};


var reClassNameCache = {},
	propertyCache = {};

var getClassRegEx = function(className) {
    var re = reClassNameCache[className];
    if (!re) {
        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
        reClassNameCache[className] = re;
    }
    return re;
};

var patterns = {
    HYPHEN: /(-[a-z])/i,
	ROOT_TAG: /^body|html$/i
};

var toCamel = function(property) {
    if ( !patterns.HYPHEN.test(property) ) {
        return property;
    }
    
    if (propertyCache[property]) {
        return propertyCache[property];
    }
   
    var converted = property;

    while( patterns.HYPHEN.exec(converted) ) {
        converted = converted.replace(RegExp.$1,
                RegExp.$1.substr(1).toUpperCase());
    }
    
    propertyCache[property] = converted;
    return converted;
};

ua = function() {
    var o={
        ie:0,
		ie6:0,
        opera:0,
        gecko:0,
        webkit:0
    };

    var ua=navigator.userAgent, m;

    if ((/KHTML/).test(ua)) {
        o.webkit=1;
    }
    m=ua.match(/AppleWebKit\/([^\s]*)/);
    if (m&&m[1]) {
        o.webkit=parseFloat(m[1]);
    }

    if (!o.webkit) {
        m=ua.match(/Opera[\s\/]([^\s]*)/);
        if (m&&m[1]) {
            o.opera=parseFloat(m[1]);
            m=ua.match(/Opera Mini[^;]*/);
        } else {
            m=ua.match(/MSIE\s([^;]*)/);
            if (m&&m[1]) {
                o.ie=parseFloat(m[1]);
				if (ua.indexOf('6') != -1){
					o.ie6 = 1;
				}
            } else {
                m=ua.match(/Gecko\/([^\s]*)/);
                if (m) {
                    o.gecko=1;
                    m=ua.match(/rv:([^\s\)]*)/);
                    if (m&&m[1]) {
                        o.gecko=parseFloat(m[1]);
                    }
                }
            }
        }
    }
    
    return o;
}();

var isOpera = ua.opera,
        isSafari = ua.webkit,
        isGecko = ua.gecko,
        isIE = ua.ie,
		isIE6 = ua.ie6;

GNW._id_counter = GNW._id_counter || 0;

GNW.util.Dom = {
	get : function get(el){
		 if (el && (el.nodeType || el.item)) {
            return el;
        }

        if (typeof(el) === 'string' || !el) {
			if (document.getElementById(el)) {
            	return document.getElementById(el);
			} else {
				return undefined;
			}
        }
        
        if (el.length !== undefined) { 
            var c = [];
            for (var i = 0, len = el.length; i < len; ++i) {
                c[c.length] = this.get(el[i]);
            }
            
            return c;
        }

        return el;
	},
	
	getElementsByClassName: function(className, tag, root, apply){
		tag = tag || '*';
        root = (root) ? this.get(root) : null || document; 
        if (!root) {
            return [];
        }

        var nodes = [],
            elements = root.getElementsByTagName(tag),
            re = getClassRegEx(className);

        for (var i = 0, len = elements.length; i < len; ++i) {
            if ( re.test(elements[i].className) ) {
                nodes[nodes.length] = elements[i];
                if (apply) {
                    apply.call(elements[i], elements[i]);
                }
            }
        }
        
        return nodes;
	},
	
	getStyle : function getStyle(el, property){
		var elm = this.get(el);
		property = toCamel(property);
		if ((document.defaultView && document.defaultView.getComputedStyle)) {
			var computed = document.defaultView.getComputedStyle(elm, '');
            if (computed) {
                value = computed[toCamel(property)];
            }
            
            return elm.style[property] || value;	
		} else if(document.documentElement.currentStyle && isIE){
			var value = elm.currentStyle ? elm.currentStyle[property] : null;
            return ( elm.style[property] || value );
		} else {
			return elm.style[property];
		}
		
	},
	
	setStyle : function setStyle(el, property, val){
		property = toCamel(property);
		
		if (this.get(el)) this.get(el).style[property] = val;	
	},
	
	hasClass: function hasClass(el, className) {
		var elm = this.get(el);
        var re = getClassRegEx(className);

        if (elm) {
			return re.test(elm.className);
		}

    },
	
	addClass : function addClass(el, className) {
		var elm = this.get(el);
        if (this.hasClass(elm, className)) {
            return false;
        }
		
        if (elm) {
	        elm.className = elm.className +' '+ className;
		}
        return true;
    },
	
	removeClass: function removeClass(el, className) {
        var elm = this.get(el);
		var re = getClassRegEx(className);
		
        if (!className || !this.hasClass(elm, className)) {
            return false;
        }                 
		
        var c = elm.className;
        elm.className = c.replace(re, ' ');
		
        if ( this.hasClass(elm, className) ) {
            this.removeClass(elm, className);
        }

        elm.className = elm.className;
        return true;
        
    },
	
	replaceClass : function replaceClass(el, oldClassName, newClassName){
		var elm = this.get(el);
		if (!newClassName || oldClassName === newClassName) {
            return false;
        }
        var re = getClassRegEx(oldClassName);

        if ( !this.hasClass(elm, oldClassName) ) {
            this.addClass(elm, newClassName);
            return true;
        }
    
        elm.className = elm.className.replace(re, ' ' + newClassName + ' ');

        if ( this.hasClass(elm, oldClassName) ) {
            this.replaceClass(elm, oldClassName, newClassName);
        }
		
		elm.className = elm.className;
        return true;
	},
	
	getViewportWidth: function getViewportWidth() {
        var width = self.innerWidth;  // Safari
        var mode = document.compatMode;
        
        if ( (mode || isIE) && !isOpera ) { // IE, Gecko, Opera
            width = (mode == 'CSS1Compat') ?
                    document.documentElement.clientWidth : // Standards
                    document.body.clientWidth; // Quirks
        }
        return width;
    },
	
	getViewportHeight: function getViewportHeight() {
        var height = self.innerHeight;  // Safari
        var mode = document.compatMode;
        
        if ( (mode || isIE) && !isOpera ) { // IE, Gecko, Opera
            height = (mode == 'CSS1Compat') ?
                    document.documentElement.clientHeight : // Standards
                    document.body.clientHeight; // Quirks
        }
        return height;
    },
	
	getXY: function(el) {
		var el = this.get(el);
        if ( (el.parentNode === null || el.offsetParent === null ||
                this.getStyle(el, 'display') == 'none') && el != el.ownerDocument.body) {
            return false;
        }
        
        if (document.documentElement.getBoundingClientRect) { // IE
            return function(el) {
                var box = el.getBoundingClientRect();
                var rootNode = el.ownerDocument;

                return [box.left + GNW.util.Dom.getDocumentScrollLeft(rootNode), box.top + GNW.util.Dom.getDocumentScrollTop(rootNode)];
            }(el);
        } else {
            return function(el) { // manually calculate by crawling up offsetParents
                var pos = [el.offsetLeft, el.offsetTop];
                var parentNode = el.offsetParent;

                // safari: subtract body offsets if el is abs (or any offsetParent), unless body is offsetParent
                var accountForBody = (GNW.util.Dom.getStyle(el, 'position') == 'absolute' && el.offsetParent == el.ownerDocument.body);

                if (parentNode != el) {
                    while (parentNode) {
                        pos[0] += parentNode.offsetLeft;
                        pos[1] += parentNode.offsetTop;
                        if (!accountForBody && GNW.util.Dom.getStyle(parentNode,'position') == 'absolute' ) { 
                            accountForBody = true;
                        }
                        parentNode = parentNode.offsetParent;
                    }
                }

                if (accountForBody) { //safari doubles in this case
                    pos[0] -= el.ownerDocument.body.offsetLeft;
                    pos[1] -= el.ownerDocument.body.offsetTop;
                } 
                parentNode = el.parentNode;

                // account for any scrolled ancestors
                while ( parentNode.tagName && !patterns.ROOT_TAG.test(parentNode.tagName) ) 
                {
                   // work around opera inline/table scrollLeft/Top bug
                   if (GNW.util.Dom.getStyle(parentNode, 'display').search(/^inline|table-row.*$/i)) { 
                        pos[0] -= parentNode.scrollLeft;
                        pos[1] -= parentNode.scrollTop;
                    }
                    
                    parentNode = parentNode.parentNode; 
                }

                return pos;
            }(el);
        }
    },
    
    getX: function(el) {
		return this.getXY(el)[0];
    },
    
    getY: function(el) {
		return this.getXY(el)[1];
    },
	
	generateId: function(el, prefix) {
        prefix = prefix || 'gnw-gen';
		var elm = this.get(el);
        if (elm && elm.id) { // do not override existing ID
            return elm.id;
        } 

        var id = prefix + GNW._id_counter++;

        if (elm) {
            elm.id = id;
        }
        
        return id;

    },
	
	getDocumentScrollLeft: function(doc) {
        doc = doc || document;
        return Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft);
    }, 

    getDocumentScrollTop: function(doc) {
        doc = doc || document;
        return Math.max(doc.documentElement.scrollTop, doc.body.scrollTop);
    },
	
	inArray : function (name, value) {
		var i;
		for (i=0; i < name.length; i++) {
			if (name[i] === value) {
				return true;
			}
		}
		return false;
	}
}

GNW.util.CustomEvent = function(type, scope) {
    this.type = type;
    this.scope = scope || window;
    this.subscribers = [];
	
	this.subscribe = function(fctn, obj, override){
        this.subscribers.push( new GNW.util.Subscriber(fctn, obj, override) );
	}
	
	this.unsubscribe = function(fctn, obj){
		var found = false;
        for (var i=0, len=this.subscribers.length; i<len; ++i) {
            var subscriber = this.subscribers[i];
            if (subscriber && subscriber.contains(fctn, obj)) {
                this._delete(i);
                found = true;
            }
        }
        return found;
	}
	
	this.fire = function(){
		var len=this.subscribers.length;
        var args=[], ret=true, i;

        for (i=0; i<arguments.length; ++i) {
            args.push(arguments[i]);
        }

        for (i=0; i<len; ++i) {
            var subscriber = this.subscribers[i];
            if (subscriber) {
                var scope = subscriber.getScope(this.scope);
                ret = subscriber.fctn.call(scope, this.type, args, subscriber.obj);
				
                if (false === ret) {
                    return false;
                }
            }
        }

        return true;
	}
	
	this._delete = function(index) {
		 var subscriber = this.subscribers[index];
        if (subscriber) {
            delete subscriber.fctn;
            delete subscriber.obj;
        }

        this.subscribers[index]=null;
	}

};

GNW.util.Subscriber = function(fctn, obj, override) {
    this.fctn = fctn;
    this.obj = obj;
    this.override = override;
	
	this.getScope = function(defaultScope){
		if (this.override) {
	        if (this.override === true) {
	            return this.obj;
	        } else {
	            return this.override;
	        }
	    }
	    return defaultScope;
	}
	
	this.contains = function(fctn, obj){
		 if (obj) {
	        return (this.fctn == fctn && this.obj == obj);
	    } else {
	        return (this.fctn == fctn);
	    }
	}
};

GNW.util.Event = {
	addListener : function(el, sType, fn, obj, override){
		this.on(el, sType, fn, obj, override);	
	}, 
	
	on : function(el, sType, fn, obj, override){
		var el = (typeof el === 'string') ? this.get(el) : el;
		var scope = el;
        if (override) {
            if (override === true) {
                scope = obj;
            } else {
                scope = override;
            }
        }
		
		var wrappedFn = function(e) {
            return fn.call(scope, GNW.util.Event.getEvent(e, el), obj);
        };
		
		if (el) {
			if (window.addEventListener) {
				el.addEventListener(sType, wrappedFn, false);
	        } else if (window.attachEvent) {
				el.attachEvent("on" + sType, wrappedFn);
	        } else {
				return function(){};
	        }
		} else {
			return function(){};
		}
	},
	
	removeListener : function(el, sType, fn, capture){
		var el = (typeof el === 'string') ? this.get(el) : el;
		
		if (window.removeEventListener) {
            return function (el, sType, fn, capture) {
                el.removeEventListener(sType, fn, (capture));
            };
        } else if (window.detachEvent) {
            return function (el, sType, fn) {
                el.detachEvent("on" + sType, fn);
            };
        } else {
            return function(){};
        }
	},
	
	getEvent: function(e, boundEl) {
        var ev = e || window.event;

        if (!ev) {
            var c = this.getEvent.caller;
            while (c) {
                ev = c.arguments[0];
                if (ev && Event == ev.constructor) {
                    break;
                }
                c = c.caller;
            }
        }

        return ev;
    },
	
	getTarget: function(ev, resolveTextNode) {
        var t = ev.target || ev.srcElement;
        return this.resolveTextNode(t);
    },

    resolveTextNode: function(n) {
        try {
            if (n && 3 == n.nodeType) {
                return n.parentNode;
            }
        } catch(e) { }

        return n;
    },
	
	 purgeElement: function(el, recurse, sType) {
        var oEl = (typeof el === 'string') ? this.get(el) : el;
        var elListeners = this.getListeners(oEl, sType), i, len;
        if (elListeners) {
            for (i=0,len=elListeners.length; i<len ; ++i) {
                var l = elListeners[i];
                this.removeListener(oEl, l.type, l.fn, l.index);
            }
        }

        if (recurse && oEl && oEl.childNodes) {
            for (i=0,len=oEl.childNodes.length; i<len ; ++i) {
                this.purgeElement(oEl.childNodes[i], recurse, sType);
            }
        }
    },
	
	getListeners: function(el, sType) {
        var results=[], searchLists;
        if (!sType) {
            searchLists = [listeners, unloadListeners];
        } else if (sType === "unload") {
            searchLists = [unloadListeners];
        } else {
            searchLists = [listeners];
        }

        var oEl = (typeof el === 'string') ? this.get(el) : el;

        for (var j=0;j<searchLists.length; j=j+1) {
            var searchList = searchLists[j];
            if (searchList && searchList.length > 0) {
                for (var i=0,len=searchList.length; i<len ; ++i) {
                    var l = searchList[i];
                    if ( l  && l[this.EL] === oEl && 
                            (!sType || sType === l[this.TYPE]) ) {
                        results.push({
                            type:   l[this.TYPE],
                            fn:     l[this.FN],
                            obj:    l[this.OBJ],
                            adjust: l[this.OVERRIDE],
                            scope:  l[this.ADJ_SCOPE],
                            index:  i
                        });
                    }
                }
            }
        }

        return (results.length) ? results : null;
    },

		stopPropagation : function(K) { 
			if(K.stopPropagation) {
				K.stopPropagation();
			} else {
				K.cancelBubble=true;
			}
		},

		onMouseWheel: function(observable, observer, observerContext) {
			var _et = (document.all) ? 'mousewheel' : 'DOMMouseScroll';
			var _scrollHandler = function(e) {
				var delta = 0;
				if (!e) { e = window.event; }
				if (e.wheelDelta) {
					delta = e.wheelDelta / 120;
					if (window.opera) { delta = -delta; }
				} else if (e.detail) { delta = -e.detail / 3; }
				if (delta) { observer(-delta); }
				GNW.util.Event.stopPropagation(e);
			};
			GNW.util.Event.on(observable, _et, _scrollHandler, observerContext);
	}

}
