if(!a10) {var a10 = {};} //if a10 not defined yet
 var param="";
//namespace declarations
a10.ajax = {};
a10.anim = {};
a10.browser = {};
a10.event = {};
a10.util = {};
a10.array = {};
a10.simulate = {}; 
a10.element = {};
a10.form = {};

a10.logToNativeConsole = false;  //whether to log to a native console
a10.logToDebugPane = false;      //whether to log to the debug panel
a10.debugPaneId = "_ask_debug";  //html id of the debug panel


/**
 * If ID is specified, the element with matching ID is returned; otherwise, the element itself
 * is returned.  The reflexive behavior is convenient for writing functions that accept either ID
 * or object reference as argument without type checking.
 *
 * @param element string id of the html element or the html element
 */
function $(element) {
    return (typeof element == 'string')? document.getElementById(element) : element;
}

/**
 * Retrieves the value property of html element with the specified name.
 *
 * @returns value of the element, or empty string if not found
 */
function $F(id) {
    var elts = document.getElementsByName(id);
    if(elts && elts.length > 0) {
        return elts[0].value;
    }
    return "";
}

/**
 * Initializes the debug panel for js debug message logging. This function needs to be included inline so that
 * document.write would work correctly
 * @addon
 */
a10.initDebugPane = function() {
    if((typeof(window) != "undefined" && window.console && window.console.log)
       || (typeof(opera) != "undefined" && opera.postError)) {
        a10.printToNativeConsole = true;  
    }

    var jsFragment = "document.getElementById('"+a10.debugPaneId+"').style.display='none'";
    if(a10.browser.isIE6()) {
        document.write('<div id="'+a10.debugPaneId+'" style="position:absolute;left:0px;bottom:0px;width:100%;z-index:10;height:130px;color:green;overflow:auto;background-color:#FFF;border-top:solid 5px #000"><input type="button" name="close" value="Close" style="right:0px;top:0px;position:absolute;width:100px" onclick="'+jsFragment+'"></div>');
    } else {
        document.write('<div id="'+a10.debugPaneId+'" style="position:fixed;bottom:0px;left:0px;width:100%;height:130px;color:green;overflow:auto;background-color:#FFF;border-top:solid 5px #000"><input type="button" name="close" value="Close" style="right:0px;top:0px;position:absolute;width:100px" onclick="'+jsFragment+'"></div>');
    }
    a10.logToDebugPane = true;
};

/**
 * Logs the specified message to the console. If a native console is available (firebug and opera), also outputs the
 * message there.
 *
 * @param msg message to output
 */
a10.debug = function(msg){
    if(a10.logToDebugPane) {
        var entry = document.createElement("span");
        entry.innerHTML = "DEBUG: " + msg;
        $(a10.debugPaneId).appendChild(entry);
        $(a10.debugPaneId).appendChild(document.createElement("br"));
    }

    if(a10.printToNativeConsole) {
        msg = "DEBUG: " + msg;
        //print to console
        if (typeof(window) != "undefined" && window.console && window.console.log) {
            // Safari and FireBug 0.4
            // Percent replacement is a workaround for cute Safari crashing bug
            window.console.log(msg.replace(/%/g, '\uFF05'));
        } else if (typeof(opera) != "undefined" && opera.postError) {
            // Opera
            opera.postError(msg);
        }
    }
};

/**
 * Fades the html element with the specified id from opacity of 0 to 1. If the display of the element is none, the
 * property will be updated to ''
 *
 * @param id string id of the html element to fade in
 * @param speed speed of the fade in seconds
 * @param oncomplete function to call when fade in is complete, this param is optional
 *
 * @addon
 */
a10.anim.fadeIn = function(id, speed, oncomplete /*optional*/) {
    var element = $(id);
    if(element) {
        a10.anim.fadeLoop(element, 0.0, 1.0, speed, oncomplete);
    }
};

/**
 * Fades the html element with the specified id from opacity of 1 to 0. The display property of the element will be set
 * to 'none' when the fade completes.
 *
 * @param id string id of the html element to fade out
 * @param speed speed of the fade in seconds
 * @param oncomplete function to call when fade in is complete, this param is optional
 */
a10.anim.fadeOut = function(id, speed, oncomplete /*optional*/) {
    var element = $(id);
    if(element) {
        a10.anim.fadeLoop(element, 1.0, 0, speed, oncomplete);
    }
};

/**
 * Fades the html element with the specified id from opacity of 1 to endOpacity.  If the endOpacity is 0, the display
 * property of the element will be set to 'none' when the fade completes.
 *
 * @param id string id of the html element to fade out
 * @param speed speed of the fade in seconds
 * @param endOpacity final opacity of the element
 * @param oncomplete function to call when fade in is complete, this param is optional
 */
a10.anim.fadeOutToOpacity = function(id, speed, endOpacity, oncomplete /*optional*/) {
    var element = $(id);
    if(element) {
        a10.anim.fadeLoop(element, 1.0, endOpacity, speed, oncomplete);
    }
};

/**
 * Changes the opacity of the element from start to stop.
 *
 * @param element html DOM element to change opacity for
 * @param start start opacity
 * @param end end opacity
 * @param duration duration in seconds for the transition
 * @param afterFinish function to call when fade in is complete, this param is optional
 */
a10.anim.fadeLoop = function(element, start, stop, duration, afterFinish) {
    var speed = (duration / 4) * 1000;
    var increment = ((stop - start) / 4);
    a10.element.setOpacity(element, start);

    if(element.style.display == 'none' && increment > 0) {
        element.style.display = '';
    }

    a10.anim.fadeLoop.step(element, increment, stop, speed, afterFinish);
}

/**
 * Updates the opacity by the specified internval. If the resulting opacity does not satisfy the stop condition,
 * another step is scheduled. The combination of the increment and stop params will determine when the animation stops.
 *
 * @param element html DOM element to change opacity for
 * @param increment increment to update the opacity by
 * @param stop opacity to stop the animation at
 * @param stepDuration time to wait before invoking the next step
 * @param afterFinish function to call when fade in is complete, this param is optional
 */
a10.anim.fadeLoop.step = function(element, increment, stop, stepDuration, afterFinish) {
    var currentValue = a10.element.getOpacity(element);

    if((increment > 0 && currentValue >= stop) || (increment < 0 && currentValue <= stop)) {

        a10.element.setOpacity(element, stop);

        if(stop == 0) {
            element.style.display = 'none';
        }

        if(afterFinish) {
            afterFinish();
        }
    } else {
        a10.element.setOpacity(element, (currentValue + increment));
        setTimeout(function() {a10.anim.fadeLoop.step(element, increment, stop, stepDuration, afterFinish)}, stepDuration);
    }
}

//TODO refactor loop into common loop for animation
a10.anim.slideX = function(id, speed, amount, oncomplete /*optional*/) {
    var element = $(id);
    if(element) {
        a10.anim.slideLoop(element, parseInt(element.style.left), amount, speed, oncomplete);
    }
};

a10.anim.slideLoop = function(element, start, stop, duration, afterFinish) {
    var speed = (duration / 10) * 1000;
    var increment = ((stop - start) / 10);
    a10.anim.slideLoop.step(element, increment, stop, speed, afterFinish);
}

a10.anim.slideLoop.step = function(element, increment, stop, stepDuration, afterFinish) {
    var currentValue = parseInt(element.style.left);

    if((increment > 0 && currentValue >= stop) || (increment < 0 && currentValue <= stop)) {

        element.style.left = stop + "px";
        if(afterFinish) {
            afterFinish();
        }
    } else {
        if(increment > 0) {
            element.style.left =Math.ceil(currentValue + increment) + "px";
        } else {
            element.style.left =Math.floor(currentValue + increment) + "px";
        }
        setTimeout(function() {a10.anim.slideLoop.step(element, increment, stop, stepDuration, afterFinish)}, stepDuration);
    }
}

a10.anim.slideY = function(id, speed, amount, oncomplete /*optional*/) {
    var element = $(id);
    if(element) {
        a10.anim.slideLoopY(element, parseInt(element.style.top), amount, speed, oncomplete);
    }
};

a10.anim.slideLoopY = function(element, start, stop, duration, afterFinish) {
    var speed = (duration / 10) * 1000;
    var increment = ((stop - start) / 10);
    a10.anim.slideLoopY.step(element, increment, stop, speed, afterFinish);
}

a10.anim.slideLoopY.step = function(element, increment, stop, stepDuration, afterFinish) {
    var currentValue = parseInt(element.style.top);

    if((increment > 0 && currentValue >= stop) || (increment < 0 && currentValue <= stop)) {

        element.style.top = stop + "px";

        if(afterFinish) {
            afterFinish();
        }
    } else {
        element.style.top =(currentValue + increment) + "px";
        setTimeout(function() {a10.anim.slideLoopY.step(element, increment, stop, stepDuration, afterFinish)}, stepDuration);
    }
}


a10.anim.shake = function(id, amount, duration, afterFinish) {
    var element = $(id);
    if(element) {
        var currentValue = parseInt(element.style.left);
        var speed = (duration / 25) * 1000;
        a10.anim.shake.step(element, amount, isNaN(currentValue) ? 0 : currentValue, speed, afterFinish);
    }
}

a10.anim.shake.step = function(element, increment, orig, stepDuration, afterFinish) {
    var currentValue = parseInt(element.style.left);
    currentValue = isNaN(currentValue) ? 0 : currentValue;

    if(increment == 0) {
        element.style.left = orig + "px";

        if(afterFinish) {
            afterFinish();
        }
    } else {
        if(currentValue == orig) {
            element.style.left =(currentValue + increment) + "px";
        } else {
            element.style.left =(currentValue + increment*2) + "px";
        }

        if(increment > 0) {
            increment = increment * -1;
        } else {
            increment = (increment * -1) - 5;
        }
        setTimeout(function() {a10.anim.shake.step(element, increment, orig, stepDuration, afterFinish)}, stepDuration);
    }
}

/**
 * Retrieves the XHR object for an AJAX request.
 *
 * @returns XHR object if browser supports it, or null
 * @addon
 */
a10.ajax.getRequestObj = function() {
    if (window.XMLHttpRequest) {
        return new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        try {
            return new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
                return new ActiveXObject("Microsoft.XMLHTTP");
            } catch (E) {
                return null;
            }
        }
    }
    else {
        return null;
    }
}

/**
 * Invokes a http get via XHR to the specified URL
 *
 * @param url url to request
 * @param onsuccess function to invoke on successful request of the url
 * @param onerror function to invoke on a failed request of the url
 * @returns a reference to the AJAX request
 */
a10.ajax.get = function(url, onsuccess, onerror) {
    a10.debug("[a10.ajax.get()]:" + url);

    var obj = a10.ajax.getRequestObj();
    if(obj) {
        obj.onreadystatechange = function() {
            if(obj.readyState==4) {
                if (obj.status == 200 && typeof(onsuccess) == "function") {
                    onsuccess(obj);
                } else if (typeof(onerror) == "function") {
                    onerror(obj);
                }

                // clean up so IE doesn't leak memory
                try {
                    delete obj['onreadystatechange'];
                } catch(e) {}  // ignore error
            }
        };
         if(param!="")
{
    if(url.indexOf("?")!=-1)
   {
        url=url+"&"+param;
        }
        else
        {
          url=url+"?"+param;
        }

}  param="";
        obj.open("GET", url, true, "", "");
        obj.send(null);
        return obj;
    } else {
        if(onerror) {
            onerror("Error creating XHR");
        } else {  
            a10.debug("Error creating XHR");
        }
        return null;
    }
};

/**
 * Invokes a http get via XHR to the specified URL
 *
 * @param url url to request
 * @param params parameters to pass
 * @param onsuccess function to invoke on successful request of the url
 * @param onerror function to invoke on a failed request of the url
 * @returns a reference to the AJAX request
 */
a10.ajax.post = function(url, params, onsuccess, onerror) {
    a10.debug("[a10.ajax.post()]:" + url);

    var obj = a10.ajax.getRequestObj();
    if(obj) {
        obj.onreadystatechange = function() {
            if(obj.readyState==4) {
                if (obj.status == 200 && typeof(onsuccess) == "function") {
                    onsuccess(obj);
                } else if (typeof(onerror) == "function") {
                    onerror(obj);
                }

                // clean up so IE doesn't leak memory
                try {
                    delete obj['onreadystatechange'];
                } catch(e) {}  // ignore error
            }
        };

        obj.open("POST", url, true, "", "");
        obj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        obj.setRequestHeader("Content-length", params.length);
        obj.send(params);
        return obj;
    } else {
        if(onerror) {
            onerror("Error creating XHR");
        } else {
            a10.debug("Error creating XHR");
        }
        return null;
    }
};

/**
 * Aborts the existing AJAX request
 *
 * @param request AJAX request to cancel
 */
a10.ajax.abort = function(request) {
    if (request){
        request.abort();
    }
};

/*
 * Build query string from the specified associative array. The result output will be key=value, separated by '&'
 *
 * @param params associated array with key value pairs to construct a request url
 * @addon
 */
a10.browser.buildParamString = function(params) {
    var paramString = "";
    if(!(typeof(params) == 'undefined' || params === null)) {
        var sortedParams = a10.util.sortProperties(params);
        var first = true;
        for(prop in sortedParams) {
            var component = encodeURIComponent(sortedParams[prop]);
            component = component.replace(/'/g, "%27");
            if(component != null && component != '') {
                if(!first) {
                    paramString += "&";
                } else {
                    first = false;
                }
                paramString += prop + "=" + component;
            }
        }
    }
    return paramString;
}

/**
 * Determines whether the current browser is IE
 * @addon
 */
a10.browser.isIE = function() {
    return navigator.userAgent.toLowerCase().indexOf('msie') != -1;
};

/**
 * Determines whether the current browser is IE6
 * @addon
 */
a10.browser.isIE6 = function() {
    return navigator.userAgent.toLowerCase().indexOf('msie 6.') != -1;
};

/**
 * Determines whether the current browser is IE7
 * @addon
 */
a10.browser.isIE7 = function() {
    return navigator.userAgent.toLowerCase().indexOf('msie 7.') != -1;
};

/**
 * Determines whether the current browser is IE8
 * @addon
 */
a10.browser.isIE8 = function() {
    return navigator.userAgent.toLowerCase().indexOf('msie 8.') != -1;
};

/**
 * Determines whether the current browser is Opera
 * @addon
 */
a10.browser.isOpera = function() {
    return navigator.userAgent.toLowerCase().indexOf('opera') != -1;
};

/**
 * Parses the user agent to determine browser version number
 * --most of the logic comes from WebBrowser.java
 *
 * @returns major version number
 * @addon
 */
a10.browser.version = function() {
    if (!a10.browser._version) {
        if( a10.browser.isIE() ) {
            a10.browser._version = a10.browser.parseVersionStr('msie');
        }
        if (a10.browser.isFirefox()) {
            a10.browser._version = a10.browser.parseVersionStr('firefox');
        }
        if (a10.browser.isNetscape()) {
            a10.browser._version = a10.browser.parseVersionStr('netscape');
        }
        if (a10.browser.isOpera()) {
            a10.browser._version = a10.browser.parseVersionStr('opera');
        }
        if(a10.browser.isSafari()) {
            var version = a10.browser.parseVersionStr('safari');
            if(version > 521) {
                version = 3;
            } else if(version > 412) {
                version = 2;
            } else {
                version = 1;
            }
            a10.browser._version = version;
        }
    }
    return a10.browser._version;
};

/**
 * Determines whether the current browser is Firefox
 * @addon
 */
a10.browser.isFirefox = function() {
    return navigator.userAgent.toLowerCase().indexOf('firefox') != -1;
};

/**
 * Determines whether the current browser is Firefox 3.5
 * @addon
 */
a10.browser.isFirefox35 = function() {
    return navigator.userAgent.toLowerCase().indexOf('firefox/3.5') != -1;
};


/**
 * Determines whether the current browser is Netscape
 * @addon
 */
a10.browser.isNetscape = function() {
    return navigator.userAgent.toLowerCase().indexOf('netscape') != -1;
}

/**
 * Determines whether the current browser is Safari
 * @addon
 */
a10.browser.isSafari = function() {
    return navigator.userAgent.toLowerCase().indexOf('safari') != -1;
};

/**
 * Parses version number from agent string for the specified browser type
 *
 * @param browserName browser name to look for in the agent string
 * @param startOverride optional index to force the start position for parsing
 * @addon
 */
a10.browser.parseVersionStr = function(browserName, startOverride) {
    var buf = '';
    if (browserName || startOverride) {
        var userAgent = navigator.userAgent.toLowerCase();
        var start = (!startOverride) ? (userAgent.indexOf(browserName) + browserName.length+1) : startOverride;
        if (start >= 0) {
            var chars = userAgent.substring(start);
            for (var i = 0; i < chars.length; i++) {
                var current = chars.substring(i,i+1);
                if (!isNaN(current) && current != ' ') {
                    buf += current;
                }
                else if (current == '.') {
                    buf += current;
                }
                else {
                    break;
                }
            }
        }
    }

    /* if there is a second decimal point, chop the rest off to make valid number */
    var firstDecimalPoint = buf.indexOf(".");
    if (firstDecimalPoint != -1) {
        var secondDecimalPoint = buf.indexOf(".", firstDecimalPoint + 1);
        if (secondDecimalPoint != -1) {
            buf = buf.substring(0,secondDecimalPoint);
        }
    }
    return buf;
};



/**
 * Returns the inner dimensions of the window or frame.  Adapted from http://www.quirksmode.org/viewport/compatibility.html
 *
 * @return  an array containing the elements [ width, height ]
 * @addon
 */
a10.browser.innerDimension = function() {
    var x,y;

    if (self.innerHeight) // all except Explorer
    {
        x = self.innerWidth;
        y = self.innerHeight;
    }
    else if (document.documentElement && document.documentElement.clientHeight)
    // Explorer 6 Strict Mode
    {
        x = document.documentElement.clientWidth;
        y = document.documentElement.clientHeight;
    }
    else if (document.body) // other Explorers
    {
        x = document.body.clientWidth;
        y = document.body.clientHeight;
    }

    return [x,y];
};

/**
 * Sorts the specified associated array so that a for in loop can retrieves the keys in alphabetical order
 *
 * @param obj associated array to sort
 * @addon
 */
a10.util.sortProperties = function(obj) {
    var keyValuePairs = [];
    var e;
    for (var prop in obj) {
        var v;
        try {
            v = obj[prop];
        } catch (e) {
            continue;
        }
        keyValuePairs.push([prop, v]);
    }

    var sorted = {};
    var kvPair = [];
    keyValuePairs.sort();
    for(var i = 0; i < keyValuePairs.length; i++) {
        kvPair = keyValuePairs[i];
        sorted[kvPair[0]] = kvPair[1];
    }
    return sorted;
};

/**
 * Finds the position of element on page.  Adopted from Quirksmode.org.
 *
 * @param obj html DOM element
 * @returns array with x coordinate as the first element, y coordinate as the second element
 */ 
a10.util.findPos = function (obj) {
	var curleft = curtop = 0;
	if (obj && obj.offsetParent) {
		curleft = obj.offsetLeft
		curtop = obj.offsetTop
		while (obj = obj.offsetParent) {
			curleft += obj.offsetLeft
			curtop += obj.offsetTop
		}
	}
	return [curleft,curtop];
}


/**
 * Gets the number of pixels the page has scrolled horizontally. Adopted from Prototype.
 *
 * @return scroll left offset
 */ 
a10.util.scrollLeft = function() {
    return window.pageXOffset
            || document.documentElement.scrollLeft
            || document.body.scrollLeft
            || 0;
};


/**
 * Gets the number of pixels the page has scrolled vertically. Adopted from Prototype.
 *
 * @return scroll top offset
 */ 
a10.util.scrollTop = function() {
    return window.pageYOffset
            || document.documentElement.scrollTop
            || document.body.scrollTop
            || 0;
};

/**
 * Trims white space from the specified string
 *
 * @param s string to trim
 * @returns string with whitespace trimmed
 */
a10.util.trim = function(s) {
    return s.replace(/^\s+/g, '').replace(/\s+$/g, '');
};

/**
 * Escape HTML special characters (<, >, &) in the string with entities like &lt; The
 * original string is not modified. 
 *
 * @return a new String, with all special characters escaped.
 */
a10.util.htmlEncode = function(s) {
    return s.replace(/&/g,"&amp;")
            .replace(/</g,"&lt;")
            .replace(/>/g,"&gt;");
}

/**
 * Returns true if a string is empty (zero length or containing only white space).
 *
 * @param s string to test
 * @returns true if string is empty
 */
a10.util.isEmpty = function(s) {
    if(s) {
        return s.match(/^\s*$/) != null;
    } else {
        //null is considered empty
        return true;
    }
};

/**
 * Applies the PNG hack for the specified div so that transparent PNG background works properly in IE6. IE6 does not
 * support transparent PNG background, an additional div will need to be added to the specified div. The overflow param
 * of the original div will be set to hidden. The new div will have a height/width equivalent to the background position
 * desired PNG section, but shifted to the left and top.
 * <pre>
 *            -------------
 *  new div-> |           |
 *            |           |
 *            |      _____|
 *            |     |/////|
 *            |     |/////|<- original div with overflow hidden
 *            -------------
 * </pre>
 */
a10.util.ie6PNGSpriteHack=function(div) {
    if(a10.browser.isIE6()) {
        var bgSrc = div.style.backgroundImage.substr(4, div.style.backgroundImage.length - 5); //get url
        var positions = div.style.backgroundPosition.split(" ");
        var leftOffset = positions.length>=1 ? parseInt(positions[0]) : 0;
        var topOffset = positions.length>=2 ? parseInt(positions[1]) : 0;
	    leftOffset = isNaN(leftOffset) ? 0 : leftOffset;
	    topOffset = isNaN(topOffset) ? 0 : topOffset;
        var width = parseInt(div.style.width);
        var height = parseInt(div.style.height);
	    width = isNaN(width) ? 0 : width;
	    height = isNaN(height) ? 0 : height;	    

        div.style.backgroundImage = "none";
        div.style.overflow = "hidden";
        var innerDiv = document.createElement("div");
        innerDiv.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+ bgSrc +"', sizingMethod='crop')";
        innerDiv.style.position = "relative";
        innerDiv.style.left = leftOffset + "px";
        innerDiv.style.top = topOffset + "px";
        innerDiv.style.width = -leftOffset + width + "px";
        innerDiv.style.height = -topOffset + height + "px";
        div.appendChild(innerDiv);
    }
}


/**
 * Returns a copy of the string that's truncated to the specified length, if necessary, and appended with
 *  '...'.  The appended suffix is counted as part of the total length.
 *
 * @param string string to truncate
 * @param length new length
 */
a10.util.truncate = function(string, length) {
    if(string == null) {
        return null;    
    }

    length = length || 30;
    var truncation = '...';
    return string.length > length ?
           string.slice(0, length - truncation.length) + truncation : string;
};


/**
 * If element is specified, it searches under the that element.  Otherwise, the whole document is
 * searched. XPath is used whenever possible for performance.  Adapted from Prototype 1.5.1
 */
if (!!document.evaluate) { // If true, XPath is enabled
  a10.util._getElementsByXPath = function(expression, parentElement) {
    var results = [];
    var query = document.evaluate(expression, $(parentElement) || document,
      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for (var i = 0, length = query.snapshotLength; i < length; i++)
      results.push(query.snapshotItem(i));
    return results;
  };

  a10.util.getElementsByClassName = function(className, parentElement) {
    var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
    return a10.util._getElementsByXPath(q, parentElement);
  }

} else {
    a10.util.getElementsByClassName = function(className, parentElement) {
      var children = ($(parentElement) || document.body).getElementsByTagName('*');
      var elements = [], child, pattern = new RegExp("(^|\\s)" + className + "(\\s|$)");
      for (var i = 0, length = children.length; i < length; i++) {
        child = children[i];
        var elementClassName = child.className;
        if (elementClassName.length == 0) continue;
        if (elementClassName == className || elementClassName.match(pattern))
          elements.push(child);
      }
      return elements;
    };
}

/**
 * Like Prototype's Object.extend, this function is useful when you want to append properties to an existing object
 * without writing out lots of prefix.  For example, instead of:
 *
 *    some.object.prop1 = ....
 *    some.object.prop2 = ....
 *
 * you write:
 *
 *    a10.util.extend(some.object, {
 *         prop1: ....,
 *         prop2: ....
 *    });
 *
 * which in some situations makes the code easier to read, like when adding methods to a namespace object.
 *
 * This method is not to be confused with a10.util.subclass()
 */
a10.util.extend = function(destination, source) {
  for (var property in source) {
    destination[property] = source[property];
  }
  return destination;
}

/**
 * Create a subclass of another class.  Unlike "Subclass.prototype = new Superclass()", this function
 * allows access to the superclass' constructor and overriden method, and sets the 'constructor' property
 * correctly.  It also avoids JavaScript exception that Superclass() may throw when invoked with no argument.
 *
 * After calling this method, superclass constructor could be accessed as:
 *
 *    ASubClass.baseConstructor.call(this, arguments....);
 *
 * and overridden methods could be accessed as: 
 *
 *    ASubClass.superClass.aMethod.call(this, arguments....);
 *
 * Note the usage of JavaScript's call() method to propagate the 'this' reference.  See unit test for usage example.
 *
 * Based on: http://www.kevlindev.com/tutorials/javascript/inheritance/
 *
 * @param subclass constructor function
 * @param baseclass (superclass) constructor function
 */
a10.util.subclass = function(subClass, baseClass) {
    // Instead of "subClass.prototype = new baseClass()", dummy constructor is used to prevent
    // exception that may be thrown when invoking baseClass constructor without argument. 
    function dummyConstructor() {}
    dummyConstructor.prototype = baseClass.prototype;
    subClass.prototype = new dummyConstructor();

    subClass.prototype.constructor = subClass;
    subClass.baseConstructor = baseClass;
    subClass.superClass = baseClass.prototype;
}

a10.util.getComputedStyle = function(element) {
    element = $(element);
    return element.currentStyle || document.defaultView.getComputedStyle(element, null) || window.getComputedStyle(element, null);
};

a10.util.generatePagingStrip = function(url, startIndex, pageSize, maxImages, backLabel, forwardLabel, skipEmpty) {
    var currentPage = 1;
    var maxPages = 10;
    var pagingHTML = '';
    var i = 1;
    var startIndexHTML = '';
    var pageHTML = '';
    var pageFirst = 1;
    var pageLast = 10;
    var shift = 0;

    var numPages = Math.ceil(maxImages/pageSize);

    var mod = (pageSize>startIndex)?0:(startIndex % pageSize);
    //if pageSize = 16 then _firstTileIndex of 0 = page 1, _firstTileIndex of 1 to 15 = page 2 etc
    currentPage = (mod == 0)?Math.round((startIndex + pageSize)/pageSize):Math.ceil(startIndex/pageSize);

    // If numPages is one, and startIndex is not zero, then we know there's at least one other page.
    if(numPages==1 && startIndex != 0){
        // Do this to prevent a bug when resizing the browser : paging strip won't show up.
        numPages += 1;
        currentPage = numPages;
    } else if( (startIndex+pageSize) >= maxImages ){
        // The current page should also be the last page : there's enough space to show ALL the remaining results.
        // Do this to prevent a bug : blank results page with error.
        if(currentPage<numPages){
            // Set total pages to value of current page.
             numPages = currentPage;
        }
    }

    //if the startIndex is offset from the default there may be an extra page
    //start index of last page
    var lpStartIndex = startIndex + ((numPages - currentPage) * pageSize);

    // lpStartIndex cannot be greater or equal than maxImages
    if(lpStartIndex >= maxImages){        
        lpStartIndex = maxImages-1;
    }

    //offset pages && last page do not include all images
    if(((startIndex % pageSize) != 0) && ((lpStartIndex + pageSize) <  maxImages)){
        numPages += 1;
    }

    //only display 10 page links, calculate start and finish index
    if(numPages > 10){
        pageFirst = currentPage - 5;
        pageLast = currentPage + 4
        if(pageFirst < 1){
            shift = Math.abs(pageFirst) + 1;
            pageFirst = pageFirst + shift;
            pageLast = pageLast + shift;
        }
        if(pageLast > numPages){
            shift = pageLast - numPages;
            pageFirst = pageFirst - shift;
            pageLast = pageLast - shift;
        }
    }else{
        if(numPages < pageLast){
            pageLast = numPages;
        }
    }

    var _psStyles;

    if (typeof(_psDefStyles) == "undefined") {
	    _psStyles = new Object();
        _psStyles.curStyle = "margin:0px 15px 0px " + (currentPage > 1 ? 15 : 0) + "px";
	    _psStyles.curCls = "plc b plsel";
	    _psStyles.curInCls = "pl";
	    _psStyles.numCls = "plc";
	    _psStyles.numInCls = "pl b";
	    _psStyles.nextStyle = "";
    } else {
        _psStyles = _psDefStyles;
    }

    var pagingStrip = $('ps');

    if(pagingStrip) {

        //no paging for single results page
        if(pageLast == 1){
            pagingHTML = "";
        }else{
            if(currentPage > 1){
                startIndexHTML = '&pstart=' + (startIndex - pageSize);
                pageHTML = '&page=' + (currentPage - 1);
                pagingHTML += '<div style="float:left;' + _psStyles.nextStyle + '"><a class="L4" style="text-decoration:none" href="' + url + startIndexHTML + pageHTML + '">'+backLabel +'</a></div>';
            }
            else if(!skipEmpty) {
                pagingHTML += '&#160;&#160;<span class="disabled">'+backLabel +'</span>';
            }
            pagingHTML += '<div style="float:left;display:inline;padding:0;' + _psStyles.curStyle + ';">';
            for (i=pageFirst;i<=pageLast;i++){

                if(i==currentPage){
                    pagingHTML += '<div id="cur" class="' + _psStyles.curCls + '"><div class="' + _psStyles.curInCls + '">&#160;&#160;' + i + '&#160;&#160;</div></div>';
                }else{
                    var diff = i - currentPage;
                    var pageStartIndex = (startIndex + (diff * pageSize));
                    if(pageStartIndex < 0){pageStartIndex = 0;}
                    if(pageStartIndex > maxImages){pageStartIndex=maxImages-1;}
                    startIndexHTML = '&pstart=' + pageStartIndex;
                    pageHTML = '&page=' + (currentPage + diff);
                    pagingHTML += '<div class="' + _psStyles.numCls + '"><a class="' + _psStyles.numInCls + '" style="text-decoration:none" href="' + url + startIndexHTML + pageHTML + '">&#160;&#160;' + i + '&#160;&#160;</a></div>';
                }

            }
            pagingHTML += '</div>';
            if(currentPage != numPages){
                startIndexHTML = '&pstart=' + (startIndex + pageSize);
                pageHTML = '&page=' + (currentPage + 1);
                pagingHTML += '<div style="float:left;' + _psStyles.nextStyle + '"><a class="L4" style="text-decoration:none" href="' + url + startIndexHTML + pageHTML + '">'+forwardLabel +'</a></div>';
            }
            else if(!skipEmpty) {
                pagingHTML += '<span class="disabled">'+ forwardLabel +'</span>';
            }
        }

        pagingStrip.innerHTML = pagingHTML;
    }
}

/**
 * Generate an ID that's unique to the local application. 
 *
 * Function adapted from: http://snipplr.com/view/2574/unique-id-generator-without-a-global-temp-variable/
 *
 * For globally unique ID (ID unique across multiple application), may want to consider using:
 * http://blog.shkedy.com/2007/01/createing-guids-with-client-side.html
 * But there is no need for this now.  
 */
a10.util.uniqueID = (
    function(){
        var id=0;
        return function(){
            return id++ ;
        };
    }
)();

/**
 * Dynamically insert an external stylesheet into the current document.
 *
 * @url   URL of the .css file.
 * @class class name for the inserted stylesheet.  Useful for removing the
 *        stylesheet afterwards. 
 */
a10.util.insertStyleSheet = function(url, className) {
    if (a10.browser.isIE()) {
        try {
            if (document.styleSheets.length > 0) {
                document.styleSheets[document.styleSheets.length - 1].addImport(url);
            } else {
                document.createStyleSheet(url);
            }
        } catch (e) {
            a10.debug("Insert style sheets error: " + url + ". Error message: " + e.message);
        }
    } else {
        var head = document.getElementsByTagName("head")[0];
        var cssNode = document.createElement('link');
        cssNode.type = 'text/css';
        cssNode.rel = 'stylesheet';
        cssNode.href = url;
        cssNode.media = 'screen';
        cssNode.className = className;
        head.appendChild(cssNode);
    }
};

/**
 * Dynamically insert an external JavaScript into the current document.  The content of the inserted
 * JavaScript will be evaluated.
 *
 * @url   URL of the .js file.
 * @class class name for the inserted JavaScript.  Useful for removing the
 *        JavaScript afterwards.
 */
a10.util.insertScript = function(url, className) {
    var head = document.getElementsByTagName("head")[0];
    var newScript = document.createElement('script');
    newScript.type = 'text/javascript';
    newScript.src = url;
    newScript.className = className;
    head.appendChild(newScript);
};


/**
 * Remove elements with the given class from the entire document.
 *
 * @className  elements with this class name will be removed.  
 */
a10.util.removeElementsWithClass = function(className) {
    // head and body need to be searched separately because on IE,
    // document.getElementsByClassName() only searches the body.
    var head = document.getElementsByTagName('head')[0];
    var elements = a10.util.getElementsByClassName(className, head);
    elements = elements.concat(a10.util.getElementsByClassName(className, document.body));

    for (var i=0; i < elements.length; i++) {
        var e = elements[i];
        e.parentNode.removeChild(e);
    }
};

/*
 * Returns the appropriate MyStuff title based on locale.
 * Locale specific rules are described within the function.
 * One of two keys are chosen based on the user's name, and then,
 * {0} is replaced within the chosen key to create the appropriate title.
 *
 * @param defaultKey - the default key to be used.
 * @param specialKey - the alternative key to be used.
 * @param name - the user's nickname
 * @param locale - the user's locale
 * @returns the user's locale specfic title to be used.
 */
a10.util.getMyStuffTitle = function(defaultKey, specialKey, name, locale) {
    //first determine the correct key to use.
    var resourceKey = defaultKey;//default
    var title = name + "'s Stuff";
        if (name != '') {
            if (locale == 'en_US' ||
                locale == 'en_GB' ||
                locale == 'nl_NL'){
                /*
                For US, UK and Dutch
                    Default: {0}'s Stuff
                    Special Case 1:
                        Nickname ending_with_z={0}' Stuff
                        Nickname ending_with_s={0}' Stuff
                */
                if (name.toLowerCase().charAt(name.length)== "s" || name.toLowerCase().charAt(name.length)== "z") {
                    resourceKey = specialKey;
                }
            /* Spain, Italy, Germany always use Default
            Italy, Spain
                Default: Stuff of {0}
            For German: (doesn't match the format of other languages, cause
                apparently something inappropriate(?) could occur):
                Default: Hans, {0}

            */
            }else if (locale == 'fr_FR'){
            /*
                French only
                    Default: Stuff of {0}
                    Special Case 1:
                        Nickname starting_with_a=Stuff d'{0}
                        Nickname starting_with_e=Stuff d'{0}
                        Nickname starting_with_i=Stuff d'{0}
                        Nickname starting_with_o=Stuff d'{0}
                        Nickname starting_with_u=Stuff d'{0}
                        Nickname starting_with_y=Stuff d'{0}
                        Nickname starting_with_h=Stuff d'{0}
            */
                if (name.toLowerCase().charAt(0) == "a" ||
                    name.toLowerCase().charAt(0) == "e" ||
                    name.toLowerCase().charAt(0) == "i" ||
                    name.toLowerCase().charAt(0) == "o" ||
                    name.toLowerCase().charAt(0) == "u" ||
                    name.toLowerCase().charAt(0) == "y" ||
                    name.toLowerCase().charAt(0) == "h") {
                        resourceKey=specialKey;
                }
            }
        //next use the appropriate resource key and replace {0} with name
        if (resourceKey.indexOf("{0}")!=-1) {
            title = resourceKey.replace("{0}",name);
        }
    }
    return title;
}

/**
 * Removes an element from the array given an index.  This is a just convenience
 * method based on splice()
 *
 * @param index  index of element to remove
 * @param array  array to remove element from
 * @returns the element removed; or null if argument's invalid.
 */
a10.array.removeElementAtIndex = function(index, array) {
    if (index < 0 || index >= array.length) return null;

    return array.splice(index, 1)[0];
};

/**
 * Return the index of the specified object in the array.  If the object
 * is not found, -1 is returned. 
 */
a10.array.indexOf = function(object, array) {
    for (var i = 0; i < array.length; i++) {
        if (array[i] == object) return i;
    }
    
    return -1;
}


/**
 * Simulates an onclick event on the given element.  This will trigger all event handlers registered for that event.
 * This function is useful for writing unit tests on UI elements.
 */
a10.simulate.click = function(e) {
    this._mouseEvent(e, 'click'); 
};

/**
 * Simulates an mousedown event on the given element.
 */
a10.simulate.mousedown = function(e) {
    this._mouseEvent(e, 'mousedown'); 
};

/**
 * Base of all mouse-related simulate functions.
 */
a10.simulate._mouseEvent = function(e, event) {
    e = $(e);

    if (document.createEvent) { // DOM Standard
        var evt = document.createEvent("MouseEvents");
        evt.initEvent(event, true, true);
        e.dispatchEvent(evt);
    }
    else if (document.createEventObject) { // IE
        var evt = document.createEventObject();
        e.fireEvent("on" + event, evt);
    }
}


/**
 * Adds event listener to the specified object.
 *
 * This implementation addresses several shortcomings of IE's attachEvent():
 *   - Fixed incorrect 'this' reference by introducing a closure.
 *   - Supports removeListener() by caching the closure.
 *   - Pass window.event as first argument to event listener.
 *   - Ignore duplicate listener added to the same element for the same event type.
 *
 * The memory leak issue in IE is not fixed, because the problem has not been observed,
 * and newest versions of IE (IE6 hot fixes, IE7) reportedly has resolved this problem.
 *
 * For more discussion about these issues, see:
 *  http://www.quirksmode.org/blog/archives/2005/08/addevent_consid.html
 *
 * This implementation is based on John Resig's work:
 *  http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html
 * The main difference being, instead of using the function's source code string
 * as key, it generates a unique ID for each function. 
 *
 * @param e element to attach event to
 * @param type event type (mousedown, keyup, etc)
 * @param fn event handler
 */
a10.event.addListener = function(e, type, fn) {
    e = $(e);
	if (e) {
		if (e.addEventListener) {
			e.addEventListener(type, fn, false);
		}
		else if (e.attachEvent) {
			// Assign unique ID to fn and use it to form the key for the closure
			if (!fn['_addListenerID']) fn['_addListenerID'] = a10.util.uniqueID();
			var closureKey = type + fn['_addListenerID'];

			// If listener already exists for this type, do nothing.
			if (e[closureKey]) return;

			// Make fn a property of e to get the correct 'this' reference. (apply() isn't used
			// here for it's not supported under IE5)
			e["fn"+closureKey] = fn;

			// Cache a reference to the closure for later removal, and pass the closure
			// to attachEvent()
			e[closureKey] = function() { e["fn"+closureKey]( window.event ); }
            e.attachEvent("on"+type, e[closureKey]);
		}
	}
};

/**
 * Remove event listener to the specified object.  This function takes the
 * same arguments as addListener().
 *
 * @param e element to remove event from
 * @param type event type (mousedown, keyup, etc)
 * @param fn event handler
 */
a10.event.removeListener = function(e, type, fn) {
	e = $(e);
    if (e.removeEventListener) {
		e.removeEventListener(type, fn, false );
	}
    else if (e.detachEvent) {
        var closureKey = type + fn['_addListenerID'];
        e.detachEvent("on"+type, e[closureKey]);
		e[closureKey] = null;
        e["fn"+closureKey] = null;
	}
};

// Same as Prototype's Event.element
a10.event.element = function(event) {
    return event.target || event.srcElement;
};


/**
 * Cancel the default action of the browser for the given event, in a cross browser compatible way.
 * This is an alternative to returning false from the event handler function.
 *
 * @param   e the event object
 */
a10.event.cancelDefaultAction = function(e) {
    if (e.preventDefault) {  // Standard
        e.preventDefault();
    }
    else {  // IE
        e.returnValue = false;
    }
};


/**
 * Determines whether the specified element or element with the specified ID is visible. This is solely based
 * on the display style property
 *
 * @param element html DOM element or html ID
 * @addon
 */
a10.element.visible = function(element) {
    var elt = $(element);
    if(elt) {
        return elt.style.display != 'none';
    }
    return false;
};

/**
 * Changes the display property of the element from hide/show or vice versa
 *
 * @param element html DOM element or html ID
 */
a10.element.toggle = function(element) {
    element = $(element);
    a10.element[a10.element.visible(element) ? 'hide' : 'show'](element);
    return element;
};

/**
 * Changes the display property of the element to none
 *
 * @param element html DOM element or html ID
 */
a10.element.hide = function(element) {
    var elt = $(element);
    if(elt && elt.style) {
        elt.style.display = 'none';
    }
    return element;    
};

/**
 * Changes the display property of the element to ''
 *
 * @param element html DOM element or html ID
 */
a10.element.show = function(element) {
    var elt = $(element);
    if(elt) {
        elt.style.display = '';
    }
    return element;
};

/**
 * Determines whether the specified element contains the x/y coordinate on the page
 *
 * @param element html DOM element or html ID
 * @param x x coordinate on the page
 * @param y y coordinate on the page
 * @param dependPageScroll whether to take scrolling into account
 */
a10.element.containsPoint = function(element, x, y, dependPageScroll) {
	if (element) {
		var pos = a10.util.findPos(element);

		if (dependPageScroll) {
			pos[1] -= a10.util.scrollTop();
		} else {
			// Subtract scrollTop/Left due to the sidebar fixed position hack in IE6
			if(a10.browser.isIE6()) {
				pos[0] -= document.body.scrollLeft;
				pos[1] -= document.body.scrollTop;
			}
		}

		return x >= pos[0] && x < (pos[0] + element.offsetWidth) &&
			   y >= pos[1] && y < (pos[1] + element.offsetHeight);
	}
};

/**
 * Sets the opacity of the element to the specified value
 * @param element dom element to change to opacity
 * @param value new opacity value
 */
a10.element.setOpacity = function(element, value) {
    element = $(element);
    if(!element) { //do nothing for invalid elements
        return;
    }

    if(a10.browser.isIE()) {
        //for ie, use alpha filter
        var filter = element.style.filter;
        var style = element.style;
        if (value == 1 || value === '') {
            style.filter = filter.replace(/alpha\([^\)]*\)/gi,'');
            return;
        } else if (value < 0.00001) {
            value = 0;
        }
        style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') + 'alpha(opacity=' + (value * 100) + ')';
    } else {
        element.style.opacity = (value == 1 || value === '') ? '' : (value < 0.00001) ? 0 : value;
    }
};

/**
 * Retrieves the opacity of the specified html element
 *
 * @returns opacity of the element.
 */
a10.element.getOpacity = function(element) {
    element = $(element);

    if(element) {
        if(a10.browser.isIE()) {
            var value = (element.style.filter || '').match(/alpha\(opacity=(.*)\)/);
            if (value && value[1]) {
                return parseFloat(value[1]) / 100;
            } else {
                return 1.0;
            }
        } else {
            var value = element.style.opacity;
            return value ? parseFloat(value) : 1.0;
        }
    }
    return null;
};

/**
 * Retrieves the height of the specified element
 *
 * @param element html DOM element or html ID
 * @returns element height
 */
a10.element.getHeight = function(element) {
    return this.getDimensions(element).height;
};

/**
 * Retrieves the width of the specified element
 *
 * @param element html DOM element or html ID
 * @returns element width
 */
a10.element.getWidth = function(element) {
    return this.getDimensions(element).width;
};

/**
 * Retrieves the width/height of the specified element
 *
 * @param element html DOM element or html ID
 * @returns array with width as first element, height as second 
 */
a10.element.getDimensions = function(element) {
  element = $(element);

  if(!element) { //bad element, return blank array
      return {width:null,height:null};
  }

  // All *Width and *Height properties give 0 on elements with display none,
  // so enable the element temporarily
  var els = element.style;
  var originalVisibility = els.visibility;
  var originalPosition = els.position;
  var originalDisplay = els.display;
  els.visibility = 'hidden';
  els.position = 'absolute';
  els.display = 'block';
  var originalWidth = element.offsetWidth;
  var originalHeight = element.clientHeight;
  els.display = originalDisplay;
  els.position = originalPosition;
  els.visibility = originalVisibility;
  return {width: originalWidth, height: originalHeight};
};

/**
 * IFrameCover positions an iframe beneath an element so the SELECT element wouldn't show through
 * the element in IE6.  This class makes it easier to create iframe covers and reposition or resize
 * them when the element they are covering for change their position or size.   This way, you
 * only need to specify the element you want to cover, and IFrameCover takes cares of displaying
 * the iframe with the right size and position on screen. 
 *
 * @param element element to cover
 * @param offset  (Optional) Offset of the cover's dimension relative to the element's.
                       { left:Number, top:Number, width:Number, height:Number }
 */
a10.IFrameCover = function(element, offset) {
    this.element = $(element);
    this.iframe = null;

    if (offset) {
        this.leftOffset = offset.left || 0;
        this.topOffset = offset.top || 0;
        this.widthOffset = offset.width || 0;
        this.heightOffset = offset.height || 0;
    } else {
        this.leftOffset = this.topOffset = this.widthOffset = this.heightOffset = 0;
    }
};

a10.IFrameCover.prototype = {
    /**
     * Display the iframe cover with proper position and size.
     */
    show: function() {
        this.iframe = document.createElement('iframe');

        this.iframe.src = 'about:blank';
        this.iframe.scrolling = 'no';
        this.iframe.frameBorder = 0;
        this.iframe.style.position = 'absolute';
        this.iframe.style.zIndex = 0; // Has to be non-negative to cover up SELECT.
        this.iframe.id = this.element.id + '_IFrameCover';

        document.body.appendChild(this.iframe);

        this.reposition();
    },

    /**
     * Hide the iframe cover. 
     */
    hide: function() {
        if (this.iframe == null) return;

        document.body.removeChild(this.iframe);
        this.iframe = null;
    },

    /**
     * Repositions and resize iframe based on current position and size of element.
     * You should call this method whenever size and position of element changes, since
     * there's no way for IFrameCover to detect when that happens. 
     */
    reposition: function() {
        if (this.iframe == null) return;

        var pos = a10.util.findPos(this.element);
        var dim = a10.element.getDimensions(this.element);

        this.iframe.style.left = pos[0] + this.leftOffset + 'px'; 
        this.iframe.style.top = pos[1] + this.topOffset + 'px';
        this.iframe.style.width = dim.width + this.widthOffset + 'px';
        this.iframe.style.height = dim.height + this.heightOffset + 'px';
    }
}

// Adapted from Prototype, slimmed down.
/**
 * Serialize (construct query string) all elements in form to a single string with key=value separated by '&'
 *
 * @param form form to serialize
 * @returns string representation of the form input values
 * @addon
 */
a10.form.serialize = function(form) {
    return this.serializeElements(this.getElements(form));
};

/**
 * Returns all input elements in form as an array.
 *
 * @param form form to serialize
 * @returns array containing children of the form
 */
a10.form.getElements = function(form) {
    var elements = [];
    var children = $(form).getElementsByTagName('*');

    for (var i = 0; i < children.length; i++) {
        if (this.serializers[children[i].tagName.toLowerCase()]) {
            elements.push(children[i]);
        }
    }

    return elements;
};

/**
 * Serialize all elements specified in an array.
 * Note, this doesn't properly serialize multiple select element yet (getValue() on it returns
 * array instead of string, which isn't expanded.), as there's no need for it now.
 * Support could be added as needed.
 *
 * @param elements array of html elements to serialize
 * @returns string representation of the serilizable elements
 */
a10.form.serializeElements = function(elements) {
    var result = '';

    for (var i = 0; i < elements.length; i++) {
        var e = elements[i];
        if (!e.disabled && e.name) {
            var key = e.name;
            var value = this.getValue(e);
            if (value != undefined) {
                var newPair = key + '=' + encodeURIComponent(value);
                if (result == '') result = newPair;
                else result += '&' + newPair;
            }
        }
    }

    return result;
};

/**
 * Extract the value of a single input element.
 */
a10.form.getValue = function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    return this.serializers[method](element);
};

/**
 * Serialization logic for each input type.
 */
a10.form.serializers = {
  input: function(element) {
    switch (element.type.toLowerCase()) {
      case 'checkbox':
      case 'radio':
        return this.inputSelector(element);
      default:
        return this.textarea(element);
    }
  },

  inputSelector: function(element) {
    return element.checked ? element.value : null;
  },

  textarea: function(element) {
    return element.value;
  },

  select: function(element) {
    return this[element.type == 'select-one' ?
      'selectOne' : 'selectMany'](element);
  },

  selectOne: function(element) {
    var index = element.selectedIndex;
    return index >= 0 ? this.optionValue(element.options[index]) : null;
  },

  selectMany: function(element) {
    var values, length = element.length;
    if (!length) return null;

    for (var i = 0, values = []; i < length; i++) {
      var opt = element.options[i];
      if (opt.selected) values.push(this.optionValue(opt));
    }
    return values;
  },

  optionValue: function(opt) {
    return opt.value;
  }
}

/**
 * Flash plugin detection -- scaled-down version of flash_detect.js, ignoring version
 */
a10.flash = {
    /*
    Copyright (c) Copyright (c) 2007, Carl S. Yestrau All rights reserved.
    Code licensed under the BSD License: http://www.featureblend.com/license.txt
    Version: 1.0.3
    */
    FlashDetect : new function(){
        var self = this;
        self.installed = false;
        var activeXDetectRules = [
            {
                "name":"ShockwaveFlash.ShockwaveFlash.7"
            },
            {
                "name":"ShockwaveFlash.ShockwaveFlash.6"
            },
            {
                "name":"ShockwaveFlash.ShockwaveFlash"
            }
        ];
        var getActiveXObject = function(name){
            var obj = -1;
            try{
                obj = new ActiveXObject(name);
            }catch(err){}
            return obj;
        };
        self.FlashDetect = function(){
            if(navigator.plugins && navigator.plugins.length>0){
                var type = 'application/x-shockwave-flash';
                var mimeTypes = navigator.mimeTypes;
                if(mimeTypes && mimeTypes[type] && mimeTypes[type].enabledPlugin && mimeTypes[type].enabledPlugin.description){
                    self.installed = true;
                }
            }else if(navigator.appVersion.indexOf("Mac")==-1 && window.execScript){
                for(var i=0; i<activeXDetectRules.length && !self.installed; i++){
                    var obj = getActiveXObject(activeXDetectRules[i].name);
                    if(typeof obj == "object"){
                        self.installed = true;
                    }
                }
            }
        }();
    }
}
a10.flash.FlashDetect.release = "1.0.3";
a10.flash.isEnabled = function() {
    return this.FlashDetect.installed;
}
