Zapatec Utils Overview

Overview | File | Class | Tree | Index | Help

Zapatec Utils

PREV NEXT | FRAMES NO FRAMES

## utils.js

#### Summary

Common functions used in all Zapatec widgets.

     Copyright (c) 2004-2007 by Zapatec, Inc.
     http://www.zapatec.com
     1700 MLK Way, Berkeley, California,
     94709, U.S.A.
     All rights reserved.

    /**
     * @fileoverview Common functions used in all Zapatec widgets.
     *
     * <pre>
     * Copyright (c) 2004-2007 by Zapatec, Inc.
     * http://www.zapatec.com
     * 1700 MLK Way, Berkeley, California,
     * 94709, U.S.A.
     * All rights reserved.
     * </pre>
     */

    /* $Id: utils.js 7850 2007-08-15 14:11:42Z alex $ */

    if (typeof Zapatec == 'undefined') {
    	/**
    	 * @ignore Namespace definition.
    	 */
    	Zapatec = function() {};
    }

    /// define the Utils namespace
    Zapatec.Utils = {};

    /// Deprecated. Use Zapatec.Utils.getElementOffset instead.
    /// Retrieves the absolute position (relative to <body>) of a given element.
    ///
    /// @param el [HTMLElement] reference to the element.
    /// @return [object] { x, y } containing the position.
    Zapatec.Utils.getAbsolutePos = function(el, scrollOff) {
    	var SL = 0, ST = 0;
    	if (!scrollOff) {
    		var is_div = /^div$/i.test(el.tagName);
    		if (is_div && el.scrollLeft)
    			SL = el.scrollLeft;
    		if (is_div && el.scrollTop)
    			ST = el.scrollTop;
    	}
    	var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };
    	if (el.offsetParent) {
    		var tmp = this.getAbsolutePos(el.offsetParent);
    		r.x += tmp.x;
    		r.y += tmp.y;
    	}
    	return r;
    };

    /**
     * Returns absolute position of an element on the page and its width and height.
     *
     * @private
     * @param {object} oEl Element object
     * @return Offset: left or x - left offset; top or y - top offset,
     * width - element width, height - element height.
     * @object
     */
    Zapatec.Utils.getElementOffset = function(oEl) {
    	var iLeft = iTop = iWidth = iHeight = 0;
    	var sTag;
    	if (oEl.getBoundingClientRect) {
    		// IE
    		var oRect = oEl.getBoundingClientRect();
    		iLeft = oRect.left;
    		iTop = oRect.top;
    		iWidth = oRect.right - iLeft;
    		iHeight = oRect.bottom - iTop;
    		// getBoundingClientRect returns coordinates relative to the window
    		// Why "- 2" is explained here:
    		// http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/reference/methods/getboundingclientrect.asp
    		iLeft += Zapatec.Utils.getPageScrollX() - 2;
    		iTop += Zapatec.Utils.getPageScrollY() - 2;
    	} else {
    		// Others
    		iWidth = oEl.offsetWidth;
    		iHeight = oEl.offsetHeight;
    		var sPos = Zapatec.Utils.getStyleProperty(oEl, 'position');
    		if (sPos == 'fixed') {
    			iLeft = oEl.offsetLeft + Zapatec.Utils.getPageScrollX();
    			iTop = oEl.offsetTop + Zapatec.Utils.getPageScrollY();
    		} else if (sPos == 'absolute') {
    			while (oEl) {
    				sTag = oEl.tagName;
    				if (sTag) {
    					sTag = sTag.toLowerCase();
    					// Safari adds margin of the body
    					if (sTag != 'body' && sTag != 'html') {
    						iLeft += parseInt(oEl.offsetLeft, 10) || 0;
    						iTop += parseInt(oEl.offsetTop, 10) || 0;
    					}
    				}
    				oEl = oEl.offsetParent;
    				sTag = oEl ? oEl.tagName : null;
    				if (sTag) {
    					sTag = sTag.toLowerCase();
    					if (sTag != 'body' && sTag != 'html') {
    						iLeft -= oEl.scrollLeft;
    						iTop -= oEl.scrollTop;
    					}
    				}
    			}
    		} else {
    			var bMoz = (Zapatec.is_gecko && !Zapatec.is_khtml);
    			var fStyle = Zapatec.Utils.getStyleProperty;
    			var oP = oEl;
    			while (oP) {
    				// -moz-box-sizing must be "border-box" to prevent subtraction of body
    				// border in Mozilla
    				if (bMoz) {
    					sTag = oP.tagName;
    					if (sTag) {
    						sTag = sTag.toLowerCase();
    						if (sTag == 'body' && !(fStyle(oP, '-moz-box-sizing') == 'border-box')) {
    							iLeft += parseInt(fStyle(oP, 'border-left-width'));
    							iTop += parseInt(fStyle(oP, 'border-top-width'));
    						}
    					}
    				}
    				iLeft += parseInt(oP.offsetLeft, 10) || 0;
    				iTop += parseInt(oP.offsetTop, 10) || 0;
    				oP = oP.offsetParent;
    			}
    			oP = oEl;
    			while (oP.parentNode) {
    				oP = oP.parentNode;
    				sTag = oP.tagName;
    				if (sTag) {
    					sTag = sTag.toLowerCase();
    					if (sTag != 'body' && sTag != 'html' && sTag != 'tr') {
    						iLeft -= oP.scrollLeft;
    						iTop -= oP.scrollTop;
    					}
    				}
    			}
    		}
    	}
    	return {
    		left: iLeft,
    		top: iTop,
    		x: iLeft,
    		y: iTop,
    		width: iWidth,
    		height: iHeight
    	};
    };

    /**
     * Returns offset of content of a scrollable element relative to the document
     * body. Offset is calulated as offset of an element minus scrollLeft/scrollTop
     * value.
     *
     * @private
     * @param {object} oEl Element object
     * @return Offset: left or x - left offset; top or y - top offset
     * @object
     */
    Zapatec.Utils.getElementOffsetScrollable = function(oEl) {
    	var oPos = Zapatec.Utils.getElementOffset(oEl);
    	if (oEl.scrollLeft) {
    		oPos.left -= oEl.scrollLeft;
    		oPos.x = oPos.left;
    	}
    	if (oEl.scrollTop) {
    		oPos.top -= oEl.scrollTop;
    		oPos.y = oPos.top;
    	}
    	return oPos;
    };

    /// Modify the position of a box to fit in browser's view.  This function will
    /// modify the passed object itself, so it doesn't need to return a value.
    ///
    /// @param [object] box { x, y, width, height } specifying the area.
    Zapatec.Utils.fixBoxPosition = function(box, leave) {
    	var screenX = Zapatec.Utils.getPageScrollX();
    	var screenY = Zapatec.Utils.getPageScrollY();
    	var sizes = Zapatec.Utils.getWindowSize();
    	leave = parseInt(leave, 10) || 0;
    	if (box.x < screenX) {
    		box.x = screenX + leave;
    	}
    	if (box.y < screenY) {
    		box.y = screenY + leave;
    	}
    	if (box.x + box.width > screenX + sizes.width) {
    		box.x = screenX + sizes.width - box.width - leave;
    	}
    	if (box.y + box.height > screenY + sizes.height) {
    		box.y = screenY + sizes.height - box.height - leave;
    	}
    };

    /// Determines if an event is related to a certain element.  This is a poor
    /// substitute for some events that are missing from DOM since forever (like
    /// onenter, onleave, which MSIE provides).  Basically onmouseover and
    /// onmouseout are fired even if the mouse was already in the element but moved
    /// from text to a blank area, so in order not to close a popup element when
    /// onmouseout occurs in this situation, one would need to first check if the
    /// event is not related to that popup element:
    ///
    /// \code
    ///      function handler_onMouseOut(event) {
    ///         if (!Zapatec.Utils.isRelated(this, event)) {
    ///            /// can safely hide it now
    ///            this.style.display = "none";
    ///         }
    ///      }
    /// \endcode
    ///
    /// @param el [HTMLElement] reference to the element to check the event against
    /// @param evt [Event] reference to the Event object
    /// @return [boolean] true if the event is related to the element
    Zapatec.Utils.isRelated = function (el, evt) {
    	evt || (evt = window.event);
    	var related = evt.relatedTarget;
    	if (!related) {
    		var type = evt.type;
    		if (type == "mouseover") {
    			related = evt.fromElement;
    		} else if (type == "mouseout") {
    			related = evt.toElement;
    		}
    	}
    	try {
    		while (related) {
    			if (related == el) {
    				return true;
    			}
    			related = related.parentNode;
    		}
    	} catch(e) {};
    	return false;
    };

    /// Remove a certain [CSS] class from the given element.
    /// @param el [HTMLElement] reference to the element.
    /// @param className [string] the class to remove.
    Zapatec.Utils.removeClass = function(el, className) {
    	if (!(el && el.className)) {
    		return;
    	}
    	var cls = el.className.split(" ");
    	var ar = [];
    	for (var i = cls.length; i > 0;) {
    		if (cls[--i] != className) {
    			ar[ar.length] = cls[i];
    		}
    	}
    	el.className = ar.join(" ");
    };

    /// Appends a certain [CSS] class to the given element.
    /// @param el [HTMLElement] reference to the element.
    /// @param className [string] the class to append.
    Zapatec.Utils.addClass = function(el, className) {
    	Zapatec.Utils.removeClass(el, className);
    	el.className += " " + className;
    };

    /// Replaces the given class with another class.
    /// @param el [HTMLElement] reference to the element.
    /// @param className [string] the class to replace.
    /// @param withClassName [string] the class to replace with.
    Zapatec.Utils.replaceClass = function(el, className, withClassName) {
    	if (!Zapatec.isHtmlElement(el) || !className) {
    		return false;
    	}
    	el.className.replace(className, withClassName);
    };

    /// Retrieves the current target element for some event (useful when bubbling).
    /// This function is not actually very useful, but it's legacy from the old calendar code.
    /// @param ev [Event] the event object.
    /// @return [HTMLElement] window.event.srcElement for MSIE, ev.currentTarget for other browsers.
    Zapatec.Utils.getElement = function(ev) {
    	if (Zapatec.is_ie) {
    		return window.event.srcElement;
    	} else {
    		return ev.currentTarget;
    	}
    };

    /// Retrieves the target element for some event (useful when bubbling).
    /// This function is not actually very useful, but it's legacy from the old calendar code.
    /// @param ev [Event] the event object.
    /// @return [HTMLElement] window.event.srcElement for MSIE, ev.target for other browsers.
    Zapatec.Utils.getTargetElement = function(ev) {
    	if (Zapatec.is_ie) {
    		return window.event.srcElement;
    	} else {
    		return ev.target;
    	}
    };

    /**
     * Returns mouse position during the event.
     *
     * @param {object} oEv Optional. Event object
     * @return Mouse position during the event:
     * <pre>
     * {
     *   pageX: [number] x coordinate relative to the document,
     *   pageY: [number] y coordinate relative to the document,
     *   clientX: [number] x coordinate relative to the window,
     *   clientY: [number] y coordinate relative to the window
     * }
     * </pre>
     * @type object
     */
    Zapatec.Utils.getMousePos = function(oEv) {
    	oEv || (oEv = window.event);
    	var oPos = {
    		pageX: 0,
    		pageY: 0,
    		clientX: 0,
    		clientY: 0
    	};
    	if (oEv) {
    		var bIsPageX = (typeof oEv.pageX != 'undefined');
    		var bIsClientX = (typeof oEv.clientX != 'undefined');
    		if (bIsPageX || bIsClientX) {
    			if (bIsPageX) {
    				oPos.pageX = oEv.pageX;
    				oPos.pageY = oEv.pageY;
    			} else {
    				oPos.pageX = oEv.clientX + Zapatec.Utils.getPageScrollX();
    				oPos.pageY = oEv.clientY + Zapatec.Utils.getPageScrollY();
    			}
    			if (bIsClientX) {
    				oPos.clientX = oEv.clientX;
    				oPos.clientY = oEv.clientY;
    			} else {
    				oPos.clientX = oEv.pageX - Zapatec.Utils.getPageScrollX();
    				oPos.clientY = oEv.pageY - Zapatec.Utils.getPageScrollY();
    			}
    		}
    	}
    	return oPos;
    };

    /// Stops bubbling and propagation of some event.
    /// @param ev [Event] the event object
    /// @return false
    Zapatec.Utils.stopEvent = function(ev) {
    	ev || (ev = window.event);
    	if (ev) {
    		if (Zapatec.is_ie) {
    			ev.cancelBubble = true;
    			ev.returnValue = false;
    		} else {
    			ev.preventDefault();
    			ev.stopPropagation();
    		}
    	}
    	return false;
    };

    Zapatec.Utils.removeOnUnload = [];

    /**
     * Adds event handler to certain element or widget using DOM addEventListener or
     * MSIE attachEvent method. Doing this means that you can add multiple handlers
     * for the same object and event, and they will be called in order.
     *
     * @param {object} oElement Element object
     * @param {string} sEvent Event name
     * @param {function} fListener Event listener
     * @param {boolean} bUseCapture Optional. Default: false. For details see
     * http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html#Events-EventTarget-addEventListener
     */
    Zapatec.Utils.addEvent = function(oElement, sEvent, fListener, bUseCapture) {
    	if (oElement.addEventListener) {
    		// W3C
    		if (!bUseCapture) {
    			bUseCapture = false;
    		}
    		oElement.addEventListener(sEvent, fListener, bUseCapture);
    	} else if (oElement.attachEvent) {
    		// IE
    		oElement.detachEvent('on' + sEvent, fListener);
    		oElement.attachEvent('on' + sEvent, fListener);
    		if (bUseCapture) {
    			oElement.setCapture(false);
    		}
    	}
    	Zapatec.Utils.removeOnUnload.push({
    		'element': oElement,
    		'event': sEvent,
    		'listener': fListener,
    		'capture': bUseCapture
    	});
    };

    /**
     * Removes event handler added with {@link Zapatec.Utils#addEvent}.
     *
     * @param {object} oElement Element object
     * @param {string} sEvent Event name
     * @param {function} fListener Event listener
     * @param {boolean} bUseCapture Optional. Default: false. For details see
     * http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html#Events-EventTarget-removeEventListener
     */
    Zapatec.Utils.removeEvent = function(oElement, sEvent, fListener, bUseCapture) {
    	if (oElement.removeEventListener) {
    		// W3C
    		oElement.removeEventListener(sEvent, fListener, bUseCapture);
    	} else if (oElement.detachEvent) {
    		// IE
    		oElement.detachEvent('on' + sEvent, fListener);
    	}
    	for (var iLis = Zapatec.Utils.removeOnUnload.length - 1; iLis >= 0; iLis--) {
    		var oParams = Zapatec.Utils.removeOnUnload[iLis];
    		if (!oParams) {
    			continue;
    		}
    		if (oElement == oParams['element'] && sEvent == oParams['event'] &&
    		 fListener == oParams['listener'] && bUseCapture == oParams['capture']) {
    			Zapatec.Utils.removeOnUnload[iLis] = null;
    			Zapatec.Utils.removeEvent(
    			 oParams['element'],
    			 oParams['event'],
    			 oParams['listener'],
    			 oParams['capture']
    			);
    		}
    	}
    };

    /// Create an element of a certain type using document.createElement().  A
    /// function was needed in order to add some common attributes to all created
    /// elements, but also in order to be able to use it in XHTML too (Gecko and
    /// other W3C-compliant browsers).
    ///
    /// This function will create an element of the given type and set certain
    /// properties to it: unselectable for IE, and the CSS "-moz-user-select" for
    /// Gecko, in order to make the element unselectable in these browsers.
    /// Optionally, if the second argument is passed, it will appendChild() the
    /// newly created element to its parent.
    ///
    /// @param type [string] the tag name of the new element.
    /// @param parent [HTMLElement, optional] a parent for the new element.
    /// @param selectable [boolean] the flag to indicate wether element is selectable(rather usefull).
    /// @return [HTMLElement] reference to the new element.
    Zapatec.Utils.createElement = function(type, parent, selectable) {
    	var el = null;
    	if (document.createElementNS)
    		// use the XHTML namespace; IE won't normally get here unless
    		// _they_ "fix" the DOM2 implementation.
    		el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
    	else
    		el = document.createElement(type);
    	if (typeof parent != "undefined" && parent != null)
    		parent.appendChild(el);
    	if (!selectable) {
    		if (Zapatec.is_ie)
    			el.setAttribute("unselectable", true);
    		if (Zapatec.is_gecko)
    			el.style.setProperty("-moz-user-select", "none", "");
    	}
    	return el;
    };

    // Cookie management

    /// Sets a cooke given certain specifications.  It overrides any existing
    /// cookie with the same name.
    ///
    /// @param name [string] the cookie name.
    /// @param value [string] the cookie value.
    /// @param domain [string, optional] the cookie domain.
    /// @param path [string, optional] the cookie path.
    /// @param exp_days [number, optional] number of days of cookie validity.
    Zapatec.Utils.writeCookie = function(name, value, domain, path, exp_days) {
    	value = escape(value);
    	var ck = name + "=" + value, exp;
    	if (domain)
    		ck += ";domain=" + domain;
    	if (path)
    		ck += ";path=" + path;
    	if (exp_days) {
    		exp = new Date();
    		exp.setTime(exp_days * 86400000 + exp.getTime());
    		ck += ";expires=" + exp.toGMTString();
    	}
    	document.cookie = ck;
    };

    /**
     * Retrieves the value of a cookie.
     *
     * @param name [string] the cookie name
     * @return [string or null] a string with the cookie value, or null if it can't be found.
     */

    /* ? inside regular expression is not supported in IE 5.0
    Zapatec.Utils.getCookie = function(name) {
    	var re = new RegExp("(^|;\\s*)" + name + "\\s*=(.*?)(;|$)");
    	if (re.test(document.cookie)) {
    		var value = RegExp.$2;
    		value = unescape(value);
    		return (value);
    	}
    	return null;
    };
    */

    Zapatec.Utils.getCookie = function(name) {
    	var pattern = name + "=";
    	var tokenPos = 0;
    	while (tokenPos < document.cookie.length) {
    		var valuePos = tokenPos + pattern.length;
    		if (document.cookie.substring(tokenPos, valuePos) == pattern) {
    			var endValuePos = document.cookie.indexOf(";", valuePos);
    			if (endValuePos == -1) { // Last cookie
    				endValuePos = document.cookie.length;
    			}
    			return unescape(document.cookie.substring(valuePos, endValuePos));
    		}
    		tokenPos=document.cookie.indexOf(" ",tokenPos)+1;
    		if (tokenPos == 0) { // No more tokens
    			break;
    		}
    	}
    	return null;
    };

    /**
     * Given an object, create a string suitable for saving the object in a cookie.
     * This is similar to serialization.  WARNING: it does not support nested
     * objects.
     *
     * @param obj [Object] reference to the object to serialize.
     * @return [string] the serialized object.
     */
    Zapatec.Utils.makePref = function(obj) {
    	function stringify(val) {
    		if (typeof val == "object" && !val)
    			return "null";
    		else if (typeof val == "number" || typeof val == "boolean")
    			return val;
    		else if (typeof val == "string")
    			return '"' + val.replace(/\x22/, "\\22") + '"';
    		else return null;
    	};
    	var txt = "", i;
    	for (i in obj)
    		txt += (txt ? ",'" : "'") + i + "':" + stringify(obj[i]);
    	return txt;
    };

    /**
     * The reverse of Zapatec.Utils.makePref(), this function unserializes the
     * given string and creates an object from it.
     *
     * @param txt [string] the serialized value.
     * @return [Object] a new object if it was created successfully or null otherwise.
     */
    Zapatec.Utils.loadPref = function(txt) {
    	var obj = null;
    	try {
    		eval("obj={" + txt + "}");
    	} catch(e) {}
    	return obj;
    };

    /**
     * Merges the values of the source object into the destination object.
     *
     * @param dest [Object] the destination object.
     * @param src [Object] the source object.
     */
    Zapatec.Utils.mergeObjects = function(dest, src) {
    	for (var i in src)
    		dest[i] = src[i];
    };

    // based on the WCH idea
    // http://www.aplus.co.yu/WCH/code3/WCH.js

    /// \defgroup WCH functions
    //@{

    Zapatec.Utils.__wch_id = 0;	/**< [number, static] used to create ID-s for the WCH objects */

    /**
     * Creates WCH object.
     *
     * <pre>
     * This function does nothing if the browser is not IE 5.5 or IE 6. A WCH object
     * is one of the most bizarre tricks to avoid a notorious IE bug: IE normally
     * shows "windowed controls" on top of any HTML elements, regardless of any
     * z-index that might be specified in CSS. This technique is described at:
     * http://www.aplus.co.yu/WCH/
     *
     * A "WCH object" is actually an HTMLIFrame element having a certain "CSS
     * filter" (proprietary MSIE extension) that forces opacity zero.  This object,
     * displayed on top of a windowed control such as a select box, will completely
     * hide the select box, allowing us to place other HTMLElement objects above.
     *
     * WCH stands for "Windowed Controls Hider".
     * </pre>
     *
     * @param {object} oEl Optional. Element where to create the WCH IFRAME.
     * @return New WCH IFRAME object for IE 5.5 and IE 6 or null for other browsers.
     * Undefined in case of error.
     * @type object
     */
    Zapatec.Utils.createWCH = function(oEl) {
    	// This feature is needed only for IE 5.5 and IE 6
    	if (!Zapatec.is_ie || Zapatec.is_ie5 || Zapatec.is_ie7) {
    		return null;
    	}
    	var sId = 'WCH' + (++Zapatec.Utils.__wch_id);
    	// javascript:false instead of about:blank prevents non-secure alert on ssl
    	// server in IE 6
    	var sIframe = [
    		'<iframe id="',
    		sId,
    		'" scrolling="no" frameborder="0" style="z-index:0;position:absolute;visibility:hidden;filter:progid:DXImageTransform.Microsoft.alpha(style=0,opacity=0);border:0;top:0;left:0;width:0;height:0" src="javascript:false"></iframe>'
    	].join('')
    	if (!oEl) {
    		oEl = document.body;
    	}
    	// According to http://msdn2.microsoft.com/en-us/library/ms536452.aspx
    	// You cannot insert text while the document is loading. Wait for the onload
    	// event to fire before attempting to call this method.
    	if (Zapatec.windowLoaded) {
    		oEl.insertAdjacentHTML('beforeEnd', sIframe);
    	} else {
    		Zapatec.Utils.addEvent(window, 'load', function() {
    			oEl.insertAdjacentHTML('beforeEnd', sIframe);
    			oEl = null;
    		});
    	}
    	return document.getElementById(sId);
    };

    /**
     * Configure a given WCH object to be displayed on top of the given element.
     * Optionally, a second element can be passed, and in this case it will setup
     * the WCH object to cover both elements.
     *
     * @param f [HTMLIFrame] the WCH object
     * @param el [HTMLElement] the element to cover.
     * @param el2 [HTMLElement, optional] another element to cover.
     */
    Zapatec.Utils.setupWCH_el = function(f, el, el2) {
    	if (f) {
    		var pos = Zapatec.Utils.getAbsolutePos(el),
    			X1 = pos.x,
    			Y1 = pos.y,
    			X2 = X1 + el.offsetWidth,
    			Y2 = Y1 + el.offsetHeight;
    		if (el2) {
    			var p2 = Zapatec.Utils.getAbsolutePos(el2),
    				XX1 = p2.x,
    				YY1 = p2.y,
    				XX2 = XX1 + el2.offsetWidth,
    				YY2 = YY1 + el2.offsetHeight;
    			if (X1 > XX1)
    				X1 = XX1;
    			if (Y1 > YY1)
    				Y1 = YY1;
    			if (X2 < XX2)
    				X2 = XX2;
    			if (Y2 < YY2)
    				Y2 = YY2;
    		}
    		Zapatec.Utils.setupWCH(f, X1, Y1, X2-X1, Y2-Y1);
    	}
    };

    /**
     * Configure a WCH object to cover a certain part of the screen.
     *
     * @param f [HTMLIFrame] the WCH object.
     * @param x [number] the X coordinate.
     * @param y [number] the Y coordinate.
     * @param w [number] the width of the area.
     * @param h [number] the height of the area.
     */
    Zapatec.Utils.setupWCH = function(f, x, y, w, h) {
    	if (f) {
    		var s = f.style;
    		(typeof x != "undefined") && (s.left = x + "px");
    		(typeof y != "undefined") && (s.top = y + "px");
    		(typeof w != "undefined") && (s.width = w + "px");
    		(typeof h != "undefined") && (s.height = h + "px");
    		s.visibility = "inherit";
    	}
    };

    /**
     * Hide a WCH object.
     *
     * @param f [HTMLIFrame] object to hide.
     */
    Zapatec.Utils.hideWCH = function(f) {
    	if (f)
    		f.style.visibility = "hidden";
    };

    //@}

    /**
     * Returns current document vertical scroll position.
     *
     * @return Current document vertical scroll position
     * @type number
     */
    Zapatec.Utils.getPageScrollY = function() {
    	if (window.pageYOffset) {
    		return window.pageYOffset;
    	} else if (document.body && document.body.scrollTop) {
    		return document.body.scrollTop;
    	} else if (document.documentElement && document.documentElement.scrollTop) {
    		return document.documentElement.scrollTop;
    	}
    	return 0;
    };

    /**
     * Returns current document horizontal scroll position.
     *
     * @return Current document horizontal scroll position
     * @type number
     */
    Zapatec.Utils.getPageScrollX = function() {
    	if (window.pageXOffset) {
    		return window.pageXOffset;
    	} else if (document.body && document.body.scrollLeft) {
    		return document.body.scrollLeft;
    	} else if (document.documentElement && document.documentElement.scrollLeft) {
    		return document.documentElement.scrollLeft;
    	}
    	return 0;
    };

    /**
     * Zapatec ScrollWithWindow Widget. Used to produce effect similar to
     * "position: fixed".
     */
    Zapatec.ScrollWithWindow = {};

    /**
     * Holds references to fixed elements.
     * @private
     */
    Zapatec.ScrollWithWindow.list = [];

    /**
     * Stickiness parameter. Set to a number between 0 and 1, lower means longer
     * scrolling.
     */
    Zapatec.ScrollWithWindow.stickiness = 0.25;

    /**
     * Registers a given object to have its style.top set equal to the window
     * scroll position as the browser scrolls.
     *
     * @param {object} oElement A reference to the element to scroll
     */
    Zapatec.ScrollWithWindow.register = function(oElement) {
    	var iTop = oElement.offsetTop || 0;
    	var iLeft = oElement.offsetLeft || 0;
    	Zapatec.ScrollWithWindow.list.push({
    		node: oElement,
    		origTop: iTop,
    		origLeft: iLeft
    	});
    	// Turn on scrolling
    	if (!Zapatec.ScrollWithWindow.interval) {
    		Zapatec.ScrollWithWindow.on();
    	}
    };

    /**
     * Unregisters a given object.
     *
     * @param {object} oElement A reference to the element to scroll
     */
    Zapatec.ScrollWithWindow.unregister = function(oElement) {
    	for (var iItem = 0; iItem < Zapatec.ScrollWithWindow.list.length; iItem++) {
    		var oItem = Zapatec.ScrollWithWindow.list[iItem];
    		if (oElement == oItem.node) {
    			Zapatec.ScrollWithWindow.list.splice(iItem, 1);
    			// Turn off scrolling when list becomes empty
    			if (!Zapatec.ScrollWithWindow.list.length) {
    				Zapatec.ScrollWithWindow.off();
    			}
    			return;
    		}
    	}
    };

    /**
     * Called each time the window is scrolled to set objects' top offset.
     *
     * @private
     * @param {number} iTop New top offset
     */
    Zapatec.ScrollWithWindow.moveTop = function(iTop) {
    	Zapatec.ScrollWithWindow.top += (iTop - Zapatec.ScrollWithWindow.top) *
    	 Zapatec.ScrollWithWindow.stickiness;
    	if (Math.abs(Zapatec.ScrollWithWindow.top - iTop) <= 1) {
    		Zapatec.ScrollWithWindow.top = iTop;
    	}
    	for (var iItem = 0; iItem < Zapatec.ScrollWithWindow.list.length; iItem++) {
    		var oItem = Zapatec.ScrollWithWindow.list[iItem];
    		var oElement = oItem.node;
    		oElement.style.position = 'absolute';
    		if (!oItem.origTop && oItem.origTop !== 0) {
    			oItem.origTop = parseInt(oElement.style.top) || 0;
    		}
    		oElement.style.top = oItem.origTop +
    		 parseInt(Zapatec.ScrollWithWindow.top) + 'px';
    	}
    };

    /**
     * Called each time the window is scrolled to set objects' left offset.
     *
     * @private
     * @param {number} iLeft New left offset
     */
    Zapatec.ScrollWithWindow.moveLeft = function(iLeft) {
    	Zapatec.ScrollWithWindow.left += (iLeft - Zapatec.ScrollWithWindow.left) *
    	 Zapatec.ScrollWithWindow.stickiness;
    	if (Math.abs(Zapatec.ScrollWithWindow.left - iLeft) <= 1) {
    		Zapatec.ScrollWithWindow.left = iLeft;
    	}
    	for (var iItem = 0; iItem < Zapatec.ScrollWithWindow.list.length; iItem++) {
    		var oItem = Zapatec.ScrollWithWindow.list[iItem];
    		var oElement = oItem.node;
    		oElement.style.position = 'absolute';
    		if (!oItem.origLeft && oItem.origLeft !== 0) {
    			oItem.origLeft = parseInt(oElement.style.left) || 0;
    		}
    		oElement.style.left = oItem.origLeft +
    		 parseInt(Zapatec.ScrollWithWindow.left) + 'px';
    	}
    };

    /**
     * Gets called every 50 ms when scrolling is on.
     * @private
     */
    Zapatec.ScrollWithWindow.cycle = function() {
    	var iTop = Zapatec.Utils.getPageScrollY();
    	var iLeft = Zapatec.Utils.getPageScrollX();
    	if (iTop != Zapatec.ScrollWithWindow.top) {
    		Zapatec.ScrollWithWindow.moveTop(iTop);
    	}
    	if (iLeft != Zapatec.ScrollWithWindow.left) {
    		Zapatec.ScrollWithWindow.moveLeft(iLeft);
    	}
    };

    /**
     * Turns scrolling on.
     * @private
     */
    Zapatec.ScrollWithWindow.on = function() {
    	if (Zapatec.ScrollWithWindow.interval) {
    		// Already on
    		return;
    	}
    	Zapatec.ScrollWithWindow.top = Zapatec.Utils.getPageScrollY();
    	Zapatec.ScrollWithWindow.left = Zapatec.Utils.getPageScrollX();
    	Zapatec.ScrollWithWindow.interval =
    	 setInterval(Zapatec.ScrollWithWindow.cycle, 50);
    };

    /**
     * Turns scrolling off.
     * @private
     */
    Zapatec.ScrollWithWindow.off = function() {
    	if (!Zapatec.ScrollWithWindow.interval) {
    		// Already off
    		return;
    	}
    	clearInterval(Zapatec.ScrollWithWindow.interval);
    	Zapatec.ScrollWithWindow.interval = null;
    };

    //Fixates the element on the screen.
    Zapatec.FixateOnScreen = {};

    /**
     * Gets IE's exspression string.
     * @param coord {number or string} coordinate or string to include.
     * @param direction {string} which coordinate we form - x or y.
     * @return {string} formed expression string.
     */
    Zapatec.FixateOnScreen.getExpression = function(coord, direction) {
    	return "Zapatec.Utils.getPageScroll" + direction.toUpperCase() + "() + " + coord;
    };

    /**
     * Gets the actual coordinates, as if the
     * element wasn't fixated.
     * @param element {HTML element} element to seek coordinate of.
     * @return {number} the actual coordinate.
     */
    Zapatec.FixateOnScreen.parseCoordinates = function(element) {
    	//maybe it is not registered
    	if (!this.isRegistered(element)) {
    		return false;
    	}
    	//getting coordinates on screen
    	var x = 0;
    	var y = 0;
    	var style = element.style;
    	if (Zapatec.is_ie && !Zapatec.is_ie7) {
    		//parsing expression's value
    		x = style.getExpression("left").split(" ");
    		x = parseInt(x[x.length - 1], 10);
    		y = style.getExpression("top").split(" ");
    		y = parseInt(y[y.length - 1], 10);
    	} else {
    		//just parsing coordinates
    		x = parseInt(style.left, 10);
    		y = parseInt(style.top, 10);
    	}
    	//adding page scrolling
    	x += Zapatec.Utils.getPageScrollX();
    	y += Zapatec.Utils.getPageScrollY();
    	return {x : x, y : y};
    };

    /**
     * Corects coordinates of the element to become fixed.
     * @param x {number} x coordinate.
     * @param y {number} y coordinate.
     * @return {object} corrected object.
     */
    Zapatec.FixateOnScreen.correctCoordinates = function(x, y) {
    	//calculating screen coordinate
    	position = {x : x, y : y};
    	if (position.x || position.x === 0) {
    		position.x -= Zapatec.Utils.getPageScrollX();
    		if (Zapatec.is_ie && !Zapatec.is_ie7) {
    			//for IE we use expression as CSS property value
    			position.x = this.getExpression(position.x, "X");;
    		} else {
    			position.x += "px";
    		}
    	}
    	if (position.y || position.y === 0) {
    		position.y -= Zapatec.Utils.getPageScrollY();
    		if (Zapatec.is_ie && !Zapatec.is_ie7) {
    			//for IE we use expression as CSS property value
    			position.y = this.getExpression(position.y, "Y");;
    		} else {
    			position.y += "px";
    		}
    	}
    	return position;
    };

    /**
     * Registers the element to be fixated on the screen.
     * @param element {HTML element} element to be fixated.
     * @return {boolean} true if success, otherwise false.
     */
    Zapatec.FixateOnScreen.register = function(element) {
    	//if not HTML element return false
    	if (!Zapatec.isHtmlElement(element)) {
    		return false;
    	}
    	//maybe it is already registered
    	if (this.isRegistered(element)) {
    		return true;
    	}
    	//getting element coordinates
    	var pos = Zapatec.Utils.getElementOffset(element);
    	pos = {
    		x : parseInt(element.style.left, 10) || pos.x,
    		y : parseInt(element.style.top, 10) || pos.y
    	}
    	pos = this.correctCoordinates(pos.x, pos.y);
    	//fixating element
    	if (!Zapatec.is_ie || Zapatec.is_ie7) {
    		//using fixed positioning for all others
    		var restorer = element.restorer;
    		if (!restorer || !restorer.getObject || restorer.getObject() != element) {
    			restorer = element.restorer = new Zapatec.SRProp(element);
    		}
    		restorer.saveProp("style.position");
    		element.style.position = "fixed";
    		element.style.left = pos.x;
    		element.style.top = pos.y;
    	} else {
    		element.style.setExpression("left", pos.x);
    		element.style.setExpression("top", pos.y);
    	}
    	//adding to the list
    	element.zpFixed = true;
    	return true;
    };

    /**
     * Unregisters the element from being fixated
     * on the screen.
     * @param element {HTML element} element to be unregistered.
     * @return {boolean} true if success, otherwise false.
     */
    Zapatec.FixateOnScreen.unregister = function(element) {
    	//if not HTML element return false
    	if (!Zapatec.isHtmlElement(element)) {
    		return false;
    	}
    	var pos = this.parseCoordinates(element);
    	if (pos === false) {
    		return true;
    	}
    	if (Zapatec.is_ie && !Zapatec.is_ie7) {
    		element.style.removeExpression("left");
    		element.style.removeExpression("top");
    	}
    	//setting new coordinates
    	element.style.left = pos.x + "px";
    	element.style.top = pos.y + "px";
    	//restoring position
    	if (!Zapatec.is_ie || Zapatec.is_ie7) {
    		element.restorer.restoreProp("style.position", true);
    	}
    	element.zpFixed = false;
    	return true;
    };

    /**
     * Checks if element is registered.
     * @param element {HTML element} element to check.
     * @return {boolean} true if it is registered, otherwise false.
     */
    Zapatec.FixateOnScreen.isRegistered = function(element) {
    	if (element.zpFixed) {
    		return true;
    	}
    	return false;
    };

    /**
     * Destroys the given element (remove it from the DOM tree) if it's not null
     * and it's parent is not null.
     *
     * @param el [HTMLElement] the element to destroy.
     */
    Zapatec.Utils.destroy = function(el) {
    	if (el && el.parentNode)
    		el.parentNode.removeChild(el);
    };

    /**
     * Opens a new window at a certain URL and having some properties.
     *
     * @param url [string] the URL to open a new window to.
     * @param windowName [string] the name of the new window (as for target attribute).
     * @param width [number] the width of the new window in pixels.
     * @param height [number] the height of the new window in pixels.
     * @param scrollbars [string] "yes" or "no" for scrollbars.
     *
     * @return [object] the new window
     */
    Zapatec.Utils.newCenteredWindow = function(url, windowName, width, height, scrollbars){
    	var leftPosition = 0;
    	var topPosition = 0;
    	if (screen.width)
    		leftPosition = (screen.width -  width)/2;
    	if (screen.height)
    		topPosition = (screen.height -  height)/2;
    	var winArgs =
    		'height=' + height +
    		',width=' + width +
    		',top=' + topPosition +
    		',left=' + leftPosition +
    		',scrollbars=' + scrollbars +
    		',resizable';
    	var win = window.open(url,windowName,winArgs);
    	return win;
    };

    /**
     * Finds the size of the current web page. This is the usable size
     * and does not include the browser's menu and buttons.
     *
     * @return [object] dimension with the height and width of the window
     */
    Zapatec.Utils.getWindowSize = function() {
    	var iWidth = 0;
    	var iHeight = 0;

    	if (Zapatec.is_opera) {
    		iWidth = document.body.clientWidth || 0;
    		iHeight = document.body.clientHeight || 0;
    	} else if (Zapatec.is_khtml) {
    		iWidth = window.innerWidth || 0;
    		iHeight = window.innerHeight || 0;
    	} else if (document.compatMode && document.compatMode == 'CSS1Compat') {
    			// Standards-compliant mode
    		iWidth = document.documentElement.clientWidth || 0;
    		iHeight = document.documentElement.clientHeight || 0;
    	} else {
    			// Non standards-compliant mode
    		iWidth = document.body.clientWidth || 0;
    		iHeight = document.body.clientHeight || 0;
    	}

    	return {
    		width: iWidth,
    		height: iHeight
    	};
    };

    /**
     * Given a reference to a select element, this function will select the option
     * having the given value and optionally will call the default handler for
     * "onchange".
     *
     * @param sel [HTMLSelectElement] reference to the SELECT element.
     * @param val [string] the value that we should select.
     * @param call_default [boolean] true if the default onchange should be called.
     */
    Zapatec.Utils.selectOption = function(sel, val, call_default) {
    	var a = sel.options, i, o;
    	for (i = a.length; --i >= 0;) {
    		o = a[i];
    		o.selected = (o.value == val);
    	}
    	sel.value = val;
    	if (call_default) {
    		if (typeof sel.onchange == "function")
    			sel.onchange();
    		else if (typeof sel.onchange == "string")
    			eval(sel.onchange);
    	}
    };

    /**
     * A more flexible way to get the "nextSibling" of a given element. If the
     * "tag" argument is passed, then this function will return the next sibling
     * that has a certain tag. Otherwise it will simply return el.nextSibling.
     *
     * @param {object} el Element object
     * @param {string} tag Optional. Tag name of the returned element
     * @param {string} alternateTag Optional. Alternate tag name of the returned
     * element
     * @return First element after el having the specified tag; null if element
     * is not found; nextSibling if tag is not specified
     * @type object
     */
    Zapatec.Utils.getNextSibling = function(el, tag, alternateTag) {
    	el = el.nextSibling;
    	if (!tag) {
    		return el;
    	}
    	tag = tag.toLowerCase();
    	if (alternateTag) alternateTag = alternateTag.toLowerCase();
    	while (el) {
    		if (el.nodeType == 1 && (el.tagName.toLowerCase() == tag ||
    		 (alternateTag && el.tagName.toLowerCase() == alternateTag))) {
    			return el;
    		}
    		el = el.nextSibling;
    	}
    	return el;
    };

    /**
     * A more flexible way to get the "previousSibling" of a given element. If the
     * "tag" argument is passed, then this function will return the previous sibling
     * that has a certain tag. Otherwise it will simply return el.previousSibling.
     *
     * @param {object} el Element object
     * @param {string} tag Optional. Tag name of the returned element
     * @param {string} alternateTag Optional. Alternate tag name of the returned
     * element
     * @return First element before el having the specified tag; previousSibling if
     * tag is not specified; null if desired element is not found
     * @type object
     */
    Zapatec.Utils.getPreviousSibling = function(el, tag, alternateTag) {
    	el = el.previousSibling;
    	if (!tag) {
    		return el;
    	}
    	tag = tag.toLowerCase();
    	if (alternateTag) alternateTag = alternateTag.toLowerCase();
    	while (el) {
    		if (el.nodeType == 1 && (el.tagName.toLowerCase() == tag ||
    		 (alternateTag && el.tagName.toLowerCase() == alternateTag))) {
    			return el;
    		}
    		el = el.previousSibling;
    	}
    	return el;
    };

    /**
     * Returns first child of the given element that has a specified tag.
     *
     * @param {object} el Element object
     * @param {string} tag Optional. Tag name of the returned element
     * @param {string} alternateTag Optional. Alternate tag name of the returned
     * element
     * @return First child of the element that has a specified tag; firstChild if
     * tag is not specified; null if desired element is not found
     * @type object
     */
    Zapatec.Utils.getFirstChild = function(el, tag, alternateTag) {
    	if (!el) {
    		return null;
    	}
    	el = el.firstChild;
    	if (!el) {
    		return null;
    	}
    	if (!tag) {
    		return el;
    	}
    	tag = tag.toLowerCase();
    	if (el.nodeType == 1) {
    		if (el.tagName.toLowerCase() == tag) {
    			return el;
    		} else if (alternateTag) {
    			alternateTag = alternateTag.toLowerCase();
    			if (el.tagName.toLowerCase() == alternateTag) {
    				return el;
    			}
    		}
    	}
    	return Zapatec.Utils.getNextSibling(el, tag, alternateTag);
    };

    /**
     * Returns last child of the given element that has a specified tag.
     *
     * @param {object} el Element object
     * @param {string} tag Optional. Tag name of the returned element
     * @param {string} alternateTag Optional. Alternate tag name of the returned
     * element
     * @return Last child of the element that has a specified tag; lastChild if
     * tag is not specified; null if desired element is not found
     * @type object
     */
    Zapatec.Utils.getLastChild = function(el, tag, alternateTag) {
    	if (!el) {
    		return null;
    	}
    	el = el.lastChild;
    	if (!el) {
    		return null;
    	}
    	if (!tag) {
    		return el;
    	}
    	tag = tag.toLowerCase();
    	if (el.nodeType == 1) {
    		if (el.tagName.toLowerCase() == tag) {
    			return el;
    		} else if (alternateTag) {
    			alternateTag = alternateTag.toLowerCase();
    			if (el.tagName.toLowerCase() == alternateTag) {
    				return el;
    			}
    		}
    	}
    	return Zapatec.Utils.getPreviousSibling(el, tag, alternateTag);
    };

    /**
     * Function that concatenates and returns all text child nodes of the
     * specified node.
     *
     * @param objNode [Node] -- reference to the node.
     * @return [string] -- concatenated text child nodes
     */
    Zapatec.Utils.getChildText = function(objNode) {
    	if (objNode == null) {
    		return '';
    	}
    	var arrText = [];
    	var objChild = objNode.firstChild;
    	while (objChild != null) {
    		if (objChild.nodeType == 3) { // Node.TEXT_NODE
    			arrText.push(objChild.data);
    		}
    		objChild = objChild.nextSibling;
    	}
    	return arrText.join(' ');
    };

    /**
     * Similar to the DOM's built in insertBefore.
     * Insert a node after an existing node.
     *
     * @param el [oldNode] The existing element
     * @param el [newNode] the new element to insert after the old one.
     *
     */
    Zapatec.Utils.insertAfter = function(oldNode, newNode) {
    	if(oldNode.nextSibling) {
    		oldNode.parentNode.insertBefore(newNode, oldNode.nextSibling);
    	} else {
    		oldNode.parentNode.appendChild(newNode);
    	}
    }

    Zapatec.Utils._ids = {};	/**< [number, static] maintains a list of generated IDs */

    /**
     * Generates an unique ID, for a certain code (let's say "class").  If the
     * optional "id" argument is passed, then it just returns the id for that code
     * (no generation).  This function is sometimes useful when we need to create
     * elements and be able to access them later by ID.
     *
     * @param code [string] the class of ids.  User defined, can be anything.
     * @param id [string, optional] specify if the ID is already known.
     *
     * @return [string] the unique ID
     */
    Zapatec.Utils.generateID = function(code, id) {
    	if (typeof id == "undefined") {
    		if (typeof this._ids[code] == "undefined")
    			this._ids[code] = 0;
    		id = ++this._ids[code];
    	}
    	return "zapatec-" + code + "-" + id;
    };

    /**
    *  Add a tooltip to the specified element.
    *
    *  Function that adds a custom tooltip for an element.  The "target" is the
    *  element to where the tooltip should be added to, and the "tooltip" is a DIV
    *  that contains the tooltip text.  Optionally, the tooltip DIV can have the
    *  "title" attribute set; if so, its value will be displayed highlighted as
    *  the title of the tooltip.
    *
    *  @param target  reference to or ID of the target element
    *  @param tooltip reference to or ID of the tooltip content element
    */

    Zapatec.Utils.addTooltip = function(target, tooltip) {
    return new Zapatec.Tooltip({target: target, tooltip: tooltip});
    };

    Zapatec.isLite=true;

    Zapatec.Utils.checkLinks = function(){
    	var anchors = document.getElementsByTagName('A');

    	for(var ii = 0; ii < anchors.length; ii++){
    		if(Zapatec.Utils.checkLink(anchors[ii])){
    			return true;
    		}
    	}

    	return false;
    }

    Zapatec.Utils.checkLink = function(lnk){
    	if(!lnk){
    		return false;
    	}

    	if(!/^https?:\/\/((dev|www)\.)?zapatec\.com/i.test(lnk.href)){
    		return false;
    	}

    	// checking if link has some text inside
    	var textContent = ""
    	for(var ii = 0; ii < lnk.childNodes.length; ii++){
    		if(lnk.childNodes[ii].nodeType == 3){
    			textContent += lnk.childNodes[ii].nodeValue;
    		}
    	}

    	if(textContent.length < 4){
    		return false;
    	}

    	var parent = lnk;

    	while(parent && parent.nodeName.toLowerCase() != "html"){
    		// check if element or any of parent nodes are invisible
    		if(
    			Zapatec.Utils.getStyleProperty(parent, "display") == "none" ||
    			Zapatec.Utils.getStyleProperty(parent, "visibility") == "hidden" ||
    			Zapatec.Utils.getStyleProperty(parent, "opacity") == "0" ||
    			Zapatec.Utils.getStyleProperty(parent, "-moz-opacity") == "0" ||
    			/alpha\(opacity=0\)/i.test(Zapatec.Utils.getStyleProperty(parent, "filter"))

    		){
    			return false;
    		}

    		parent = parent.parentNode;
    	}

    	// check if link is outside visible screen area
    	var coords = Zapatec.Utils.getElementOffset(lnk);
    	if(coords.left < 0 || coords.top < 0){
    		return false;
    	}

    	return true;
    }

    Zapatec.Utils.checkActivation = function() {
    	if (!Zapatec.isLite)	return true;

    	var arrProducts=[]

    	add_product=function(script, webdir_in, name_in)
    	{
    	arrProducts[script]={webdir:webdir_in, name:name_in, bActive:false}
    	}

    	add_product('calendar.js', 'prod1',   'Calendar')
    	add_product('zpmenu.js',   'menu',   'Menu')
    	add_product('tree.js',     'prod3',   'Tree')
    	add_product('form.js',     'forms',   'Forms')
    	add_product('effects.js',  'effects', 'Effects')
    	add_product('hoverer.js',  'effects', 'Effects - Hoverer')
    	add_product('slideshow.js','effects', 'Effects - Slideshow')
    	add_product('zpgrid.js',   'grid',    'Grid')
    	add_product('slider.js',   'slider',  'Slider')
    	add_product('zptabs.js',   'tabs',    'Tabs')
    	add_product('zptime.js',   'time',    'Time')
    	add_product('window.js',   'windows', 'Window')

    	var strName, arrName, i
    	var bProduct=false // Flag yes if we have a zapatec script
    	var scripts = document.getElementsByTagName('script');
    	for (i=0; i<scripts.length; i++)
    	{
    		// If wizard then do NOT do link back check, which makes wizard err out
    		if (/wizard.js/i.test(scripts[i].src))
    			return true

    		arrName=scripts[i].src.split('/')
    		if (arrName.length==0)
    			strName=scripts[i]
    		else
    			strName=arrName[arrName.length-1]
    		strName=strName.toLowerCase()
    		// Get each active product
    		if (typeof arrProducts[strName] != 'undefined')
    			{
    			bProduct=true
    			arrProducts[strName].bActive=true
    			}
    	}

    	// Is a LITE product even being used or back link is wrong?
    	if(!bProduct || Zapatec.Utils.checkLinks()){
    		return true;
    	}

    	var strMsg='You are using the Free version of the Zapatec Software.\n'+
    	'While using the Free version, a link to www.zapatec.com in this page is required.'

    	for (i in arrProducts)
    		// Get each active product
    		if (arrProducts[i].bActive==true)
    			strMsg+='\nTo purchase the Zapatec ' + arrProducts[i].name + ' visit www.zapatec.com/website/main/products/' + arrProducts[i].webdir + '/'

    	alert(strMsg)

    	return false;
    }

    /**
     * Makes a copy of an object.
     *
     * @param {object} oSrc Source object to clone
     */
    Zapatec.Utils.clone = function(oSrc) {
    	// If object and not null
    	if (typeof oSrc == 'object' && oSrc) {
    		var oClone = new oSrc.constructor();
    		var fClone = Zapatec.Utils.clone;
    		for (var sProp in oSrc) {
    			oClone[sProp] = fClone(oSrc[sProp]);
    		}
    		return oClone;
    	}
    	return oSrc;
    };

    // Browser sniffing

    /// detect Opera browser
    Zapatec.is_opera = /opera/i.test(navigator.userAgent);

    /// detect a special case of "web browser"
    Zapatec.is_ie = ( /msie/i.test(navigator.userAgent) && !Zapatec.is_opera );

    /// detect IE5.0/Win
    Zapatec.is_ie5 = ( Zapatec.is_ie && /msie 5\.0/i.test(navigator.userAgent) );

    /// detect IE7.0/Win
    Zapatec.is_ie7 = ( Zapatec.is_ie && /msie 7\.0/i.test(navigator.userAgent) );

    /// detect IE for Macintosh
    Zapatec.is_mac_ie = ( /msie.*mac/i.test(navigator.userAgent) && !Zapatec.is_opera );

    /// detect KHTML-based browsers
    Zapatec.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);

    /// detect Konqueror
    Zapatec.is_konqueror = /Konqueror/i.test(navigator.userAgent);

    /// detect Gecko
    Zapatec.is_gecko = /Gecko/i.test(navigator.userAgent);

    /// detect WebKit
    Zapatec.is_webkit = /WebKit/i.test(navigator.userAgent);

    /// detect WebKit version
    Zapatec.webkitVersion = Zapatec.is_webkit?parseInt(navigator.userAgent.replace(
    				/.+WebKit\/([0-9]+)\..+/, "$1")):-1;

    /**
     * Simulation of Object hasOwnProperty() method that is missing in IE 5.0.
     */
    if (!Object.prototype.hasOwnProperty) {
    	Object.prototype.hasOwnProperty = function(strProperty) {
    		try {
    			var objPrototype = this.constructor.prototype;
    			while (objPrototype) {
    				if (objPrototype[strProperty] == this[strProperty]) {
    					return false;
    				}
    				objPrototype = objPrototype.prototype;
    			}
    		} catch (objException) {}
    		return true;
    	};
    }

    /**
     * Simulation of Function call() method that is missing in IE 5.0.
     */
    if (!Function.prototype.call) {
    	Function.prototype.call = function() {
    		var objThis = arguments[0];
    		objThis._this_func = this;
    		var arrArgs = [];
    		for (var iArg = 1; iArg < arguments.length; iArg++) {
    			arrArgs[arrArgs.length] = 'arguments[' + iArg + ']';
    		}
    		var ret = eval('objThis._this_func(' + arrArgs.join(',') + ')');
    		objThis._this_func = null;
    		return ret;
    	};
    }

    /**
     * Simulation of Function apply() method that is missing in IE 5.0.
     */
    if (!Function.prototype.apply) {
    	Function.prototype.apply = function() {
    		var objThis = arguments[0];
    		var objArgs = arguments[1];
    		objThis._this_func = this;
    		var arrArgs = [];
    		if (objArgs) {
    			for (var iArg = 0; iArg < objArgs.length; iArg++) {
    				arrArgs[arrArgs.length] = 'objArgs[' + iArg + ']';
    			}
    		}
    		var ret = eval('objThis._this_func(' + arrArgs.join(',') + ')');
    		objThis._this_func = null;
    		return ret;
    	};
    }

    /**
     * Simulation of Array pop() method that is missing in IE 5.0.
     */
    if (!Array.prototype.pop) {
    	Array.prototype.pop = function() {
    		var last;
    		if (this.length) {
    			last = this[this.length - 1];
    			this.length -= 1;
    		}
    		return last;
    	};
    }

    /**
     * Simulation of Array push() method that is missing in IE 5.0
     */
    if (!Array.prototype.push) {
    	Array.prototype.push = function() {
    		for (var i = 0; i < arguments.length; i++) {
    			this[this.length] = arguments[i];
    		}
    		return this.length;
    	};
    }

    /**
     * Simulation of Array shift() method that is missing in IE 5.0.
     */
    if (!Array.prototype.shift) {
    	Array.prototype.shift = function() {
    		var first;
    		if (this.length) {
    			first = this[0];
    			for (var i = 0; i < this.length - 1; i++) {
    				this[i] = this[i + 1];
    			}
    			this.length -= 1;
    		}
    		return first;
    	};
    }

    /**
     * Simulation of Array unshift() method that is missing in IE 5.0.
     */
    if (!Array.prototype.unshift) {
    	Array.prototype.unshift = function() {
    		if (arguments.length) {
    			var i, len = arguments.length;
    			for (i = this.length + len - 1; i >= len; i--) {
    				this[i] = this[i - len];
    			}
    			for (i = 0; i < len; i++) {
    				this[i] = arguments[i];
    			}
    		}
    		return this.length;
    	};
    }

    /**
     * Simulation of Array splice() method that is missing in IE 5.0.
     */
    if (!Array.prototype.splice) {
    	Array.prototype.splice = function(index, howMany) {
    		var elements = [], removed = [], i;
    		for (i = 2; i < arguments.length; i++) {
    			elements.push(arguments[i]);
    		}
    		for (i = index; (i < index + howMany) && (i < this.length); i++) {
    			removed.push(this[i]);
    		}
    		for (i = index + howMany; i < this.length; i++) {
    			this[i - howMany] = this[i];
    		}
    		this.length -= removed.length;
    		for (i = this.length + elements.length - 1; i >= index + elements.length;
    		 i--) {
    			this[i] = this[i - elements.length];
    		}
    		for (i = 0; i < elements.length; i++) {
    			this[index + i] = elements[i];
    		}
    		return removed;
    	};
    }

    /**
     * Crossbrowser replacement for Array indexOf() method. See:
     * http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:indexOf
     *
     * @param {object} aArr Array to search in
     * @param {any} vSearchEl Element to locate in the array
     * @param {any} iFromInd The index at which to begin the search
     * @return The first index at which a given element can be found in the array,
     * or -1 if it is not present
     * @type number
     */
    Zapatec.Utils.arrIndexOf = function(aArr, vSearchEl, iFromInd) {
    	if (!(aArr instanceof Array)) {
    		return -1;
    	}
    	if (Array.prototype.indexOf) {
    		return aArr.indexOf(vSearchEl, iFromInd);
    	}
    	if (!iFromInd) {
    		iFromInd = 0;
    	}
    	var iEls = aArr.length;
    	for (var iEl = iFromInd; iEl < iEls; iEl++) {
    		if (aArr[iEl] == vSearchEl) {
    			return iEl;
    		}
    	}
    	return -1;
    };

    /**
     * Displays error message. Override this if needed.
     *
     * \param objArgs [number] error object:
     * {
     *   severity: [string, optional] error severity,
     *   description: [string] human readable error description
     * }
     */
    Zapatec.Log = function(objArgs) {
    	// Check arguments
    	if (!objArgs) {
    		return;
    	}
    	// Form error message
    	var strMessage = objArgs.description;
    	if (objArgs.severity) {
    		strMessage = objArgs.severity + ':\n' + strMessage;
    	}
    	// Display error message
    	if (objArgs.type != "warning") {
    		alert(strMessage);
    	}
    };

    /// Zapatec.Utils.Array object which contains additional for arrays method
    Zapatec.Utils.Array = {};

    /**
     * Inserts the element into array.
     * It influences the order in which the elements will be iterated in the for...in cycle.
     *
     * @param arr [array] array to work with.
     * @param el [mixed] element to insert.
     * @param key [string] element to insert.
     * @param nextKey [string] element to be inserted before.
     * @return [string] new Array.
     */
    Zapatec.Utils.Array.insertBefore = function (arr, el, key, nextKey) {
    	var tmp = new Array();
    	for(var i in arr) {
    		if (i == nextKey) {
    			if (key) {
    				tmp[key] = el;
    			} else {
    				tmp.push(el);
    			}
    		}
    		tmp[i] = arr[i];
    	}
    	return tmp;
    }

    /**
     * Extends one class with another. Gives ability to access properties and
     * methods of the superclass having the same names as in subclass.
     *
     * <pre>
     * Zapatec.Widget specific feature: If superclass has static property
     * <b>path</b>, by default subclass will get path to js file from which this
     * function is called into <b>path</b> property instead of value from
     * superclass. This behaviour can be changed with <b>leavePath</b> option below.
     *
     * To determine path correctly when widget module is included using
     * Zapatec.include function, static property <b>id</b> should be set in widget
     * class. Value of this property must be passed to Zapatec.include function as
     * script id.
     *
     * Following additional options are accepted:
     * {
     *   keepPath: [boolean] if true, static property <b>path</b> is inherited from
     *    superclass; useful, for example, when you inherit a widget and want to use
     *    the same path to themes as superclass
     * }
     *
     * Should be used as follows:
     *
     * // Define SuperClass and its methods
     * Zapatec.SuperClass = function(oArg) {
     *   ...
     * };
     *
     * Zapatec.SuperClass.prototype.init = function(oArg) {
     *   ...
     * };
     *
     * // Define SubClass and its methods
     * Zapatec.SubClass = function(oArg) {
     *   ...
     *   // Call constructor of superclass
     *   Zapatec.SubClass.SUPERconstructor.call(this, oArg);
     *   ...
     * };
     *
     * // Unique static id of the widget class to determine path correctly
     * // Neded only if class is derived from Zapatec.Widget class
     * Zapatec.SubClass.id = 'Zapatec.SubClass';
     *
     * // Inherit SuperClass
     * Zapatec.inherit(Zapatec.SubClass, Zapatec.SuperClass);
     *
     * Zapatec.SubClass.prototype.init = function(oArg) {
     *   ...
     *   // Call method of superclass
     *   Zapatec.SubClass.SUPERclass.init.call(this, oArg);
     *   ...
     * };
     *
     * Note: This function must be invoked during page load to determine path to js
     * file from which it is called correctly.
     * </pre>
     *
     * @param {object} oSubClass Inheriting class
     * @param {object} oSuperClass Class from which to inherit
     * @param {object} oArg Additional options (see above)
     */
    Zapatec.inherit = function(oSubClass, oSuperClass, oArg) {
    	// Duplicate prototype of superclass
    	var Inheritance = function() {};
    	Inheritance.prototype = oSuperClass.prototype;
    	oSubClass.prototype = new Inheritance();
    	// Fix constructor property to point to the self constructor because it is
    	// pointing to the nested Inheritance function
    	oSubClass.prototype.constructor = oSubClass;
    	// Reference to constructor of superclass to be able to invoke it
    	oSubClass.SUPERconstructor = oSuperClass;
    	// Reference to prototype of superclass to be able to invoke its methods
    	oSubClass.SUPERclass = oSuperClass.prototype;
    	// Path to js file from which this function is called
    	if (typeof oSuperClass.path != 'undefined') {
    		if (oArg && oArg.keepPath) {
    			oSubClass.path = oSuperClass.path;
    		} else {
    			oSubClass.path = Zapatec.getPath(oSubClass.id);
    		}
    	}
    };

    /**
     * Returns path from the script element with specified id. If id is not
     * specified or specified id is not found on the page, returns path from the
     * last loaded script element. Splits src attribute value and returns path
     * without js file name. Returns empty string if path is not found.
     *
     * <pre>
     * If specified id is not found on the page, returns last script element instead
     * of alerting. This gives ability for developer to include script using
     * Zapatec#include and specify id because in some cases it may be needed to get
     * correct path; and for user to include script directly into the page without
     * remembering internal Zapatec ids because id is not needed to determine path
     * correctly in this case.
     *
     * Developer is responsible for checking that the same script id is passed to
     * Zapatec#include and Zapatec#getPath because there is no alert if they are
     * different and result may be wrong.
     *
     * Note: This function must be invoked during page load to determine path to js
     * file from which it is called correctly.
     * </pre>
     *
     * @param {string} sId Optional. Id of the script element
     * @return Path to the script, e.g. '../src/' or '' if path is not found
     * @type string
     */
    Zapatec.getPath = function(sId) {
    	// Get script source
    	var sSrc;
    	if (typeof sId == 'string') {
    		// Try to use element with specified id
    		var oScript = document.getElementById(sId);
    		if (oScript) {
    			sSrc = oScript.getAttribute('src');
    		}
    	}
    	if (!sSrc) {
    		if (typeof Zapatec.lastLoadedModule == 'string') {
    			return Zapatec.lastLoadedModule;
    		}
    		// Get src from last script element
    		// Simply document.getElementsByTagName('script') doesn't work here because
    		// FF 2 delays with adding a script to the list
    		if (document.documentElement) {
    			var sHtml = document.documentElement.innerHTML;
    			// Value from innerHTML doesn't contain redundant spaces
    			var aMatch = sHtml.match(/<script[^>]+src=[^>]+>/gi);
    			if (aMatch && aMatch.length) {
    				sHtml = aMatch[aMatch.length - 1];
    				// Value from innerHTML is always inside double quotes
    				aMatch = sHtml.match(/src="([^"]+)/i);
    				if (aMatch && aMatch.length == 2) {
    					sSrc = aMatch[1];
    				}
    			}
    		}
    		if (!sSrc) {
    			return '';
    		}
    	}
    	// Backslashes are allowed in IE
    	sSrc = sSrc.replace(/\\/g, '/');
    	// Get path
    	var aTokens = sSrc.split('?');
    	aTokens = aTokens[0].split('/');
    	// Remove last token
    	aTokens = aTokens.slice(0, -1);
    	if (!aTokens.length) {
    		return '';
    	}
    	return aTokens.join('/') + '/';
    };

    /**
     * Assigns event object to "window.event". Used in
     * {@link Zapatec.Utils#emulateWindowEvent} to prevent multiple identical event
     * listeners. For details see:
     * http://developer.mozilla.org/en/docs/DOM:element.addEventListener
     *
     * @private
     * @param {object} oEvent Event object
     */
    Zapatec.Utils.setWindowEvent = function(oEvent) {
    	if (oEvent) {
    		window.event = oEvent;
    	}
    };

    /**
     * Emulates "window.event" for certain events in Mozilla. To be able to access
     * Event object when event handler is set using element attribute, e.g.
     * &lt;a onclick="eventHandler()"&gt;.
     *
     * @param {object} aEvents Array of event names where "window.event" is needed,
     * e.g. ['click', 'dblclick'].
     */
    Zapatec.Utils.emulateWindowEvent = function(aEvents) {
    	if (document.addEventListener) {
    		// Set up emulation for the events
    		var iEvents = aEvents.length;
    		var oUtils = Zapatec.Utils;
    		var iEvent;
    		for (iEvent = 0; iEvent < iEvents; iEvent++) {
    			document.addEventListener(aEvents[iEvent], oUtils.setWindowEvent, true);
    		}
    	}
    };

    /**
     * This is some kind of indicator that window has already loaded.
     */
    Zapatec.windowLoaded = typeof(document.readyState) != 'undefined' ?
    	(
    		document.readyState == 'loaded' || // Konqueror
    		document.readyState == 'complete' // IE/Opera
    	) :
    	// Mozilla
    	document.getElementsByTagName != null && typeof(document.getElementsByTagName('body')[0]) != 'undefined'
    ;

    Zapatec.Utils.addEvent(window, "load", function() {Zapatec.windowLoaded = true;});

    /*
     * Use this method to display your custom message before unload event.
     * For example you could warn user that he has some unsaved changes
     * on page.
     *
     * \param msg [string] - message to display. Default value - "All your changes will be lost.":
     * \param win [object] - reference to window object. By default - current window
     */
    Zapatec.Utils.warnUnload = function(msg, win){
    		Zapatec.Utils.warnUnloadFlag = true;
    	if(typeof(msg) != "string"){
    		msg = "All your changes will be lost.";
    	}

    	if(typeof(win) == 'undefined'){
    		win = window;
    	}

    	Zapatec.Utils.addEvent(win, 'beforeunload', function(ev){
    		if(Zapatec.Utils.warnUnloadFlag != true){
    			return true;
    		}

    		if(typeof(ev) == 'undefined'){
    			ev = window.event;
    			}

    		ev.returnValue = msg;

    		return false;
    	});
    }

    /*
     * Using this method you can remove displaying of your message on page unload.
     */
    Zapatec.Utils.unwarnUnload = function(msg, win){
    		Zapatec.Utils.warnUnloadFlag = false;
    }

    /*
     * \internal Variable that determines if unload handler should be used.
     */
    Zapatec.Utils.warnUnloadFlag = false;

    /**
     * @return Max z-index value
     * @type number
     */
    Zapatec.Utils.getMaxZindex = function() {
    	if (window.opera || Zapatec.is_khtml) {
    		return 2147483583;
    	} else if (Zapatec.is_ie){
    		return 2147483647;
    	} else {
    		return 10737418239;
    	}
    };

    /**
     * Corrects CSS length.
     *
     * @param {any} val Value to correct
     * @return Valid CSS length
     * @type string
     */
    Zapatec.Utils.correctCssLength = function(val) {
    	if (typeof val == 'undefined' || (typeof val == 'object' && !val)) {
    		// Undefined or null
    		return 'auto';
    	}
    	// Convert to string
    	val += '';
    	if (!val.length) {
    		// Empty
    		return 'auto';
    	}
    	if (/\d$/.test(val)) {
    		// Number
    		val += 'px';
    	}
    	return val;
    };

    /**
     * @ignore Holds properties of DOM objects that must be set to null on window
     * unload event to prevent memory leaks in IE.
     */
    Zapatec.Utils.destroyOnUnload = [];

    /**
     * Saves a property that must be set to null on window unload event. Should be
     * used for properties that can't be destroyed by garbage collector due to
     * circular references.
     *
     * @param {object} objElement DOM object
     * @param {string} strProperty Property name
     */
    Zapatec.Utils.addDestroyOnUnload = function(objElement, strProperty) {
    	Zapatec.Utils.destroyOnUnload.push([objElement, strProperty]);
    };

    /**
     * Assigns a value to a custom property of DOM object. This property will be
     * set to null on window unload event. Use this function to create properties
     * that can't be destroyed by garbage collector due to circular references.
     *
     * @param {object} objElement DOM object
     * @param {string} strProperty Property name
     * @param {any} val Property value
     */
    Zapatec.Utils.createProperty = function(objElement, strProperty, val) {
    	objElement[strProperty] = val;
    	Zapatec.Utils.addDestroyOnUnload(objElement, strProperty);
    };

    // Remove circular references to prevent memory leaks in IE
    Zapatec.Utils.addEvent(window, 'unload', function() {
    	for (var iObj = Zapatec.Utils.destroyOnUnload.length - 1; iObj >= 0; iObj--) {
    		var objDestroy = Zapatec.Utils.destroyOnUnload[iObj];
    		objDestroy[0][objDestroy[1]] = null;
    		objDestroy[0] = null;
    	}

    	for (var iLis = Zapatec.Utils.removeOnUnload.length - 1; iLis >= 0; iLis--) {
    		var oParams = Zapatec.Utils.removeOnUnload[iLis];
    		if (!oParams) {
    			continue;
    		}
    		Zapatec.Utils.removeOnUnload[iLis] = null;
    		Zapatec.Utils.removeEvent(
    		 oParams['element'],
    		 oParams['event'],
    		 oParams['listener'],
    		 oParams['capture']
    		);
    	}
    });

    /**
     * Escapes all special HTML characters so string can be added via innerHTML property as-is
     *
     * @param {string} str String to escape
     * @return escaped string
     * @type string
     */
    Zapatec.Utils.htmlEncode = function(str) {
    	// we don't need regexp for that, but.. so be it for now.
    	str = str.replace(/&/ig, "&amp;");
    	str = str.replace(/</ig, "&lt;");
    	str = str.replace(/>/ig, "&gt;");
    	str = str.replace(/\x22/ig, "&quot;");
    	// \x22 means '"' -- we use hex reprezentation so that we don't disturb
    	// JS compressors (well, at least mine fails.. ;)

    	return str;
    };

    /**
     * Applies given style to given element.
     *	@param {any} elRef reference to target element. Required.
     *	@param {string} style string value of style. Required.
     */
    Zapatec.Utils.applyStyle = function(elRef, style){
    	if(typeof(elRef) == 'string'){
    		elRef = document.getElementById(elRef);
    	}

    	if(elRef == null || style == null || elRef.style == null){
    		return null;
    	}

    	if(Zapatec.is_opera){
    		var pairs = style.split(";");

    		for(var ii =0; ii < pairs.length; ii++){
    			var kv = pairs[ii].split(":");

    			// trim value
    			if (!kv[1]) {
    				continue;
    			}
    			var value = kv[1].replace(/^\s*/, '').replace(/\s*$/, '');
    			var key = "";

    			for(var jj = 0; jj < kv[0].length; jj++){
    				if(kv[0].charAt(jj) == "-"){
    					jj++;

    					if(jj < kv[0].length){
    						key += kv[0].charAt(jj).toUpperCase();
    					}

    					continue;
    				}

    				key += kv[0].charAt(jj);
    			}

    			switch(key){
    				case "float":
    					key = "cssFloat";
    					break;
    			}

    			try{
    				elRef.style[key] = value;
    			} catch(e){}
    		}
    	} else {
    		elRef.style.cssText = style;
    	}

    	return true;
    }

    /**
     * Retrieves computed CSS property values from an element.
     *
     * @param {object} oEl Element object
     * @param {string} sPr Property name of element's style, e.g. "borderLeftWidth"
     * @return Computed CSS property value
     * @type string
     */
    Zapatec.Utils.getStyleProperty = function(oEl, sPr) {
    	var oDV = document.defaultView;
    	if (oDV && oDV.getComputedStyle) {
    		// Standard
    		var oCS = oDV.getComputedStyle(oEl, '');
    		if (oCS) {
    			sPr = sPr.replace(/([A-Z])/g, '-$1').toLowerCase();
    			return oCS.getPropertyValue(sPr);
    		}
    	} else if (oEl.currentStyle) {
    		// IE
    		return oEl.currentStyle[sPr];
    	}
    	// None
    	return oEl.style[sPr];
    };

    /**
     * Returns precision of a number with floating point.
     *
     * @param {number} dFloat Number with floating point
     * @return Precision
     * @type number
     */
    Zapatec.Utils.getPrecision = function(dFloat){
    	return (dFloat + '').replace(/^-?\d*\.*/, '').length;
    };

    /**
     * Sets precision of a number with floating point. Useful to correct
     * precision before displaying the result of arithmetic operation. E.g.
     * 2.7 + 0.2 == 2.9000000000000004 and
     * Zapatec.Utils.setPrecision(2.7 + 0.2, 1) == 2.9 .
     *
     * If param iPrecision is 0, returns integer.
     *
     * @param {number} dFloat Number with floating point
     * @param {number} iPrecision Desired precision
     * @return Number with floating point with desired precision
     * @type number
     */
    Zapatec.Utils.setPrecision = function(dFloat, iPrecision){
    	// Convert to number
    	dFloat *= 1;
    	if (dFloat.toFixed) {
    		return dFloat.toFixed(iPrecision) * 1;
    	}
    	var iPow = Math.pow(10, iPrecision);
    	return parseInt(dFloat * iPow, 10) / iPow;
    };

    /**
     * Converts a number with floating point to string with desired precision.
     * Uses Zapatec.Utils.setPrecision to set precision. Then converts result into
     * string and adds zeros to the end if needed.
     *
     * @param {number} dFloat Number with floating point
     * @param {number} iPrecision Desired precision
     * @return String with floating point with desired precision
     * @type string
     */
    Zapatec.Utils.setPrecisionString = function(dFloat, iPrecision){
    	var sFloat = Zapatec.Utils.setPrecision(dFloat, iPrecision) + '';
    	var iOldPrecision = Zapatec.Utils.getPrecision(sFloat);
    	var iZeros = iPrecision - iOldPrecision;
    	if (iZeros) {
    		if (!iOldPrecision) {
    			sFloat += '.';
    		}
    		for (var iZero = 0; iZero < iZeros; iZero++) {
    			sFloat += '0';
    		}
    	}
    	return sFloat;
    };

    /**
     * Creates nested hash elements with given name. For example for arguments
     * Zapatec.Utils.createNestedHash(Zapatec, ["Module", "key", "anotherKey"])
     * Element Zapatec.Module.key.anotherKey will be created. If property does not
     * exist - it will be created and "{}" (new Object()) will be used as default
     * value.
     *
     * @param {Object} parent Reference to element where nested properties will be
     *	created.
     * @param {Array} keys Array of properties to create.
     * @param {Object} value Value to assign to deepest element. Optional.
     */
    Zapatec.Utils.createNestedHash = function(parent, keys, value){
    	if(parent == null || keys == null){
    		return null;
    	}

    	var tmp = parent;

    	for(var ii = 0; ii < keys.length; ii++){
    		if(typeof(tmp[keys[ii]]) == 'undefined'){
    			tmp[keys[ii]] = {};
    		}

    		if(ii == keys.length - 1 && typeof(value) != 'undefined'){
    			tmp[keys[ii]] = value;
    		}

    		tmp = tmp[keys[ii]];
    		}
    }

    /**
     * This is a type of interface technology (mixin).
     * It takes the methods and properties of interface
     * object and add them to our class or object instance.
     * If property is present in our class and is not
     * core method of interface then it will not be added.
     * Our class or object will also be added hasInterface
     * method to work with interfaces.
     * @param classOrObject {object or function} the object or
     * class to enhance with the interface.
     * @param interfaceObj {object} object holding interface logic.
     * @return {boolean} true if success, otherwise false.
     */
    Zapatec.implement = function(classOrObject, interfaceStr) {
    	if (typeof interfaceStr != "string") {
    		return false;
    	}
    	//if this is class we take its prototype
    	if (typeof classOrObject == "function") {
    		classOrObject = classOrObject.prototype;
    	}
    	//if itis still not object then this is some mistake
    	if (!classOrObject || typeof classOrObject != "object") {
    		return false;
    	}
    	var interfaceObj = window;
    	var objs = interfaceStr.split(".");
    	try {
    		for(var i = 0; i < objs.length; ++i) {
    			interfaceObj = interfaceObj[objs[i]];
    		}
    	} catch(e) {
    		return false;
    	}
    	//creating interface array if it doesn't exists and new interface there
    	if (typeof classOrObject.interfaces != "object") {
    		classOrObject.interfaces = {};
    		classOrObject.interfaces[interfaceStr] = true;
    	} else if (classOrObject.interfaces[interfaceStr] !== true) {
    		classOrObject.interfaces = Zapatec.Utils.clone(classOrObject.interfaces);
    		classOrObject.interfaces[interfaceStr] = true;
    	} else {
    		//if object already has interface why executing this?
    		return true;
    	}
    	//adding new properties and methods
    	for(var iProp in interfaceObj) {
    		classOrObject[iProp] = interfaceObj[iProp];
    	}
    	//adding hasInterface method
    	classOrObject.hasInterface = function(interfaceStr) {
    		if (this.interfaces[interfaceStr] === true) {
    			return true;
    		}
    		return false;
    	};
    	//defining require interface method
    	classOrObject.requireInterface = function(interfaceStr) {
    		if (!this.hasInterface(interfaceStr)) {
    			Zapatec.Log({description : "The object with ID '" + this.id + "' has no " + interfaceStr + " interface!"});
    			return false;
    		}
    		return true;
    	};
    	//this is a general approach for setting and getting properties of interface.
    	//this is done to have more flexible property naming, which can be change by implementing object.
    	interfaceObj.setNamedProperty = classOrObject.setNamedProperty = function(name, val) {
    		this[name] = val;
    	};
    	interfaceObj.getNamedProperty = classOrObject.getNamedProperty = function(name) {
    		return this[name];
    	};

    	return true;
    };

    /**
     * Processes input data and returns key code and corresponding character.
     * @param event {Object} Event
     * @return returns hash with two keys:
     * charCode - [int] code of key that was pressed(could be null for mozilla(when
     *	alphanumeric key was pressed) and opera(when unknown key (charCode == 0)
     *	was pressed))
     * chr - [char] letter, that was pressed(could be null for mozilla(when
     *	nonalphanumeric key was pressed) and opera(when unknown key (charCode == 0)
     *	was pressed))
     * @type Object
     */
    Zapatec.Utils.getCharFromEvent = function(evt){
    	if(!evt) {
    		evt = window.event;
    	}

    	var response = {};

    	if(Zapatec.is_gecko && !Zapatec.is_khtml && evt.type != "keydown" && evt.type != "keyup"){
    		if(evt.charCode){
    			response.chr = String.fromCharCode(evt.charCode);
    		} else {
    			response.charCode = evt.keyCode;
    		}
    	} else {
    		response.charCode = evt.keyCode || evt.which;
    		response.chr = String.fromCharCode(response.charCode);
    	}

    	if(Zapatec.is_opera && response.charCode == 0){
    		response.charCode = null;
    		response.chr = null;
    	}

    	if(Zapatec.is_khtml && response.charCode == 63272){
    		response.charCode = 46;
    		response.chr = null;
    	}

    	return response;
    }

    /**
     * Create DOM element from text. Useful when you need to create node from HTML
     * fragment and add it to the DOM tree.
     * @param {Object} txt HTML string
     * @return Created object
     * @type Object
     */
    Zapatec.Utils.convertHTML2DOM = function(txt){
    	// create temp container
    	var el = document.createElement("div");
    	el.innerHTML = txt;

    	var currEl = el.firstChild;

    	// search for first element node
    	while(!currEl.nodeType || currEl.nodeType != 1){
    		currEl = currEl.nextSibling;
    	}

    	Zapatec.Utils.destroy(currEl);

    	return currEl;
    };

    /**
     * Escapes special characters in the string which will be passed as argument to
     * the RegExp constructor.
     *
     * @param {string} s Regular expression string
     * @return Regular expression string with escaped special characters
     * @type string
     */
    Zapatec.Utils.escapeRegExp = function(s) {
    	return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
    };

Overview | File | Class | Tree | Index | Help

Zapatec Utils

PREV NEXT | FRAMES NO FRAMES

Documentation generated by JSDoc on Thu Aug 16 12:18:39 2007