//
// The contents of this file are subject to the terms
// of the Common Development and Distribution License
// (the License).  You may not use this file except in
// compliance with the License.
// 
// You can obtain a copy of the license at
// https://woodstock.dev.java.net/public/CDDLv1.0.html.
// See the License for the specific language governing
// permissions and limitations under the License.
// 
// When distributing Covered Code, include this CDDL
// Header Notice in each file and include the License file
// at https://woodstock.dev.java.net/public/CDDLv1.0.html.
// If applicable, add the following below the CDDL Header,
// with the fields enclosed by brackets [] replaced by
// you own identifying information:
// "Portions Copyrighted [year] [name of copyright owner]"
// 
// Copyright 2007 Sun Microsystems, Inc. All rights reserved.
//
dojo.provide("webui.suntheme.browser");

/**
 * @class This class contains functions for testing browser properties.
 * @static
 */
webui.suntheme.browser = {
    /**
     * Get user agent. 
     * <p>
     * Note: Characters are converted to lowercase to simplify testing.
     * </p>
     * @return {String} The user agent.
     */
    getAgent: function() {
        return navigator.userAgent.toLowerCase();
    },

    /**
     * Get major version number.
     * <p>
     * Note: On IE5, this returns 4, so use isIe5up to detect IE5.
     * </p>
     * @return {int} The major version number.
     */
    getMajor: function() {
        return parseInt(navigator.appVersion);
    },

    /**
     * Get minor version number.
     *
     * @return {int} The major version number.
     */
    getMinor: function() {
        return parseFloat(navigator.appVersion);
    },

    // Platform tests.

    /**
     * Test for the Linux platform.
     *
     * @return {boolean} true if Linux.
     */
    isLinux: function() {
        var agent = webui.suntheme.browser.getAgent();
        return (agent.indexOf("inux")!= -1);
    },

    /**
     * Test for the Sun platform.
     *
     * @return {boolean} true if Sun.
     */
    isSun: function() {
        var agent = webui.suntheme.browser.getAgent();
        return (agent.indexOf("sunos")!= -1);
    },

    /**
     * Test for the Windows platform.
     *
     * @return {boolean} true if Windows.
     */
    isWin: function() {
        var agent = webui.suntheme.browser.getAgent();
        return (agent.indexOf("win")!= -1 || agent.indexOf("16bit")!= -1);
    },

    // Firefox/Mozilla tests.

    /**
     * Test for Gecko.
     *
     * @return {boolean} true if Gecko.
     */
    isGecko: function() {
        var agent = webui.suntheme.browser.getAgent();
        return (agent.indexOf('gecko') != -1);
    },

    // Internet Explorer tests.

    /**
     * Test for Internet Explorer.
     *
     * @return {boolean} true if IE.
     */
    isIe: function() {
        var agent = webui.suntheme.browser.getAgent();
        return (agent.indexOf("msie") != -1 && agent.indexOf("opera") == -1);
    },

    /**
     * Test for Internet Explorer 3.
     *
     * @return {boolean} true if IE 3.
     */
    isIe3: function() {
        return (webui.suntheme.browser.isIe() && 
            webui.suntheme.browser.getMajor() < 4);
    },

    /**
     * Test for Internet Explorer 4.
     *
     * @return {boolean} true if IE 4.
     */
    isIe4: function() {
        var agent = webui.suntheme.browser.getAgent();
        return (webui.suntheme.browser.isIe() && 
            webui.suntheme.browser.getMajor() == 4 && 
            agent.indexOf("msie 4") != -1);
    },

    /**
     * Test for Internet Explorer 4 and up.
     *
     * @return {boolean} true if IE 4 and up.
     */
    isIe4up: function() {
        return (webui.suntheme.browser.isIe() && 
            webui.suntheme.browser.getMajor() >= 4);
    },
 
    /**
     * Test for Internet Explorer 5.
     *
     * @return {boolean} true if IE 5.
     */
    isIe5: function() {
        var agent = webui.suntheme.browser.getAgent();
        return (webui.suntheme.browser.isIe() && 
            webui.suntheme.browser.getMajor() == 4 && 
            agent.indexOf("msie 5.0") != -1);
    },

    /**
     * Test for Internet Explorer 5.5.
     *
     * @return {boolean} true if IE 5.5.
     */
    isIe5_5: function() {
        var agent = webui.suntheme.browser.getAgent();
        return (webui.suntheme.browser.isIe() && 
            webui.suntheme.browser.getMajor() == 4 &&
            agent.indexOf("msie 5.5") != -1);
    },

    /**
     * Test for Internet Explorer 5 and up.
     *
     * @return {boolean} true if IE 5 and up.
     */
    isIe5up: function() {
        return (webui.suntheme.browser.isIe() && 
            !webui.suntheme.browser.isIe3() &&
            !webui.suntheme.browser.isIe4());
    },

    /**
     * Test for Internet Explorer 5.5 and up.
     *
     * @return {boolean} true if IE 5.5 and up.
     */
    isIe5_5up: function() {
        return (webui.suntheme.browser.isIe() && 
            !webui.suntheme.browser.isIe3() && 
            !webui.suntheme.browser.isIe4() && 
            !webui.suntheme.browser.isIe5());
    },

    /**
     * Test for Internet Explorer 6.
     *
     * @return {boolean} true if IE 6.
     */
    isIe6: function() {
        var agent = webui.suntheme.browser.getAgent();
        return (webui.suntheme.browser.isIe() && 
            webui.suntheme.browser.getMajor() == 4 && 
            agent.indexOf("msie 6.") != -1);
    },

    /**
     * Test for Internet Explorer 6 and up.
     *
     * @return {boolean} true if IE 6 and up.
     */
    isIe6up: function() {
        return (webui.suntheme.browser.isIe() && 
            !webui.suntheme.browser.isIe3() && 
            !webui.suntheme.browser.isIe4() && 
            !webui.suntheme.browser.isIe5() && 
            !webui.suntheme.browser.isIe5_5());
    },

    /**
     * Test for Internet Explorer 7.
     *
     * @return {boolean} true if IE 7.
     */
    isIe7: function() {
        var agent = webui.suntheme.browser.getAgent();
        return (webui.suntheme.browser.isIe() && 
            webui.suntheme.browser.getMajor() == 4 && 
            agent.indexOf("msie 7.") != -1);
    },

    /**
     * Test for Internet Explorer 7 and up.
     *
     * @return {boolean} true if IE 7 and up.
     */
    isIe7up: function() {
        return (webui.suntheme.browser.isIe() && 
            !webui.suntheme.browser.isIe3() && 
            !webui.suntheme.browser.isIe4() && 
            !webui.suntheme.browser.isIe5() && 
            !webui.suntheme.browser.isIe5_5() && 
            !webui.suntheme.browser.isIe6());
    },

    // Navigator tests.

    /**
     * Test for Navigator.
     *
     * @return {boolean} true if Navigator.
     */
    isNav: function() {
        // Note: Opera and WebTV spoof Navigator.
        var agent = webui.suntheme.browser.getAgent();
        return (agent.indexOf('mozilla') != -1 &&
            agent.indexOf('spoofer') == -1 &&
            agent.indexOf('compatible') == -1);
    },

    /**
     * Test for Navigator only.
     *
     * @return {boolean} true if Navigator only.
     */
    isNavOnly: function() {
        var agent = webui.suntheme.browser.getAgent();
        return (webui.suntheme.browser.isNav() && 
            agent.indexOf(";nav") != -1 ||
            agent.indexOf("; nav") != -1);
    },

    /**
     * Test for Navigator 4.
     *
     * @return {boolean} true if Navigator 4.
     */
    isNav4: function() {
        return (webui.suntheme.browser.isNav() && 
            webui.suntheme.browser.getMajor() == 4);
    },

    /**
     * Test for Navigator 4 and up.
     *
     * @return {boolean} true if Navigator 4 and up.
     */
    isNav4up: function() {
        return (webui.suntheme.browser.isNav() && 
            webui.suntheme.browser.getMajor() >= 4);
    },

    /**
     * Test for Navigator 6.
     *
     * @return {boolean} true if Navigator 6.
     */
    isNav6: function() {
        return (webui.suntheme.browser.isNav() && 
            webui.suntheme.browser.getMajor() == 5);
    },

    /**
     * Test for Navigator 6 and up.
     *
     * @return {boolean} true if Navigator 6 and up.
     */
    isNav6up: function() {
        return (webui.suntheme.browser.isNav() && 
            webui.suntheme.browser.getMajor() >= 5);
    },

    // Safari tests.

    /**
     * Test for Safari.
     *
     * @return {boolean} true if Safari.
     */
    isSafari: function() {
        var agent = webui.suntheme.browser.getAgent();
        return (agent.indexOf('safari') != -1);
    }
}
dojo.provide("webui.suntheme.theme.common");

dojo.require("dojo.i18n");

/**
 * @class This class contains common functions to obtain theme properties.
 * It is also the base of the Dojo nls localized theme namespace.
 * <p>
 * The client theme is composed of the following theme categories.
 * <ul>
 * <li>messages</li>
 * <li>styles</li>
 * <li>images</li>
 * <li>templates</li>
 * </ul>
 * Each category has a set of properties. See the methods in
 * webui.suntheme.theme.common for obtaining the theme property values.
 * </p>
 * dojo.requireLocalization is reimplemented here in order to perform
 * hierarchical extension of the theme for application theme overrides.
 * <p>
 * NOTE THE SPACE AFTER THE "dojo." SEGMENT, IN  REFERENCES TO DOJO 
 * METHODS THAT LOAD A MODULE. IF THERE IS NO SPACE "debugAtAllCosts"
 * RESULTS IN JAVASCRIPT ERRORS DUE TO PATTERN MATCHING BY DOJO TO 
 * FIND MODULE LOADING METHODS.
 * </p>
 * @static
 */
webui.suntheme.theme.common = {
    /**
     * This function is used to set widget properties with Object literals.
     *
     * @param props Key-Value pairs of properties.
     * @config {String} [bundle] The javascript theme basename "sunttheme" for suntheme.js
     * @config {String} [locale] The locale in dojo form <lang>-<country>-<variant>
     * @config {String} [module] The module into which dojo will load the theme properties.
     * @config {String} [modulePath] A relative URL defining the root directory of the nls directory
     * @config {String} [prefix] The application context and the theme servlet context.
     * @config {String} [custom] An array of basenames identifying an application's 
     * javascript theme files. The last segment of this "dot" separated 
     * string, is treated as the "bundle", and the initial segments are
     * treated as the module path.
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    init: function(props) {
        if (props == null) {
            return false;
        }

	webui.suntheme.theme.common.modulePath = null;
        webui.suntheme.theme.common.prefix = props.prefix;
        webui.suntheme.theme.common.module = props.module;
        webui.suntheme.theme.common.bundle = props.bundle;
        webui.suntheme.theme.common.locale = props.locale;

	if (props.modulePath != null && props.modulePath != "") {
	    webui.suntheme.theme.common.modulePath = props.prefix;
	    if (props.modulePath.charAt(0) != '/') {
		webui.suntheme.theme.common.modulePath = 
			webui.suntheme.theme.common.modulePath + "/";
	    }
	    webui.suntheme.theme.common.modulePath = 
		webui.suntheme.theme.common.modulePath + props.modulePath;
	    dojo.registerModulePath(props.module, 
		webui.suntheme.theme.common.modulePath);
	}

        // Load the javascript theme
        //
        webui.suntheme.theme.common.requireLocalization(props.module, 
            props.bundle, props.locale);

        webui.suntheme.theme.common.baseTheme = dojo.i18n.getLocalization(
            props.module, props.bundle, props.locale);

        if (props.custom instanceof Array) {
            for (var i = 0; i < props.custom.length; ++i) {
                webui.suntheme.theme.common.extendBaseTheme(props.custom[i]);
            }
        } else if (typeof(props.custom) == "string") {
            webui.suntheme.theme.common.extendBaseTheme(props.custom);
        }
        return true;
    },

    /**
     * Return the theme prefix, this is the application context
     * concatenated with the theme servlet context.
     *
     * @return {String} The theme prefix.
     */
    getPrefix : function() {
	return webui.suntheme.theme.common.prefix;
    },

    /**
     * Returns a theme property "theme[category][key]" or null, never "".
     *
     * @param {String} category
     * @param {String} key
     * @return {String} The theme property.
     */
    getProperty: function(category, key) {
        try {
            var p = webui.suntheme.theme.common.baseTheme[category][key];
            return p == null || p == "" ? null : p;
        } catch (e) {
            return null;
        }
    },

    /**
     * Returns the theme properties for a theme category or null.
     *
     * @param {String} category
     * @return {Object} Key-Value pairs of properties.
     */
    getProperties: function(category) {
        try {
            var p = webui.suntheme.theme.common.baseTheme[category];
            return p == null || p == "" ? null : p;
        } catch (e) {
            return null;
        }
    },

    /**
     * Returns a formatted message if "params" is not null, else
     * the literal value of "getProperty("messages", prop) or
     * null if there is no value for property.
     *
     * @param {String} property
     * @param {Array} params
     * @return {String} A formatted message.
     */
    getMessage : function(property, params) {
	var msg = webui.suntheme.theme.common.getProperty("messages", property);
	if (msg == null) {
	    return null;
	}
	if (params != null) {
            return msg.replace(/\$\{(\w+)\}/g, function(match, key){
                if (typeof(params[key]) != "undefined" && params[key] != null) {
                    return params[key];
                }
            });
	} else {
	    return msg;
	}
    },

    /**
     * @private
     * @return {Object} Key-Value pairs of properties.
     */
    _getImageProp: function(prop, isText) {

	var value = webui.suntheme.theme.common.getProperty("images", prop);
	if (value == null || value.length == 0) {
	    return null;
	}
	if (isText) {
	    var msg = webui.suntheme.theme.common.getMessage(value, null);
	    if (msg != null && msg.length != 0) {
		value = msg;
	    }
	}
	return value;
    },

    /**
     * Returns the followin Object literals or null
     * <ul>
     * <li>src - the image path with the theme prefix</li>
     * <li>width - image width</li>
     * <li>height - image height</li>
     * <li>title - value for the "title" attribute</li>
     * <li>alt - value for the "alt" attribute</li>
     * </ul>
     * If "alt" or "title" are message properties and not a literal value
     * the property is resolved to its message value.
     * This method should be called with the actual message property
     * and not one of its variants like "ALARM_CRITICAL_ALT". Use
     * "webui.suntheme.theme.common.getProperty("images", "ALARM_CRITICAL_ALT")"
     * to get individual values if desired.
     * If the literal path is desired, without the prefix, use
     * "webui.suntheme.theme.common.getProperty("images", imageprop)"
     * where imageprop is the actual image property like "ALARM_CRITICAL".
     *
     * @param {String} srcProperty the image theme key, the image key without any suffix.
     * @return {Object} Key-Value pairs of properties.
     */
    getImage : function(srcProperty) {

	if (srcProperty == null || srcProperty.length == 0) {
	    return null;
	}
	// Enforce srcProperty as the one without the extension
	//
	var imageSuffixes = [ "ALT", "TITLE", "WIDTH", "HEIGHT" ];
	var srcPropertyParts = srcProperty.split("_");
	if (srcPropertyParts.length > 1) {
	    for (var i = 0; i < imageSuffixes.length; ++i) {
		if (srcPropertyParts[srcPropertyParts.length - 1] ==
			imageSuffixes[i]) {
		    return null;
		}
	    }
	}
	     
	// If this key does not have a value the image is not defined
	// in the theme
	//
	var src = webui.suntheme.theme.common._getImageProp(srcProperty, false);
	if (src == null) {
	    return null;
	}
	var imageObj = {};
	imageObj["src"] = webui.suntheme.theme.common.getPrefix() + src;

	var value = webui.suntheme.theme.common._getImageProp(
		srcProperty + "_WIDTH", false);
	if (value != null) {
	    imageObj["width"] = value;
	}
	value = webui.suntheme.theme.common._getImageProp(
	    srcProperty + "_HEIGHT", false);
	if (value != null) {
	    imageObj["height"] = value;
	}
        var value = webui.suntheme.theme.common._getImageProp(
                    srcProperty + "_MAP", false);
        if (value != null) {
            imageObj["map_key"] = value;
            var value = webui.suntheme.theme.common._getImageProp(
                    srcProperty + "_MAP_WIDTH", false);
            if (value != null) {
                imageObj["actual_width"] = value;
            }
            value = webui.suntheme.theme.common._getImageProp(
                srcProperty + "_MAP_HEIGHT", false);
            if (value != null) {
                imageObj["actual_height"] = value;
            }
            value = webui.suntheme.theme.common._getImageProp(
                srcProperty + "_MAP_TOP", false);
            if (value != null) {
                imageObj["top"] = value;
            }
        }
	value = webui.suntheme.theme.common._getImageProp(
	    srcProperty + "_ALT", true);
	if (value != null) {
	    imageObj["alt"] = value;
	}
	value =  webui.suntheme.theme.common._getImageProp(
	    srcProperty + "_TITLE", true);
	if (value != null) {
	    imageObj["title"] = value;
	}

	return imageObj;
    },

    /**
     * Return the selector from the "styles" theme category for property
     * else null.
     *
     * @param {String} property
     * @return {String} The selector property.
     */
    getClassName : function(property) {
	return webui.suntheme.theme.common.getProperty("styles", property);
    },

    /**
     * This function is used to obtain a the literal "templates"
     * theme value for "key"
     *
     * @param {String} key A key defining a theme "templates" property.
     * @return {String} The template property.
     */
    getTemplate: function(key) {
        return webui.suntheme.theme.common.getProperty("templates", key);
    },

    /**
     * Merge the baseTheme with an application's theme overrides
     * "themePackage" is a "dot" separated string. The "bundle"
     * is the last segment and the prefix segments are the module.
     * Return true if the base theme was extended else false.
     *
     * @param {String} themePackage
     * @return {boolean} true if successful; otherwise, false.
     */
    extendBaseTheme: function(themePackage) {
        if (themePackage == null || themePackage == "") {
            return false;
        }
        var segments = themePackage.split(".");
        var bundle = segments[segments.length - 1];
        var module = segments.slice(0, segments.length - 1).join(".");

        try {
            // If there is no module, i.e. just a bundle segment
            // create a module name in the theme namespace.
            //
            var prefix = webui.suntheme.theme.common.prefix;
            if (module == null || module == "") {
                    webui.suntheme.theme.common.custom = {};
                    module = "webui.suntheme.theme.common.custom";
            } else {
                var re = new RegExp("\\.", "g");
                prefix = prefix + "/" + module.replace(re, "/");
            }
            // NOTE: Shouldn't need to set a module prefix to obtain a module???
	    // Only when the theme files are located under the initial
	    // set prefix for "webui.suntheme."
	    // If they are not located there then a prefix must be set.
	    // For example an application's theme javascript files.
	    //
            dojo.registerModulePath(module, prefix);

            webui.suntheme.theme.common.requireLocalization(
                module, bundle, webui.suntheme.theme.common.locale);
        } catch(e) {
	    return false;
        }
        var newTheme = null;
        try {
            newTheme = dojo.i18n.getLocalization(module, bundle, 
                webui.suntheme.theme.common.locale);
        } catch(e) {
	    return false;
        }
        // Not sure if we should do the "prototype" magic like
        // dojo, vs. just replacing the orginal baseTheme values.
        //
        if (newTheme != null) {
            webui.suntheme.theme.common.extend(
                webui.suntheme.theme.common.baseTheme, newTheme);
        }
        return true;
    },

    /**
     * Extend "theme" with "props". "props" is organized hierarchically.
     *
     * @param {Object} theme
     * @param {Object} props
     * @return {boolean} true if successful; otherwise, false.
     */
    extend: function(theme, props) {
        // To do: A duplicate function is also found in widget/common.js
        if (theme == null || props == null) {
            return false;
        }
        for (var property in props) {
            if (theme[property] && typeof theme[property] == "object") {
                webui.suntheme.theme.common.extend(theme[property], props[property]);
            } else {
                theme[property] = props[property];
            }
        }
        return true;
    },

    /**
     * Declares translated resources and loads them if necessary, in the same 
     * style as dojo.require. Contents of the resource bundle are typically 
     * strings, but may be any name/value pair, represented in JSON format. 
     * See also dojo.i18n.getLocalization.
     * <p>
     * Load translated resource bundles provided underneath the "nls" directory
     * within a package. Translated resources may be located in different
     * packages throughout the source tree.  For example, a particular widget
     * may define one or more resource bundles, structured in a program as
     * follows, where moduleName is mycode.mywidget and bundleNames available
     * include bundleone and bundletwo:
     * </p><p><pre>
     * ...
     * mycode/
     *  mywidget/
     *   nls/
     *    bundleone.js (the fallback translation, English in this example)
     *    bundletwo.js (also a fallback translation)
     *    de/
     *     bundleone.js
     *     bundletwo.js
     *    de-at/
     *     bundleone.js
     *    en/
     *     (empty; use the fallback translation)
     *    en-us/
     *     bundleone.js
     *    en-gb/
     *     bundleone.js
     *    es/
     *     bundleone.js
     *     bundletwo.js
     *   ...etc
     * ...
     * </pre></p><p>
     * Each directory is named for a locale as specified by RFC 3066, 
     * (http://www.ietf.org/rfc/rfc3066.txt), normalized in lowercase. Note that
     * the two bundles in the example do not define all the same variants. For a
     * given locale, bundles will be loaded for that locale and all more general
     * locales above it, including a fallback at the root directory. For 
     * example, a declaration for the "de-at" locale will first load 
     * nls/de-at/bundleone.js, then nls/de/bundleone.js and finally 
     * nls/bundleone.js. The data will be flattened into a single Object so that
     * lookups will follow this cascading pattern.  An optional build step can 
     * preload the bundles to avoid data redundancy and the multiple network 
     * hits normally required to load these resources.
     * </p><p>
     * NOTE: When loading these resources, the packaging does not match what is 
     * on disk. This is an implementation detail, as this is just a private 
     * data structure to hold the loaded resources. For example, 
     * tests/hello/nls/en-us/salutations.js is loaded as the object 
     * tests.hello.nls.salutations.en_us={...} The structure on disk is intended
     * to be most convenient for developers and translators, but in memory it is
     * more logical and efficient to store in a different order. Locales cannot 
     * use dashes, since the resulting path will not evaluate as valid JS, so we 
     * translate them to underscores.
     * </p>
     * @param {String} moduleName Name of the package containing the "nls" 
     * directory in which the bundle is found.
     * @param {String} bundleName The bundle name, i.e. the filename without the
     * '.js' suffix locale: the locale to load (optional). By default, the 
     * browser's user locale as defined by dojo.locale
     * @param {String} locale The current locale.
     * @param {String} availableFlatLocales A comma-separated list of the 
     * available, flattened locales for this bundle.
     * @return {boolean} true if successful; otherwise, false.
     */
    requireLocalization: function(moduleName, bundleName, locale, 
            availableFlatLocales) {
        // Taken from dojo.js in order to override the callback function that is 
        // passed to loadPath, in to perform hierarchical "extension" of properties.

        var targetLocale = dojo.i18n.normalizeLocale(locale);
        var bundlePackage = [moduleName, "nls", bundleName].join(".");
        
        // Find the best-match locale to load if we have available flat locales.
        var bestLocale = "";
        if (availableFlatLocales) {
            var flatLocales = availableFlatLocales.split(",");
            for (var i = 0; i < flatLocales.length; i++) {
                //Locale must match from start of string.
                if (targetLocale.indexOf(flatLocales[i]) == 0) {
                    if (flatLocales[i].length > bestLocale.length) {
                        bestLocale = flatLocales[i];
                    }
                }
            }
            if (!bestLocale) {
                bestLocale = "ROOT";
            }               
        }

        // See if the desired locale is already loaded.
        var tempLocale = availableFlatLocales ? bestLocale : targetLocale;
        var bundle = dojo._loadedModules[bundlePackage];
        var localizedBundle = null;
        if (bundle) {
            if (djConfig.localizationComplete && bundle._built) {
                return false;    
            }
            var jsLoc = tempLocale.replace(/-/g, '_');
            var translationPackage = bundlePackage+"."+jsLoc;
            localizedBundle = dojo._loadedModules[translationPackage];
        }

        if (!localizedBundle) {
            bundle = dojo["provide"](bundlePackage);
            var syms = dojo._getModuleSymbols(moduleName);
            var modpath = syms.concat("nls").join("/");
            var parent;

            dojo.i18n._searchLocalePath(tempLocale, availableFlatLocales, function(loc) {
                var jsLoc = loc.replace(/-/g, '_');
                var translationPackage = bundlePackage + "." + jsLoc;
                var loaded = false;
                if (!dojo._loadedModules[translationPackage]) {
                    // Mark loaded whether it's found or not, 
                    // so that further load attempts will not 
                    // be made
                    dojo["provide"](translationPackage);
                    var module = [modpath];
                    if (loc != "ROOT") {
                        module.push(loc);    
                    }
                    module.push(bundleName);
                    var filespec = module.join("/") + '.js';

                    loaded = dojo._loadPath(filespec, null,
                    function(hash) {
                        // Use singleton with prototype to point to parent
                        // bundle, then mix-in result from loadPath
                        var clazz = function() {};
                        clazz.prototype = parent;
                        bundle[jsLoc] = new clazz();
                        // Use "hierarchical" extend.
                        // for (var j in hash){ bundle[jsLoc][j] = hash[j]; }
                        webui.suntheme.theme.common.extend(bundle[jsLoc], hash);
                    });
                } else {
                    loaded = true;
                }
                if (loaded && bundle[jsLoc]) {
                    parent = bundle[jsLoc];
                } else {
                    bundle[jsLoc] = parent;
                }
                if (availableFlatLocales) {
                    // Stop the locale path searching if we 
                    // know the availableFlatLocales, since
                    // the first call to this function will 
                    // load the only bundle that is needed.
                    return true;
                }
            });
        }

        // Save the best locale bundle as the target locale bundle 
        // when we know the
        // the available bundles.
        //
        if (availableFlatLocales && targetLocale != bestLocale) {
            bundle[targetLocale.replace(/-/g, '_')] = 
                bundle[bestLocale.replace(/-/g, '_')];
        }
        return true;
    }
}

// Initialize theme.
webui.suntheme.theme.common.init(webui.suntheme.bootstrap.theme);
dojo.provide("webui.suntheme.common");


/**
 * @class This class contains functions common to HTML elements.
 * @static
 */
webui.suntheme.common = {
    /**
     * Variables needed when submitting form so timeout will work properly.
     * @private
     */
    formToSubmit: null,
    submissionComponentId: null,

    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // String functions
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    /**
     * Replace occurences of delimiter with the escapeChar and the
     * delimiter. For example replace "," with "/," if delimiter == "," and
     * escapeChar is "/".
     *
     * @param {String} s The string to escape.
     * @param {String} delimiter The character to replace.
     * @param {String} escapeChar The character used for the escape.
     * @return {String} The escaped string.
     */
    escapeString: function(s, delimiter, escapeChar) {
        if (s == null) {
            return null;
        }
        if (delimiter == null) {
            return s;
        }
        if (escapeChar == null) {
            return null;
        }
        
        // Escape occurrences of delimiter with 
        // escapeChar and the delimiter.
        //
        // First escape the escape char.
        //
        var escape_escapeChar = escapeChar;
        if (escapeChar == "\\") {
            escape_escapeChar = escapeChar + escapeChar;
        }
        
        var rx = new RegExp(escape_escapeChar, "g");
        var s1 = s.replace(rx, escapeChar + escapeChar);
        
        rx = new RegExp(delimiter, "g");
        return s1.replace(rx, escapeChar + delimiter);
    },
    
    /**
     * Replace occurences of a sequence of 2 instances of delimiter 
     * with 1 instance of the delimiter. For example replace ",," with "," if delimiter == ","
     *
     * @param {String} s The string to escape.
     * @param {String} delimiter The character to replace.
     * @param {String} escapeChar The character used for the escape.
     * @return {String} The unescaped string.
     */
    unescapeString: function(s, delimiter, escapeChar) {
        if (s == null) {
            return null;
        }
        if (delimiter == null) {
            return s;
        }
        if (escapeChar == null) {
            return null;
        }
        
        // UnEscape occurrences of delimiter with 
        // single instance of the delimiter
        //
        var escape_escapeChar = escapeChar;
        if (escapeChar == "\\") {
            escape_escapeChar = escapeChar + escapeChar;
        }
        
        // First unescape the escape char.
        //
        var rx = new RegExp(escape_escapeChar + escape_escapeChar, "g");
        var s1 = s.replace(rx, escapeChar);
        
        // Now replace escaped delimters
        //
        rx = new RegExp(escape_escapeChar + delimiter, "g");
        return s1.replace(rx, delimiter);
    },
    
    /**
     * Return an array of unescaped strings from escapedString
     * where the escaped character is delimiter.
     * If delimiter is "," escapedString might have the form
     * <p><pre>
     * XX\,XX,MM\,MM
     *
     * where "\" is the escape char.
     * 
     * and is returned as an array
     * array[0] == "XX,XX"
     * array[1] == "MM,MM"
     * </pre><p>
     *
     * @param {String} escapedString The string to escape.
     * @param {String} delimiter The character to replace.
     * @param {String} escapeChar The character used for the escape.
     * @return {Array} An array of unescaped strings.
     */
    unescapeStrings: function(escapedString, delimiter, escapeChar) {
        if (escapedString == null || escapedString == "") {
            return null;
        }
        if (delimiter == null || delimiter == "") {
            return escapedString;
        }
        if (escapeChar == null || escapeChar == "") {
            return null;
        }
        
        // Need to do this character by character.
        var selections = new Array();
        var index = 0;
        var escseen = 0;
        var j = 0;
        
        for (var i = 0; i < escapedString.length; ++i) {
            if (escapedString.charAt(i) == delimiter) {
                if (escseen % 2 == 0) {
                    selections[index++] = escapedString.slice(j, i);
                    j = i + 1;
                }
            }
            if (escapedString.charAt(i) == escapeChar) {
                ++escseen;
                continue;
            } else {
                escseen = 0;
            }
        }
        
        // Capture the last split.
        selections[index] = escapedString.slice(j);
        
        // Now unescape each selection
        var unescapedArray = new Array();
        
        // Now replace escaped delimiters
        // i.e.  "\," with ","
        for (i = 0; i < selections.length; ++i) {
            unescapedArray[i] = webui.suntheme.common.unescapeString(
            selections[i], delimiter, escapeChar);
        }
        return unescapedArray;
    },
    
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Style functions
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    /**
     * Use this function add any styleClass to an html tag
     *
     * @param {Node} element the dom html tag element
     * @param {String} styleClass the name of the class to add
     * @return {boolean} true if successful; otherwise, false.
     */
    addStyleClass: function(element, styleClass) {
        // routine protection in javascript
        if (element == null || styleClass == null) {
            return false;
        }
        
        // handle easy case first
        if (element.className == null) {
            element.className = styleClass;
            return true;
        }
        
        // break out style classes into an array  
        var classes = webui.suntheme.common.splitStyleClasses(element);
        if (classes == null) {
            return false;
        }
        
        // For each styleClass, check if it's hidden and remove otherwise write 
        // it back out to the class
        for (var i = 0; i < classes.length; i++) {
            if (classes[i] != null && classes[i] == styleClass) {
                return true;
            }
        }
        element.className = element.className + " " + styleClass;
        return true;
    },
    
    /**
     * Use this function to check if an array has a style class
     *
     * @param {Array} styleArray of style classes to check
     * @param {String} styleClass the styleClass to check
     * @return {Array} An array of classes.
     */
    checkStyleClasses: function(styleArray, styleClass) {
        if (styleArray == null || styleClass == null) {
            return false;
        }
        for (var i = 0; i < styleArray.length; i++) {
            if (styleArray[i] != null && styleArray[i] == styleClass) {
                return true;
            }
        }   
        return false;
    },
    
    /**
     * Use this function to get array of style classes
     *
     * @param {Node} element the dom html tag element
     * @return {Array} An array of classes.
     */
    splitStyleClasses: function(element) {
        if (element != null && element.className != null) {
            return element.className.split(" ");
        } else {
            return null;
        }
    },
    
    /**
     * Use this function remove any styleClass for an html tag
     *
     * @param {Node} element the dom html tag element
     * @param {String} styleClass the name of the class to remove
     * @return {boolean} true if successful; otherwise, false.
     */
    stripStyleClass: function(element, styleClass) {
        // routine protection in javascript
        if (element == null || styleClass == null || element.className == null) {
            return false;
        }
        
        // break out style classes into an array  
        var classes = webui.suntheme.common.splitStyleClasses(element);
        if (classes == null) {
            return false;
        }
        
        // For each styleClass, check if it's hidden and remove otherwise write
        // it back out to the class
        for (var i = 0; i < classes.length; i++) {
            if (classes[i] != null && classes[i] == styleClass) {
                classes.splice(i,1);  	
            }
        }
        element.className = classes.join(" ");
        return true;
    },
    
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Submit functions
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    /**
     * Use this function to insert a hidden field element in the page.
     *
     * @param {String} elementId The element ID of the html tag 
     * @param {String} elementValue The value of the html tag.
     * @param {Node} parentForm The parent form of the html tag.
     * @return {boolean} true if successful; otherwise, false.
     */
    insertHiddenField: function(elementId, elementValue, parentForm) {
        // We have to assume that there is only one element
        // with elementId. document.getElementById, returns
        // the first one it finds, which appears to be the 
        // first one created dynamically, if more than one 
        // element is created dynamically with the same id.
        //
        // appendChild just appends even if there is an element
        // with the same id that exists.
        //
        // The assumption is that there should only be 
        // one element in the document with such an id.
        //
        // If the elementId exists just modifiy its value
        // instead of creating and appending.
        //
        var element = document.getElementById(elementId);
        if (element != null) {
            element.value = elementValue;
            return true;
        }
        
        var newElement = document.createElement('input');
        newElement.type = 'hidden';
        newElement.id = elementId;
        newElement.value = elementValue;
        newElement.name = elementId;
        parentForm.appendChild(newElement);

        return true;
    },
    
    /**
     * Use this function to submit a virtual form.
     *
     * @return {boolean} true if successful; otherwise, false.
     */
    submitForm: function() {
        // "formToSubmit" is a literal (not virtual) form.
        // "submissionComponentId" is a component id (not client id).
        // the virtual form implementation uses _submissionComponentId
        // to determine which virtual form (if any) was submitted.
        if (webui.suntheme.common.formToSubmit == null) {
            return false;
        }
        if (webui.suntheme.common.submissionComponentId != null &&
                webui.suntheme.common.submissionComponentId.length > 0) {
            webui.suntheme.common.insertHiddenField('_submissionComponentId', 
                webui.suntheme.common.submissionComponentId,
                webui.suntheme.common.formToSubmit);
        }
        webui.suntheme.common.formToSubmit.submit();
        return false;
    },
    
    /**
     * Helper function to submit a virtual form.
     *
     * @param {Node} form The HTML form element to submit.
     * @param {String} submissionComponentId The Id of the component submitting the form.
     * @return {boolean} true if successful; otherwise, false.
     */
    timeoutSubmitForm: function(form, submissionComponentId) {
        webui.suntheme.common.formToSubmit = form;
        webui.suntheme.common.submissionComponentId = submissionComponentId;
        setTimeout('webui.suntheme.common.submitForm()', 0);
        return true;
    },
    
    /**
     * Helper function to submit a virtual form.
     *
     * @param {Node} form The HTML form element to submit.
     * @param {String} submissionComponentId The Id of the component submitting the form.
     * @return {boolean} true if successful; otherwise, false.
     */
    leaveSubmitterTrace: function(form, submissionComponentId) {
        // This function only needs to be called in the onclick handler of 
        // an ActionSource component that appears within a -standard- table.
        // Under those circumstances, if this function is not called, then when
        // the component is clicked, the virtual form implementation will have 
        // no way of knowing that a virtual form was submitted.
        if (form != null && submissionComponentId != null && 
                submissionComponentId.length > 0) {
            webui.suntheme.common.insertHiddenField('_submissionComponentId',
            submissionComponentId, form);
        }
        return true;
    },
    
    /**
     * delete a previously created element by createSubmittableArray.
     *
     * @param {String} name The element ID of the html tag 
     * @param {Node} form The HTML form element to submit.
     * @return {boolean} true if successful; otherwise, false.
     */
    deleteSubmittableArray: function(name, parentForm) {
        try {
            var submittableArray  = document.getElementById(name);
            if (submittableArray != null) {
                parentForm.removeChild(submittableArray);
            }
        } catch (e) {}
        return true;
    },
    
    /**
     * This method creates a hidden "select" element with id 
     * and name attributes set name, values taken from the values
     * array argument, and display labels from the labels array.
     * It adds the element to the parentForm argument.
     * <p>
     * The pupose of this method is to create an array of values
     * that can be decoded using "name" as the key from a FacesContext
     * external context's "getRequestParameterValuesMap" as an
     * array of Strings. This reduces the need of rendering hidden input
     * field and delimiting several strings so that a multiple selection
     * can be returned.
     * </p><p>
     * The labels array provides an additional piece of data
     * for use on the client, but it is not contained in the submit.
     * All values added to the select are selected so that the
     * values will be submitted.
     * </p>
     *
     * @param {String} name The element ID of the html tag 
     * @param {Node} form The HTML form element to submit.
     * @param {Array} labels
     * @param {Array} values
     * @return {Node} The newly created select element.
     */
    createSubmittableArray: function(name, parentForm, labels, values) {
        // An attempt is made to remove a possibly previously created element
        // by this name. It always deletes an element of name from parentForm.
        webui.suntheme.common.deleteSubmittableArray(name, parentForm);
        
        if (values == null || values.length <= 0) {
            return;
        }
        
        var selections = document.createElement('select');
        selections.className = webui.suntheme.theme.common.getClassName("HIDDEN");
        selections.name = name;
        selections.id = name;
        selections.multiple = true;
        
        // Start from the end of the array because
        // add puts things in at the head.
        //
        for (var i = 0; i < values.length; ++i) {
            var opt = document.createElement('option');
            opt.value = values[i];
            if (labels != null) {
                opt.label = labels[i];
            }
            opt.defaultSelected = true;
            selections.add(opt, null);
        }
        parentForm.appendChild(selections);
        return selections;
    },
    
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Visible functions
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    /**
     * Use this function to test if the specified element is visible (i.e., it
     * does not contain the hidden className).
     *
     * @param {String} elementId The element ID of the html tag 
     * @return {boolean} true if visible; otherwise, false
     */
    isVisible: function(elementId) {
        if (elementId == null) {
            return false;
        }
        // Get element.
        var element = document.getElementById(elementId);
        return webui.suntheme.common.isVisibleElement(element);
    },
    
    /**
     * Use this function to test if the given element is visible (i.e., it
     * does not contain the hidden className).
     *
     * @param {Node} element The HTML element
     * @return {boolean} true if visible; otherwise, false
     */
    isVisibleElement: function(element) {
        if (element == null) {
            return false;
        }
        // Test for the hidden style class.
        var styleClasses = webui.suntheme.common.splitStyleClasses(element); 
        return !webui.suntheme.common.checkStyleClasses(styleClasses,
            webui.suntheme.theme.common.getClassName("HIDDEN"));
    },
    
    /**
     * Use this function to show or hide any html element in the page
     *
     * @param {String} elementId The element ID of the html tag 
     * @param {boolean} visible true to make the element visible, false to hide the element
     * @return {boolean} true if successful; otherwise, false.
     */
    setVisible: function(elementId, visible) {
        if (elementId == null || visible == null ) {
            return false;
        }
        // Get element.
        var element = document.getElementById(elementId);
        return webui.suntheme.common.setVisibleElement(element, visible);
    },
    
    /**
     * Use this function to show or hide any html element in the page
     *
     * @param {Node} element The HTML element
     * @param {boolean} visible true to make the element visible, false to hide the element
     * @return {boolean} true if successful; otherwise, false.
     */
    setVisibleElement: function(element, visible) {
        if (element == null || visible == null) {
            return false;
        }
        if (visible) {
            return webui.suntheme.common.stripStyleClass(element,
                webui.suntheme.theme.common.getClassName("HIDDEN"));
        } else {
            return webui.suntheme.common.addStyleClass(element,
                webui.suntheme.theme.common.getClassName("HIDDEN"));
        }
    }
}
dojo.provide("webui.suntheme.cookie");

/**
 * @class This class contains functions to manipulate cookies.
 * @static
 */
webui.suntheme.cookie = {
    /**
     * This function will get the cookie value.
     *
     * @return {String} The cookie value.
     */
    get: function() {
        // Get document cookie.
        var cookie = document.cookie;

        // Parse webui_ScrollCookie value.
        var pos = cookie.indexOf(this.$cookieName + "=");
        if (pos == -1) {
            return null;
        }

        var start = pos + this.$cookieName.length + 1;
        var end = cookie.indexOf(";", start);
        if (end == -1) {
            end = cookie.length;
        }

        // return cookie value
        return cookie.substring(start, end);
    },

    /**
     * This function will load the cookie value.
     *
     * @return {boolean} true if successful; otherwise, false.
     */
    load: function() {
        // Get document cookie.
        var cookieVal = this.get();
        if (cookieVal == null) {
            return false;
        }

        // Break cookie into names and values.
        var a = cookieVal.split('&');

        // Break each pair into an array.
        for (var i = 0; i < a.length; i++) {
            a[i] = a[i].split(':');
        }

        // Set name and values for this object.
        for (i = 0; i < a.length; i++) {
            this[a[i][0]] = unescape(a[i][1]);
        }
        return true;
    },

    /**
     * This function will reset the cookie value.
     *
     * @return {boolean} true if successful; otherwise, false.
     */
    reset: function() {
        // Clear cookie value.
        document.cookie = this.$cookieName + "=";
        return true;
    },

    /**
     * This function will store the cookie value.
     *
     * @return {boolean} true if successful; otherwise, false.
     */
    store: function() {
        // Create cookie value by looping through object properties
        var cookieVal = "";

        // Since cookies use the equals and semicolon signs as separators,
        // we'll use colons and ampersands for each variable we store.
        for (var prop in this) {
            // Ignore properties that begin with '$' and methods.
            if (prop.charAt(0) == '$' || typeof this[prop] == 'function') {
                continue;
            }
            if (cookieVal != "") {
                cookieVal += '&';
            }
            cookieVal += prop + ':' + escape(this[prop]);
        }
        var cookieString = this.$cookieName + "=" + cookieVal;
        if (this.$path != null) {
            cookieString += ";path=" + this.$path;
        }
        // Store cookie value.
        document.cookie = cookieString;
        return true;
    }
}

/**
 * @constructor This function is used to construct a javascript object for
 * maintaining scroll position via cookie.
 * @param {String} viewId
 * @param {String} path
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.scrollCookie = function(viewId, path) {    
    // All predefined properties of this object begin with '$' because
    // we don't want to store these values in the cookie.
    this.$cookieName = viewId;
    this.$path = path;

    // Default properties.
    this.left = "0";
    this.top  = "0";

    // This function will load the cookie and restore scroll position.
    this.restore = function() {
        // Load cookie value.
        this.load();
        scrollTo(this.left, this.top);
        return true;
    };

    // This function will set the cookie value.
    this.set = function() {
        var documentElement = window.document.documentElement;
        if (documentElement && documentElement.scrollTop) {
            this.left = documentElement.scrollLeft;
            this.top  = documentElement.scrollTop;
        } else {
            this.left = window.document.body.scrollLeft;
            this.top  = window.document.body.scrollTop;
        }
        // if the left and top scroll values are still null
        // try to extract it assuming the browser is IE
        if (this.left == null && this.top == null) {
            this.left = window.pageXOffset;
            this.top = window.pageYOffset;
        }
        // Store cookie value.
        this.store();
        return true;
    };
    return true;
}

// Inherit cookie properties.
webui.suntheme.scrollCookie.prototype = webui.suntheme.cookie;
dojo.provide("webui.suntheme.body");


/**
 * @class This class contains functions used to maintain focus and scroll position.
 * <p>
 * There can be an initial focus element and a default focus element. The
 * initial focus element is identifed by the "focusElementId" argument.
 * This argument is typically null on the first display of the page. If
 * the Body component is not preserving the focus then it may also be null,
 * at other times, since it represents the element id that last received
 * the focus before the request.
 * </p><p>
 * Whenever the page is displayed and "focusElementId" is null
 * "defaultFocusElementId" will receive the focus if it is not null. This id is
 * defined by the application using the Body "focus" attribute. If the
 * application wants to control the focus in all cases then it should set
 * the Body component "preserveFocus" attribute to "false". The application then
 * explicitly sets the Body "focus" attribute to the element id to receive
 * focus on every request/response.
 * </p><p>
 * In order to preserve focus across requests, the "focusElementFieldId"
 * element is used to preserve the id of the element that receives the focus
 * last. It is a hidden field that is submitted in the
 * request. See the "com.sun.webui.jsf.util.FocusManager" class for
 * information on how the focus is managed. This field exists in all
 * forms, since that it is the only way to guarantee that the last
 * element that received the focus is sent to the server. Which form
 * is submitted can never be known.
 * </p>
 * @constructor This function is used to construct a body object.
 * @param {String} viewId Used to name the scroll cookie
 * @param {String} path A member of the scroll cookie
 * @param {String} defaultFocusElementId The HTML element id that will receive focus.
 * @param {String} focusElementId The id of the element to receive the initial focus.
 * @param {String} focusElementFieldId The id of a hidden field to maintain
 * the id of the last element to have the focus.
 * @param {boolean} preserveScroll if true or not defined the scroll position is 
 * maintained else scroll is not maintained.
 */
webui.suntheme.body = function(viewId, path, defaultFocusElementId, 
	focusElementId, focusElementFieldId, preserveScroll)  {
    /**
     * The id of the HTML element to receive the focus, if the
     * element identified in focusElementFieldId cannot receive the focus.
     */
    this.defaultFocusId = defaultFocusElementId;

    /**
     * The id of a hidden input element whose value is the id
     * of the HTML element to receive the focus. It is set by
     * the focusListener and calls to setFocusBy{Id,Element}.
     */
    this.focusElementFieldId = focusElementFieldId;

    /**
     * The element id to receive the preserved, or initial focus.
     * This member should not be referenced once the onload listener
     * has been invoked. After that point the hidden field will have
     * have the element with the focus. We do this so that two locations
     * do not have to be kept in sync. Developers can just set the focus
     * to the element itself and the focus handler will manage the
     * focus persisitence.
     */
    this.focusElementId = focusElementId;

    // "==" also handles "null"
    //
    this.preserveScroll = (preserveScroll == undefined 
        ? true : new Boolean(preserveScroll).valueOf());

    /**
     * Create the scroll cookie object.
     */
    if (this.preserveScroll == true) {
	this.scrollCookie = new webui.suntheme.scrollCookie(viewId, path);
    }

    /**
     * This funtionality is taken care of by the body onload listener,
     * body.onLoadListener.
     *
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated
     * @ignore Until JsDoc supports deprecated tag.
     */
    this.setInitialFocus = function() {
	// In old code this method was called as an onload handler.
	// therefore do nothing. We use an onload listener to
	// initialize the focus now.
	// Assume that it was never called by developers.
	// Actually, we really only have to be concerned with
	// early adopters of woodstock, and there aren't many.
	//
        return true; 
    };

    /**
     * Set the focus on element with id "fid".
     * If focus can be set returns true else false.
     *
     * @param {String} fid The id of the DOM node to have focus.
     * @return {boolean} true if successful; otherwise, false.
     */
    this.setFocusById = function(fid) {
	if (fid == null || fid.length == 0) {
	    return false;
	}
	return this.setFocusByElement(window.document.getElementById(fid));
    };

    /**
     * Set the focus on "focusElement".
     * If focus can be set returns true else false.
     *
     * @param {Node} focusElement The DOM node to have focus.
     * @return {boolean} true if successful; otherwise, false.
     */
    this.setFocusByElement = function(focusElement) {
	if (focusElement == null || !this.canAcceptFocus(focusElement)) {
	    return false;
	}

	// canAcceptFocus tests the existence of the "focus" handler.
	// So it is safe to call it outside of a try catch.
	// This should trigger the focus listener.
	//
	focusElement.focus();
	// Assume that this update is performed by the 
	// focus listener. This policy was changed in order to 
	// call "setDefaultFocus" using "setTimeout" in order for
	// javascript to have time to be evaluated, probably for
	// on load processing to complete.
	//
	//this.updateFocusElementField(focusElement);

	return true;
    };

    /**
     * Set the default focus by restoring the focus from a previous
     * request or to the application's chosen default focus element.
     * This method should only  be called once to prevent recursive
     * calls since it calls "focus()" on the focus element.
     * Called currently from the onload listener.
     * <p>
     * If "this.focusElementId" is not null it will receive the focus.
     * If that element can't receive the focus
     * then the application defined "this.defaultFocusId" receives the focus.
     * If that element cannot receive the focus, no focus is set.
     * </p>
     * @return {boolean} false if a default focus cannot be established, else true.
     */
    this.setDefaultFocus = function() {

	// Try to set focus to "this.focusElementId". If this fails
	// fallback to the app defined default 
	// "this.defaultFocusElementId", if there is one.
	//
	if (this.setFocusById(this.focusElementId)) {
	    return true;
	}

	// Focus not set try the default.
	//
	if (this.setFocusById(this.defaultFocusId)) {
	    return true;
	}

	/* For now it doesn't sound like a good idea to ever set
	 * a "heuristic" default focus. It is better for screen readers to start
	 * from the top of the page which we will assume that that
	 * browser starts from there when focus is not set explicitly.
	 * This code can be removed, but left it in case we want to
	 * for some reason.

	// No previously set focus element and no default.
	// Find the first focusable element in the first available form.
	//
	for each (var f in window.document.forms) {
	    for each (var e in f.elements) {
		if (this.setFocusByElement(e)) {
		    return true;
		}
	    }
	}
	// If there is no form, set on the first available link
	//
	for each (var l in window.document.links) {
	    if (this.setFocusByElement(l)) {
		return true;
	    }
	}
	*/
	return false;
    };

    /**
     * According to HTML spec only these elements have
     * "onfocus" which we will equate to "focus".
     * A, AREA, BUTTON, INPUT, LABEL, SELECT, TEXTAREA
     * However just check for a non null "focus" or 
     * catch the exception when trying to reference it.
     * Returns true if the element has a "focus" method, is not
     * disabled, and isVisible, else false.
     *
     * @param {Node} element The DOM node to have focus.
     * @return {boolean} true if DOM Node can have focus.
     */
    this.canAcceptFocus = function(element) {
	var result = false;
	try {
	    result = element != null && element.focus && !element.disabled &&
		element.type != "hidden" &&
		webui.suntheme.common.isVisible(element.id);
	} catch (e) {
	}
	return result;
    };

    /** 
     * Update the hidden field that maintains the last element to 
     * receive the focus. If the body has multiple forms every form's
     * hidden field is updated with the "focusElement".
     *
     * @param {Node} focusElement The DOM node to have focus.
     * @return {boolean} true if successful; otherwise, false.
     */
    this.updateFocusElementField = function(focusElement) {

	// Don't know if we'll have issues if multiple forms contain
	// an element with the same id. I know getElementById gets
	// confused.
	//

        if (focusElement == null) {
	    return false;
	}
	// Get the form that contains the focus element.
	//
	for (var i = 0;  i < document.forms.length; ++i) {
	    var fform = document.forms[i];

	    // Get the hidden field that maintains the focus element id.
	    // If it exists return it. We know its name is the same
	    // as its id.
	    //
	    try {
		var ffield = fform.elements[this.focusElementFieldId];
		if (ffield != null) {
		    ffield.value = focusElement.id;
		    continue;
		}
	    } catch (e) {
		// the array access of a non existent element
		// probably threw exception so create the field.
	    }
		
	    // If it doesn't exist create it.
	    // and add it to the form.
	    //
	    ffield = document.createElement('input');
	    ffield.type = 'hidden';
	    ffield.id = this.focusElementFieldId;
	    ffield.name = this.focusElementFieldId;
	    ffield.value = focusElement.id;
	    fform.appendChild(ffield);
	}
	return true;
    };

    /**
     * This method is invoked in the onunload event listener
     * body.onUnloadListener
     *
     * @return {boolean} true if successful; otherwise, false.
     */
    this.storeScrollPosition = function() {
	if (!this.preserveScroll) {
	    return false;
	}
	try {
	    this.scrollCookie.set(); 
	} catch (e) {
	}
        return true; 
    };

    /**
     * This method is invoked in the onload listener, body.onLoadListener.
     *
     * @return {boolean} true if successful; otherwise, false.
     */
    this.setDefaultScrollPosition = function() {
	if (!this.preserveScroll) {
	    return false;
	}
	// # char found, anchor being used. forego scrolling.
	// CR 6342635. 
	//
        if (window.location.href.indexOf('#') == -1) {
	    this.scrollCookie.restore(); 
	} else {
	    // destroy the recent scroll data
	    //
	    this.scrollCookie.reset();
	}
        return true;
    };

    this.showAlert = true;

    /**
     * Record the id of the element that has just receivied focus.
     * This is called whenever an element receives the focus.
     * This is set on the document so that the cursor entering the
     * window does not trigger this listener.
     *
     * @param {Event} event The object generated by the focus event.
     * @return {boolean} true if successful; otherwise, false.
     */
    this.focusListener = function(event) {
	// If it's not an element node just return
	//
	var node = null;
	var isElementNode = false;
	
	// is IE 
	//
	if (document.attachEvent) {
	    node = event.srcElement;
	
	    // We have to hard code "1" as the Node.ELEMENT_NODE in
	    // ie, because ie does not make the constant accessible.
	    //
	    isElementNode = (node == null ? false : node.nodeType == 1);
	} else {
	    node = event.target;
	    isElementNode = node.nodeType == Node.ELEMENT_NODE;
	}

	if (isElementNode) {
	    // Note that there is no reliable way to set
	    // focus to some other element if the event element
	    // deemed to receive the focus can't accept the focus.
	    //
	    this.updateFocusElementField(node);
	}
	return true;
    };

    /**
     * Set the initial focus and the scroll position.
     *
     * @param {Event} event The object generated by the focus event.
     * @return {boolean} true if successful; otherwise, false.
     */
    this.onLoadListener = function(event) {

	// register the focus listener first.
	// Then call "setDefaultFocus" using "setTimeout" to
	// allow javascript processing to complete, probably
	// to allow "onload" to complete. The focus listener
	// will update the hidden focus fields as a result
	// of the call to "focus" in setDefaultFocus.
	//

	// Add the focus listener, in the onload to prevent
	// recursive calls from calling setDefaultFocus.
	//
        if (webui.suntheme.browser.isIe()) {
            dojo.connect(document, "onfocusin", this, "focusListener");
        } else {
            dojo.connect(window, "onfocus", this, "focusListener");
        }

        // use settimeout to delay setting focus on the default
        // element so that it has been instantiated in the page.
        // Also rely on the focus listener to update the focus
        // hidden fields by catching the 'element.focus()' in
        // setDefaultFocus
        //
	window.setTimeout('webui.suntheme.bootstrap.body.setDefaultFocus()', 10);

	// Set up the scroll position after the focus has been
	// restored. Need to make sure that this takes into
	// account the default focus that was just set.
	//
	return this.setDefaultScrollPosition();
    };

    /**
     * Update the page's scroll position.
     *
     * @param {Event} event The object generated by the focus event.
     * @return {boolean} true if successful; otherwise, false.
     */
    this.onUnloadListener = function(event) {
	return this.storeScrollPosition();
    };

    // The focus listener is set on the document so that the cursor 
    // entering the window does not trigger this listener. If we are not 
    // preserving scroll don't add the unload listener.
    this.onLoadListener();
    if (this.preserveScroll == true) {
        dojo.addOnUnload(this, "onUnloadListener");
    }
}
dojo.provide("webui.suntheme.formElements");


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// button functions
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/**
 * @class This class contains functions for button components.
 * @static
 *
 * @deprecated See webui.suntheme.widget.button
 * @ignore Until JsDoc supports deprecated tag.
 */
webui.suntheme.button = {
    /**
     * This function is used to initialize HTML element properties with Object
     * literals.
     *
     * @param {Object} props Key-Value pairs of properties.
     * @config {String} [id] The element id.
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated See webui.suntheme.widget.button
     * @private
     */
    init: function(props) {
        if (props == null || props.id == null) {
            return false;
        }
        var widget = dijit.byId(props.id);
        if (widget == null) {
            return false;
        }

        // Set functions
        widget.domNode.isSecondary = webui.suntheme.button.isSecondary;
        widget.domNode.setSecondary = webui.suntheme.button.setSecondary;
        widget.domNode.isPrimary = webui.suntheme.button.isPrimary;
        widget.domNode.setPrimary = webui.suntheme.button.setPrimary;
        widget.domNode.isMini = webui.suntheme.button.isMini;
        widget.domNode.setMini = webui.suntheme.button.setMini;
        widget.domNode.getDisabled = webui.suntheme.button.getDisabled;
        widget.domNode.setDisabled = webui.suntheme.button.setDisabled;
        widget.domNode.getVisible = webui.suntheme.button.getVisible;
        widget.domNode.setVisible = webui.suntheme.button.setVisible;
        widget.domNode.getText = webui.suntheme.button.getText;
        widget.domNode.setText = webui.suntheme.button.setText;
        widget.domNode.doClick = webui.suntheme.button.click;

        return true;
    },

    /**
     * Simulate a mouse click in a button. 
     *
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).click();
     * @ignore Until JsDoc supports deprecated tag.
     */
    click: function() {
        return this.click();
    },

    /**
     * Get the textual label of a button. 
     *
     * @return {String} The element value.
     * @deprecated Use document.getElementById(id).getProps().value;
     * @ignore Until JsDoc supports deprecated tag.
     */
    getText: function() {
        return this.getProps().value;
    },

    /**
     * Set the textual label of a button. 
     *
     * @param {String} text The element value
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({value: "text"});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setText: function(text) {
        return this.setProps({value: text});
    },

    /**
     * Use this function to show or hide a button. 
     *
     * @param {boolean} show true to show the element, false to hide the element
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({visible: boolean});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setVisible: function(show) {
        if (show == null) {
            return null;
        }
        return this.setProps({visible: show});
    },

    /**
     * Use this function to find whether or not this is visible according to our
     * spec.
     *
     * @return {boolean} true if visible; otherwise, false
     * @deprecated Use document.getElementById(id).getProps().visible;
     * @ignore Until JsDoc supports deprecated tag.
     */
    getVisible: function() {
        return this.getProps().visible;
    },

    /**
     * Test if button is set as "primary".
     *
     * @return {boolean} true if primary; otherwise, false for secondary
     * @deprecated Use document.getElementById(id).getProps().primary;
     * @ignore Until JsDoc supports deprecated tag.
     */
    isPrimary: function() {
        return this.getProps().primary;
    },

    /**
     * Set button as "primary".
     *
     * @param {boolean} primary true for primary, false for secondary
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({primary: boolean});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setPrimary: function(primary) {
        if (primary == null) {
            return null;
        }
        return this.setProps({primary: primary});
    },

    /**
     * Test if button is set as "secondary".
     *
     * @return {boolean} true if secondary; otherwise, false for primary
     * @deprecated Use !(document.getElementById(id).getProps().primary);
     * @ignore Until JsDoc supports deprecated tag.
     */
    isSecondary: function() {
        return !(this.getProps().primary);
    },

    /**
     * Set button as "secondary".
     *
     * @param {boolean} secondary true for secondary, false for primary
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({primary: false});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setSecondary: function(secondary) {
        if (secondary == null) {
            return null;
        }
        return this.setProps({primary: !secondary});
    },

    /**
     * Test if button is set as "mini".
     *
     * @return {boolean} true if mini; otherwise, false
     * @deprecated Use document.getElementById(id).getProps().mini;
     * @ignore Until JsDoc supports deprecated tag.
     */
    isMini: function() {
        return this.getProps().mini;
    },

    /**
     * Set button as "mini".
     *
     * @param {boolean} mini true for mini, false for standard button
     * @deprecated Use document.getElementById(id).setProps({mini: boolean});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setMini: function(mini) {
        if (mini == null) {
            return null;
        }
        return this.setProps({mini: mini});
    },

    /**
     * Test disabled state of button.
     *
     * @return {boolean} true if disabled; otherwise, false
     * @deprecated Use document.getElementById(id).getProps().disabled;
     * @ignore Until JsDoc supports deprecated tag.
     */
    getDisabled: function() {
        return this.getProps().disabled;
    },

    /**
     * Test disabled state of button.
     *
     * @param {boolean} disabled true if disabled; otherwise, false
     * @deprecated Use document.getElementById(id).setProps({disabled: boolean});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setDisabled: function(disabled) {
        if (disabled == null) {
            return null;
        }
        return this.setProps({disabled: disabled});
    }
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// checkbox functions
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/**
 * @class This class contains functions for checkbox components.
 * @static
 * 
 * @deprecated See webui.suntheme.widget.checkbox
 * @ignore Until JsDoc supports deprecated tag.
 */
webui.suntheme.checkbox = {
    /**
     * Set the disabled state for the given checkbox element Id. If the disabled 
     * state is set to true, the element is shown with disabled styles.
     *
     * @param {String} elementId The element Id
     * @param {boolean} disabled true or false
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({disabled: boolean});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setDisabled: function(elementId, disabled) {
        return webui.suntheme.rbcb.setDisabled(elementId, disabled,
            "checkbox", "Cb", "CbDis");
    },

    /** 
     * Set the disabled state for all the checkboxes in the check box
     * group identified by controlName. If disabled
     * is set to true, the check boxes are shown with disabled styles.
     *
     * @param {String} controlName The checkbox group control name
     * @param {boolean} disabled true or false
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({disabled: boolean});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setGroupDisabled: function(controlName, disabled) {    
        return webui.suntheme.rbcb.setGroupDisabled(controlName,
            disabled, "checkbox", "Cb", "CbDis");
    },

    /**
     * Set the checked property for a checkbox with the given element Id.
     *
     * @param {String} elementId The element Id
     * @param checked true or false
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({checked: boolean});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setChecked: function(elementId, checked) {
        return webui.suntheme.rbcb.setChecked(elementId, checked,
            "checkbox");
    }
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// dropdown functions
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/**
 * @class This class contains functions for dropDown components.
 * @static
 * 
 * @deprecated See webui.suntheme.widget.dropDown
 * @ignore Until JsDoc supports deprecated tag.
 */
webui.suntheme.dropDown = {
    /**
     * Use this function to access the HTML select element that makes up
     * the dropDown.
     *
     * @param {String} elementId The component id of the JSF component (this id is
     * assigned to the span tag enclosing the HTML elements that make up
     * the dropDown).
     * @return {Node} a reference to the select element. 
     * @deprecated Use document.getElementById(elementId).setSelectElement()
     * @ignore Until JsDoc supports deprecated tag.
     */
    getSelectElement: function(elementId) { 
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.getSelectElement();
        }
        return null;
    },

    /**
     * This function is invoked by the choice onselect action to set the
     * selected, and disabled styles.
     *
     * Page authors should invoke this function if they set the 
     * selection using JavaScript.
     *
     * @param {String} elementId The component id of the JSF component (this id is
     * rendered in the div tag enclosing the HTML elements that make up
     * the list).
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(elementId).changed();
     * @ignore Until JsDoc supports deprecated tag.
     */
    changed: function(elementId) {         
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.changed();
        }
        return false;
    },

    /**
     * Set the disabled state for given dropdown element Id. If the disabled 
     * state is set to true, the element is shown with disabled styles.
     *
     * Page authors should invoke this function if they dynamically
     * enable or disable a dropdown using JavaScript.
     * 
     * @param {String} elementId The component id of the JSF component (this id is
     * rendered in the div tag enclosing the HTML elements that make up
     * the list).
     * @param {boolean} disabled true or false
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(elementId).setProps({disabled: boolean});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setDisabled: function(elementId, disabled) { 
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.setProps({ disabled: disabled});
        }
        return null;
    },

    /**
     * Invoke this JavaScript function to get the value of the first
     * selected option on the dropDown. If no option is selected, this
     * function returns null. 
     *
     * @param {String} elementId The component id of the JSF component (this id is
     * rendered in the div tag enclosing the HTML elements that make up
     * the list).
     * @return {String} The value of the selected option, or null if none is
     * selected. 
     * @deprecated Use document.getElementById(elementId).getSelectedValue();
     * @ignore Until JsDoc supports deprecated tag.
     */
    getSelectedValue: function(elementId) { 
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.getSelectedValue();
        }
        return null;
    },

    /**
     * Invoke this JavaScript function to get the label of the first
     * selected option on the dropDown. If no option is selected, this
     * function returns null.
     * 
     * @param {String} elementId The component id of the JSF component (this id is
     * rendered in the div tag enclosing the HTML elements that make up
     * the list).
     * @return {String} The label of the selected option, or null if none is
     * selected. 
     * @deprecated Use document.getElementById(elementId).getSelectedLabel();
     * @ignore Until JsDoc supports deprecated tag.
     */
    getSelectedLabel: function(elementId) { 
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.getSelectedLabel();
        }
        return null;
    }
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// field functions
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/**
 * @class This class contains functions for field components.
 * @static
 *
 * @deprecated See webui.suntheme.widget.field
 * @ignore Until JsDoc supports deprecated tag.
 */
webui.suntheme.field = {
    /**
     * Use this function to get the HTML input or textarea element
     * associated with a TextField, PasswordField, HiddenField or TextArea
     * component.
     *
     * @param {String} elementId The element ID of the field 
     * @return {Node} the input or text area element associated with the field component
     * @deprecated Use document.getElementById(elementId).getInputElement()
     * @ignore Until JsDoc supports deprecated tag.
     */
    getInputElement: function(elementId) {
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.getInputElement();
        }
        return null;
    },

    /**
     * Use this function to get the value of the HTML element 
     * corresponding to the Field component.
     *
     * @param {String} elementId The element ID of the Field component
     * @return {String} the value of the HTML element corresponding to the Field component 
     * @deprecated Use document.getElementById(id).getProps().value;
     * @ignore Until JsDoc supports deprecated tag.
     */
    getValue: function(elementId) {
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.getProps().value;
        }
        return null;
    },

    /**
     * Use this function to set the value of the HTML element 
     * corresponding to the Field component
     *
     * @param {String} elementId The element ID of the Field component
     * @param {String} newValue The new value to enter into the input element Field component 
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({value: "text"});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setValue: function(elementId, newValue) {
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.setProps({value: newValue});
        }
        return null;
    },

    /** 
     * Use this function to get the style attribute for the field. 
     * The style retrieved will be the style on the span tag that 
     * encloses the (optional) label element and the input element.
     *
     * @param {String} elementId The element ID of the Field component
     * @return {String} The style property of the field.
     * @deprecated Use document.getElementById(id).getProps().style;
     * @ignore Until JsDoc supports deprecated tag.
     */
    getStyle: function(elementId) {
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.getProps().style;
        }
        return null;
    },

    /**
     * Use this function to set the style attribute for the field. 
     * The style will be set on the <span> tag that surrounds the field.
     *
     * @param {String} elementId The element ID of the Field component
     * @param {String} newStyle The new style to apply
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({style: newStyle});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setStyle: function(elementId, newStyle) { 
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.setProps({style: newStyle});
        }
        return null;
    },

    /**
     * Use this function to disable or enable a field. As a side effect
     * changes the style used to render the field. 
     *
     * @param {String} elementId The element ID of the field 
     * @param {boolean} newDisabled true to disable the field, false to enable the field
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({disabled: boolean});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setDisabled: function(elementId, newDisabled) {  
        if (newDisabled == null) {
            return null;
        }
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.setProps({disabled: newDisabled});
        }
        return null;
    }
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// hyperlink functions
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/**
 * @class This class contains functions for hyperlink components.
 * @static
 *
 * @deprecated See webui.suntheme.widget.hyperlink
 * @ignore Until JsDoc supports deprecated tag.
 */
webui.suntheme.hyperlink = {
    /**
     * This function is used to submit a hyperlink.
     * <p>
     * Note: Params are name value pairs but all one big string array so 
     * params[0] and params[1] form the name and value of the first param.
     * </p>
     *
     * @params {Object} hyperlink The hyperlink element
     * @params {String} formId The form id
     * @params {Object} params Name value pairs
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated See webui.suntheme.widget.hyperlink
     * @ignore Until JsDoc supports deprecated tag.
     */
    submit: function(hyperlink, formId, params) {
        // Need to test widget for tab and common task components. If a widget 
        // does not exist, fall back to the old code.
	var widget = dijit.byId(hyperlink.id);
	if (widget == null) {
            // If a widget does not exist, we shall create one in order to call
            // the submit function directly.
            dojo.require("webui.suntheme.widget.hyperlink");
            widget = new webui.suntheme.widget.hyperlink({id: hyperlink.id});
	}
        return widget.submitFormData(formId, params);
    },

    /**
     * Use this function to access the HTML img element that makes up
     * the icon hyperlink.
     *
     * @param {String} elementId The component id of the JSF component (this id is
     * assigned to the outter most tag enclosing the HTML img element).
     * @return {Node} The HTML image element.
     * @deprecated Use document.getElementById(elementId).getProps().enabledImage;
     * @ignore Until JsDoc supports deprecated tag.
     */
    getImgElement: function(elementId) {
        // Need to test widget for alarmStatus, jobstatus and notification phrase
        // components. If a widget does not exist, fall back to the old code.
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.getProps().enabledImage;
        }

        // Image hyperlink is now a naming container and the img element id 
        // includes the ImageHyperlink parent id.
        if (elementId != null) {
            var parentid = elementId;
            var colon_index = elementId.lastIndexOf(":");
            if (colon_index != -1) {
                parentid = elementId.substring(colon_index + 1);
            }
            return document.getElementById(elementId + ":" + parentid + "_image");
        }
        return document.getElementById(elementId + "_image");
    }
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// jumpDropDown functions
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/**
 * @class This class contains functions for jumpDropDown components.
 * @static
 *
 * @deprecated See webui.suntheme.widget.dropDown
 * @ignore Until JsDoc supports deprecated tag.
 */
webui.suntheme.jumpDropDown = {
    /**
     * This function is invoked by the jumpdropdown onchange action to set the
     * form action and then submit the form.
     *
     * Page authors should invoke this function if they set the selection using 
     * JavaScript.
     *
     * @param {String} elementId The component id of the JSF component (this id is
     * rendered in the div tag enclosing the HTML elements that make up
     * the list).
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(elementId).changed()
     * @ignore Until JsDoc supports deprecated tag.
     */
    changed: function(elementId) {
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.changed();
        }
        return false;
    }
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// listbox functions
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/**
 * @class This class contains functions for listbox components.
 * @static
 * 
 * @deprecated See webui.suntheme.widget.listbox
 * @ignore Until JsDoc supports deprecated tag.
 */
webui.suntheme.listbox = {
    /**
     * Use this function to access the HTML select element that makes up
     * the list. 
     *
     * @param {String} elementId The component id of the JSF component (this id is
     * assigned to the span tag enclosing the HTML elements that make up
     * the list).
     * @return {Node} The HTML select element.
     * @deprecated Use document.getElementById(elementId).getSelectElement()
     * @ignore Until JsDoc supports deprecated tag.
     */
    getSelectElement: function(elementId) { 
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.getSelectElement();
        }
        return null;
    },

    /**
     * This function is invoked by the list onselect action to set the selected, 
     * and disabled styles.
     *
     * Page authors should invoke this function if they set the selection
     * using JavaScript.
     *
     * @param {String} elementId The component id of the JSF component (this id is
     * rendered in the div tag enclosing the HTML elements that make up
     * the list).
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(elementId).changed();
     * @ignore Until JsDoc supports deprecated tag.
     */
    changed: function(elementId) {         
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.changed();
        }
        return false;
    },

    /**
     * Invoke this JavaScript function to set the enabled/disabled state
     * of the listbox component. In addition to disabling the list, it
     * also changes the styles used when rendering the component. 
     *
     * Page authors should invoke this function if they dynamically
     * enable or disable a list using JavaScript.
     * 
     * @param {String} elementId The component id of the JSF component (this id is
     * rendered in the div tag enclosing the HTML elements that make up
     * the list).
     * @param {boolean} disabled true or false
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(elementId).setProps({disabled: boolean});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setDisabled: function(elementId, disabled) { 
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.setProps({disabled: disabled});
        }
        return null;
    },

    /**
     * Invoke this JavaScript function to get the value of the first
     * selected option on the listbox. If no option is selected, this
     * function returns null. 
     * 
     * @param {String} elementId The component id of the JSF component (this id is
     * rendered in the div tag enclosing the HTML elements that make up
     * the list).
     * @return {String} The value of the selected option, or null if none is
     * selected.
     * @deprecated Use document.getElementById(elementId).getSelectedValue();
     * @ignore Until JsDoc supports deprecated tag.
     */
    getSelectedValue: function(elementId) { 
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.getSelectedValue();
        }
        return null;
    },

    /**
     * Invoke this JavaScript function to get the label of the first
     * selected option on the listbox. If no option is selected, this
     * function returns null. 
     * 
     * @param {String} elementId The component id of the JSF component (this id is
     * rendered in the div tag enclosing the HTML elements that make up
     * the list).
     * @return {String} The label of the selected option, or null if none is selected.
     * @deprecated Use document.getElementById(elementId).getSelectedLabel();
     * @ignore Until JsDoc supports deprecated tag.
     */
    getSelectedLabel: function(elementId) { 
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.getSelectedLabel();
        }
        return null;
    }
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Generic checkbox and radio button functions
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/**
 * @class This class contains functions for rbcbGroup components.
 * @static
 *
 * @deprecated See webui.suntheme.widget.rbcbGroup
 * @ignore Until JsDoc supports deprecated tag.
 */
webui.suntheme.rbcb = {
    /**
     * 
     * @param {String} elementId The element Id.
     * @param {boolean} checked true or false
     * @param {String} type
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({checked: boolean});
     * @ignore Until JsDoc supports deprecated tag.
     */ 
    setChecked: function(elementId, checked, type) {
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.setProps({checked: checked});
        }
        return null; 
    },

    /**
     *
     * @param {String} elementId The element Id.
     * @param {boolean} disabled true or false
     * @param {String} type
     * @param {String} enabledStyle
     * @param {String} disabledStyle
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({disabled: boolean}); 
     * @ignore Until JsDoc supports deprecated tag.
     */ 
    setDisabled: function(elementId, disabled, type, enabledStyle,
            disabledStyle) {
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.setProps({disabled: disabled});
        }
        return null; 
    },

    /** 
     * Set the disabled state for all radio buttons with the given controlName.
     * If disabled is set to true, the element is shown with disabled styles.
     *
     * @param {String} elementId The element Id
     * @param {String} formName The name of the form containing the element
     * @param {boolean} disabled true or false
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({disabled: boolean});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setGroupDisabled: function(controlName, disabled, type, enabledStyle,
            disabledStyle) {
        var widget = dijit.byId(elementId);
        if (widget) {
            return widget.setProps({disabled: disabled});
        }
        return null;
    }
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// radiobutton functions
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/**
 * @class This class contains functions for radioButton components.
 * @static
 *
 * @deprecated See webui.suntheme.widget.radioButton
 * @ignore Until JsDoc supports deprecated tag.
 */
webui.suntheme.radiobutton = {
    /**
     * Set the disabled state for the given radiobutton element Id. If the disabled 
     * state is set to true, the element is shown with disabled styles.
     *
     * @param {String} elementId The element Id.
     * @param {boolean} disabled true or false
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({disabled: boolean});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setDisabled: function(elementId, disabled) {    
        return webui.suntheme.rbcb.setDisabled(elementId, disabled, 
            "radio", "Rb", "RbDis");
    },

    /**
     * Set the disabled state for all the radio buttons in the radio button
     * group identified by controlName. If disabled
     * is set to true, the check boxes are displayed with disabled styles.
     *
     * @param {String} controlName The radio button group control name
     * @param {boolean} disabled true or false
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({disabled: boolean});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setGroupDisabled: function(controlName, disabled) {    
        return webui.suntheme.rbcb.setGroupDisabled(controlName, disabled, 
            "radio", "Rb", "RbDis");
    },

    /**
     * Set the checked property for a radio button with the given element Id.
     *
     * @param {String} elementId The element Id
     * @param {boolean} checked true or false
     * @return {boolean} true if successful; otherwise, false.
     * @deprecated Use document.getElementById(id).setProps({checked: boolean});
     * @ignore Until JsDoc supports deprecated tag.
     */
    setChecked: function(elementId, checked) {
        return webui.suntheme.rbcb.setChecked(elementId, checked, "radio");
    }
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// upload functions
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/**
 * @class This class contains functions for upload components.
 * @static
 */
webui.suntheme.upload = {
    /**
     * Use this function to get the HTML input element associated with the
     * Upload component.  
     * @param {String} elementId The client id of the Upload component
     * @return {Node} the input element associated with the Upload component
     * else null if elementId is null or "".
     */
    getInputElement: function(elementId) { 
        if (elementId == null || elementId == "") {
	    return null;
	}

	// The upload component MUST always render the input element
	// with the following suffix on the id 
	// "_com.sun.webui.jsf.upload".
	// This "binds" this version of the component to this theme
	// version.
	// This will change when "field" becomes a widget.
	//
        var element = document.getElementById(elementId + 
            "_com.sun.webui.jsf.upload");
        if (element && element.tagName == "INPUT") { 
            return element; 
        } else {
	    return null;
	}
    },

    /**
     * Use this function to disable or enable a upload. As a side effect
     * changes the style used to render the upload. 
     *
     * @param {String} elementId The client id of the upload component.
     * @param {boolean} disabled true to disable the upload, false to enable the upload
     * @return {boolean} true if successful; otherwise, false.
     */
    setDisabled: function(elementId, disabled) {  

        if (elementId == null || elementId == "" || 
		disabled == null || disabled == "") {
            // must supply an elementId && state
            return false;
        }
        var input = webui.suntheme.upload.getInputElement(elementId); 
        if (input == null) {
            // specified elementId not found
            return false;
        }
        input.disabled = disabled;
	return true;
    },

    /**
     * Set the encoding type of the form to "multipart/form-data".
     * 
     *
     * @param {String} elementId The client id of the upload component.
     * @return {boolean} true if encoding type can be set, else false.
     */
    setEncodingType: function(elementId) { 
	if (elementId == null || elementId == "") {
	    return false;
	}

        var upload = webui.suntheme.upload.getInputElement(elementId); 
        var form = upload != null ? upload.form : null;
	if (form != null) {

            // form.enctype does not work for IE, but works Safari
            // form.encoding works on both IE and Firefox
	    //
            if (webui.suntheme.browser.isSafari()) {
                form.enctype = "multipart/form-data";
            } else {
                form.encoding = "multipart/form-data";
            }
	    return true;
        }
	return false;
    },

    /**
     * Create a hidden field with id "preservePathId" and add a listener
     * to the upload's input element, "uploadId". The listener is
     * is added for the onchange event of the upload's input field,
     * see preservePathListener.
     *
     * @param {String} uploadId The client id of the upload component.
     * @param {String} preservePathId
     * @return {boolean} true if the hidden element is created and a listener is
     * added, else false.
     */
    preservePath: function(uploadId, preservePathId) {
	if (uploadId == null || uploadId == "" ||
		preservePathId == null || preservePathId == "") {
	    return false;
	}

	// If there is no upload component, don't do anything.
	// I'm not sure if there is a timing issue here.
	//
	var uploadElement = webui.suntheme.upload.getInputElement(uploadId);
	if (uploadElement == null) {
	    return false;
	}
	var theForm = uploadElement.form;

	// Create the change listener.
	// The event target/srcElement is the upload input element
	// its value is the changed value, save it in the 
	// preservePath hidden field.
	//
	var onChangeListener = function(evt) {

	    // Is IE
	    if (document.attachEvent) {
		node = evt.srcElement;
	    } else {
		node = evt.target;
	    }
	    // node is the upload input element
	    //
	    var preservePath = null;
	    try {
		preservePath = theForm.elements[preservePathId];
	    } catch (e) {
	    }

	    // If the hidden field isn't there create it and assign
	    // the node's value
	    //
	    if (preservePath != null) {
		preservePath.value = node.value;
	    } else {
		webui.suntheme.common.insertHiddenField(preservePathId, 
			node.value, theForm);
	    }
	    return true;
	};

	if (uploadElement.addEventListener) {
	    uploadElement.addEventListener('change', onChangeListener, true);
	} else {
	    uploadElement.attachEvent('onchange', onChangeListener);
	}
	return true;
    }
}
dojo.provide("webui.suntheme.table");


/** 
 * @class This class contains functions for table components.
 * <p>
 * Once the table is rendered, you will be able to invoke functions directly on 
 * the HTML element. For example:
 * </p><p><pre>
 * var table = document.getElementById("form1:table1");
 * var count = table.getAllSelectedRowsCount();
 * </pre></p><p>
 * Note: It is assumed that formElements.js has been included in the page. In
 * addition, all given HTML element IDs are assumed to be the outter most tag
 * enclosing the component.
 * </p>
 * @static
 */
webui.suntheme.table = {
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Public functions
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /**
     * This function is used to confirm the number of selected components (i.e., 
     * checkboxes or radiobuttons used to de/select rows of the table), affected
     * by a delete action. This functionality requires the selectId property of
     * the tableColumn component and hiddenSelectedRows property of the
     * tableRowGroup component to be set.
     * <p>
     * If selections are hidden from view, the confirmation message indicates the
     * number of selections not displayed in addition to the total number of
     * selections. If selections are not hidden, the confirmation message indicates
     * only the total selections.
     * </p>
     *
     * @return {boolean} A value of true if the user clicks the "OK" button, or 
     * false if the user clicks the "Cancel" button.
     */
    confirmDeleteSelectedRows: function() {
        return this.confirmSelectedRows(this.deleteSelectionsMsg);
    },

    /**
     * This function is used to confirm the number of selected components (i.e., 
     * checkboxes or radiobuttons used to de/select rows of the table), affected by
     * an action such as edit, archive, etc. This functionality requires the 
     * selectId property of the tableColumn component and hiddenSelectedRows
     * property of the tableRowGroup component to be set.
     * <p>
     * If selections are hidden from view, the confirmation message indicates the
     * number of selections not displayed in addition to the total number of
     * selections. If selections are not hidden, the confirmation message indicates
     * only the total selections.
     * </p>
     *
     * @param {String} message The confirmation message (e.g., Archive all selections?).
     * @return {boolean} A value of true if the user clicks the "OK" button, or 
     * false if the user clicks the "Cancel" button.
     */
    confirmSelectedRows: function(message) {
        // Get total selections message.
        var totalSelections = this.getAllSelectedRowsCount();
        var totalSelectionsArray = this.totalSelectionsMsg.split("{0}");
        var totalSelectionsMsg = totalSelectionsArray[0] + totalSelections;

        // Append hidden selections message.
        var hiddenSelections = this.getAllHiddenSelectedRowsCount();
        if (hiddenSelections > 0) {
            // Get hidden selections message.
            var hiddenSelectionsArray = this.hiddenSelectionsMsg.split("{0}");
            var hiddenSelectionsMsg = hiddenSelectionsArray[0] + hiddenSelections;

            totalSelectionsMsg = hiddenSelectionsMsg + totalSelectionsMsg;
        }
        return (message != null)
            ? confirm(totalSelectionsMsg + message)
            : confirm(totalSelectionsMsg);
    },

    /**
     * This function is used to toggle the filter panel from the filter menu. This
     * functionality requires the filterId of the table component to be set. In 
     * addition, the selected value must be set as well to restore the default
     * selected value when the embedded filter panel is closed.
     * <p>
     * If the "Custom Filter" option has been selected, the table filter panel is 
     * toggled. In this scenario, false is returned indicating the onChange event,
     * generated by the table filter menu, should not be allowed to continue.
     * </p><p>
     * If the "Custom Filter Applied" option has been selected, no action is taken.
     * Instead, the is selected filter menu is reverted back to the "Custom Filter" 
     * selection. In this scenario, false is also returned indicating the onChange 
     * event, generated by the table filter menu, should not be allowed to continue.
     * </p><p>
     * For all other selections, true is returned indicating the onChange event, 
     * generated by the table filter menu, should be allowed to continue.
     * </p>
     *
     * @return {boolean} true if successful; otherwise, false.
     */
    filterMenuChanged: function() {
        // Validate panel IDs.
        if (this.panelToggleIds == null || this.panelToggleIds.length == 0) {
            return false;
        }

        // Get filter menu.
        var menu = webui.suntheme.dropDown.getSelectElement(
            this.panelToggleIds[this.FILTER]);
        if (menu == null) {
            return true;
        }

        // Test if table filter panel should be opened.
        if (menu.options[menu.selectedIndex].value == this.customFilterOptionValue) {
            this.toggleFilterPanel();
            return false;
        } else if (menu.options[menu.selectedIndex].
                value == this.customFilterAppliedOptionValue) {
            // Set selected option.
            menu.selectedIndex = 0;
            for (var i = 0; i < menu.options.length; i++) {
                if (menu.options[i].value == this.customFilterOptionValue) {
                    menu.options[i].selected = true;
                    break;
                }
            }
            return false;
        }
        return true;
    },

    /**
     * This function is used to get the number of selected components in all row groups
     * displayed in the table (i.e., checkboxes or radiobuttons used to de/select 
     * rows of the table). This functionality requires the selectId property of the
     * tableColumn component and hiddenSelectedRows property of the table
     * component to be set.
     *
     * @return {int} The number of components selected in the current page.
     */
    getAllSelectedRowsCount: function() {
        return this.getAllHiddenSelectedRowsCount() +
            this.getAllRenderedSelectedRowsCount();
    },

    /**
     * This function is used to get the number of selected components, in all row groups
     * displayed in the table (i.e., checkboxes or radiobuttons used to de/select
     * rows of the table), currently hidden from view. This functionality requires 
     * the selectId property of the tableColumn component and hiddenSelectedRows
     * property of the table component to be set.
     *
     * @return {int} The number of selected components hidden from view.
     */
    getAllHiddenSelectedRowsCount: function() {
        var count = 0;

        // Validate group IDs.
        if (this.groupIds == null || this.groupIds.length == 0) {
            return count;
        }

        // For each group, get the row and select id.
        for (var i = 0; i < this.groupIds.length; i++) {
            count = count + this.getGroupHiddenSelectedRowsCount(this.groupIds[i]);
        }
        return count;
    },

    /**
     * This function is used to get the number of selected components, in all row groups
     * displayed in the table (i.e., checkboxes or radiobuttons used to de/select
     * rows of the table), currently rendered. This functionality requires 
     * the selectId property of the tableColumn component to be set.
     *
     * @return {int} The number of selected components hidden from view.
     */
    getAllRenderedSelectedRowsCount: function() {
        var count = 0;

        // Validate group IDs.
        if (this.groupIds == null || this.groupIds.length == 0) {
            return count;
        }

        // For each group, get the row and select id.
        for (var i = 0; i < this.groupIds.length; i++) {
            count = count + this.getGroupRenderedSelectedRowsCount(this.groupIds[i]);
        }
        return count;
    },

    /**
     * This function is used to initialize all rows displayed in the table when the
     * state of selected components change (i.e., checkboxes or radiobuttons used to
     * de/select rows of the table). This functionality requires the selectId
     * property of the tableColumn component to be set.
     *
     * @return {boolean} true if successful; otherwise, false.
     */
    initAllRows: function() {
        // Validate groupIDs.
        if (this.groupIds == null || this.groupIds.length == 0) {
            return false;
        }

        // For each group, get the row and select id.
        for (var i = 0; i < this.groupIds.length; i++) {
            this.initGroupRows(this.groupIds[i]);
        }
        return true;
    },

    /**
     * This function is used to toggle the filter panel open or closed. This
     * functionality requires the filterId of the table component to be set. In 
     * addition, the selected value must be set as well to restore the default
     * selected value when the embedded filter panel is closed.
     *
     * @return {boolean} true if successful; otherwise, false.
     */
    toggleFilterPanel: function() {
        // Validate panel IDs.
        if (this.panelIds == null || this.panelIds.length == 0) {
            return false;
        }

        // Toggle filter panel.
        this.togglePanel(this.panelIds[this.FILTER],
        this.panelFocusIds[this.FILTER], this.panelToggleIds[this.FILTER]);
        return this.resetFilterMenu(this.panelToggleIds[this.FILTER]); // Reset filter menu.
    },

    /**
     * This function is used to toggle the preferences panel open or closed. This
     * functionality requires the filterId of the table component to be set.
     *
     * @return {boolean} true if successful; otherwise, false.
     */
    togglePreferencesPanel: function() {
        // Validate panel IDs.
        if (this.panelIds == null || this.panelIds.length == 0) {
            return false;
        }

        // Toggle preferences panel.
        this.togglePanel(this.panelIds[this.PREFERENCES],
        this.panelFocusIds[this.PREFERENCES], this.panelToggleIds[this.PREFERENCES]);
        return this.resetFilterMenu(this.panelToggleIds[this.FILTER]); // Reset filter menu.
    },

    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Private functions
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /**
     * This function is used to initialize HTML element properties with Object
     * literals.
     *
     * @param {Object} props Key-Value pairs of properties.
     * @config {String} [id] The HTML element ID for the component.
     *
     * // Panel Properties
     * @config {Array} [panelIds] An array of embedded panel IDs.</li>
     * @config {Array} [panelFocusIds] An array of IDs used to set focus for open panels.</li>
     * @config {Array} [panelToggleIds] An array of IDs used to toggle embedded panels.</li>
     * @config {Array} [panelToggleIconsOpen] An array of toggle icons for open panels.</li>
     * @config {Array} [panelToggleIconsClose] An array of toggle icons for closed panels.</li>
     *
     * // Filter Properties
     * @config {String} [basicFilterStyleClass] The style class for basic or no filters.</li>
     * @config {String} [customFilterStyleClass] The style class for custom filters.</li>
     * @config {String} [customFilterOptionValue] The custom filter menu option value.</li>
     * @config {String} [customFilterAppliedOptionValue] The custom filter applied menu option value.</li>
     *
     * // Sort Panel Properties
     * @config {Array} [sortColumnMenuIds] An array of HTML element IDs for sort column menu components.</li>
     * @config {Array} [sortOrderMenuIds] An array of HTML element IDs for sort order menu components.</li>
     * @config {Array} [sortOrderToolTips] An array of tool tips used for sort order menus.</li>
     * @config {Array} [sortOrderToolTipsAscending] An array of ascending tool tips used for sort order menus.</li>
     * @config {Array} [sortOrderToolTipsDescending] An array of descending tool tips used for sort order menus.</li>
     * @config {String} [duplicateSelectionMsg] The message displayed for duplicate menu selections.</li>
     * @config {String} [missingSelectionMsg] The message displayed for missing menu selections.</li>
     * @config {String} [selectSortMenuOptionValue] The sort menu option value for the select column.</li>
     * @config {boolean} [hiddenSelectedRows] Flag indicating that selected rows might be currently hidden from view.</li>
     * @config {boolean} [paginated] Flag indicating table is in pagination mode.</li>
     *
     * // Group Properties
     * @config {String} [selectRowStylClass] The style class for selected rows.</li>
     * @config {Array} [selectIds] An arrary of component IDs used to select rows of the table.</li>
     * @config {Array} [groupIds] An array of TableRowGroup IDs rendered for the table.</li>
     * @config {Array} [rowIds] An array of row IDs for rendered for each TableRowGroup.</li>
     * @config {Array} [hiddenSelectedRowCounts] An array of selected row counts hidden from view.</li>
     * @config {String} [hiddenSelectionsMsg] The hidden selections message for confirm dialog.</li>
     * @config {String} [totalSelectionsMsg] The total selections message for confirm dialog.</li>
     * @config {String} [deleteSelectionsMsg] The delete selections message for confirm dialog.</li>
     *
     * // Group Panel Properties
     * @param {String} [columnFooterId] ID for column footer.</li>
     * @param {String} [columnHeaderId] ID for column header.</li>
     * @param {String} [tableColumnFooterId] ID for table column footer.</li>
     * @param {String} [groupFooterId] ID for group footer.</li>
     * @param {String} [groupPanelToggleButtonId] ID for group panel toggle button.</li>
     * @param {String} [groupPanelToggleButtonToolTipOpen] tool tip for open row group.</li>
     * @param {String} [groupPanelToggleButtonToolTipClose] tool tip for closed row group.</li>
     * @param {String} [groupPanelToggleIconOpen] The toggle icon for open row group.</li>
     * @param {String} [groupPanelToggleIconClose] The toggle icon for closed row group.</li>
     * @param {String} [warningIconId] ID for warning icon.</li>
     * @param {String} [warningIconOpen] The warning icon for open row group.</li>
     * @param {String} [warningIconClosed] The warning icon for closed row group.</li>
     * @param {String} [warningIconToolTipOpen] The warning icon tool tip for open row group.</li>
     * @param {String} [warningIconToolTipClose] The warning icon tool tip for closed row group.</li>
     * @param {String} [collapsedHiddenFieldId ID] for collapsed hidden field.</li>
     * @param {String} [selectMultipleToggleButtonId ID] for select multiple toggle button.</li>
     * @param {String} [selectMultipleToggleButtonToolTip] The select multiple toggle button tool tip.</li>
     * @param {String} [selectMultipleToggleButtonToolTipSelected] The select multiple toggle button tool tip when selected.</li>
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    init: function(props) {
        if (props == null || props.id == null) {
            return false;
        }
        var domNode = document.getElementById(props.id);
        if (domNode == null) {
            return false;
        }

        // Set given properties on domNode.
        Object.extend(domNode, props);

        // Misc properties.
        domNode.SEPARATOR = ":";   // NamingContainer separator.

        // Panel toggle keys.
        domNode.SORT        = 0;
        domNode.PREFERENCES = 1;
        domNode.FILTER      = 2;

        // Sort keys.
        domNode.PRIMARY   = 0;
        domNode.SECONDARY = 1;
        domNode.TERTIARY  = 2;

        // Replace extra backslashes, JSON escapes new lines (e.g., \\n).
	domNode.hiddenSelectionsMsg = props.hiddenSelectionsMsg.replace(/\\n/g, "\n");
        domNode.totalSelectionsMsg = props.totalSelectionsMsg.replace(/\\n/g, "\n");
	domNode.missingSelectionMsg = props.missingSelectionMsg.replace(/\\n/g, "\n");
	domNode.duplicateSelectionMsg = props.duplicateSelectionMsg.replace(/\\n/g, "\n");
        domNode.deleteSelectionsMsg = props.deleteSelectionsMsg.replace(/\\n/g, "\n");

        // Private functions.
        domNode.initSortOrderMenu = webui.suntheme.table.initSortOrderMenu;
        domNode.initSortOrderMenuToolTip = webui.suntheme.table.initSortOrderMenuToolTip;
        domNode.resetFilterMenu = webui.suntheme.table.resetFilterMenu;
        domNode.togglePanel = webui.suntheme.table.togglePanel;

        // Public functions.
        domNode.confirmDeleteSelectedRows = webui.suntheme.table.confirmDeleteSelectedRows;
        domNode.confirmSelectedRows = webui.suntheme.table.confirmSelectedRows;
        domNode.filterMenuChanged = webui.suntheme.table.filterMenuChanged;
        domNode.getAllSelectedRowsCount = webui.suntheme.table.getAllSelectedRowsCount;
        domNode.getAllHiddenSelectedRowsCount = webui.suntheme.table.getAllHiddenSelectedRowsCount;
        domNode.getAllRenderedSelectedRowsCount = webui.suntheme.table.getAllRenderedSelectedRowsCount;
        domNode.getGroupSelectedRowsCount = webui.suntheme.table.getGroupSelectedRowsCount;
        domNode.getGroupHiddenSelectedRowsCount = webui.suntheme.table.getGroupHiddenSelectedRowsCount;
        domNode.getGroupRenderedSelectedRowsCount = webui.suntheme.table.getGroupRenderedSelectedRowsCount;
        domNode.initAllRows = webui.suntheme.table.initAllRows;
        domNode.initGroupRows = webui.suntheme.table.initGroupRows;
        domNode.initPrimarySortOrderMenu = webui.suntheme.table.initPrimarySortOrderMenu;
        domNode.initPrimarySortOrderMenuToolTip = webui.suntheme.table.initPrimarySortOrderMenuToolTip;
        domNode.initSecondarySortOrderMenu = webui.suntheme.table.initSecondarySortOrderMenu;
        domNode.initSecondarySortOrderMenuToolTip = webui.suntheme.table.initSecondarySortOrderMenuToolTip;
        domNode.initSortColumnMenus = webui.suntheme.table.initSortColumnMenus;
        domNode.initSortOrderMenus = webui.suntheme.table.initSortOrderMenus;
        domNode.initTertiarySortOrderMenu = webui.suntheme.table.initTertiarySortOrderMenu;
        domNode.initTertiarySortOrderMenuToolTip = webui.suntheme.table.initTertiarySortOrderMenuToolTip;
        domNode.selectAllRows = webui.suntheme.table.selectAllRows;
        domNode.selectGroupRows = webui.suntheme.table.selectGroupRows;
        domNode.toggleSortPanel = webui.suntheme.table.toggleSortPanel;
        domNode.toggleFilterPanel = webui.suntheme.table.toggleFilterPanel;
        domNode.togglePreferencesPanel = webui.suntheme.table.togglePreferencesPanel;
        domNode.toggleGroupPanel = webui.suntheme.table.toggleGroupPanel;
        domNode.validateSortPanel = webui.suntheme.table.validateSortPanel;

        return true;
    },

    /**
     * This function is used to get the number of selected components for the given row 
     * group (i.e., checkboxes or radiobuttons used to de/select rows of the table).
     * This functionality requires the selectId property of the tableColumn component
     * and the hiddenSelectedRows property of the table component to be set.
     *
     * @param {String} groupId The HTML element ID for the tableRowGroup component.
     * @return {int} The number of components selected in the current page.
     * @private
     */
    getGroupSelectedRowsCount: function(groupId) {
        return this.getGroupHiddenSelectedRowsCount(groupId) + 
            this.getGroupRenderedSelectedRowsCount(groupId);
    },

    /**
     * This function is used to get the number of selected components, for the given row 
     * group (i.e., checkboxes or radiobuttons used to de/select rows of the table),
     * currently hidden from view. This functionality requires the selectId property
     * of the tableColumn component and hiddenSelectedRows property of the table
     * component to be set.
     *
     * @param {String} groupId The HTML element ID for the tableRowGroup component.
     * @return {int} The number of selected components hidden from view.
     * @private
     */
    getGroupHiddenSelectedRowsCount: function(groupId) {
        var count = 0;

        // Validate group IDs.
        if (this.groupIds == null || this.groupIds.length == 0
                || groupId == null) {
            return count;
        }

        // Find the given group Id in the groupIds array.
        for (var i = 0; i < this.groupIds.length; i++) {
            // Get selectId and rowIds array associated with groupId.
            if (groupId == this.groupIds[i]) {
                count = this.hiddenSelectedRowCounts[i];
                break;
            }
        }
        return count;
    },

    /**
     * This function is used to get the number of selected components, for the given row 
     * group (i.e., checkboxes or radiobuttons used to de/select rows of the table),
     * currently rendered. This functionality requires the selectId property of the
     * tableColumn component to be set.
     *
     * @param {String} groupId The HTML element ID for the tableRowGroup component.
     * @return {int} The number of components selected in the current page.
     * @private
     */
    getGroupRenderedSelectedRowsCount: function(groupId) {
        var count = 0;

        // Validate group IDs.
        if (this.groupIds == null || this.groupIds.length == 0
                || groupId == null) {
            return count;
        }

        // Get selectId and rowIds array associated with groupId.
        var selectId = null;
        var rowIds = null;
        for (var i = 0; i < this.groupIds.length; i++) {
            if (groupId == this.groupIds[i]) {
                selectId = this.selectIds[i];
                rowIds = this.rowIds[i];
                break;
            }
        }

        // If selectId or rowIds could not be found, do not continue.
        if (selectId == null || rowIds == null) {
            return false;
        }

        // Update the select component for each row.
        for (var k = 0; k < rowIds.length; k++) {
            var select = document.getElementById(
                this.groupIds[i] + this.SEPARATOR + rowIds[k] + 
                this.SEPARATOR + selectId);
            if (select != null && select.getProps().checked) {
                count++;
            }
        }
        return count;
    },

    /**
     * This function is used to initialize rows for the given groupwhen the state
     * of selected components change (i.e., checkboxes or radiobuttons used to
     * de/select rows of the table). This functionality requires the selectId
     * property of the tableColumn component to be set.
     *
     * @param {String} groupId The HTML element ID for the tableRowGroup component.
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    initGroupRows: function(groupId) {
        // Validate groupIDs.
        if (this.groupIds == null || this.groupIds.length == 0
                || groupId == null) {
            return false;
        }

        // Get selectId and rowIds array associated with groupId.
        var selectId = null;
        var rowIds = null;
        for (var i = 0; i < this.groupIds.length; i++) {
            if (groupId == this.groupIds[i]) {
                selectId = this.selectIds[i];
                rowIds = this.rowIds[i];
                break;
            }
        }

        // If selectId or rowIds could not be found, do not continue.
        if (selectId == null || rowIds == null) {
            return false;
        }

        // Update the select component for each row.
        var checked = true; // Checked state of multiple select button.
        var selected = false; // At least one component is selected.
        for (var k = 0; k < rowIds.length; k++) {
            var select = document.getElementById(
                this.groupIds[i] + this.SEPARATOR + rowIds[k] + 
                this.SEPARATOR + selectId);
            if (select == null) {
                continue;
            }

            // Set row style class.
            var row = document.getElementById(this.groupIds[i] + 
                this.SEPARATOR + rowIds[k]);
            var props = select.getProps();
            if (select.getProps().checked == true) {
                webui.suntheme.common.addStyleClass(row, 
                    this.selectRowStyleClass);
                selected = true;
            } else {
                webui.suntheme.common.stripStyleClass(row, 
                    this.selectRowStyleClass);
                checked = false;
            }
        }

        // Set multiple select button state.
        var checkbox = document.getElementById(groupId + this.SEPARATOR + 
            this.selectMultipleToggleButtonId);
        if (checkbox != null) {
            checkbox.setProps({"checked": checked});
            if (checked) {    
                checkbox.title = this.selectMultipleToggleButtonToolTipSelected;
            } else {
                checkbox.title = this.selectMultipleToggleButtonToolTip;
            }
        }

        // Get flag indicating groupis collapsed.
        var prefix = groupId + this.SEPARATOR;
        var collapsed = !webui.suntheme.common.isVisible(prefix + rowIds[0]);

        // Set next warning image.
        var image = document.getElementById(prefix + this.warningIconId);
        if (image != null) {
            // Don't show icon when multiple select is checked.
            if (collapsed && selected && !checked) {
                image.src = this.warningIconClose;
                image.title = this.warningIconToolTipClose;
            } else {
                image.src = this.warningIconOpen;
                image.title = this.warningIconToolTipOpen;
            }
        }
        return true;
    },

    /**
     * This function is used to initialize the primary sort order menus used in the 
     * table sort panel.
     *
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    initPrimarySortOrderMenu: function() {
        return this.initSortOrderMenu(this.sortColumnMenuIds[this.PRIMARY], 
            this.sortOrderMenuIds[this.PRIMARY]);
    },  

    /**
     * This function is used to initialize the primary sort order menu tool tips 
     * used in the table sort panel.
     *
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    initPrimarySortOrderMenuToolTip: function() {
        // Get sort order menu.
        var sortOrderMenu = webui.suntheme.dropDown.getSelectElement(
            this.sortOrderMenuIds[this.PRIMARY]);
        if (sortOrderMenu != null) {
            // IE hack so disabled option is not selected -- bugtraq #6269683.
            if (sortOrderMenu.options[sortOrderMenu.selectedIndex].disabled == true) {
                sortOrderMenu.selectedIndex = 0;
            }
        }
        return this.initSortOrderMenuToolTip(this.sortColumnMenuIds[this.PRIMARY], 
            this.sortOrderMenuIds[this.PRIMARY]);
    },

    /**
     * This function is used to initialize the secondary sort order menus used in the 
     * table sort panel.
     *
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    initSecondarySortOrderMenu: function() {
        return this.initSortOrderMenu(this.sortColumnMenuIds[this.SECONDARY], 
            this.sortOrderMenuIds[this.SECONDARY]);
    },

    /**
     * This function is used to initialize the secondary sort order menu tool tips
     * used in the table sort panel.
     *
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    initSecondarySortOrderMenuToolTip: function() {
        // Get sort order menu.
        var sortOrderMenu = webui.suntheme.dropDown.getSelectElement(
            this.sortOrderMenuIds[this.SECONDARY]);
        if (sortOrderMenu != null) {
            // IE hack so disabled option is not selected -- bugtraq #6269683.
            if (sortOrderMenu.options[sortOrderMenu.selectedIndex].disabled == true) {
                sortOrderMenu.selectedIndex = 0;
            }
        }
        return this.initSortOrderMenuToolTip(this.sortColumnMenuIds[this.SECONDARY], 
            this.sortOrderMenuIds[this.SECONDARY]);
    },

    /**
     * This function is used to initialize the primary, secondary, and tertiary 
     * sort column menus used in the table sort panel.
     *
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    initSortColumnMenus: function() {
        // Validate sort column menu IDs.
        if (this.sortColumnMenuIds == null || this.sortColumnMenuIds.length == 0) {
            return false;
        }

        // Set initial selected option for all sort column menus.
        for (var i = 0; i < this.sortColumnMenuIds.length; i++) {
            // Get sort column menu.
            var sortColumnMenu = webui.suntheme.dropDown.getSelectElement(
                this.sortColumnMenuIds[i]);
            if (sortColumnMenu == null) {
                continue;
            }

            // Set default selected value.
            sortColumnMenu.selectedIndex = 0;

            // Set selected option.
            for (var k = 0; k < sortColumnMenu.options.length; k++) {
                if (sortColumnMenu.options[k].defaultSelected == true) {
                    sortColumnMenu.options[k].selected = true;
                    break;
                }
            }
            // Ensure hidden filed values are updated.
            webui.suntheme.dropDown.changed(this.sortColumnMenuIds[i]);
        }
        return true;
    },

    /**
     * This function is used to initialize the primary, secondary, and tertiary 
     * sort order menus used in the table sort panel.
     *
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    initSortOrderMenus: function() {
        // Validate sort order menu IDs.
        if (this.sortOrderMenuIds == null || this.sortOrderMenuIds.length == 0) {
            return false;
        }

        // Set initial selected option for all sort column menus.
        for (var i = 0; i < this.sortOrderMenuIds.length; i++) {
            // Get sort order menu.
            var sortOrderMenu = webui.suntheme.dropDown.getSelectElement(
                this.sortOrderMenuIds[i]);
            if (sortOrderMenu == null) {
                continue;
            }

            // Set default selected value.
            sortOrderMenu.selectedIndex = 0;

            // Get sort column menu.
            var sortColumnMenu = webui.suntheme.dropDown.getSelectElement(
                this.sortColumnMenuIds[i]);
            if (sortColumnMenu != null) {
                // If the table is paginated and there are no hidden selected rows, the select
                // column cannot be sorted descending.
                if (sortColumnMenu.options[sortColumnMenu.selectedIndex].
                        value == this.selectSortMenuOptionValue
                        && !this.hiddenSelectedRows && this.paginated) {
                    sortOrderMenu.options[1].disabled = true;
                } else {
                    sortOrderMenu.options[1].disabled = false;
                }
            }

            // Set selected option.
            for (var k = 0; k < sortOrderMenu.options.length; k++) {
                if (sortOrderMenu.options[k].defaultSelected == true) {
                    sortOrderMenu.options[k].selected = true;
                    break;
                }
            }
            // Ensure hidden filed values and styles are updated.
            webui.suntheme.dropDown.changed(this.sortOrderMenuIds[i]);

            // Initialize tool tip.
            this.initSortOrderMenuToolTip(this.sortColumnMenuIds[i], this.sortOrderMenuIds[i]);
        }
        return true;
    },

    /**
     * This function is used to initialize sort order menus used in the
     * sort panel. When a sort column menu changes, the given sort order 
     * menu is initialized based on the the selected value of the given
     * sort column menu.
     *
     * @param {String} sortColumnMenuId The HTML element ID for the sort column menu component.
     * @param {String} sortOrderMenuId The HTML element ID for the sort order menu component.
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    initSortOrderMenu: function(sortColumnMenuId, sortOrderMenuId) {
        if (sortColumnMenuId == null || sortOrderMenuId == null) {
            return false;
        }

        // Validate sort column menu IDs.
        if (this.sortColumnMenuIds == null || this.sortColumnMenuIds.length == 0) {
            return false;
        }

        // Validate sort order menu IDs.
        if (this.sortOrderMenuIds == null || this.sortOrderMenuIds.length == 0) {
            return false;
        }

        // Get sort column menu.
        var sortColumnMenu = webui.suntheme.dropDown.getSelectElement(sortColumnMenuId);
        if (sortColumnMenu == null) {
            return false;
        }

        // Get sort order menu.
        var sortOrderMenu = webui.suntheme.dropDown.getSelectElement(sortOrderMenuId);
        if (sortOrderMenu == null) {
            return false;
        }

        // Reset selected option.
        sortOrderMenu.selectedIndex = 0; // Default ascending.

        // Get sort column menu selected index.            
        var selectedIndex = (sortColumnMenu.selectedIndex > -1)
            ? sortColumnMenu.selectedIndex : 0; // Default to first option.

        // If the table is paginated and there are no hidden selected rows, the select
        // column cannot be sorted descending.
        if (sortColumnMenu.options[selectedIndex].value == this.selectSortMenuOptionValue
                && !this.hiddenSelectedRows && this.paginated) {
            sortOrderMenu.options[1].disabled = true;
        } else {
            sortOrderMenu.options[1].disabled = false;
        }

        // Attempt to match the selected index of the given sort column menu with the
        // default selected value from either sort column menu. If a match is found, the 
        // default selected value of the associated sort order menu is retrieved. This
        // default selected value is set as the current selection of the given sort 
        // order menu.
        for (var i = 0; i < this.sortColumnMenuIds.length; i++) {
            // Get the current sort column menu to test the default selected value.
            var currentSortColumnMenu = webui.suntheme.dropDown.getSelectElement(
                this.sortColumnMenuIds[i]);
            if (currentSortColumnMenu == null) {
                continue;
            }

            // Find default selected value for the current sort column menu.
            var defaultSelected = null;
            for (var k = 0; k < currentSortColumnMenu.options.length; k++) {
                if (currentSortColumnMenu.options[k].defaultSelected == true) {
                    defaultSelected = currentSortColumnMenu.options[k].value;
                    break;
                }
            }

            // Match default selected value with selected index value.
            if (defaultSelected != null && defaultSelected ==
                    sortColumnMenu.options[selectedIndex].value) {
                // Get current sort order menu to test the default selected value.
                var currentSortOrderMenu = webui.suntheme.dropDown.getSelectElement(
                    this.sortOrderMenuIds[i]);
                if (currentSortOrderMenu == null) {
                    continue;
                }

                // Find default selected index for the current sort order menu.
                var defaultSelectedIndex = -1;
                for (var k = 0; k < currentSortOrderMenu.options.length; k++) {
                    if (currentSortOrderMenu.options[k].defaultSelected == true) {
                        defaultSelectedIndex = k;
                        break;
                    }
                }

                // Set selected value for given sort order menu.
                if (defaultSelectedIndex > -1) {
                    sortOrderMenu.options[defaultSelectedIndex].selected = true;
                } else {
                    sortOrderMenu.options[0].selected = true; // Default ascending.
                }
                break;
            }
        }
        // Ensure hidden field values and styles are updated.
        webui.suntheme.dropDown.changed(sortOrderMenuId);

        // Set sort order menu tool tip.
        return this.initSortOrderMenuToolTip(sortColumnMenuId, sortOrderMenuId);
    },

    /**
     * This function is used to initialize sort order menu tool tips used in the 
     * sort panel. When a sort column menu changes, the given sort order 
     * menu tool tip is initialized based on the the selected value of the given
     * sort column menu.
     *
     * @param {String} sortColumnMenuId The HTML element ID for the sort column menu component.
     * @param {String} sortOrderMenuId The HTML element ID for the sort order menu component.
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    initSortOrderMenuToolTip: function(sortColumnMenuId, sortOrderMenuId) {
        if (sortColumnMenuId == null || sortOrderMenuId == null) {
            return false;
        }

        // Get sort column menu.
        var sortColumnMenu = webui.suntheme.dropDown.getSelectElement(sortColumnMenuId);
        if (sortColumnMenu == null) {
            return false;
        }

        // Get sort order menu.
        var sortOrderMenu = webui.suntheme.dropDown.getSelectElement(sortOrderMenuId);
        if (sortOrderMenu == null) {
            return false;
        }

        // Get tool tip associated with given sort order menu.
        var toolTip = "";
        if (this.sortOrderToolTips != null && this.sortOrderToolTips.length != 0
                && this.sortOrderMenuIds != null) {
            for (var i = 0; i < this.sortOrderMenuIds.length; i++) {
                // The tool tip is at index zero, after splitting the message.
                if (sortOrderMenuId == this.sortOrderMenuIds[i]) {
                    toolTip = this.sortOrderToolTips[i].split("{0}")[0];
                    break;
                }
            }
        }

        // Get sort column menu selected index.            
        var selectedIndex = (sortColumnMenu.selectedIndex > -1)
            ? sortColumnMenu.selectedIndex : 0; // Default to first option.

        // Set tool tip.
        if (sortOrderMenu.options[sortOrderMenu.selectedIndex].value == "true") {
            sortOrderMenu.title = toolTip + this.sortOrderToolTipsDescending[selectedIndex];
        } else {
            // Default ascending.
            sortOrderMenu.title = toolTip + this.sortOrderToolTipsAscending[selectedIndex];
        }
        return true;
    },

    /**
     * This function is used to initialize the tertiary sort order menus used in the 
     * table sort panel.
     *
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    initTertiarySortOrderMenu: function() {
        return this.initSortOrderMenu(this.sortColumnMenuIds[this.TERTIARY], 
            this.sortOrderMenuIds[this.TERTIARY]);
    },

    /**
     * This function is used to initialize the tertiary sort order menu tool tips
     * used in the table sort panel.
     *
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    initTertiarySortOrderMenuToolTip: function() {
        // Get sort order menu.
        var sortOrderMenu = webui.suntheme.dropDown.getSelectElement(
            this.sortOrderMenuIds[this.TERTIARY]);
        if (sortOrderMenu != null) {
            // IE hack so disabled option is not selected -- bugtraq #6269683.
            if (sortOrderMenu.options[sortOrderMenu.selectedIndex].disabled == true) {
                sortOrderMenu.selectedIndex = 0;
            }
        }
        return this.initSortOrderMenuToolTip(this.sortColumnMenuIds[this.TERTIARY], 
            this.sortOrderMenuIds[this.TERTIARY]);
    },

    /**
     * This function is used to reset filter drop down menu. This functionality 
     * requires the filterId of the table component to be set. In addition,
     * the selected value must be set as well to restore the default selected
     * value when the embedded filter panel is closed.
     *
     * @param {String} filterId The HTML element ID of the filter menu.
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    resetFilterMenu: function(filterId) {
        if (filterId == null) {
            return false;
        }

        // Get filter menu.
        var menu = webui.suntheme.dropDown.getSelectElement(filterId);
        if (menu == null) {
            return true;
        }

        // Get div element associated with the filter panel.
        var div = document.getElementById(this.panelIds[this.FILTER]);
        if (div == null) {
            return false;
        }

        // Set selected style.
        if (webui.suntheme.common.isVisibleElement(div)) {
            webui.suntheme.common.stripStyleClass(menu, this.basicFilterStyleClass);
            webui.suntheme.common.addStyleClass(menu, this.customFilterStyleClass);
        } else {
            // Reset default selected option.
            menu.selectedIndex = 0;
            for (var i = 0; i < menu.options.length; i++) {
                if (menu.options[i].defaultSelected == true) {
                    menu.options[i].selected = true;
                    break;
                }
            }
            webui.suntheme.common.stripStyleClass(menu, this.customFilterStyleClass);
            webui.suntheme.common.addStyleClass(menu, this.basicFilterStyleClass);
        }
        return true;
    },

    /**
     * This function is used to set the selected state components in all row groups
     * displayed in the table (i.e., checkboxes or radiobuttons used to de/select
     * rows of the table). This functionality requires the selectId property of
     * the tableColumn component to be set.
     *
     * @param {boolean} selected Flag indicating components should be selected.
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    selectAllRows: function(selected) {
        // Validate groupIDs.
        if (this.groupIds == null || this.groupIds.length == 0) {
            return false;
        }

        // For each group, get the row and select id.
        for (var i = 0; i < this.groupIds.length; i++) {
            this.selectGroupRows(this.groupIds[i], selected);
        }
        return true;
    },

    /**
     * This function is used to set the selected state components for the given row group
     * (i.e., checkboxes or radiobuttons used to de/select rows of the table). This 
     * functionality requires the selectId property of the tableColumn component to be
     * set.
     *
     * @param {String} groupId The HTML element ID for the tableRowGroup component.
     * @param {boolean} selected Flag indicating components should be selected.
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    selectGroupRows: function(groupId, selected) {
        // Validate groupIDs.
        if (this.groupIds == null || this.groupIds.length == 0
                || groupId == null) {
            return false;
        }

        // Get selectId and rowIds array associated with groupId.
        var selectId = null;
        var rowIds = null;
        for (var i = 0; i < this.groupIds.length; i++) {
            if (groupId == this.groupIds[i]) {
                selectId = this.selectIds[i];
                rowIds = this.rowIds[i];
                break;
            }
        }

        // If selectId or rowIds could not be found, do not continue.
        if (selectId == null || rowIds == null) {
            return false;
        }

        // Update the select component for each row.
        for (var k = 0; k < rowIds.length; k++) {
            var select = document.getElementById(
                this.groupIds[i] + this.SEPARATOR + rowIds[k] + this.SEPARATOR + selectId);
            if (select == null) {
                continue;
            }
            select.setProps({checked: new Boolean(selected).valueOf()});
        }
        return this.initGroupRows(groupId); // Set row highlighting.
    },

    /**
     * This function is used to toggle row group panels open or closed.
     *
     * @param {String} groupId The HTML element ID for the tableRowGroup component.
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    toggleGroupPanel: function(groupId) {
        // Validate groupIDs.
        if (this.groupIds == null || this.groupIds.length == 0
                || groupId == null) {
            return false;
        }

        // Get rowIds array associated with groupId.
        var rowIds = null;
        for (var i = 0; i < this.groupIds.length; i++) {
            if (groupId == this.groupIds[i]) {
                rowIds = this.rowIds[i];
                break;
            }
        }

        // If row IDs could not be found, do not continue.
        if (rowIds == null) {
            return false;
        }

        // Get flag indicating group is collapsed.
        var prefix = groupId + this.SEPARATOR;
        var collapsed = !webui.suntheme.common.isVisible(prefix + rowIds[0]);

        // Get the number of column headers and table column footers for all 
        // TableRowGroup children.
        var columnHeadersCount = 0;
        var tableColumnFootersCount = 0;
        for (var i = 0; i < this.groupIds.length; i++) {
            // Only need to test first nested column header/footer; thus, index 0 is used.        
            var _prefix = this.groupIds[i] + this.SEPARATOR;
            var columnHeaderId = _prefix + this.columnHeaderId + this.SEPARATOR + "0";
            var tableColumnFooterId = _prefix + this.tableColumnFooterId + this.SEPARATOR + "0";
            if (document.getElementById(columnHeaderId) != null) {
                columnHeadersCount++;
            }
            if (document.getElementById(tableColumnFooterId) != null) {
                tableColumnFootersCount++;
            }
        }

        // Toggle nested column footer.
        var rowIndex = 0;
        while (true) {
            var columnFooterId = prefix + this.columnFooterId + 
                this.SEPARATOR + rowIndex++;
            if (document.getElementById(columnFooterId) == null) {
                break;
            }
            webui.suntheme.common.setVisible(columnFooterId, collapsed);
        }

        // Toggle column header only if multiple column headers are shown.
        if (columnHeadersCount > 1) {
            rowIndex = 0;
            while (true) {
                var columnHeaderId = prefix + this.columnHeaderId + 
                    this.SEPARATOR + rowIndex++;
                if (document.getElementById(columnHeaderId) == null) {
                    break;
                }            
                webui.suntheme.common.setVisible(columnHeaderId, collapsed);
            }
        }

        // Toggle table column footer only if multiple column footers are shown.
        if (tableColumnFootersCount > 1) {
            rowIndex = 0;
            while (true) {
                var tableColumnFooterId = prefix + this.tableColumnFooterId + 
                    this.SEPARATOR + rowIndex++;
                if (document.getElementById(tableColumnFooterId) == null) {
                    break;
                }
                webui.suntheme.common.setVisible(tableColumnFooterId, collapsed);
            }
        }

        // Toggle group rows.
        for (var k = 0; k < rowIds.length; k++) {
            var rowId = prefix + rowIds[k];
            webui.suntheme.common.setVisible(rowId, collapsed);
        }

        // Toggle group footers.
        webui.suntheme.common.setVisible(prefix + this.groupFooterId, collapsed);

        // Set next toggle button image.
        var groupPanelToggleButtonId = prefix + this.groupPanelToggleButtonId;
        var hyperlink = document.getElementById(groupPanelToggleButtonId);
        var image = webui.suntheme.hyperlink.getImgElement(groupPanelToggleButtonId);
        if (hyperlink != null && image != null) {
            if (collapsed) {
                image.src = this.groupPanelToggleIconOpen;
                hyperlink.title = this.groupPanelToggleButtonToolTipOpen;
            } else {
                image.src = this.groupPanelToggleIconClose;
                hyperlink.title = this.groupPanelToggleButtonToolTipClose;
            }
        }

        // Set collapsed hidden field.
        var hiddenField = document.getElementById(prefix + this.collapsedHiddenFieldId);
        if (hiddenField != null) {
            hiddenField.value = !collapsed;
        }
        return this.initGroupRows(groupId); // Set next warning image.
    },

    /**
     * This function is used to toggle embedded panels.
     *
     * @param {String} panelId The panel ID to toggle.
     * @param {String} panelFocusIdOpen The ID used to set focus when panel is opened.
     * @param {String} panelFocusIdClose The ID used to set focus when panel is closed.
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    togglePanel: function(panelId, panelFocusIdOpen, panelFocusIdClose) {
        if (panelId == null) {
            return false;
        }

        // Toggle the given panel, hide all others.
        for (var i = 0; i < this.panelIds.length; i++) {
            // Get div element associated with the panel.
            var div = document.getElementById(this.panelIds[i]);
            if (div == null) {
                continue;
            }

            // Set display value. Alternatively, we could set div.style.display
            // equal to "none" or "block" (i.e., hide/show).
            if (this.panelIds[i] == panelId) {
                // Set focus when panel is toggled -- bugtraq 6316565.
                var focusElement = null;

                if (webui.suntheme.common.isVisibleElement(div)) {
                    webui.suntheme.common.setVisibleElement(div, false); // Hide panel.
                    focusElement = document.getElementById(panelFocusIdClose);
                } else {
                    webui.suntheme.common.setVisibleElement(div, true); // Show panel.
                    focusElement = document.getElementById(panelFocusIdOpen);
                }

                // Set focus.
                if (focusElement != null) {
                    focusElement.focus();
                }
            } else {
                // Panels are hidden by default.
                webui.suntheme.common.setVisibleElement(div, false);
            }

            // Get image from icon hyperlink component.
            var image = webui.suntheme.hyperlink.getImgElement(this.panelToggleIds[i]);
            if (image == null) {
                continue; // Filter panel uses a drop down menu.
            }

            // Set image.
            if (webui.suntheme.common.isVisibleElement(div)) {
                image.src = this.panelToggleIconsOpen[i];
            } else {
                image.src = this.panelToggleIconsClose[i];
            }
        }
        return true;
    },

    /**
     * This function is used to toggle the sort panel open or closed. This
     * functionality requires the filterId of the table component to be set.
     *
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    toggleSortPanel: function() {
        // Validate panel IDs.
        if (this.panelIds == null || this.panelIds.length == 0) {
            return false;
        }

        // Initialize sort column and order menus.
        this.initSortColumnMenus(); 
        this.initSortOrderMenus();

        // Toggle sort panel.
        this.togglePanel(this.panelIds[this.SORT], 
        this.panelFocusIds[this.SORT], this.panelToggleIds[this.SORT]);
        return this.resetFilterMenu(this.panelToggleIds[this.FILTER]); // Reset filter menu.
    },

    /**
     * This function is used to validate sort column menu selections 
     * for the sort panel.
     *
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    validateSortPanel: function() {
        // Validate sort column menu IDs.
        if (this.sortColumnMenuIds == null || this.sortColumnMenuIds.length == 0) {
            return false;
        }

        // Get sort column menus.
        var primarySortColumnMenu = webui.suntheme.dropDown.getSelectElement(
            this.sortColumnMenuIds[this.PRIMARY]);
        var secondarySortColumnMenu = webui.suntheme.dropDown.getSelectElement(
            this.sortColumnMenuIds[this.SECONDARY]);
        var tertiarySortColumnMenu = webui.suntheme.dropDown.getSelectElement(
            this.sortColumnMenuIds[this.TERTIARY]);

        // Test primary and secondary menu selections.
        if (primarySortColumnMenu != null && secondarySortColumnMenu != null) {
            // Test if secondary sort is set, but primary is not.
            if (primarySortColumnMenu.selectedIndex < 1 
                    && secondarySortColumnMenu.selectedIndex > 0) {
                alert(this.missingSelectionMsg);
                return false;
            }
            // If a selection has been made, test for duplicate.
            if (primarySortColumnMenu.selectedIndex > 0
                  && primarySortColumnMenu.selectedIndex == secondarySortColumnMenu.selectedIndex) {
                alert(this.duplicateSelectionMsg);
                return false;
            }
        }

        // Test primary and tertiary menu selections.
        if (primarySortColumnMenu != null && tertiarySortColumnMenu != null) {
            // Test if tertiary sort is set, but primary is not.
            if (primarySortColumnMenu.selectedIndex < 1 
                    && tertiarySortColumnMenu.selectedIndex > 0) {
                alert(this.missingSelectionMsg);
                return false;
            }
            // If a selection has been made, test for duplicate.
            if (primarySortColumnMenu.selectedIndex > 0
                    && primarySortColumnMenu.selectedIndex == tertiarySortColumnMenu.selectedIndex) {
                alert(this.duplicateSelectionMsg);
                return false;
            }
        }

        // Test secondary and tertiary menu selections.
        if (secondarySortColumnMenu != null && tertiarySortColumnMenu != null) {
            // Test if tertiary sort is set, but secondary is not.
            if (secondarySortColumnMenu.selectedIndex < 1 
                    && tertiarySortColumnMenu.selectedIndex > 0) {
                alert(this.missingSelectionMsg);
                return false;
            }
            // If a selection has been made, test for duplicate.
            if (secondarySortColumnMenu.selectedIndex > 0
                    && secondarySortColumnMenu.selectedIndex == tertiarySortColumnMenu.selectedIndex) {
                alert(this.duplicateSelectionMsg);
                return false;
            }
        }
        return true;
    }
}
dojo.provide("webui.suntheme.wizard");


/** 
 * @class This class contains functions for wizard components.
 * <p>
 * The wizard JavaScript object is accessed using the getElementById()
 * function. Methods defined on that javascript object instance maybe
 * called using that identifier. For example, the following javascript
 * could be used to close and forward to a page when the wizard closes.
 * </p><p><code>
 *   <ui:wizard ...other attributes... 
 *	onPopupDismiss="document.getElementById('form1:wizard1').closeAndForward('launchform', '/faces/wizardData.jsp', true);" >
 *
 *	...wizard step tags...
 *
 *   </ui:wizard>
 * </code></p>
 * @static
 */
webui.suntheme.wizard = {
    /**
     * This function is used to initialize HTML element properties with Object
     * literals.
     *
     * @param {Object} props Key-Value pairs of properties.
     * @config {String} [id] The element id.
     * @return {boolean} true if successful; otherwise, false.
     * @private
     */
    init: function(props) {
        if (props == null || props.id == null) {
            return false;
        }
        var domNode = document.getElementById(props.id);
        if (domNode == null) {
            return false;
        }

        // Set given properties on domNode.
        Object.extend(domNode, props);

        // Set functions.
        domNode.nextClicked = webui.suntheme.wizard.nextClicked;
        domNode.previousClicked = webui.suntheme.wizard.previousClicked;
        domNode.cancelClicked = webui.suntheme.wizard.cancelClicked;
        domNode.finishClicked = webui.suntheme.wizard.finishClicked;
        domNode.closeClicked = webui.suntheme.wizard.closeClicked;
        domNode.gotoStepClicked = webui.suntheme.wizard.gotoStepClicked;
        domNode.closePopup = webui.suntheme.wizard.closePopup;
        domNode.closeAndForward = webui.suntheme.wizard.closeAndForward;
        domNode.wizOnLoad = webui.suntheme.wizard.wizOnLoad;
        domNode.resize_hack = webui.suntheme.wizard.resize_hack;

        return true;
    },

    /**
     * @private
     */
    nextClicked: function() {
        return true;
    },

    /**
     * @private
     */
    previousClicked: function() {
        return true;
    },

    /**
     * @private
     */
    cancelClicked: function() {
        return true;
    },

    /**
     * @private
     */
    closeClicked: function() {
        return true;
    },

    /**
     * @private
     */
    finishClicked: function() {
        return true;
    },

    /**
     * @private
     */
    gotoStepClicked: function() {
        return true;
    },

    /**
     * Close popup.
     *
     * @return {boolean} true if successful; otherwise, false.
     */
    closePopup: function() {
        window.close();
        return true;
    },

    /**
     * Close the wizard popup and forward to "submitPage" by submitting
     * "submitForm".
     * <p>
     * When the wizard closes it is often necessary to send
     * a request to a page that will make use of the data collected
     * during a wizard session. This method does this by obtaining the
     * form element "submitForm" from the page that launched the
     * the wizard. This means that the page that launched the wizard
     * popup must still be visible in a browser window. If that form
     * is found, the "action" property is set to "submitPage" and the
     * "submit" method of that "submitForm" is executed.
     * The popup window is then closed. 
     * </p><p>
     * However due to JSF's client side state saving mode an extra step
     * must be taken. If the application is operating with client side
     * state saving, JSF will ignore the "submitPage" value of the
     * submitted form's "action" property and will send the request to the
     * view defined in the saved state, saved in an element in "submitForm".
     * </p><p>
     * If the application is configured for client side state saving and
     * the "submitPage" is different from the page that lauched the wizard,
     * set "cleartState" to true. This method will clear the saved state 
     * before submitting the form. The "clearState" default value is false
     * and the saved state will not be cleared.
     * </p><p>
     * The "clearState" functionality only works with Sun's RI.
     * </p>
     *
     * @param {boolean} submitForm
     * @param {boolean} submitPage
     * @param {boolean} clearState
     * @return {boolean} true if successful; otherwise, false.
     */
    closeAndForward: function(submitForm, submitPage, clearState) {
        var f = window.opener.document.getElementById(submitForm);
        if (f == null) {
            alert("Can't find form " + submitForm);
            window.close();
        }

        if (clearState != null && clearState == true) {
            var elements = f.elements;
            var clientstate = null;
            for (var i = 0; i < elements.length; ++i) {
                // This only works for the Sun RI and is
                // dependent on the RIConstants.FACES_VIEW value
                // of "com.sun.faces.VIEW"
                //
                if (elements[i].name == this.facesViewState) {
                    clientstate = elements[i];
                    break;
                }
            }
            if (clientstate != null) {
                f.removeChild(clientstate);
            }
        }

        f.action = submitPage;
        f.submit();
        window.close();
        return true;
    }, 

    /**
     * This method must be assigned to the onload handler of the onLoad
     * attribute of the ui:body tag if the wizard is to operate properly on IE.
     *
     * @return {boolean} true if successful; otherwise, false.
     */
    wizOnLoad: function() {
        var stepsid = this.id + "_stepspane";
        var helpid = this.id + "_helppane";
        var wizbdyid = this.id + "_WizBdy";
        return this.resize_hack(helpid, stepsid, wizbdyid);
    },

    /**
     * used only for popup window and IE, and called by wizOnLoad.
     *
     * @return {boolean} true if successful; otherwise, false.
     */
    resize_hack: function(helpid, stepsid, wizbdyid) {
        if (webui.suntheme.browser.isIe5up()) {
            var bdy = document.getElementById(wizbdyid);

            if (bdy != null) {
                bdy.style.height = document.body.clientHeight - 145;

                if (helpid != null && helpid != '') {
                    var help = document.getElementById(helpid);
                    if (help != null) {
                        help.style.height = document.body.clientHeight - 90;
                    }
                }
                if (stepsid != null && stepsid != '') {
                    var steps = document.getElementById(stepsid);
                    if (steps != null) {
                        steps.style.height = document.body.clientHeight - 90;
                    }
                }
            }
        }
        return true;
    }
}
dojo.provide("webui.suntheme.widget.textArea");


/**
 * @name webui.suntheme.widget.textArea
 * @extends webui.suntheme.widget.textField
 * @class This class contains functions for the textArea widget.
 * @constructor This function is used to construct a textArea widget.
 */
dojo.declare("webui.suntheme.widget.textArea", webui.suntheme.widget.textField, {
    // Set defaults.
    autoSave: 0,
    cols: 20,
    rows: 3,
    widgetName: "textArea" // Required for theme properties.
});

/**
 * Helper function to create callback for timer event.
 *
 * @return {Function} The callback function.
 */
webui.suntheme.widget.textArea.prototype.createSubmitCallback = function() {
    var _id = this.id;

    // New literals are created every time this function
    // is called, and it's saved by closure magic.
    return function(event) { 
        var widget = dijit.byId(_id);
        if (widget == null) {
            return false;
        }
        //Create a submit request only if field has been modified
        if (widget.lastSaved != widget.fieldNode.value) {
            widget.lastSaved = widget.fieldNode.value;
            widget.submit();
        }
        return true;
    };
}

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.textArea.event =
        webui.suntheme.widget.textArea.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_textArea_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_textArea_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_textArea_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_textArea_event_state_end"
    },

    /**
     * This object contains submit event topics.
     * @ignore
     */
    submit: {
        /** Submit event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_textArea_event_submit_begin",

        /** Submit event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_textArea_event_submit_end"
    }
}

/**
 * Helper function to obtain HTML input element class names.
 *
 * @return {String} The HTML input element class name.
 */
webui.suntheme.widget.textArea.prototype.getInputClassName = function() {
    // Set readOnly style
    if (this.fieldNode.readOnly) {
        return this.widget.getClassName("TEXT_AREA_READONLY", "");
    }

    // Apply invalid style.
    var validStyle =  (this.valid == false) 
        ? " " + this.widget.getClassName("TEXT_AREA_INVALID", "")
        : " " + this.widget.getClassName("TEXT_AREA_VALID", "");

    // Set default style.    
    return (this.disabled == true)
        ? this.widget.getClassName("TEXT_AREA_DISABLED", "") 
        : this.widget.getClassName("TEXT_AREA", "") + validStyle;    
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.textArea.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);
    
    // Set properties.
    if (this.cols > 0 ) { props.cols = this.cols; }
    if (this.rows > 0) { props.rows = this.rows; }
    if (this.autoSave > 0 ) { props.autoSave = this.autoSave; }
   
    return props;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.textArea.prototype.postCreate = function () {
    // Set events.                
    if (this.autoSave > 0) {
        this.autoSaveTimerId = setInterval(this.createSubmitCallback(), 
            this.autoSave);  
    }
    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [accesskey] 
 * @config {boolean} [autoSave] 
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [label]
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {boolean} [readOnly] 
 * @config {boolean} [required] 
 * @config {int} [rows] 
 * @config {String} [style] Specify style rules inline.
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [valid]
 * @config {String} [value] Value of input.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.textArea.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.textArea.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set properties.   
    if (props.cols > 0) { this.fieldNode.cols = props.cols; }
    if (props.rows > 0) { this.fieldNode.rows = props.rows; }
    
    // Cancel autosave if it has been changed to <=0
    if (props.autoSave <= 0 && this.autoSaveTimerId && this.autoSaveTimerId != null ) {
        clearTimeout(this.autoSaveTimerId);
        this.autoSaveTimerId = null;
    }

    // Set label className -- must be set before calling superclass.
    if (props.label) {
        props.label.className = (props.label.className)
            ? this.widget.getClassName("TEXT_AREA_TOPLABELALIGN", "")  + " " + props.label.className
            : this.widget.getClassName("TEXT_AREA_TOPLABELALIGN", "") ;
    }

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.common");



/**
 * @class This class contains functions common to all widgets.
 * @static
 */
webui.suntheme.widget.common = {
    /**
     * This function is used to add a widget, HTML fragment, or static string to
     * the given domNode.
     * <p>
     * Note: If props is a string, it shall be added as the innerHTML of the 
     * given domNode. By default, all strings shall be HTML escaped.
     * <p></p>
     * If props is a JSON object, containing a fragment property instead of
     * widgetType, it shall be added as the innerHTML of the given domNode. A
     * fragment is also a string; however it is evaluated as JavaScript and not
     * HTML escaped.
     * <p></p>
     * If props is a JSON object, containing a widgetType property, a widget
     * shall be created. The newly created widget shall be added as a child of 
     * the given domNode.
     * <p></p>
     * Valid values for the position param consist of "last" or null. In 
     * general, position only applies when creating new widgets; however, if the 
     * position param is null, any existing child nodes are removed from the
     * domNode.
     * </p>
     *
     * @param {Node} domNode The DOM node to add widget.
     * @param {Object} props Key-Value pairs of properties.
     * @param {boolean} position The position to add widget.
     * @param {boolean} escape HTML escape strings (default).
     * @return {boolean} true if successful; otherwise, false.
     */
    addFragment: function(domNode, props, position, escape) {
        if (domNode == null || props == null) {
            return false;
        }

        // If position is null, remove existing nodes. The contents shall be
        // replaced by the newly created widget.
        if (position == null) {
            webui.suntheme.widget.common.removeChildNodes(domNode);

            // Note: To ensure Dojo does not replace the given domNode, always
            // provide a default position to the createWidget function. The
            // domNode may be used as a place holder for later updates.
            position = "last";            
        }

        // Add fragment.
        if (typeof props == 'string') {
            // Strip script fragments, set innerHTML property, and
            // eval scripts using a timeout.
            //
            // Note that using Dojo's ContentPane widget would have
            // been more preferable than creating a new dependency
            // based on Prototype. However, as of version .4.1, Dojo
            // still does not use a timeout to eval JavaScript; thus,
            // IE generates errors with innerHTML.
            //
            // "The problem has to do with the browser's poor
            // threading model. Basically, once some JavaScript code
            // is running, all other threads of execution are on hold
            // until the running thread is finished with it's
            // task. This includes whatever thread of execution that
            // updates the DOM model when new content is inserted via
            // innerHTML. For example if you do:
            //
            // foo.innerHTML = '<span id="bar">Bar</span>';
            // var bar = document.getElementById('bar');
            //
            // This code will sometimes fail because although you
            // added the content via innerHTML the DOM may not have
            // been updated and therefore the "bar" element does not
            // exist in the DOM yet. To work around this you can do:
            //
            // foo.innerHTML = '<span id="bar">Bar</span>';
            // setTimeout(function() {
            //     var bar = document.getElementById('bar');
            // }, 10);
            //
            // This will work because in 10 milliseconds whatever
            // event handler is running will have finished, the DOM
            // will update, then the callback will execute and have no
            // problem accessing the DOM element."
            //
            // The full discussion on this topic can be found at:
            //
            // http://www.ruby-forum.com/topic/73990
            //
            if (escape != null && new Boolean(escape).valueOf() == false) {
                webui.suntheme.widget.common.appendHTML(domNode, 
                    props.stripScripts());

                // Evaluate JavaScript.
                setTimeout(function() {props.evalScripts()}, 10);
            } else {
                // Static strings must be HTML escaped by default.
                webui.suntheme.widget.common.appendHTML(domNode,
                    webui.suntheme.widget.common.escapeHTML(props));
            }
        } else if (props.fragment) {
            // Add fragment -- do not HTML escape.
            return webui.suntheme.widget.common.addFragment(domNode, props.fragment, position, false);
        } else {
            // Create widget.
            webui.suntheme.widget.common.createWidget(domNode, props, position, false);
        }
        return true;
    },

    /**
     * This function is used to append HTML strings to the innerHTML property of
     * the given domNode.
     * <p>
     * Note: Concatenating innerHTML with new strings does not always work. When
     * adding multiple HTML elements to domNode, we can get into a situation
     * where domNode.innerHTML may not yet contain all the changes made to 
     * the previously added DOM node. Therefore, we shall wrap new strings in an
     * HTML span element so it may be added as a child of domNode.
     * </p>
     *
     * @param {Node} domNode The DOM node to append string.
     * @param {String} html The HTML string to append.
     * @return {boolean} true if successful; otherwise, false.
     */
    appendHTML: function(domNode, html) {
        if (domNode.innerHTML != null && domNode.innerHTML.length > 0) {
            var span = document.createElement('span');            
            span.innerHTML = html;
            domNode.appendChild(span);
        } else {
            // Don't need span when innerHTML is empty.
            domNode.innerHTML = html;
        }
        return true;
    },

    /**
     * This function is used to create and start a widget.
     * <p>
     * Note: The props param must be a JSON object containing a widgetType
     * value so the correct widget may be created.
     * <p></p>
     * Valid values for the position param consist of "last" or null. If 
     * the position is "last", resulting HTML is appended to the given domNode. 
     * If the position is null, the given domNode is replaced by the resulting
     * HTML.
     * </p>
     *
     * @param {Node} domNode The DOM node to add widget.
     * @param {Object} props Key-Value pairs of properties.
     * @param {boolean} position The position to add widget.
     * @returns {Object} The newly created widget.
     */
    createWidget: function(domNode, props, position) {
        var widget = null;
        if (props == null || props.id == null || props.widgetType == null) {
            return widget;
        }

        // Destroy previously created widgets, events, etc.
        webui.suntheme.widget.common.destroyWidget(props.id);

        // Retrieve required module.
        dojo.require(props.widgetType);
        
        try {
            // Get widget object.
            var obj = dojo.getObject(props.widgetType);

            // Instantiate widget. Note: Dojo replaces domNode, if provided.
            widget = new obj(props, (position) ? null : domNode);
        } catch (err) {
            var message = "Error: createWidget falied for id=" + props.id;
            if (err && err.description != null) {
                message += ", " + err.description;
            }
            console.debug(message); // See Firebug console.
            return null;
        }

        // Append widget as child.
        if (position == "last") {
            domNode.appendChild(widget.domNode);
        }

        // Start widget.
        widget.startup();
        return widget;
    },

    /**
     * This function is used to detroy a widget.
     * <p>
     * Note: By default, all descendant widgets are destroyed as well.
     * </p>
     *
     * @param {String} id The widget id to destroy.
     * @return {boolean} true if successful; otherwise, false.
     */
    destroyWidget: function(id) {
        if (id == null) {
            return false;
        }

        // Destroy previously created widgets, events, etc.
        var widget = dijit.byId(id);
        if (widget) {
            return widget.destroyRecursive();
        }
        return false;
    },   

    /**
     * This function adds escape sequences for special characters in HTML: &<>"'
     *
     * @param {String} html The string to HTML escape.
     * @return {String} The HTML escaped string.
     */
    escapeHTML: function(html){ 
        return html.replace(/&/gm, "&amp;").
            replace(/</gm, "&lt;").
            replace(/>/gm, "&gt;").
            replace(/"/gm, "&quot;"); 
    },

    /**
     * This function is used to extend the given object with Key-Value pairs of
     * properties. 
     * <p>
     * Note: If a property is an object containing Key-Value pairs itself, this
     * function is called recursively to preserve data which is not explicitly
     * extended. If only top level properties must be replaced, use Prototype's 
     * Object.extend() function.
     * </p>
     *
     * @param {Object} obj The object to extend.
     * @param {Object} props Key-Value pairs of properties.
     * @return {boolean} true if successful; otherwise, false.
     */
    extend: function(obj, props) {
        if (obj == null || props == null) {
            return false;
        }
        for (var property in props) {
            if (obj[property] && typeof obj[property] == "object") {
                webui.suntheme.widget.common.extend(obj[property], props[property]);
            } else {
                obj[property] = props[property];
            }
        }
        return true;
    },

    /**
     * This function returns style class name for a specified selector.
     * <p>
     * Note: If the given key doesn't exist in the theme, the method returns the
     * defaultValue param or null.
     * </p>
     * @param {String} key A key defining a theme class name property.
     * @param {Object} defaultValue Value returned if specified key is not found.
     * @return {boolean} The style class name for a specified selector.
     */
    getClassName: function(key, defaultValue) {
        var ret =  webui.suntheme.theme.common.getClassName(key);
        return (ret != null) 
            ? ret
            : (defaultValue) 
                ? defaultValue
                : null;                
    },

    /**
     * This function returns Object literals for a drop down widget.
     * <p>
     * Note: In addition to widgetType and other theme properties, the values in props 
     * param is added to the returned Object literals. The props param passed should
     * contain the id of the element to be created. 
     * </p>
     * @param {Object} props Key-Value pairs of properties (optional).
     * @config {String} [id] Uniquely identifies an element within a document.   
     */
    getDropDownProps: function(props) {
        if (props == null || props.options == null) {
            return;
        }
        
        if (props.size == null) {
            props.size = 1;
        }
        
        // Set default widgetType.        
        var _props = webui.suntheme.widget.common.getWidgetProps("dropDown", _props);
        
        // Add extra properties        
        webui.suntheme.widget.common.extend(_props, props);
        
        return _props;        
    },

    /**
     * Return the appropriate event object depending on the browser.
     *
     * @param {Event} event The client side event generated
     * @return {Event} The appropriate event object 
     */
    getEvent: function(event) {
        return (event) 
            ? event 
            : ((window.event) ? window.event : null);          
    },
    
    /**
     * This function returns the closest form ancestor of the given DOM node.
     * <p>
     * Note: Traversing the DOM can be slow, but all HTML input elements have a
     * form property. Therefore, avoid using this function when the form can be
     * retrieved via an HTML input element.
     * </p>
     * @param {Node} domNode A DOM node contained in the form.
     * @return {Node} The HTML form element or null if not found.
     */
    getForm: function(domNode) {
        var form = null;
        var obj = domNode;
        while (obj != null) {
            if (obj.tagName == "FORM") {
                form = obj;
                break;
            }
            obj = obj.parentNode;
        }
        return form;
    },
    
    /**
     * This function returns Object literals for a hyperlink.
     * <p>
     * Note: In addition to widgetType and other theme properties, the props 
     * param is add to the returned Object literals. If the given key doesn't 
     * exist in the theme, null is returned.
     * </p>
     * @param {Object} props Key-Value pairs of properties (optional).
     * @config {String} [id] Uniquely identifies an element within a document. 
     * @return {Object} Key-Value pairs of properties.
     */
    getHyperlinkProps: function(props) {
        if (props == null || props.id == null) {
            return;
        }
        var _props = {};
       
        //Set default module and widget name
        _props = webui.suntheme.widget.common.getWidgetProps("hyperlink", _props); 
       
        // Add extra properties               
        if (props != null) {
            webui.suntheme.widget.common.extend(_props, props);
        }
        return _props;
    },

    /**
     * This function returns Object literals for an imageHyperlink.
     * <p>
     * Note: In addition to widgetType and other theme properties, the props 
     * param is add to the returned Object literals. If the given key doesn't 
     * exist in the theme, null is returned.
     * </p><p>
     * If properties are to be defined for the enabled and the disabled images
     * then they should be defined as props.enabledImage and props.disabledImage
     * object literals each with its own id.
     * </p>
     * @param {Object} props Key-Value pairs of properties
     * @param {String} enabledImage A key defining a theme images property.
     * @param {String} disabledImage A key defining a theme images property.
     * @config {String} [id] Uniquely identifies an element within a document.
     * @return {Object} Key-Value pairs of properties.
     */    
    getImageHyperlinkProps: function(props, enabledImage, disabledImage) {
        if (props == null || props.id == null) {
            return;
        }
       
        // Set default module and widget name        
        var _props = webui.suntheme.widget.common.getWidgetProps("imageHyperlink", _props);        
       
        // Add the enabled image properties 
        if (enabledImage != null) {
            _props.enabledImage = webui.suntheme.widget.common.getImageProps(enabledImage, props.enabledImage);
        }
                
        // Add the disabled image properties
        if (disabledImage != null) {
            _props.disabledImage = webui.suntheme.widget.common.getImageProps(disabledImage, props.disabledImage);
        }
              
        // Add extra properties.
        webui.suntheme.widget.common.extend(_props, props);

        return _props;
    },    
    
    /**
     * This function returns Object literals for a theme based image widget.
     * <p>
     * Note: In addition to widgetType and other theme properties, the props 
     * param is add to the returned Object literals. If the given key doesn't 
     * exist in the theme, null is returned.
     * </p>
     * @param {String} key A key defining a theme images property.
     * @param {Object} props Key-Value pairs of properties (optional).
     * @config {String} [id] Uniquely identifies an element within a document.
     * @return {Object} Key-Value pairs of properties.
     */
    getImageProps: function(key, props) {
        var _props = webui.suntheme.theme.common.getImage(key);
        if (_props == null) {
            return null;
        }        
        // Set default widgetType.
        _props = webui.suntheme.widget.common.getWidgetProps("image", _props);

        // To do: Fix for Safari.

        // IE6 has issues with "png" images. IE6 png issue can be fixed but that
        // needs an outermost <span> tag. 
        //
        // <span style="overflow: hidden; width:13px;height:13px; padding: 0px;zoom: 1";>
        // <img src="dot.gif"
        //  style="filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='testImage.png',sizingMethod='crop');
        //  margin-left:-0px;
        //  margin-top:-26px; border: none; height:39px;width:13px;"/>
        // </span>
        //
        // For now, skipping the combined image approach for IE6.
        var mapKey = _props["map_key"];
        var hcFlag = webui.suntheme.widget.common.isHighContrastMode();
        if (mapKey != null && !hcFlag && !webui.suntheme.browser.isIe6()) {
            var transImage = webui.suntheme.theme.common.getImage("DOT");
            var combinedImage = webui.suntheme.theme.common.getImage(mapKey);        
            if (_props['top'] != null 
                    && (_props['actual_height'] == _props['height'] 
                    && _props['actual_width'] == _props['width'])) {
                
                props.style =
                    "background-image:url(" + combinedImage["src"] + ");" +
                    "background-position:" + 0 + "px" +  " " + 
                    _props['top'] + "px" + ";" + "height:" +
                    _props['actual_height'] + "px"+ ";" + "width:" + 
                    _props['actual_width'] + "px" + "border:0" + ";";

                _props["src"] = transImage["src"];
                if (props != null) {
                    props.src = transImage["src"];
                }                   
            }           
        }
        // Add extra properties
        if (props != null) {
            webui.suntheme.widget.common.extend(_props, props);
        }
        return _props;
    },

    /**
     * Return the key code of the key which generated the event.
     *
     * @param {Event} event The client side event generated
     * @return {String} The key code of the key which generated the event     
     */
    getKeyCode: function(event) {    
        return (event.keyCode) 
            ? event.keyCode 
            : ((event.which) ? event.which : event.charCode);              
    },

    /**
     * Get array containing the absolute left and top position of the given DOM
     * node relative to the browser window.
     *
     * @param {Node} domNode The DOM node compute position for.
     * @return {Array} Array containing the absolute left and top position.
     */
    getPosition: function(domNode) {
        var leftPos = topPos = 0;
        if (domNode.offsetParent) {
            leftPos = domNode.offsetLeft;
            topPos = domNode.offsetTop;
            while (domNode = domNode.offsetParent) {
                leftPos += domNode.offsetLeft;
                topPos += domNode.offsetTop;
            }
        }
        return [leftPos, topPos];
    },

    /**
     * Get the page height, handling standard noise to mitigate browser
     * differences.
     *
     * @return {int} The page height or null if not available.
     */
    getPageHeight: function() {
        // Mozilla browsers.
        if (window.innerHeight) {
            return window.innerHeight;
        }

        // IE strict mode
        if (document.documentElement.clientHeight > 0) {
            return document.documentElement.clientHeight; 
        }                

        // IE quirks mode.
        if (document.body.clientHeight) {
            return document.body.clientHeight;
        }
        return null;
    },

    /**
     * Get the page width, handling standard noise to mitigate browser 
     * differences.
     *
     * @return {int} The page height or null if not available.
     */
    getPageWidth: function() {
        // Mozilla browsers.
        if (window.innerWidth) {
            return window.innerWidth;
        }

        // IE strict mode.
        if (document.documentElement.clientWidth > 0) {
            return document.documentElement.clientWidth; 
        }

        // IE quirks mode.
        if (document.body.clientWidth) {
            return document.body.clientWidth;
        }
        return null;
    },

    /**
     * This function is used to obtain a template path, or returns null
     * if key is not found or is not a path, i.e. begins with "<".
     * 
     * @param {String} key A key defining a theme "templates" property.
     * @return {String} The template path.
     */
    getTemplatePath: function(key) {
        var template = webui.suntheme.theme.common.getTemplate(key);
        if (webui.suntheme.widget.common.isTemplatePath(template)) {
            return webui.suntheme.theme.common.getPrefix() + "/" + template;
        } else {
            return null;
        }
    },

    /**
     * This function is used to obtain a template string, or returns null
     * if key is not found or is not a string, i.e. does not begin with "<".
     *
     * @param {String} key A key defining a theme "templates" property.
     * @return {String} The template string.
     */
    getTemplateString: function(key) {
        var template = webui.suntheme.theme.common.getTemplate(key);
        if (!webui.suntheme.widget.common.isTemplatePath(template)) {
            return template;
        } else {
            return null;
        }
    },

    /**
     * This function returns common Object literals used by widgets. For 
     * example, it adds the necessary widgetType.
     *
     * @param {String} widgetName The widget name to add properties for.
     * @param {Object} props Key-Value pairs of properties (optional).
     * @return {Object} Key-Value pairs of properties.
     */
    getWidgetProps: function(widgetName, props) {
        var _props = {};

        // Set default widgetType.
        _props.widgetType = "webui.suntheme.widget."  + widgetName;    

        // Add extra properties
        if (props != null) {
            webui.suntheme.widget.common.extend(_props, props);
        }
        return _props;
    },

    /**
     * This function checks for the high contrast mode.
     *  
     * @return {boolean} true if high contrast mode.
     */
    isHighContrastMode:  function() {
        // Dojo appends the following div tag in body tag for a11y support.
        //
        // <div style="border-style: solid; border-color: red green; border-width: 1px; 
        //  position: absolute; left: -999px; top: -999px; background-image: 
        //  url(/example/theme/META-INF/dojo/dijit/form/templates/blank.gif);" id="a11yTestNode">
        // </div>

        // Currently high contrast mode check is supported for firefox and ie on
        // windows. High contrast mode is not supported for Safari.
        if (webui.suntheme.browser.isSafari()) {
            return false;            
        }

        // Detect the high contrast mode. 
        var divA11y = document.getElementById('a11yTestNode');
        var bImg = null;
        if (divA11y != null && window.getComputedStyle) {
            var styleValue = getComputedStyle(divA11y, "");
            bImg = styleValue.getPropertyValue("background-image");
        } else {
            bImg = divA11y.currentStyle.backgroundImage;
        }
        if (bImg != null && (bImg == "none" || bImg == "url(invalid-url:)" )) {
            return true; //High Contrast Mode
        }
        return false;    
    },
    
    /**
     * This function is used to test HTML template strings. 
     * <p>
     * Note: This function returns true if the "template" is a template path, 
     * and false if it is a template String. False is also returned if the value
     * is null or the empty string.
     * </p>
     * @param {String} template The template string to test.
     * @return boolean true if string is an HTML template.
     */
    isTemplatePath: function(template) {
        return (template != null && template.charAt(0) != '<');
    },

    /**
     * This function is used to remove child nodes from given DOM node.
     * <p>
     * Note: Child nodes may be cleared using the innerHTML property. However,
     * IE fails when this property is set via the widget's fillInTemplate 
     * function. In this case, DOM nodes shall be removed manually using the 
     * Node APIs.
     * </p>
     * @param {Node} domNode The DOM node to remove child nodes.
     * @return {boolean} true if successful; otherwise, false.
     */
    removeChildNodes: function(domNode) {
        if (domNode == null) {
            return false;
        }

        try {
            domNode.innerHTML = ""; // Cannot be null on IE.
        } catch (e) {
            // Iterate over child nodes.
            while (domNode.hasChildNodes()) {
                var node = domNode.childNodes[0];
                domNode.removeChild(node);
            }
        }
        return true;
    },

    /**
     * This function is used to replace an HTML element with a newly created
     * widget -- see the createWidget() function.
     * <p>
     * Note: An HTML element is normally used as a temporary place holder so
     * that a widget may be added to the document in the proper location. 
     * Typically, the HTML element has the same id as the newly created widget.
     * </p><p>
     * Minimally, the props argument must be a JSON object containing an id and 
     * widgetType property so the correct widget may be created.
     * </p>
     * @param {String} elementId The id of the HTML element to replace.
     * @param {Object} props Key-Value pairs of properties.
     * @config {String} [id] The widget id.
     * @config {String} [widgetType] The widget type to create.
     * @return {Object} The newly created widget.
     */
    replaceElement: function(elementId, props, onLoad) {
        if (props == null) {
            return null;
        }
        var domNode = document.getElementById(elementId);
        return (domNode)
            ? webui.suntheme.widget.common.createWidget(domNode, props) : null;
    },

    /**
     * This function sleeps for specified milli seconds.
     * 
     * @param {int} delay The amount to delay.
     * @return {boolean} true if current time is greater than the exit time.
     */
    sleep:  function(delay) {
        var start = new Date();
        var exitTime = start.getTime() + delay;

        while (true) {
            start = new Date();
            if (start.getTime() > exitTime) {
                return true;
            }
        }
        return false;
    },
        
    /**
     * This function is used to update a widget, HTML fragment, or static
     * string for the given domNode.
     * <p>
     * Note: If the widget associated with props.id already exists, the widget's 
     * setProps() function is invoked with the given props param. If the widget 
     * does not exist, the widget object is instantiated via the addFragment()
     * function -- all params are passed through.
     * </p><p>
     * See webui.suntheme.widget.label._setProps for example.
     * </p>
     * @param {Node} domNode The DOM node used to add widget.
     * @param {Object} props Key-Value pairs of properties.
     * @param {String} position The position (e.g., "first", "last", etc.) to add widget.
     * @param {boolean} escape HTML escape static strings -- default is true.
     * @return {boolean} true if successful; otherwise, false.
     */
    updateFragment: function(domNode, props, position, escape) {
        if (props == null) {
            return false;
        }

        // Ensure props is not a string.
        var widget = (typeof props != 'string') 
            ? dijit.byId(props.id) : null;

        // Update widget or add fragment.
        if (widget && typeof widget.setProps == "function") {
            widget.setProps(props);
        } else {
            webui.suntheme.widget.common.addFragment(domNode, props, position, escape);
        }
        return true;
    }
}
dojo.provide("webui.suntheme.widget.eventBase");

dojo.require("dijit._Widget"); 
dojo.require("dijit._Templated"); 

/**
 * @name webui.suntheme.widget.eventBase
 * @extends dijit._Widget, dijit._Templated
 * @class This class contains functions for widgets that extend eventBase.
 * @static
 */
dojo.declare("webui.suntheme.widget.eventBase", [dijit._Widget, dijit._Templated]);

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.eventBase.event =
        webui.suntheme.widget.eventBase.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: null,

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: null
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: null,

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: null
    },

    /**
     * This object contains submit event topics.
     * @ignore
     */
    submit: {
        /** Submit event topic for custom AJAX implementations to listen for. */
        beginTopic: null,

        /** Submit event topic for custom AJAX implementations to listen for. */
        endTopic: null
    }
}

/**
 * Initialize public functions.
 * <p>
 * Note: If this.event.<eventName> is not null, a public function shall be added
 * to the DOM node. To avoid name clashes, do not create private functions with
 * the names; refresh, stateChanged, or submit.
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.eventBase.prototype.initFunctions = function () {
    if (this.event == null) {
        return false;
    }

    // Since the anchor id and name must be the same on IE, we cannot obtain the
    // widget using the DOM node ID via the public functions below. Therefore, 
    // we need to set the widget id via closure magic.
    var _id = this.id;

    // Refresh.
    if (this.event.refresh != null) {
        // Set public function.
        this.domNode.refresh = function(execute) {
            return dijit.byId(_id).refresh(execute);
        };
    }

    // Submit.
    if (this.event.submit != null) {
        // Set public function.
        this.domNode.submit = function(execute) {
            return dijit.byId(_id).submit(execute);    
        };
    }

    // State.
    if (this.event.state != null) {
        // Remove prototyped function.
        this.stateChanged = null;
    }
    return true;
}

/**
 * Process refresh event.
 * <p>
 * Note: If this.event.refresh is not null, an event is published for custom
 * Ajax implementations to listen for. If event topics are not implemented for 
 * this widget, the function returns and a message is output to the console.
 * </p>
 * @param {String} execute The string containing a comma separated list 
 * of client ids against which the execute portion of the request 
 * processing lifecycle must be run.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.eventBase.prototype.refresh = function(execute) {
    if (this.event.refresh == null) {
        console.debug("Error: Refresh event topics not implemented for " + 
            this.widgetName); // See Firebug console.
        return false;
    }

    // Publish an event for custom AJAX implementations to listen for.
    dojo.publish(this.event.refresh.beginTopic, [{
        id: this.id,
        execute: execute,
        endTopic: this.event.refresh.endTopic
    }]);
    return true;
}

/**
 * Process state event.
 * <p>
 * Note: If this.event.state is not null, an event is published for custom
 * Ajax implementations to listen for. If event topics are not implemented for 
 * this widget, the function returns and a message is output to the console.
 * </p>
 * @param {Object} props Key-Value pairs of widget properties to update.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.eventBase.prototype.stateChanged = function(props) {
    if (this.event.state == null) {
        console.debug("Error: State event topics not implemented for " + 
            this.widgetName); // See Firebug console.
        return false;
    }

    // Publish an event for custom AJAX implementations to listen for.
    dojo.publish(this.event.state.beginTopic, [{
        id: this.id,
        endTopic: this.event.state.endTopic,
        props: props
    }]);
    return true;
}

/**
 * Process submit event.
 * <p>
 * Note: If this.event.submit is not null, an event is published for custom
 * Ajax implementations to listen for. If event topics are not implemented for 
 * this widget, the function returns and a message is output to the console.
 * </p>
 * @param {String} execute Comma separated string containing a list of 
 * client ids against which the execute portion of the request 
 * processing lifecycle must be run.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.eventBase.prototype.submit = function(execute) {
    if (this.event.submit == null) {
        console.debug("Error: Submit event topics not implemented for " + 
            this.widgetName); // See Firebug console.
        return false;
    }

    // Publish an event for custom AJAX implementations to listen for.
    dojo.publish(this.event.submit.beginTopic, [{
        id: this.id,
        execute: execute,
        endTopic: this.event.submit.endTopic
    }]);
    return true;
}
dojo.provide("webui.suntheme.widget.widgetBase");
 

/**
 * @name webui.suntheme.widget.widgetBase
 * @extends webui.suntheme.widget.eventBase
 * @class This class contains functions used for base functionality in all 
 * widgets. 
 * <p>
 * The widgetBase class inherits from dijit._Widget and dijit_Templated. The 
 * dijit._Widget class is responsible for calling the buildRendering() and 
 * postCreate() functions in that order. The dijit_Templated function overrides
 * the buildRendering() functon to fill in template properties.
 * <p></p>
 * The postCreate() function is responible for initializing CSS selectors, 
 * events, and public functions. Commonly used functions (e.g., getProps(), 
 * setProps(), and refresh() are set on the outermost DOM node via the 
 * "superclass" function of widgetBase. This inherited function is also 
 * responsible for invoking the private _setProps() function. 
 * <p></p>
 * The private _setProps() function is used to set widget properties that can be
 * updated by a web app developer. This helps encapsolate functionality and 
 * brand changes while providing a common API for all widgets. In addition, the 
 * widget is selctively updated (i.e., if and only if a key-value pair has been 
 * given). Saving given properties is deferred to the public setProps() function
 * which allows _setProps() to be used during initialization.
 * <p></p>
 * The private _setProps() function is also responsible for invoking 
 * setCommonProps() and setEventProps(). These properties may not always be set 
 * on the outermost DOM node; however, core (i.e., id, class, style, etc.) 
 * properties are. Core properties are set on the DOM via the "superclass" 
 * function of widgetBase which invokes the setCoreProps() function.
 * <p></p>
 * The getClassName() function is responsible for obtaining the selector that
 * will be set on the outermost DOM node. The private _setProps() function 
 * of widgetBase ensures that the getClassName() function is called prior to 
 * invoking setCoreProps(). In most cases, this function will be overridded in
 * order to apply widget specific selectors. However, selectors should be 
 * concatinated in order of precedence (e.g., the user's className property is 
 * always appended last).
 * <p></p>
 * The public setProps() function is responsible for extending the widget class
 * with properties so they can be used during later updates. After extending the
 * widget, the private _setProps() function is called. In some cases, the public
 * setProps() function may be overridden. For example, the label clears the
 * contents property from the widget because that is something we do not want to
 * extend.
 * <p></p>
 * The startup() function is typically called after the widget has been 
 * instantiated. For example, a progressBar might start a timer to periodically
 * refresh. 
 * <p></p>
 * Warning: It's not possible to append HTML elements from script that is 
 * not a direct child of the BODY element. If there is any Javascript
 * running inside the body that is a direct child of body, IE will throw
 * an "Internet Explorer cannot open the Internet site" error. For example,
 * dijit._Templated._createNodesFromText generates such an error by calling
 * appendChild(). Therefore, widget creation must be deferred to the
 * window.onLoad event. See http://trac.dojotoolkit.org/ticket/4631
 * </p>
 * @static
 */
dojo.declare("webui.suntheme.widget.widgetBase", webui.suntheme.widget.eventBase, {
    // Note: If your class contains arrays or other objects, they should be
    // declared in the constructor function so that each instance gets it's own
    // copy. Simple types (literal strings and numbers) are fine to declare in 
    // the class directly.

    // Set defaults.
    theme: webui.suntheme.theme.common, // Common theme utils.
    widget: webui.suntheme.widget.common // Common widget utils. 
});

/**
 * This function is used to render the widget from a template.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.widgetBase.prototype.buildRendering = function () {
    // Get default templates.
    if (this.templatePath == null && this.templateString == null) {
        this.templatePath = this.widget.getTemplatePath(this.widgetName);
        this.templateString = this.widget.getTemplateString(this.widgetName);
    }

    // The templatePath should have precedence. Therefore, in order for the 
    // templatePath to be used, templateString must be null.
    if (this.templatePath != null) {
        this.templateString = null;
    }

    // Template must be set prior to calling "superclass".
    return this.inherited("buildRendering", arguments);
}

/**
 * This function is used to obtain the outermost HTML element class name.
 * <p>
 * Note: Selectors should be concatinated in order of precedence (e.g., the 
 * user's className property is always appended last).
 * </p>
 * @return {String} The outermost HTML element class name.
 */
webui.suntheme.widget.widgetBase.prototype.getClassName = function() {
    return this.className;
}

/**
 * This function is used to get common properties from the widget. Please see
 * the setCommonProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.widgetBase.prototype.getCommonProps = function() {
    var props = {};

    // Set properties.
    if (this.accessKey) { props.accessKey = this.accessKey; }
    if (this.dir) { props.dir = this.dir; }
    if (this.lang) { props.lang = this.lang; }
    if (this.tabIndex) { props.tabIndex = this.tabIndex; }
    if (this.title) { props.title = this.title; }

    return props;
}

/**
 * This function is used to get core properties from the widget. Please see
 * the setCoreProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.widgetBase.prototype.getCoreProps = function() {
    var props = {};

    // Set properties.
    if (this.className) { props.className = this.className; }
    if (this.id) { props.id = this.id; }
    if (this.style) { props.style = this.style; }
    if (this.visible != null) { props.visible = this.visible; }

    return props;
}

/**
 * This function is used to get event properties from the widget. Please
 * see the setEventProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.widgetBase.prototype.getEventProps = function() {
    var props = {};

    // Set properties.
    if (this.onBlur) { props.onBlur = this.onBlur; }
    if (this.onChange) { props.onChange = this.onChange; }
    if (this.onClick) { props.onClick = this.onClick; }
    if (this.onDblClick) { props.onDblClick = this.onDblClick; }
    if (this.onFocus) { props.onFocus = this.onFocus; }
    if (this.onKeyDown) { props.onKeyDown = this.onKeyDown; }
    if (this.onKeyPress) { props.onKeyPress = this.onKeyPress; }
    if (this.onKeyUp) { props.onKeyUp = this.onKeyUp; }
    if (this.onMouseDown) { props.onMouseDown = this.onMouseDown; }
    if (this.onMouseOut) { props.onMouseOut = this.onMouseOut; }
    if (this.onMouseOver) { props.onMouseOver = this.onMouseOver; }
    if (this.onMouseUp) { props.onMouseUp = this.onMouseUp; }
    if (this.onMouseMove) { props.onMouseMove = this.onMouseMove; }
    if (this.onSelect) { props.onSelect = this.onSelect; }

    return props;
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.widgetBase.prototype.getProps = function() {
    var props = {};

    // Set properties.
    Object.extend(props, this.getCommonProps());
    Object.extend(props, this.getCoreProps());
    Object.extend(props, this.getEventProps());

    return props;
}

/**
 * This function is used to test if widget has been initialized.
 * <p>
 * Note: It is assumed that an HTML element is used as a place holder for the
 * document fragment.
 * </p>
 * @return {boolean} true if widget is initialized.
 */
webui.suntheme.widget.widgetBase.prototype.isInitialized = function() {
    // Testing if the outermost DOM node has been added to the document and
    // ensuring a Dojo attach point exists works fine for JSP. However, the 
    // following code always returns null for facelets.
    //
    // var domNode = document.getElementById(this.id);
    // if (domNode && domNode.getAttribute("dojoattachpoint")) {
    if (this.initialized == true) {
        return true;
    }
    return false;
}

/**
 * This is called after the buildRendering() function.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet.
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.widgetBase.prototype.postCreate = function () {
    this.inherited("postCreate", arguments);

    // In order to register widgets properly, the DOM node id must be set prior 
    // to creating any widget children. Otherwise, widgets may not be destroyed.
    this.domNode.id = this.id;

    // Since the anchor id and name must be the same on IE, we cannot obtain the
    // widget using the DOM node ID via the public functions below. Therefore, 
    // we need to set the widget id via closure magic.
    var _id = this.id;

    // Set public functions.
    this.domNode.getProps = function() { return dijit.byId(_id).getProps(); }
    this.domNode.setProps = function(props, notify) { return dijit.byId(_id).setProps(props, notify); }

    // Initialize refresh(), stateChanged(), and submit().
    this.initFunctions();

    // Set properties.
    this._setProps(this.getProps());

    // All widget properties have been set.
    return this.initialized = true;
}

/**
 * This function is used to set common properties for the given domNode.
 *
 * @param {Node} domNode The DOM node to assign properties to.
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [accessKey] Shortcut key.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.widgetBase.prototype.setCommonProps = function(domNode, props) {
    if (domNode == null || props == null) {
        return false;
    }
    if (props.accessKey) { 
        domNode.accessKey = props.accessKey;
    }
    if (props.dir) {
        domNode.dir = props.dir;
    }
    if (props.lang) {
        domNode.lang = props.lang;
    }
    if (props.tabIndex > -1 && props.tabIndex < 32767) {
        domNode.tabIndex = props.tabIndex;
    }
    if (props.title) {
        domNode.title = props.title;
    }
    return true;
}

/**
 * This function is used to set core properties for the given domNode. These
 * properties are typically set on the outermost element.
 *
 * Note: The className is typically provided by a web app developer. If 
 * the widget has a default className, it should be added to the DOM node
 * prior to calling this function. For example, the "myCSS" className would
 * be appended to the existing "Tblsun4" className (e.g., "Tbl_sun4 myCSS").
 *
 * @param {Node} domNode The DOM node to assign properties to.
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [className] CSS selector.
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [style] Specify style rules inline.
 * @config {boolean} [visible] Hide or show element.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.widgetBase.prototype.setCoreProps = function(domNode, props) {
    if (domNode == null || props == null) {
        return false;
    }
    if (props.className) {
        domNode.className = props.className;
    }
    if (props.id) { 
        domNode.id = props.id;
    }
    if (props.style) { 
        domNode.style.cssText = props.style;
    }
    if (props.visible != null) {
        webui.suntheme.common.setVisibleElement(domNode, 
            new Boolean(props.visible).valueOf());
    }
    return true;
}

/**
 * This function is used to set event properties for the given domNode.
 *
 * @param {Node} domNode The DOM node to assign properties to.
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onChange] Element value changed.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {String} [onSelect] Element text selected.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.widgetBase.prototype.setEventProps = function(domNode, props) {
    if (domNode == null || props == null) {
        return false;
    }

    // Note: JSON strings are not recognized as JavaScript. In order for
    // events to work properly, an anonymous function must be created.
    if (props.onBlur) { 
        domNode.onblur = (typeof props.onBlur == 'string')
            ? new Function("event", props.onBlur)
            : props.onBlur;
    }
    if (props.onClick) {
        domNode.onclick = (typeof props.onClick == 'string')
            ? new Function("event", props.onClick)
            : props.onClick;
    }
    if (props.onChange) {
        domNode.onchange = (typeof props.onChange == 'string')
            ? new Function("event", props.onChange)
            : props.onChange;
    }
    if (props.onDblClick) {
        domNode.ondblclick = (typeof props.onDblClick == 'string')
            ? new Function("event", props.onDblClick)
            : props.onDblClick;
    }
    if (props.onFocus) {
        domNode.onfocus = (typeof props.onFocus == 'string')
            ? new Function("event", props.onFocus)
            : props.onFocus;
    }
    if (props.onKeyDown) {
        domNode.onkeydown = (typeof props.onKeyDown == 'string')
            ? new Function("event", props.onKeyDown)
            : props.onKeyDown;
    }
    if (props.onKeyPress) {
        domNode.onkeypress = (typeof props.onKeyPress == 'string')
            ? new Function("event", props.onKeyPress)
            : props.onKeyPress;
    }
    if (props.onKeyUp) {
        domNode.onkeyup = (typeof props.onKeyUp == 'string')
            ? new Function("event", props.onKeyUp)
            : props.onKeyUp;
    }
    if (props.onMouseDown) {
        domNode.onmousedown = (typeof props.onMouseDown == 'string')
            ? new Function("event", props.onMouseDown)
            : props.onMouseDown;
    }
    if (props.onMouseOut) {
        domNode.onmouseout = (typeof props.onMouseOut == 'string')
            ? new Function("event", props.onMouseOut)
            : props.onMouseOut;
    }
    if (props.onMouseOver) {
        domNode.onmouseover = (typeof props.onMouseOver == 'string')
            ? new Function("event", props.onMouseOver)
            : props.onMouseOver;
    }
    if (props.onMouseUp) {
        domNode.onmouseup = (typeof props.onMouseUp == 'string')
            ? new Function("event", props.onMouseUp)
            : props.onMouseUp;
    }
    if (props.onMouseMove) {
        domNode.onmousemove = (typeof props.onMouseMove == 'string')
            ? new Function("event", props.onMouseMove)
            : props.onMouseMove;
    }
    if (props.onSelect) {
        domNode.onselect = (typeof props.onSelect == 'string')
            ? new Function("event", props.onSelect)
            : props.onSelect;
    }
    return true;
}

/**
 * This function is used to set widget properties.
 *
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 *
 * Note: If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [className] CSS selector.
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [style] Specify style rules inline.
 * @config {boolean} [visible] Hide or show element.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.widgetBase.prototype.setProps = function(props, notify) {
    if (props == null) {
        return false;
    }

    // Extend widget object for later updates.
    this.widget.extend(this, props);

    // Set properties.
    this._setProps(props);

    // Notify listeners state has changed.
    if (new Boolean(notify).valueOf() == true &&
            typeof this.stateChanged == "function") {
        this.stateChanged(props);
    }
    return true;
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.widgetBase.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set style class -- must be set before calling setCoreProps().
    props.className = this.getClassName();

    // Set more properties.
    return this.setCoreProps(this.domNode, props);
}

/**
 * This function is used to "start" the widget, after the widget has been
 * instantiated.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.widgetBase.prototype.startup = function () {
    if (this._started) {
        return false;
    }
    this.inherited("startup", arguments);
    return this._started = true;
}
dojo.provide("webui.suntheme.widget.accordionTab");


/**
 * @name webui.suntheme.widget.accordionTab
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for the accordionTab widget.
 * @constructor This function is used to construct an accordionTab widget.
 */
dojo.declare("webui.suntheme.widget.accordionTab", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    isContainer: true,
    selected: false,
    widgetName: "accordionTab" // Required for theme properties.    
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.accordionTab.event =
        webui.suntheme.widget.accordionTab.prototype.event = {
    /**
     * This object contains load event topics.
     * @ignore
     */
    load: {
        /** Load event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_accordionTab_event_load_begin",

        /** Load event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_accordionTab_event_load_end"
    },

    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_accordionTab_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_accordionTab_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_accordionTab_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_accordionTab_event_state_end"
    },

    /**
     * This object contains title event topics.
     * @ignore
     */
    title: {
        /** Action event topic for custom AJAX implementations to listen for. */
        selectedTopic: "webui_suntheme_widget_accordionTab_event_tab_selected"
    }
}

/**
 * Process load event.
 *
 * @param {String} execute The string containing a comma separated list 
 * of client ids against which the execute portion of the request 
 * processing lifecycle must be run.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordionTab.prototype.loadContent = function(execute) {
    // Publish event.
    dojo.publish(webui.suntheme.widget.accordionTab.event.load.beginTopic, [{
        id: this.id
    }]);
    return true;
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.accordionTab.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.selected) { props.selected = this.selected; }
    if (this.title) { props.title = this.title; }
    if (this.tabContent) { props.tabContent = this.tabContent; }
    if (this.visible != null) { props.visible = this.visible; }
    if (this.actions != null) { props.actions = this.actions; }
    if (this.className != null) { props.className = this.className; }
    if (this.style != null) { props.style = this.style; }
    if (this.contentHeight != null) { props.contentHeight = this.contentHeight; }
    if (this.id) { props.id = this.id; }
    if (this.type) { props.type = this.type; }

    return props;
}

/**
 * Get title height.
 *
 * @return {int} The title height.
 */
webui.suntheme.widget.accordionTab.prototype.getTitleHeight = function () {
    // Warning: This function has been made private.
    return dojo._getMarginBox(this.titleContainer).height;
}

/**
 * Handle menu onClick event.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordionTab.prototype.onMenuClickCallback = function(event) {
    dojo.stopEvent(event);
    return true;
}

/**
 * Handle title onClick event.
 * <p>
 * This function selects the child tab when the user clicks on its label. The 
 * actual behavior of the accordion depends on multipleSelect being enabled.
 * </p>
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordionTab.prototype.onTitleClickCallback = function (event) {
    dojo.publish(webui.suntheme.widget.accordionTab.event.title.selectedTopic, [{
        id: this.id
    }]);
    return true;
}

/**
 * Handle title onMouseOut event.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordionTab.prototype.onTitleMouseOutCallback = function(event) {
    if (this.selected) {
        this.titleContainer.className = this.theme.getClassName("ACCORDION_TABEXPANDED");
        this.turnerContainer.className = this.theme.getClassName("ACCORDION_DOWNTURNER");
        return false;
    }
    this.titleContainer.className = this.theme.getClassName("ACCORDION_TABCOLLAPSED");
    this.turnerContainer.className = this.theme.getClassName("ACCORDION_RIGHTTURNER");
    return true;
}

/**
 * Handle title onMouseOver event.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordionTab.prototype.onTitleMouseOverCallback = function(event) {
    if (this.selected) {
        this.turnerContainer.className = this.theme.getClassName("ACCORDION_DOWNTURNER");
    } else {
        this.turnerContainer.className = this.theme.getClassName("ACCORDION_RIGHTTURNER");
    }
    return true;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordionTab.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {
        this.domNode.id = this.id;
        this.titleNode.id = this.id + "_tabTitle";
        this.turnerContainer.id = this.id + "_tabTitleTurner";
        this.menuContainer.id = this.id + "_tabMenu";
        this.hiddenFieldNode.id = this.id + ":selectedState";
        this.hiddenFieldNode.name = this.hiddenFieldNode.id;
    }

    // Set style classes.
    this.titleContainer.className = this.theme.getClassName("ACCORDION_TABCOLLAPSED");
    this.turnerContainer.className = this.theme.getClassName("ACCORDION_RIGHTTURNER");
    this.menuContainer.className = this.theme.getClassName("HIDDEN");
    this.titleNode.className = this.theme.getClassName("ACCORDION_TABTITLE");
    this.contentNode.className = this.theme.getClassName("ACCORDION_TABCONTENT");
    
    // Set public functions.
    // TBD...

    // Set events.
    dojo.connect(this.titleContainer, "onclick", this, "onTitleClickCallback");
    dojo.connect(this.titleContainer, "onmouseover", this, "onTitleMouseOverCallback");
    dojo.connect(this.turnerContainer, "onmouseover", this, "onTitleMouseOverCallback");
    dojo.connect(this.turnerContainer, "onclick", this, "onTitleClickCallback");
    dojo.connect(this.menuContainer, "onmouseover", this, "onTitleMouseOverCallback");
    dojo.connect(this.menuContainer, "onclick", this, "onMenuClickCallback");
    dojo.connect(this.titleContainer, "onmouseout", this, "onTitleMouseOutCallback");
    dojo.connect(this.turnerContainer, "onmouseout", this, "onTitleMouseOutCallback");
    dojo.connect(this.menuContainer, "onmouseout", this, "onTitleMouseOutCallback");

    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [className] CSS selector.
 * @config {int} [contentHeight] CSS selector.
 * @config {String} [hiddenField] 
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {Array} [tabContent] 
 * @config {String} [style] Specify style rules inline.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordionTab.prototype.setProps = function(props, notify) {
    if (props == null) {
        return false;
    }

    // Replace contents -- do not extend.
    if (props.tabContent) {
        this.tabContent = null;
    }

    // Extend widget object for later updates.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.accordionTab.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set properties.
    if (props.contentHeight) {
        this.contentNode.style.height = props.contentHeight;
    }

    if (props.title) {
        this.setTitle(props.title);
    }

    if (props.tabContent) {
        this.setTabContent(props.tabContent);
        if (this.selected) {
            this.hiddenFieldNode.value = "true";
            this.titleContainer.className = this.theme.getClassName("ACCORDION_TABEXPANDED");
            this.turnerContainer.className = this.theme.getClassName("ACCORDION_DOWNTURNER");
            this.contentNode.style.display = "block";
        } else {
            this.hiddenFieldNode.value = "false";
            this.titleContainer.className = this.theme.getClassName("ACCORDION_TABCOLLAPSED");
            this.turnerContainer.className = this.theme.getClassName("ACCORDION_RIGHTTURNER");
            this.contentNode.style.display = "none";
        }
    }

    // Set more properties.
    this.setCommonProps(this.domNode, props);
    this.setEventProps(this.domNode, props);

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}

/**
 * Set tab selected.
 *
 * @param {boolean} isSelected true if selected.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordionTab.prototype.setSelected = function (isSelected) {
    if (this.selected) {
        this.selected = false;
    } else {
        this.selected = isSelected;
    }

    if (this.selected) {
        this.hiddenFieldNode.value = "true";
        this.titleContainer.className = this.theme.getClassName("ACCORDION_TABEXPANDED");
        this.turnerContainer.className = this.theme.getClassName("ACCORDION_DOWNTURNER");
        this.contentNode.style.display = "block";

        // if the tab does not have content and "loadOnSelect" is set
        // to true go ahead and refresh the widget. 
        if (!this.tabContent) {
            if (this.parent.loadOnSelect) {
                this.loadContent();
            }
        }
    } else {
        this.hiddenFieldNode.value = "false";
        this.titleContainer.className = this.theme.getClassName("ACCORDION_TABCOLLAPSED");
        this.turnerContainer.className = this.theme.getClassName("ACCORDION_RIGHTTURNER");
        this.contentNode.style.display = "none";
    }
    return true;
}

/**
 * Set the contents of the accordion tab.
 *
 * @param {Array} content The Contents of the tab body.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordionTab.prototype.setTabContent = function(content) {
    if (content) {
        for (var i = 0; i < content.length; i++) {
            this.widget.addFragment(this.contentNode, content[i], "last");
        }
    }
    return true;
}

/**
 * Set the title associated with the accordion tab.
 *
 * @param {String} title Title property associated with the tab.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordionTab.prototype.setTitle = function (title) {
    if (title) {
        // NOTE: If you set this value manually, text must be HTML escaped.
        this.widget.addFragment(this.titleHref, title);
    }
    return true;
}
dojo.provide("webui.suntheme.widget.accordion");


/**
 * @name webui.suntheme.widget.accordion
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for the accordion widget.
 * @constructor This function is used to construct an accordion widget.
 */
dojo.declare("webui.suntheme.widget.accordion", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    duration: 250,
    isContainer: true,
    loadOnSelect: false,
    multipleSelect: false,
    widgetName: "accordion" // Required for theme properties.
});

/**
 * Helper function to add accordion header controls
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {Object} [collapseAllImage] 
 * @config {Object} [expandAllImage] 
 * @config {Object} [isRefreshIcon] 
 * @config {Object} [multipleSelect] 
 * @config {Object} [refreshImage] 
 * @config {Object} [toggleControls]
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordion.prototype.addControls = function(props) {       
    // Add expand and collapse icons only if multiple select is set to
    // true and the icons have been supplied.
    if (props.toggleControls && props.multipleSelect) {
        // Set expand all image properties.
        if (props.expandAllImage) {
            // Set properties.
            // props.expandAllImage.id = this.expandAllImage.id; // Required for updateFragment().

            // Update/add fragment.
            this.widget.updateFragment(this.expandAllImgContainer, props.expandAllImage);
        }

        // Set collapse all image properties.
        if (props.collapseAllImage) {
            // Update/add fragment.
            this.widget.updateFragment(this.collapseAllImgContainer, props.collapseAllImage);
        }
        
        // a divider should only be added if expand/collapse icons exist.
        this.dividerNodeContainer.className = this.theme.getClassName("ACCORDION_HDR_DIVIDER");
    }

    // Set refresh image properties.
    if (props.isRefreshIcon && props.refreshImage) {
        // Update/add fragment.
        this.widget.updateFragment(this.refreshImgContainer, props.refreshImage);
    }
    return true;
}

/**
 * Close all open accordions and leave the others as is.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordion.prototype.collapseAllTabs = function(event) {
    // Iterate over all tabs.
    for (var i = 0; i < this.tabs.length; i++) {
        var widget = dijit.byId(this.tabs[i].id);
        if (widget && widget.selected) {
            widget.setSelected(false);
        }
    }
    return true;
}

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.accordion.event = 
        webui.suntheme.widget.accordion.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_accordion_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_accordion_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_accordion_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_accordion_event_state_end"
    }
}

/**
 * Open all closed tabs and leave the others as is.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordion.prototype.expandAllTabs = function(event) {
    // Iterate over all tabs.
    for (var i = 0; i < this.tabs.length; i++) {
        var widget = dijit.byId(this.tabs[i].id);
        if (widget && !widget.selected) {
            widget.setSelected(true);
        }
    }
    return true;
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.accordion.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.collapseAllImage != null) { props.collapseAllImage = this.collapseAllImage; }
    if (this.expandAllImage != null) { props.expandAllImage = this.expandAllImage; }
    if (this.isRefreshIcon != null) { props.isRefreshIcon = this.isRefreshIcon; }
    if (this.loadOnSelect) { props.loadOnSelect = this.loadOnSelect; }
    if (this.multipleSelect) { props.multipleSelect = this.multipleSelect; }
    if (this.refreshImage != null) { props.refreshImage = this.refreshImage; }
    if (this.style != null) { props.style = this.style; }
    if (this.tabs != null) { props.tabs = this.tabs; }
    if (this.toggleControls) { props.toggleControls = this.toggleControls; }
    if (this.type) { props.type = this.type; }
 
    return props;
}

/**
 * This function is used to obtain the outermost HTML element class name.
 * <p>
 * Note: Selectors should be concatinated in order of precedence (e.g., the
 * user's className property is always appended last).
 * </p>
 * @return {String} The outermost HTML element class name.
 */
webui.suntheme.widget.accordion.prototype.getClassName = function() {
    // Get theme property.
    var className = this.theme.getClassName("ACCORDION_DIV", "");
    return (this.className)
        ? className + " " + this.className
        : className;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordion.prototype.postCreate = function () {
    with (this.domNode.style) {
        if (position != "absolute") {
            position = "relative";
        }
        overflow = "hidden";
    }

    // Set ids.
    if (this.id) {
        this.domNode.id = this.id;
        this.headerContainer.id = this.id + "_accHeader";
        this.expandAllContainer.id = this.id + "_expandAllNode";
        this.expandAllImgContainer.id = this.expandAllContainer.id + "_expandAllImage";
        this.collapseAllContainer.id = this.id + "_collapseAllNode";
        this.collapseAllImgContainer.id = this.collapseAllImgContainer.id + "_collapseAllImage";
        this.dividerNodeContainer.id = this.id + "_dividerNode";
        this.refreshNodeContainer.id = this.id + "_refreshNode";
    }

    // Set class names.
    this.headerContainer.className = this.theme.getClassName("ACCORDION_HDR");
    this.collapseAllContainer.className = this.theme.getClassName("ACCORDION_HDR_CLOSEALL");
    this.expandAllContainer.className = this.theme.getClassName("ACCORDION_HDR_OPENALL");
    
    // the divider should initially be hidden
    this.dividerNodeContainer.className = this.theme.getClassName("HIDDEN");
    this.refreshNodeContainer.className = this.theme.getClassName("ACCORDION_HDR_REFRESH");

    // Set events.
    var _id = this.id;
    dojo.connect(this.collapseAllContainer, "onclick", this, "collapseAllTabs");
    dojo.connect(this.expandAllContainer, "onclick", this, "expandAllTabs");
    dojo.connect(this.refreshNodeContainer, "onclick", function(event) {
        // New literals are created every time this function is called, and it's 
        // saved by closure magic.
        var widget = dijit.byId(_id);
        widget.refresh(_id);
    });

    // Subscribe to the "dayClicked" event present in the calendar widget.
    dojo.subscribe(webui.suntheme.widget.accordionTab.event.title.selectedTopic,
        this, "tabSelected");

    // Generate the accordion header buttons on the client side.

    if (this.toggleControls && this.multipleSelect) {
        if (this.expandAllImage == null) {
            var btnTitle = this.theme.getMessage("Accordion.expandAll");
            this.expandAllImage = this.widget.getImageProps("ACCORDION_EXPAND_ALL", {
                id: this.id + "_expandAll", 
                title: btnTitle,
                alt: btnTitle
            });
        }
        
        if (this.collapseAllImage == null) {
            var btnTitle = this.theme.getMessage("Accordion.collapseAll");
            this.collapseAllImage = this.widget.getImageProps("ACCORDION_COLLAPSE_ALL", {
                id: this.id + "_collapseAll", 
                title: btnTitle,
                alt: btnTitle
            });
        }
    }
    // Set refresh image properties.
    if (this.isRefreshIcon) {
        if (this.refreshImage == null) {
            var btnTitle = this.theme.getMessage("Accordion.refresh");
            this.refreshImage = this.widget.getImageProps("ACCORDION_REFRESH", {
                id: this.id + "_refresh", 
                title: btnTitle,
                alt: btnTitle
            });
         }
    }
    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [className] CSS selector.
 * @config {Object} [collapseAllImage] 
 * @config {Object} [expandAllImage] 
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {boolean} [isRefreshIcon] 
 * @config {boolean} [loadOnSelect] 
 * @config {boolean} [multipleSelect] 
 * @config {Object} [refreshImage] 
 * @config {String} [style] Specify style rules inline.
 * @config {Array} [tabs] 
 * @config {boolean} [toggleControls]
 * @config {String} [type] 
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordion.prototype.setProps = function(props, notify) {
    if (props == null) {
        return false;
    }

    // Replace tabs -- do not extend.
    if (props.tabs) {
        this.tabs = null;
    }

    // Extend widget object for later updates.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.accordion.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // add control icons - refresh, expandall, collapseall.
    this.addControls(props); 

    // If we are coming here for
    // the first time there will be no children. The other case is when 
    // the accordion is being rerendered because of a refresh in which 
    // we want to use the latest set of children. addFragment is supposed
    // to do that.
    if (props.tabs) {
        // Remove child nodes.
        this.widget.removeChildNodes(this.tabsContainer);

        // Add tabs.
        for (var i = 0; i < props.tabs.length; i++) {
            this.widget.addFragment(this.tabsContainer, props.tabs[i], "last");
        }
    }

    // Set more properties.
    this.setCommonProps(this.domNode, props);
    this.setEventProps(this.domNode, props);

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}

/**
 * Process tab selected events.
 *
 * @param props Key-Value pairs of properties.
 * @config {String} [id] The id of the selected tab.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.accordion.prototype.tabSelected = function(props) {
    var widget = null;

    // Iterate over all tabs to ensure id is valid.
    for (var i = 0; i < this.tabs.length; i++) {
        if (props.id == this.tabs[i].id) {
            widget = dijit.byId(this.tabs[i].id);
            break;   
        }
    }
    
    // Return if id was not valid.
    if (widget == null) {
        return false;
    }

    if (this.multipleSelect) {
        widget.setSelected(true);
    } else {
        for (var i = 0; i < this.tabs.length; i++) {
            widget = dijit.byId(this.tabs[i].id);
            if (widget) {
                widget.setSelected(props.id == this.tabs[i].id);
            }
        }
    }
    return true;
}
dojo.provide("webui.suntheme.widget.alarm");


/**
 * @name webui.suntheme.widget.alarm
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for the alarm widget.
 * @constructor This function is used to construct an alarm widget.
 */
dojo.declare("webui.suntheme.widget.alarm", webui.suntheme.widget.widgetBase, {
    widgetName: "alarm" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.alarm.event =
        webui.suntheme.widget.alarm.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_alarm_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_alarm_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_alarm_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_alarm_event_state_end"
    }
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.alarm.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.text != null) { props.text = this.text; }
    if (this.indicators != null) { props.indicators = this.indicators; }
    if (this.textPosition != null) { props.textPosition = this.textPosition; }
    if (this.type != null) { props.type = this.type; }
    
    return props;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.alarm.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {
        this.rightText.id = this.id + "_rightText";
        this.leftText.id = this.id + "_leftText";
        this.imageContainer.id = this.id + "_imageContainer";        
    }
    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [className] CSS selector.
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {Array} [indicators] 
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {String} [style] Specify style rules inline.
 * @config {String} [text] 
 * @config {String} [textPosition]
 * @config {String} [title] Provides a title for element.
 * @config {String} [type] Provides a title for element.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.alarm.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.alarm.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set properties.
    if (props.dir) { this.domNode.dir = props.dir; }
    if (props.lang) { this.domNode.lang = props.lang; }    
    
    // Set right text.
    if (props.textPosition == "right" || props.textPosition == null && props.text != null) {
        webui.suntheme.common.setVisibleElement(this.leftText, false);
        this.widget.addFragment(this.rightText, props.text);
    }

    // Set left text.
    if (props.textPosition == "left" && props.text != null) {
        webui.suntheme.common.setVisibleElement(this.rightText, false);
        this.widget.addFragment(this.leftText, props.text);
    }    
    
    // Set indicator properties.
    if (props.indicators || props.type != null && this.indicators) {
        // Iterate over each indicator.
        for (var i = 0; i < this.indicators.length; i++) {
            // Ensure property exists so we can call setProps just once.
            var indicator = this.indicators[i]; // get current indicator.
            if (indicator == null) {
                indicator = {}; // Avoid updating all props using "this" keyword.
            }

            // Set properties.
            indicator.image.visible = (indicator.type == this.type) ? true: false;

            // Update/add fragment.
            this.widget.updateFragment(this.imageContainer, indicator.image, "last");
        }
    }

    // Do not call setCommonProps() here. 

    // Set more properties.
    this.setEventProps(this.domNode, props);

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.alert");


/**
 * @name webui.suntheme.widget.alert
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for the alert widget.
 * @constructor This function is used to construct an alert widget.
 */
dojo.declare("webui.suntheme.widget.alert", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    widgetName: "alert" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.alert.event =
        webui.suntheme.widget.alert.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_alert_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_alert_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_alert_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_alert_event_state_end"
    }
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.alert.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.detail != null) { props.detail = this.detail; }
    if (this.indicators != null) { props.indicators = this.indicators; }
    if (this.summary != null) { props.summary = this.summary; }
    if (this.type != null) { props.type = this.type; }
    if (this.moreInfo != null) { props.moreInfo = this.moreInfo; }
    if (this.spacerImage != null) { props.spacerImage = this.spacerImage; }
    
    return props;
}

/**
 * This function is used to process notification events with Object
 * literals.
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [detail] Message detail text.
 * @config {String} [summary] Message summary text.
 * @config {boolean} [valid] Flag indicating validation state.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.alert.prototype.notify = function(props) {
    if (props == null) {
        return false;
    }
    return this.setProps({
        summary: props.summary,
        detail: props.detail,
        type: "error",
        visible: !props.valid
    });
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.alert.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {
        this.bottomLeftContainer.id = this.id + "_bottomLeftContainer";
        this.bottomMiddleContainer.id = this.id + "_bottomMiddleContainer";
        this.bottomRightContainer.id = this.id + "_bottomRightContainer";
        this.detailContainer.id = this.id + "_detailContainer";
        this.imageContainer.id = this.id + "_imageContainer";
        this.leftMiddleContainer.id = this.id + "_leftMiddleContainer";
        this.rightMiddleContainer.id = this.id + "_rightMiddleContainer";
        this.summaryContainer.id = this.id + "_summaryContainer";
        this.topLeftContainer.id = this.id + "_topLeftContainer";
        this.topMiddleContainer.id = this.id + "_topMiddleContainer";
        this.topRightContainer.id = this.id + "_topRightContainer";
        this.detailContainerLink.id = this.id + "_detailContainerLink";
    }
    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [className] CSS selector.
 * @config {String} [detail]
 * @config {String} [dir] Specifies the directionality of text.
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {Array} [indicators] 
 * @config {String} [moreInfo] 
 * @config {String} [spacerImage] 
 * @config {String} [summary] 
 * @config {String} [type] 
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.alert.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.alert.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set properties.
    if (props.dir) { this.domNode.dir = props.dir; }
    if (props.lang) { this.domNode.lang = props.lang; }    
    
    // Set summary.
    if (props.summary) {
        this.widget.addFragment(this.summaryContainer, props.summary);
    }

    // Set detail.
    if (props.detail) {
        this.widget.addFragment(this.detailContainer, props.detail);
    }

    // Set moreInfo.
    if (props.moreInfo) {
        this.widget.addFragment(this.detailContainerLink, props.moreInfo);
    }

    // Set spacer image.
    if (props.spacerImage) {
        var containers = [
            this.bottomLeftContainer,
            this.bottomMiddleContainer,
            this.bottomRightContainer,
            this.leftMiddleContainer,
            this.rightMiddleContainer,
            this.topLeftContainer,
            this.topMiddleContainer,
            this.topRightContainer];

        // Avoid widget ID collisions.
        for (var i = 0; i < containers.length; i++) {
            if (typeof props != 'string') {
                props.spacerImage.id = this.id + "_spacerImage" + i;
            }
            // Replace container with image.
            if (!dijit.byId(props.spacerImage.id)) {
                this.widget.addFragment(containers[i], props.spacerImage);
            }
        }
    }

    // Set indicator properties.
    if (props.indicators || props.type != null && this.indicators) {
        // Iterate over each indicator.
        for (var i = 0; i < this.indicators.length; i++) {
            // Ensure property exists so we can call setProps just once.
            var indicator = this.indicators[i]; // get current indicator.
            if (indicator == null) {
                indicator = {}; // Avoid updating all props using "this" keyword.
            }

            // Set properties.
            indicator.image.visible = (indicator.type == this.type) ? true: false;
            indicator.image.tabIndex = this.tabIndex;

            // Update/add fragment.
            this.widget.updateFragment(this.imageContainer, indicator.image, "last");
        }
    }

    // Do not call setCommonProps() here. 

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.anchorBase");


/**
 * @name webui.suntheme.widget.anchorBase
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for widgets that extend anchorBase.
 * @static
 */
dojo.declare("webui.suntheme.widget.anchorBase", webui.suntheme.widget.widgetBase);

/**
 * Helper function to add children.
 *
 * @param props Key-Value pairs of properties.
 * @config {Array} [contents] The contents of the anchor body.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.anchorBase.prototype.addContents = function(props) {
    if (props.contents == null) {
        return false;
    }

    // Remove child nodes.
    this.widget.removeChildNodes(this.domNode);

    // Add contents.
    for (i = 0; i < props.contents.length; i++) {
        this.widget.addFragment(this.domNode, props.contents[i], "last");
    }
    return true;
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.anchorBase.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.hrefLang) { props.hrefLang = this.hrefLang; }
    if (this.target) { props.target = this.target; }
    if (this.type) { props.type = this.type; }
    if (this.rev) { props.rev = this.rev; }
    if (this.rel) { props.rel = this.rel; }
    if (this.shape) { props.shape = this.shape; }
    if (this.coords) { props.coords = this.coords; }
    if (this.charset) { props.charset = this.charset; }
    if (this.accessKey) { props.accesskey = this.accessKey; }
    if (this.href) { props.href = this.href; }
    if (this.name) { props.name = this.name; } 
    if (this.contents) { props.contents = this.contents; }
    if (this.disabled != null) { props.disabled = this.disabled; }

    return props;
}

/**
 * Helper function to create callback for onClick event.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.anchorBase.prototype.onClickCallback = function(event) {
    if (this.disabled == true) {
        event.preventDefault();
        return false;
    }

    // If function returns false, we must prevent the request.
    var result = (this.domNode._onclick)
        ? this.domNode._onclick(event) : true;
    if (result == false) {
        event.preventDefault();
        return false;
    }
    return true;
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [accessKey]
 * @config {String} [charset]
 * @config {String} [className] CSS selector.
 * @config {Array} [contents]
 * @config {String} [coords]
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [href]
 * @config {String} [hrefLang]
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {String} [name] 
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {String} [rel]
 * @config {String} [rev]
 * @config {String} [shape]
 * @config {String} [style] Specify style rules inline.
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.anchorBase.prototype.setProps = function(props, notify) {
    if (props == null) {
        return false;
    }

    // Replace contents -- do not extend.
    if (props.contents) {
        this.contents = null;
    }

    // Extend widget object for later updates.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.anchorBase.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Add contents.
    this.addContents(props);

    // Set properties.
    if (props.accessKey) { this.domNode.accesskey = props.accessKey; }
    if (props.charset) { this.domNode.charset = props.charset; }
    if (props.coords) { this.domNode.coords = props.coords; }
    if (props.href) { this.domNode.href = props.href }
    if (props.hrefLang) { this.domNode.hrefLang =  props.hrefLang; }
    if (props.name) { this.domNode.name = props.name; }
    if (props.rev) { this.domNode.rev = props.rev; }
    if (props.rel) { this.domNode.rel = props.rel; }
    if (props.shape) { this.domNode.shape = props.shape; }
    if (props.target) { this.domNode.target = props.target; }
    if (props.type) { this.domNode.type = props.type; }

    // Set id -- anchors must have the same id and name on IE.
    if (props.name) {
        props.id = props.name;
    }

    // A web app devleoper could return false in order to cancel the 
    // submit. Thus, we will handle this event via the onClick call back.
    if (props.onClick) {
        // Set private function scope on DOM node.
        this.domNode._onclick = (typeof props.onClick == 'string')
            ? new Function("event", props.onClick) : props.onClick;

        // Must be cleared before calling setEventProps() below.
        props.onClick = null;
    }

    // Set more properties.
    this.setCommonProps(this.domNode, props);
    this.setEventProps(this.domNode, props);

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.anchor");


/**
 * @name webui.suntheme.widget.anchor
 * @extends webui.suntheme.widget.anchorBase
 * @class This class contains functions for the anchor widget.
 * @constructor This function is used to construct an anchor widget.
 */
dojo.declare("webui.suntheme.widget.anchor", webui.suntheme.widget.anchorBase, {
    // Set defaults.
    widgetName: "anchor" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.anchor.event =
        webui.suntheme.widget.anchor.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_anchor_event_refresh_begin",
        
        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_anchor_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_anchor_event_state_begin",
        
        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_anchor_event_state_end"
    }
}

/**
 * This function is used to obtain the outermost HTML element class name.
 * <p>
 * Note: Selectors should be concatinated in order of precedence (e.g., the 
 * user's className property is always appended last).
 * </p>
 * @return {String} The outermost HTML element class name.
 */
webui.suntheme.widget.anchor.prototype.getClassName = function() {
    // Set default style.
    var className = (this.href && this.disabled == false)
        ? this.widget.getClassName("ANCHOR","")
        : this.widget.getClassName("ANCHOR_DISABLED","");

    return (this.className)
        ? className + " " + this.className
        : className;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.anchor.prototype.postCreate = function () {
    // Create callback function for onclick event.
    dojo.connect(this.domNode, "onclick", this, "onClickCallback");

    return this.inherited("postCreate", arguments);
}
dojo.provide("webui.suntheme.widget.bubble");


/**
 * @name webui.suntheme.widget.bubble
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for the bubble widget.
 * @constructor This function is used to construct a bubble widget.
 */
dojo.declare("webui.suntheme.widget.bubble", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    defaultTime: 2000,
    openDelayTime: 500,
    bubbleLeftConst: 5,
    topConst: 2,    
    widgetName: "bubble" // Required for theme properties.
});

/**
 * This function is used to close bubble help.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.bubble.prototype.close = function() {
    if (this.openTimerId != null) {
        clearTimeout(this.openTimerId);
    }
    if (this.getProps().visible == false) {
        return false;
    }
     
    var _id = this.id;
    this.timerId = setTimeout(function() {
        // New literals are created every time this function is called, and it's 
        // saved by closure magic.
        dijit.byId(_id).setProps({visible: false});
    }, this.defaultTime);

    return true;
}

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.bubble.event =
        webui.suntheme.widget.bubble.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_bubble_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_bubble_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_bubble_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_bubble_event_state_end"
    }
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.bubble.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.title != null) { props.title = this.title; }
    if (this.contents != null) { props.contents = this.contents; }
    if (this.height != null) { props.height = this.height; }
    if (this.width != null) { props.width = this.width; }
    if (this.autoClose != null) { props.autoClose = this.autoClose; }
    if (this.duration != null) { props.duration = this.duration; }
    if (this.closeButton != null) {props.closeButton = this.closeButton;}
    if (this.openDelay != null) {props.openDelay = this.openDelay;}
    
    return props;
}

/**
 * Helper function to create callback for onClick event.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.bubble.prototype.onClickCallback = function(event) {
    // Close the popup if close button is clicked.
    event = this.widget.getEvent(event);

    var target = (event.target)
        ? event.target 
        : ((event.srcElement) 
            ? event.srcElement : null);

    if (webui.suntheme.browser.isIe5up()) {
        if (window.event != null) {
            window.event.cancelBubble = true;
        }
    } else {
        event.stopPropagation();
    }
    if (this.closeBtn == target) {
        clearTimeout(this.timerId);
        this.setProps({visible: false});
    }
    return true;
}

/**
 * Helper function to create callback for close event.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.bubble.prototype.onCloseCallback = function(event) {
    if (event == null) {
        return false;
    }
    if ((event.type == "keydown" && event.keyCode == 27)
            || event.type == "click") {
        clearTimeout(this.timerId); 
        this.setProps({visible: false});  
    }
    return true;
}

/**
 * Helper function to create callback for onMouseOver event.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.bubble.prototype.onMouseOverCallback = function(event) {
    clearTimeout(this.timerId);
    return true;
}

/**
 * Helper function to create callback for onMouseOut event.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.bubble.prototype.onMouseOutCallback = function(event) {
    if (this.autoClose == true) {
        clearTimeout(this.timerId);            
        this.close();            
    }
    return true;
}

/**
 * This function is use to invoke buuble help.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.bubble.prototype.open = function(event) {
    // Get the absolute position of the target.
    var evt = this.widget.getEvent(event);

    this.target = (evt.target) 
        ? evt.target : ((evt.srcElement) 
            ? evt.srcElement : null);

    var absPos = this.widget.getPosition(this.target);
    this.target.targetLeft = absPos[0];
    this.target.targetTop = absPos[1];
   
    if (this.timerId != null) {
        clearTimeout(this.timerId);
        this.timerId = null;
    }
    
    if (this.openDelay != null && this.openDelay >= 0) {
        this.openDelayTime = this.openDelay;
    }

    // There should be delay before opening the bubble if open delay is specified.
    // If openDelay is less than zero then there will be dafault 0.5 sec delay.  
    
    var id = this.id; // Closure magic.
    this.openTimerId = setTimeout(function() {
        // Store the active bubble id to form element.
        // Check for the id if its available then close the pending bubble.
        if (webui.suntheme.widget.bubble.activeBubbleId && webui.suntheme.widget.bubble.activeBubbleId != id) {                
            clearTimeout(dijit.byId(webui.suntheme.widget.bubble.activeBubbleId).timerId);
            dijit.byId(webui.suntheme.widget.bubble.activeBubbleId).setProps({visible: false});
            webui.suntheme.widget.bubble.activeBubbleId = null;                
        }     
        webui.suntheme.widget.bubble.activeBubbleId = id;            
        dijit.byId(id).setProps({visible: true});
        dijit.byId(id).setPosition();
    }, this.openDelayTime);           
    
    if (this.duration != null && this.duration >= 0) {
        this.defaultTime = duration;
    } 
    return true;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.bubble.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {
        this.bottomLeftArrow.id = this.id + "_bottomLeftArrow";
        this.bottomRightArrow.id = this.id + "_bottomRightArrow";
        this.topLeftArrow.id = this.id + "_topLeftArrow";
        this.topRightArrow.id = this.id + "_topRightArrow";
    }

    // Set public functions.
    this.domNode.close = function() { return dijit.byId(this.id).close(); }
    this.domNode.open = function(event) { return dijit.byId(this.id).open(event); }

    // Set events.

    // The onClick on window should close bubble.
    dojo.connect(document, "onclick", this, "onCloseCallback");

    // The escape key should also close bubble.
    dojo.connect(document, "onkeydown", this, "onCloseCallback");

    // The onClick event for component body. Closes the bubble only when
    // close button is clicked.
    dojo.connect(this.domNode, "onclick", this, "onClickCallback");

    // Do not close the popup if mouseover on bubble if mouseover on bubble 
    // component then clear the timer and do not close bubble.
    dojo.connect(this.domNode, "onmouseover", this, "onMouseOverCallback");

    // Close the popup if mouseout and autoClose is true if onmouseout and 
    // autoClose is true then close the bubble.
    dojo.connect(this.domNode, "onmouseout", this, "onMouseOutCallback");

    // Initialize the BubbleTitle width as a percentage of the bubble header.
        
    if (this.bubbleTitle != null) {
        this.bubbleTitle.style.width = this.theme.getProperty("styles", 
            "BUBBLE_TITLEWIDTH") + "%";
    }
    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to position the bubble.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.bubble.prototype.setPosition = function() {
    
    // THIS CODE BLOCK IS NECESSARY WHEN THE PAGE FONT IS VERY SMALL,
    // AND WHICH OTHERWISE CAUSES THE PERCENTAGE OF THE HEADER WIDTH
    // ALLOCATED TO THE BUBBLE TITLE TO BE TOO LARGE SUCH THAT IT
    // ENCROACHES ON THE SPACE ALLOCATED FOR THE CLOSE BUTTON ICON,
    // RESULTING IN LAYOUT MISALIGNMENT IN THE HEADER.

    // Assume BubbleTitle width max percentage of the bubble header.
    var maxPercent = this.theme.getProperty("styles", "BUBBLE_TITLEWIDTH");

    // Sum of widths of all elements in the header BUT the title.  This includes
    // the width of the close button icon, and the margins around the button and
    // the title.  This should be a themeable parameter that matches the left/right
    // margins specified in the stylesheet for "BubbleTitle" and "BubbleCloseBtn".
    var nonTitleWidth = this.theme.getProperty("styles", "BUBBLE_NONTITLEWIDTH");

    // Get the widths (in pixels) of the bubble header and title
    var headerWidth = this.bubbleHeader.offsetWidth;
    var titleWidth = this.bubbleTitle.offsetWidth;

    // Revise the aforementioned percentage downward until the title no longer
    // encroaches on the space allocated for the close button.  We decrement by
    // 5% each time because doing so in smaller chunks when the font gets very small so 
    // only results in unnecessary extra loop interations.
    //
    if (headerWidth > nonTitleWidth) {
        while ((maxPercent > 5) && (titleWidth > (headerWidth - nonTitleWidth))) {
            maxPercent -= 5;
            this.bubbleTitle.style.width = maxPercent + "%";
            titleWidth = this.bubbleTitle.offsetWidth;
        }
    }

    // Get DOM bubble object associated with this Bubble instance.
    var bubble = this.domNode;

    // If this.style is not null that means developer has specified positioning
    // for component. 
    if (this.domNode != null && this.style != null) {
        if (bubble.style.length != null) {
            for (var i = 0; i < bubble.style.length; i++) {
                if (bubble.style[i] == "top") {
                    this.top = bubble.style.top;
                }
                if (bubble.style[i] == "left") {
                    this.left = bubble.style.left;
                }
            }
        } else {
            // For IE, simply query the style attributes.
            if (bubble.style.top != "") {
                this.top = bubble.style.top;
            }
            if (bubble.style.left != "") {
                this.left = bubble.style.left;
            }
        }
    }

    if ((this.top != null) && (this.left != null)) {
        bubble.style.left = this.left;
        bubble.style.top = this.top;    
    } else {        

        var topLeftArrow = document.getElementById(this.topLeftArrow.id);
        var topRightArrow = document.getElementById(this.topRightArrow.id);
        var bottomLeftArrow = document.getElementById(this.bottomLeftArrow.id);
        var bottomRightArrow = document.getElementById(this.bottomRightArrow.id);
        // hide all callout arrows.
        webui.suntheme.common.setVisible(bottomLeftArrow, false);
        webui.suntheme.common.setVisible(bottomRightArrow, false);
        webui.suntheme.common.setVisible(topLeftArrow, false);
        webui.suntheme.common.setVisible(topRightArrow, false);

        bottomLeftArrow.style.display = "none";
        bottomRightArrow.style.display = "none";
        topLeftArrow.style.display = "none";
        topRightArrow.style.display = "none";

        var slidLeft = false;

        // Assume default bubble position northeast of target, which implies a 
        // bottomLeft callout arrow
        this.arrow = bottomLeftArrow;

        // Try to position bubble to right of target.
        var bubbleLeft = this.target.targetLeft + this.target.offsetWidth + this.bubbleLeftConst;

        // Check if right edge of bubble exceeds page boundary.
        var rightEdge = bubbleLeft + bubble.offsetWidth;
        if (rightEdge > this.widget.getPageWidth()) {

            // Shift bubble to left side of target;  implies a bottomRight arrow.
            bubbleLeft = this.target.targetLeft - bubble.offsetWidth;
            this.arrow = bottomRightArrow;
            slidLeft = true;

            // If left edge of bubble crosses left page boundary, then
            // reposition bubble back to right of target and implies to go
            // back to bottomLeft arrow.  User will need to use scrollbars
            // to position bubble into view.
            if (bubbleLeft <= 0) {
                bubbleLeft = this.target.targetLeft + this.target.offsetWidth + this.bubbleLeftConst;
                this.arrow = bottomLeftArrow;
                slidLeft = false;
            }
        }

        // Try to position bubble above target
        var bubbleTop = this.target.targetTop - bubble.offsetHeight;

        // Check if top edge of bubble crosses top page boundary
        if (bubbleTop <= 0) {
            // Shift bubble to bottom of target.  User may need to use scrollbars
            // to position bubble into view.
            bubbleTop = this.target.targetTop + this.target.offsetHeight + this.bubbleLeftConst;

            // Use appropriate top arrow depending on left/right position.
            if (slidLeft == true)
                this.arrow = topRightArrow;
            else
                this.arrow = topLeftArrow;
        }

        // Set new bubble position.
        bubble.style.left = bubbleLeft + "px";
        bubble.style.top = bubbleTop + "px";

        // If rendering a callout arrow, set it's position relative to the bubble.
        if (this.arrow != null) {
           this.arrow.style.display = "block";
           webui.suntheme.common.setVisible(this.arrow, true);

           if (this.arrow == topLeftArrow) {
               this.arrow.style.top = -(bubble.offsetHeight - this.topConst) + "px";               
           }
           if (this.arrow == topRightArrow) {
               this.arrow.style.top = -(bubble.offsetHeight - this.topConst) + "px";               
           }
        }
    }
    return true;
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {boolean} [autoClose] 
 * @config {Object} [closeButton] 
 * @config {Array} [contents] 
 * @config {int} [duration] 
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {int} [openDelay] 
 * @config {String} [title] Provides a title for element.
 * @config {int} [width] 
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.bubble.prototype.setProps = function(props, notify) {
    if (props == null) {
        return false;
    }

    // Replace contents -- do not extend.
    if (props.contents) {
        this.contents = null;
    }

    // Extend widget object for later updates.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.bubble.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }
        
    // Set title.
    if (props.title) {
        this.widget.addFragment(this.titleNode, props.title);
    }

    // hide/display close button
    if (props.closeButton != null) {
        var classNames = this.closeBtn.className.split(" ");
        var closeButtonClass = this.theme.getClassName("BUBBLE_CLOSEBTN");
        var noCloseButtonClass = this.theme.getClassName("BUBBLE_NOCLOSEBTN");

        if (props.closeButton == false) {
            webui.suntheme.common.stripStyleClass(this.closeBtn, closeButtonClass);
            if (!webui.suntheme.common.checkStyleClasses(classNames, noCloseButtonClass))
             webui.suntheme.common.addStyleClass(this.closeBtn, noCloseButtonClass);
        } else {          
          if (!webui.suntheme.common.checkStyleClasses(classNames, closeButtonClass))
             webui.suntheme.common.addStyleClass(this.closeBtn, closeButtonClass);
        }
    }

    // Set width.
    if (props.width > 0) {                    
        this.domNode.style.width = props.width;        
    }

    // Set contents.
    if (props.contents) {
        // Remove child nodes.
        this.widget.removeChildNodes(this.childNode);

        for (var i = 0; i < props.contents.length; i++) {
            this.widget.addFragment(this.childNode, props.contents[i], "last");
        }
    }

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.button");

 
/**
 * @name webui.suntheme.widget.button
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for the button widget.
 * @constructor This function is used to construct a button widget.
 */
dojo.declare("webui.suntheme.widget.button", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    disabled: false,
    escape: true,
    mini: false,
    primary: true,
    widgetName: "button" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.button.event =
        webui.suntheme.widget.button.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_button_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_button_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_button_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_button_event_state_end"
    }
}

/**
 * This function is used to obtain the outermost HTML element class name.
 * <p>
 * Note: Selectors should be concatinated in order of precedence (e.g., the 
 * user's className property is always appended last).
 * </p>
 * @return {String} The outermost HTML element class name.
 */
webui.suntheme.widget.button.prototype.getClassName = function() {
    var key = null;

    if (this.mini == true && this.primary == true) {
        key = (this.disabled == true)
            ? "BUTTON1_MINI_DISABLED" // primaryMiniDisabledClassName
            : "BUTTON1_MINI";         // primaryMiniClassName;
    } else if (this.mini == true) {
        key = (this.disabled == true)
            ? "BUTTON2_MINI_DISABLED" // secondaryMiniDisabledClassName
            : "BUTTON2_MINI";         // secondaryMiniClassName;
    } else if (this.primary == true) {
        key = (this.disabled == true)
            ? "BUTTON1_DISABLED"      // primaryDisabledClassName
            : "BUTTON1";              // primaryClassName
    } else {
        key = (this.disabled == true)
            ? "BUTTON2_DISABLED"      // secondaryDisabledClassName
            : "BUTTON2";	      // secondaryClassName
    }

    var className = this.widget.getClassName(key, "");
    return (this.className)
        ? className + " " + this.className
        : className;
}

/**
 * This function is used to obtain the outermost HTML element class name during
 * an onFocus or onMouseOver event.
 * <p>
 * Note: Selectors should be concatinated in order of precedence (e.g., the 
 * user's className property is always appended last).
 * </p>
 * @return {String} The outermost HTML element class name.
 */
webui.suntheme.widget.button.prototype.getHoverClassName = function() {
    var key = null;

    if (this.mini == true && this.primary == true) {
        key = "BUTTON1_MINI_HOVER"; 	// primaryMiniHovClassName;
    } else if (this.mini == true) {
        key = "BUTTON2_MINI_HOVER"; 	// secondaryMiniHovClassName;
    } else if (this.primary == true) {
        key = "BUTTON1_HOVER"; 		// primaryHovClassName;
    } else {
        key = "BUTTON2_HOVER";		// secondaryHovClassName;
    }

    var className = this.widget.getClassName(key, "");
    return (this.className)
        ? className + " " + this.className
        : className;
}
    
/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.button.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.alt) { props.alt = this.alt; }
    if (this.align) { props.align = this.align; }
    if (this.disabled != null) { props.disabled = this.disabled; }
    if (this.escape != null) { props.escape = this.escape; }
    if (this.mini != null) { props.mini = this.mini; }
    if (this.primary != null) { props.primary = this.primary; }
    if (this.value) { props.value = this.value; }

    return props;
}

/**
 * Helper function to create callback for onBlur event.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.button.prototype.onBlurCallback = function(event) {
    if (this.disabled == true) {
        return true;
    }
    // Prevent errors during page submit, when modules have not been loaded.
    try {
        // Set style class.
        this.domNode.className = this.getClassName();
    } catch (err) {}
    return true;
}

/**
 * Helper function to create callback for onFocus event.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.button.prototype.onFocusCallback = function(event) {
    if (this.disabled == true) {
        return true;
    }
    // Prevent errors during page submit, when modules have not been loaded.
    try {
        // Set style class.
        this.domNode.className = this.getHoverClassName();
    } catch (err) {}
    return true;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.button.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {
        this.domNode.name = this.id;
    }

    // Initialize the deprecated functions in formElements.js. 
    // 
    // Note: Although we now have a setProps function to update properties,
    // these functions were previously added to the DOM node; thus, we must
    // continue to be backward compatible.
    webui.suntheme.button.init({id: this.id});

    // Set events.
    dojo.connect(this.domNode, "onblur", this, "onBlurCallback");
    dojo.connect(this.domNode, "onfocus", this, "onFocusCallback");
    dojo.connect(this.domNode, "onmouseout", this, "onBlurCallback");
    dojo.connect(this.domNode, "onmouseover", this, "onFocusCallback");

    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [alt] Alternate text for image input.
 * @config {String} [align] Alignment of image input.
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [escape] HTML escape value (default).
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {boolean} [mini] Set button as mini if true.
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {boolean} [primary] Set button as primary if true.
 * @config {String} [style] Specify style rules inline.
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {String} [value] Value of input.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.button.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.button.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set properties.
    if (props.alt) { this.domNode.alt = props.alt; }
    if (props.align) { this.domNode.align = props.align; }

    // Set disabled.
    if (props.disabled != null) { 
        this.domNode.disabled = new Boolean(props.disabled).valueOf();
    }

    // Set value (i.e., button text).
    if (props.value) {
        // If escape is true, we want the text to be displayed literally. To 
        // achieve this behavior, do nothing.
        //
        // If escape is false, we want any special sequences in the text 
        // (e.g., "&nbsp;") to be displayed as evaluated (i.e., unescaped).
        this.domNode.value = (new Boolean(this.escape).valueOf() == false)
            ? props.value.unescapeHTML() // Prototype method.
            : props.value;
    }

    // Set more properties.
    this.setCommonProps(this.domNode, props);
    this.setEventProps(this.domNode, props);

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.calendar");


/**
 * @name webui.suntheme.widget.calendar
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for the calendar widget.
 * @constructor This function is used to construct a calendar widget.
 */
dojo.declare("webui.suntheme.widget.calendar", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    widgetName: "calendar" // Required for theme properties.
});

/**
 * Helper function to add a day link in a cell.
 *
 * @param {Node} rowNodeClone
 * @param {Object} day
 * @param {String} id
 * @param {String} className
 * @param {boolean} setFocus
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendar.prototype.addDayLink = function(rowNodeClone, day, 
        id, className, setFocus) {
    // Clone <td> and <a> elements. 
    var colNodeClone = this.dayColumnContainer.cloneNode(false);
    rowNodeClone.appendChild(colNodeClone);    
    var linkNodeClone = this.dayLinkContainer.cloneNode(false);            
    colNodeClone.appendChild(linkNodeClone);
    
    // Format the date.      
    var formattedDate = this.formatDate(day.getMonth() + 1, day.getDate(), day.getFullYear()); 
  
    // set the link's properties for this day.
    linkNodeClone.title = formattedDate;
    linkNodeClone.id = id;
    linkNodeClone.href = "#";
    linkNodeClone.className = className;

    // NOTE: If you set this value manually, text must be HTML escaped.
    this.widget.addFragment(linkNodeClone, "" + day.getDate());

    var widgetId = this.id;
    linkNodeClone.onclick = function() { 
        dijit.byId(widgetId).daySelected(formattedDate); 
        return false;
    };  
    
    // If the setFocus is set to true, then when you tab out of the linkNode,
    // the focus should go to the close button. 
    if (setFocus) {
        var node = dijit.byId(linkNodeClone.id);        
        linkNodeClone.onkeydown = function(event) {
            var widget = dijit.byId(widgetId);
            
            // Get hold of the close button and set focus on it.
            var evt = (event) ? event : ((window.event) ? window.event : null);
            if (evt.keyCode == 9) {
                var elem = document.getElementById(widget.closeButtonLink.id);
                if (elem != null) {
                    if (elem.focus) {
                        elem.focus();
                    }
                    if (evt.preventDefault) {
                        evt.preventDefault();
                    } else {
                        evt.returnValue = false;
                    }
                    return false;                                  
                }
            }
            return true;
        }
    }
    return true;
}  

/**
 * Helper function to add days in the month -- week data rows.
 *
 * @param {Object} currentValue The current value of the text field.
 * @param {boolean} initialize Flag indicating to initialze the year and month menus
 * with the current value. The value is true only when the calendar is opened.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendar.prototype.addDaysInMonth = function(currentValue, initialize) {
    // Date representing a day in a month.
    var day;    
    // Number of columns in a row.
    var column = 0;
    // Row number.    
    var rowNum = 0;    
    // Today's day.
    var today = 0;
    // Selected date.
    var selected = 0;     
    // Day link number
    var linkNum = 0; 
    // Prefix used for a day link id.
    var id = this.id + "_link:";
    // Day link id. ie, id + linkNum.
    var linkId;
    // One day in milliseconds -- 1000 * 60 * 60 * 24    
    var oneDayInMs = 86400000;     

    var todayDate = new Date();
    var todayYear = todayDate.getFullYear();
    var todayMonth = todayDate.getMonth() + 1;
    var todayDay = todayDate.getDate();                  
    
    // selectedYear, selectedMonth, selectedDay:
    // The date to show as highlighted (currentValue) provided
    // that the user is viewing that month and year.
    var selectedYear = null;
    var selectedMonth = null;
    var selectedDay = null;
    if (currentValue != null) {
        selectedYear = currentValue.getFullYear();
        selectedMonth = currentValue.getMonth() + 1;
        selectedDay = currentValue.getDate();
    }
    
    // Get month and year menu widgets.
    var monthMenuWidget = dijit.byId(this.monthMenu.id);        
    var yearMenuWidget = dijit.byId(this.yearMenu.id);
    if (monthMenuWidget == null || yearMenuWidget == null) {
        return;
    }
               
    if (initialize) {
         // Set showMonth as selected in the month menu
	 // Set showYear as selected in the year menu
         // Use todayMonth and todayYear if currentValue is null.
	 var showMonth = todayMonth;
	 var showYear = todayYear;
	 if (currentValue != null) {
             // We have a currentValue, so use that for showMonth and showYear
             showMonth = selectedMonth;
	     showYear = selectedYear;
         }         
         this.setLimitedSelectedValue(monthMenuWidget.getSelectElement(), showMonth);
         this.setLimitedSelectedValue(yearMenuWidget.getSelectElement(), showYear);
    }
    
    var month = parseInt(monthMenuWidget.getSelectedValue());
    var year = parseInt(yearMenuWidget.getSelectedValue());
    
    //set selected
    if (currentValue != null && selectedYear == year && selectedMonth == month) {
        selected = selectedDay;
    }
        
    //set today
    if (todayYear == year && todayMonth == month) {
        today = todayDay;
    }
    
    // Add first week data row.
    var rowNodeClone = this.weekRowContainer.cloneNode(false);
    this.tbodyContainer.appendChild(rowNodeClone); 
    rowNodeClone.id = this.id + ":row" + rowNum;
    
    // Convert to javascript month numbering.
    month--;
    
    // Calculate the first of the main month to display in "first" row.
    var first = new Date(year, month, 1);                         
    var firstDay = first.getDay();    
    var className = this.theme.getClassName("DATE_TIME_OTHER_LINK");
    if (firstDay == this.firstDayOfWeek - 1) {
        // First cell on first row is the first of the current month
        day = first;
    } else {
        // First cell on first row is in previous month.
        var backDays = (firstDay - (this.firstDayOfWeek - 1) + 7) % 7;        
        
        // Calculate the date of first cell on first row in previous month.
        day = new Date(first.getTime() - backDays * oneDayInMs);        
        
        // Generate start of first row up to first of month
        while (day.getMonth() !=  first.getMonth()) {
            linkId = id + linkNum;
            this.addDayLink(rowNodeClone, day, linkId, className);
            day = new Date(day.getTime() + oneDayInMs);
            column++;
            linkNum++;            
        }
    }

    // Add any cells in the first row of the main month.
    while (column < 7) {
        // Set appropriate class name.
        if (day.getDate() == selected) {
            className = this.theme.getClassName("DATE_TIME_BOLD_LINK");
        } else if (day.getDate() == today) {
            className = this.theme.getClassName("DATE_TIME_TODAY_LINK");
        } else {
           className = this.theme.getClassName("DATE_TIME_LINK");
        }
            
        linkId = id + linkNum;
        this.addDayLink(rowNodeClone, day, linkId, className);        
        day = new Date(day.getTime() + oneDayInMs);
        column++;
        linkNum++;
    } 
    
    // This variable is used to decide whether the focus should be set
    // on the calendar's close button when tabbing    
    var setFocus = false;            
    
    // Add intermediate rows
    while (day.getDate() != 1) {
        rowNum++;
        // Clone a <tr> node
        rowNodeClone = this.weekRowContainer.cloneNode(false);
        this.tbodyContainer.appendChild(rowNodeClone); 
        rowNodeClone.id = this.id + ":row" + rowNum;

        column = 0;
        while (column < 7 && day.getDate() != 1) {            
            // Set appropriate class name.
            if (day.getDate() == selected) {
                className = this.theme.getClassName("DATE_TIME_BOLD_LINK");
            } else if (day.getDate() == today) {
                className = this.theme.getClassName("DATE_TIME_TODAY_LINK");
            } else {
                className = this.theme.getClassName("DATE_TIME_LINK");
            }
                 
            linkId = id + linkNum;
            var tmpDate = new Date(day.getTime() + oneDayInMs);

            // On some platforms, the date is not incremented correctly (e.g.,
            // October 28th 1990, 2007, and 2012). In this case, try again.
            if (tmpDate.getDate() == day.getDate()) {
                tmpDate = new Date(tmpDate.getTime() + oneDayInMs);
            } 
            
            // Check whether this is the last date in the calendar and if so
            // set the setFocus variable to true. This will mean that when the
            // user tabs away from this day, the focus will be set on the
            // close button.
            if (tmpDate.getDate() == 1 && column == 6) {
                setFocus = true;
            } else {
                setFocus = false;
            }
            this.addDayLink(rowNodeClone, day, linkId, className, setFocus);
            day = tmpDate;
            column++;
            linkNum++;
        }
    }
    
    // Add any cells in the last row of the following month
    while (column < 7) {
        var className = this.theme.getClassName("DATE_TIME_OTHER_LINK");
        linkId = id + linkNum;
        
        // Check whether this is the last date in the calendar and if so
        // set the setFocus variable to true. This will mean that when the
        // user tabs away from this day, the focus will be set on the
        // close button.        
        if (column == 6) {
            setFocus = true;
        } else {
            setFocus = false;
        }
        this.addDayLink(rowNodeClone, day, linkId, className, setFocus);                    
        day = new Date(day.getTime() + oneDayInMs);
        column++;
        linkNum++;
    }
    return true;
}

/**
 * Helper function to add the week day headers row.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendar.prototype.addWeekDays = function() {            
    var colNodeClone;
    var spanNodeClone;    
    var firstDay = this.firstDayOfWeek - 1;
    
    // Clone the <tr> node and append it to <tbody>
    var rowNodeClone = this.weekDayRow.cloneNode(false);
    this.tbodyContainer.appendChild(rowNodeClone);
        
    for (var i = 0; i < 7; i++) {
        // Clone the <th> node and append it to <tr>
        colNodeClone = this.weekDayColumn.cloneNode(false);
        rowNodeClone.appendChild(colNodeClone)
               
        // Clone the <span> node and append it to <th>
        spanNodeClone = this.weekDayContainer.cloneNode(false);
        colNodeClone.appendChild(spanNodeClone);
        
        // NOTE: If you set this value manually, text must be HTML escaped.
        this.widget.addFragment(spanNodeClone, this.weekDays[firstDay]);

        firstDay++;
        if (firstDay == 7) {
            firstDay = 0;
        }     
    }
    return true;
}

/**
 * Close the calendar if the enter key is pressed and set the initial focus
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */            
webui.suntheme.widget.calendar.prototype.closeCalendar = function(event) {
    var evt = (event) ? event : ((window.event) ? window.event : null);
     
    // If key pressed and enter, then close the menu
    if ((evt.type == "keydown") && (evt.keyCode == 13)) {
        this.toggleCalendar();
        document.getElementById(this.toggleLink.id).focus();        
        return false;
    } 
    this.setInitialFocus();
    return true;    
}

/**
 * Process day selected event.
 * <p>
 * When a day link is selected, an event is published which the 
 * calendarField widget will use to update its text field.
 * </p>
 * @param {String} Formatted date string.
 * @return {boolean} false to cancel JavaScript event.
 */
webui.suntheme.widget.calendar.prototype.daySelected = function(formattedDate) {
    this.toggleCalendar();    
    dojo.publish(webui.suntheme.widget.calendar.event.day.selectedTopic, [{
        id: this.id,
        date:formattedDate
    }]);
    return false;
}

/**
 * This function is used to decrease the month by one.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendar.prototype.decreaseMonth = function() {
    var monthMenu = dijit.byId(this.monthMenu.id).getSelectElement();
    // If the monthMenu has no value, set it to January (that's what
    // it will have appeared like in the browser). Can happen on IE.  
    if (monthMenu.value == null) {
        monthMenu.value = monthMenu.options[0].value;
    }
    
    var month = parseInt(monthMenu.value);
    if (month == 1) {
        var yearMenu = dijit.byId(this.yearMenu.id).getSelectElement();        
         if (yearMenu.value == null) {
             // If the yearMenu has no value, set it to the first available year            
             // (that's what it will have appeared like in the browser). Can happen on IE.
             yearMenu.value = yearMenu.options[0].value;
         } else if (yearMenu.value == yearMenu.options[0].value) {
             // No need to update the calendar in this case,
             // we don't change anything.
             return;           
         } else {
             // Decrease the year by one and set the month to December
             var year = parseInt(yearMenu.value);
             year--;
             yearMenu.value = year;
             month = 12;
         }
    } else {
        month--;
    }
    monthMenu.value = month;    
    return this.updateMonth(false);
}

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.calendar.event =
        webui.suntheme.widget.calendar.prototype.event = {
    /**
     * This object contains day event topics.
     * @ignore
     */
    day: {
        /** Day event topic for custom AJAX implementations to listen for. */
        selectedTopic: "webui_suntheme_widget_calendar_event_selected"
    },

    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_calendar_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_calendar_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_calendar_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_calendar_event_state_end"
    },

    /**
     * This object contains toggle event topics.
     * @ignore
     */
    toggle: {
        /** Open event topic for custom AJAX implementations to listen for. */
        openTopic: "webui_suntheme_widget_calendar_event_toggle_open",

        /** Close event topic for custom AJAX implementations to listen for. */
        closeTopic: "webui_suntheme_widget_calendar_event_toggle_close"
    }
}

/**
 * Helper function to format the date.
 *
 * @param {String} month
 * @param {String} day
 * @param {String} year
 * @return {String} The date format.
 */
webui.suntheme.widget.calendar.prototype.formatDate = function(month, day, year) {
    var date = new String(this.dateFormat);      
    date = date.replace("yyyy", new String(year));
    if (month < 10) {
        date = date.replace("MM", "0" + new String(month));
    } else {
        date = date.replace("MM", new String(month));
    }
    if (day < 10) {
        date = date.replace("dd", "0" + new String(day));
    } else {
        date = date.replace("dd", new String(day));
    }
    return date;
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.calendar.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);
    
    // Set properties.
    if (this.todayDateMsg) { props.todayDateMsg = this.todayDateMsg; }
    if (this.spacerImage) { props.spacerImage = this.spacerImage; }
    if (this.topLeftImage) { props.topLeftImage = this.topLeftImage; }
    if (this.topRightImage) { props.topRightImage = this.topRightImage; }
    if (this.closeButtonLink) { props.closeButtonLink = this.closeButtonLink; }
    if (this.increaseLink) { props.increaseLink = this.increaseLink; }
    if (this.decreaseLink) { props.decreaseLink = this.decreaseLink; }
    if (this.monthMenu) { props.monthMenu = this.monthMenu; }
    if (this.yearMenu) { props.yearMenu = this.yearMenu; }   
    if (this.firstDayOfWeek) { props.firstDayOfWeek = this.firstDayOfWeek; }
    if (this.toggleLink) { props.toggleLink = this.toggleLink; }
    if (this.weekDays) { props.weekDays = this.weekDays; }    
    if (this.maxDate) { props.maxDate = this.maxDate; }
    if (this.minDate) { props.minDate = this.minDate; }
    
    return props;
}

/**
 * Workaround IE bug where popup calendar appears under other components.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendar.prototype.ieStackingContextFix = function() {
    var div = this.calendarContainer;
    if (div.style.display == "block") {
        // This popup should be displayed
        // Get the current zIndex for the div
        var divZIndex = div.currentStyle.zIndex;
        
        // Propogate the zIndex up the offsetParent tree
        var tag = div.offsetParent;
        while (tag != null) {
            var position = tag.currentStyle.position;
            if (position == "relative" || position == "absolute") {

                // Save any zIndex so it can be restored
                tag.raveOldZIndex = tag.style.zIndex;

                // Change the zIndex
                tag.style.zIndex = divZIndex;
            }
            tag = tag.offsetParent;
        }

        // Hide controls unaffected by z-index
        this.ieShowShim();
    } else {
        // This popup should be hidden so restore zIndex-s
        var tag = div.offsetParent;
        while (tag != null) {
            var position = tag.currentStyle.position;
            if (position == "relative" || position == "absolute") {
                if (tag.raveOldZIndex != null) {
                    tag.style.zIndex = tag.raveOldZIndex;
                }
            }
            tag = tag.offsetParent;
        }
        this.ieHideShim();
    }
    return true;
}

/**
 * Hides components unaffected by z-index.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendar.prototype.ieShowShim = function() {  
    var popup = this.calendarContainer;
    var shim = this.shimContainer;
    
    shim.style.position = "absolute";
    shim.style.left = popup.style.left;
    shim.style.top = popup.style.top;
    shim.style.width = popup.offsetWidth;
    shim.style.height = popup.offsetHeight;
    shim.style.zIndex = popup.currentStyle.zIndex - 1;
    shim.style.display = "block";

    return true;
}

/**
 * Hide the shim iframe.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendar.prototype.ieHideShim = function() {
    var shim = this.shimContainer;
    shim.style.display = "none";
    return true;
}   

/**
 * This function is used to increment the current month.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendar.prototype.increaseMonth = function() {            
    var monthMenu = dijit.byId(this.monthMenu.id).getSelectElement();          
    
    // If the monthMenu has no value, set it to January (that's what
    // it will have appeared like in the browser). Can happen on IE. 
    if (monthMenu.value == null) {
        monthMenu.value = monthMenu.options[0].value;
    }
    
    var month = parseInt(monthMenu.value);
    if (month == 12) {
        var yearMenu = dijit.byId(this.yearMenu.id).getSelectElement();
        var numOptions = yearMenu.options.length;
        if (yearMenu.value == null) {
            // If the yearMenu has no value, set it to the first available year            
            // (that's what it will have appeared like in the browser). Can happen on IE.
            yearMenu.value = yearMenu.options[0].value;
        } else if (yearMenu.value == yearMenu.options[numOptions-1].value) {
            // No need to update the calendar in this case,
            // we don't change anything.
            return;            
        } else {
            // Increase the year by one and set the month to January.
            var year = parseInt(yearMenu.value);
            year++;
            yearMenu.value = year;
            month = 1;
        }
    } else {
        month++;
    }
    monthMenu.value = month;   
    return this.updateMonth(false);    
}

/**
 * This function returns a JSON array of months to be displayed in the month 
 * drop down. 
 * return {Object} A JSON array of months.
 */
webui.suntheme.widget.calendar.prototype.getMonthOptions = function() {
    var monthMenu = new Array();
    
    // Get the number of months in a calendar year.
    // Some calendars may have more than 12 months a year.
    var numOfMonths = parseInt(this.theme.getMessage("calendar.numOfMonths"));
    
    for ( var i = 0; i < numOfMonths; i++ ) {
        monthMenu[i] = {};
        monthMenu[i].value = i+1;
        monthMenu[i].disabled = false;
        monthMenu[i].separator = false;
        monthMenu[i].escape = true;
        monthMenu[i].group = false;
        monthMenu[i].label=this.theme.getMessage("calendar."+i);
    }    
    return monthMenu;
}
/**
 * This function returns a JSON array of years to be displayed in the year
 * drop down
 * @param {String} minYear the minimum year of the calendar display
 * @param {String} maxYear the maximum year of the calendar display
 * @return {Object} A JSON array of calendar years.
 */
webui.suntheme.widget.calendar.prototype.getYearOptions = function(minYear, maxYear) {    
    var yearMenu =new Array();       
    var diff = maxYear - minYear;
    for ( var i = 0; i <= diff; i++ ) {
        yearMenu[i] = {};
        yearMenu[i].value = minYear;
        yearMenu[i].disabled = false;
        yearMenu[i].separator = false;
        yearMenu[i].escape = true;
        yearMenu[i].group = false;
        yearMenu[i].label=minYear;
        minYear++;
    }
    return yearMenu;
}
/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendar.prototype.postCreate = function () {
    // Set ids. 
    if (this.id) {
        this.calendarMenuContainer.id = this.id + "_calendarMenuContainer";
        this.linkNode.id = this.id + "_linkNodeContainer";
        this.todayDateContainer.id = this.id + "_todayDateContainer";
        this.closeButtonContainer.id = this.id + "_closeButtonContainer";
        this.previousLinkContainer.id = this.id + "_previousLinkContainer";
        this.monthMenuContainer.id = this.id + "_monthMenuContainer";
        this.nextLinkContainer.id = this.id + "_nextLinkContainer";
        this.yearMenuContainer.id = this.id + "_yearMenuContainer";
        this.shimContainer.id = this.id + "_shim";
    }

    // Create client side widgets for the calendar.
    // When the _setProps() function is called, these widgets will be
    // instantiated via the props param. 

    // If toggle link is null, create the image hyperlink.
    if (this.toggleLink == null) {
        this.toggleLink = this.widget.getImageHyperlinkProps({
                id: this.id + "_datePickerLink",
                contents: [],
                imagePosition: "left",
                title: this.theme.getMessage("calendar.popupImageAlt"),
                enabledImage: {
                    id: this.id + "_datePickerLink_image",
                    border:0
                },
                disabledImage:{
                    id: this.id + "_datePickerLink_image_disabled", 
                    border:0
                },
                align:"middle"
            },            
            "CALENDAR_BUTTON",
            "CALENDAR_BUTTON_DISABLED"
        );
    }

    // Create the spacer image.
    if (this.spacerImage == null) {
        this.spacerImage = this.widget.getImageProps("DOT", {
            id: this.id + ":DOT"
        });
    }
    
    // Create the top left image.
    if (this.topLeftImage == null) {
        this.topLeftImage = this.widget.getImageProps("SCHEDULER_TOP_LEFT", {
            id: this.id + ":topLeft"
        });        
    }        
        
    //Create the top right image.
    if (this.topRightImage == null) {
        this.topRightImage = this.widget.getImageProps("SCHEDULER_TOP_RIGHT", {
            id: this.id + ":topRight"
        });        
    }

    // Create the increase link imageHyperlink widget.
    if (this.increaseLink == null) {
        this.increaseLink = this.widget.getImageHyperlinkProps({
                id: this.id + ":nextMonthLink",
                enabledImage: {
                    border: 0,
                    id: this.id + ":nextMonthLink_image"
                },
                title: this.theme.getMessage("CalendarMonth.goForward")             
            },
            "SCHEDULER_FORWARD"
         );
    }   
    
    // Create the decrease link imageHyperlink widget.
    if (this.decreaseLink == null) {
        this.decreaseLink = this.widget.getImageHyperlinkProps({
                "id": this.id + ":previousMonthLink",
                enabledImage: {
                    border: 0,
                    id: this.id + ":previousMonthLink_image"
                },
                title: this.theme.getMessage("CalendarMonth.goBack")
             },
             "SCHEDULER_BACKWARD"                          
         );
    }        
    
    // Create the close button link imageHyperlink widget
    if (this.closeButtonLink == null) {
        this.closeButtonLink = this.widget.getImageHyperlinkProps({
                id: this.id + ":closeButtonLink",
                enabledImage: {
                    border: 0,
                    id: this.id + "closeButtonLink_close"
                },
                title: this.theme.getMessage("CalendarMonth.close"),
                className: this.theme.getClassName("CALENDAR_CLOSE_BUTTON")            
            },            
            "CALENDAR_CLOSE_BUTTON"            
        );    
    }
    
    // If the dateFormatPattern is null, get one from the themes.
    if (this.dateFormat == null) {
        this.dateFormat = this.theme.getMessage("calendar.dateFormat");
    }
    
    // If the minDate and maxDate are not specified, create a default values.
    // The minDate's year is set to 100 years previous to the current year
    // and maxDate is set to 200 years forward from the minDate's year'
    var minDate = new Date();
    var maxDate = new Date();
    if (this.minDate == null) {
        minDate.setFullYear(minDate.getFullYear() - 100);
        this.minDate = this.formatDate(minDate.getMonth(), 
                            minDate.getDate(), minDate.getFullYear());        
    } else {
        minDate = this.convertStringToDate(this.minDate);
    } 

    if (this.maxDate == null) {
        maxDate.setFullYear(minDate.getFullYear() + 200);
        this.maxDate = this.formatDate(maxDate.getMonth(), 
                            maxDate.getDate(), maxDate.getFullYear());
    } else {
        maxDate = this.convertStringToDate(this.maxDate);
    }             
  
    // Initialize the days of the week.
    if (this.weekDays == null) {
        this.weekDays = new Array();
        this.weekDays[0] = this.theme.getMessage("CalendarMonth.weekdaySun");
        this.weekDays[1] = this.theme.getMessage("CalendarMonth.weekdayMon");
        this.weekDays[2] = this.theme.getMessage("CalendarMonth.weekdayTue");                
        this.weekDays[3] = this.theme.getMessage("CalendarMonth.weekdayWed");
        this.weekDays[4] = this.theme.getMessage("CalendarMonth.weekdayThu");
        this.weekDays[5] = this.theme.getMessage("CalendarMonth.weekdayFri");
        this.weekDays[6] = this.theme.getMessage("CalendarMonth.weekdaySat");
    }           
    
    // Get the first day of week for that particular locale.
    if (this.firstDayOfWeek == null) {
        this.firstDayOfWeek = parseInt(this.theme.getMessage("calendar.firstDayOfWeek"));
    }
    
    // This will append a localized string along with the
    // today's date
    if (this.todayDateMsg == null) {        
        var d = new Date();
        var todayDateMsg = this.theme.getMessage("CalendarMonth.todayIs");
        
        // Remove the "$0" argument used for the server side param
        var index = todayDateMsg.indexOf(":");
        this.todayDateMsg = todayDateMsg.substr(0, index+1);
        
        var month = this.theme.getMessage(
                        "calendar." + (d.getMonth()));
        month=month.substr(0,3);
        if (this.dateFormat.indexOf("MM") == 0) {
            this.todayDateMsg += " " + month + " " + d.getDay();
        } else {
            this.todayDateMsg += " " + d.getDay() + " " + month;        
        }
        this.todayDateMsg += ", "+d.getFullYear();
    }

    // Initialize the month menu if one does not exist.
    if (this.monthMenu == null) {                  
        this.monthMenu = this.widget.getDropDownProps({
            id: this.id + ":monthMenu",
            options:this.getMonthOptions(),
            title: this.theme.getMessage("CalendarMonth.selectMonth")
        });                  
    }
    
    // Initialize the year menu if one does not exist.
    if (this.yearMenu == null) {
        this.yearMenu = this.widget.getDropDownProps({
            id: this.id + ":yearMenu",
            options: this.getYearOptions(minDate.getYear(), maxDate.getYear()),
            title: this.theme.getMessage("CalendarMonth.selectYear")   
        });          
    }
    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to obtain the Date object from the given string
 * date value
 *
 * @param {String} inputDate The date to be converted into Dataee object
 * @param {boolean} yearCheck Check whether the year falls within the specified range
 * @return {Object}  The Date object corresponding to the input String
 */
webui.suntheme.widget.calendar.prototype.convertStringToDate = function(inputDate, yearCheck) {   
    if (inputDate == "") {
        property = null;
        return false;
    }
    
    var pattern = this.dateFormat;
    var yearIndex = pattern.indexOf("yyyy");
    var monthIndex = pattern.indexOf("MM");
    var dayIndex = pattern.indexOf("dd");

    // If the format is invalid, set the current value to null
    if (yearIndex < 0 || monthIndex < 0 || dayIndex < 0) {
        return null;
    } 
    
    var counter = 0;
    var number;
    var selectedDate = new Date();
    var found = 0;
    var dateString;

    while (counter < inputDate.length) {
        if (counter == yearIndex) {
            try {
                number = parseInt(inputDate.substr(counter, 4));
                if (isNaN(number)) {
                    property = null;
                    return false;
                }                
             // Check if the input date's year range is inbetween the 
             // allowed dates.   
               if (yearCheck == true) {
                   var index = 0;
                    var foundYear = false;                               
                    yearMenu = dijit.byId(this.yearMenu.id).getSelectElement();
                    while (index < yearMenu.length) {
                        if (number == yearMenu.options[index].value) {
                            selectedDate.setFullYear(number);
                            ++found;
                            foundYear = true;
                            break;
                        }
                        index++;
                    }
                    if (!foundYear) {
                        break;
                    }
                } else {            
                    selectedDate.setFullYear(number);
                    ++found;
                }                    
            } catch(e) {}
        } else if (counter == monthIndex) {
            try {    
                dateString = inputDate.substr(counter, 2);
                // This is a workaround for Firefox! 
                // parseInt() returns 0 for values 08 and 09
                // while all other leading zeros work.
                if (dateString.charAt(0) == '0') {
                    dateString = dateString.substr(1, 1);
                }
                number = parseInt(dateString);
                if (isNaN(number)) {
                    property = null;
                    return false;
                }
                selectedDate.setMonth(number-1);
                ++found;
            } catch(e) {}
        } else if (counter == dayIndex) {
            try {
                dateString = inputDate.substr(counter, 2);
                // This is a workaround for Firefox! 
                // parseInt() returns 0 for values 08 and 09
                // while all other leading zeros work.
                if (dateString.charAt(0) == '0') {
                    dateString = dateString.substr(1, 1);
                }
                number = parseInt(dateString);
                if (isNaN(number)) {
                    return null;
                }
                selectedDate.setDate(number);
                ++found;
            } catch(e) {}
        }
        ++counter;
    }

    if (found == 3) {
        return selectedDate;
    } else {
        return null;
    }    
    return true;       
}

/**
 * Helper function to set the initial focus on the menus.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendar.prototype.setInitialFocus = function() {    
    var pattern = new String(this.dateFormat);
    var yearIndex = pattern.indexOf("yyyy");
    var monthIndex = pattern.indexOf("MM");
    
    // Moving the year menu around based on the date format is not supported yet.
    // So, the code for setting focus on the year menu has been commented out.
    // if (yearIndex < monthIndex) {        
    //    var yearMenu = document.getElementById(this.calendarMonth.yearMenu.id).getSelectElement();
    //    yearMenu.focus();                 
    // } else {
        var monthMenu = dijit.byId(this.monthMenu.id).getSelectElement();          
        monthMenu.focus();
    // }
    return true;
}

/**
 * Set the value of an HTML select element, but limit value to min and max.
 *
 * @param {Node} select The HTML select element.
 * @param {String} value The selected value.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendar.prototype.setLimitedSelectedValue = function(select, value) {
    var min = select.options[0].value;
    var max = select.options[select.length - 1].value;
    if (value < min) {        
        select.value = min;
    } else if ( value > max) {        
        select.value = max;
    } else {
        this.setSelectedValue(select, value);        
    }
    return true;
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [className] CSS selector.
 * @config {Object} [closeButtonLink] 
 * @config {String} [date] 
 * @config {String} [dateFormat] 
 * @config {Object} [decreaseLink] 
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {Object} [increaseLink] 
 * @config {Object} [monthMenu]
 * @config {String} [style] Specify style rules inline.
 * @config {Object} [todayDateMsg]
 * @config {Object} [toggleLink]
 * @config {boolean} [visible] Hide or show element.
 * @config {Object} [yearMenu]
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendar.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.calendar.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set properties.        
    if (props.todayDateMsg) {
        // NOTE: If you set this value manually, text must be HTML escaped.
        this.widget.addFragment(this.todayDateContainer, props.todayDateMsg);
    }

    if (props.spacerImage) {
        if (!dijit.byId(this.spacerImage.id)) {
            this.widget.addFragment(this.spacerImageContainer, props.spacerImage);
        }
    }

    if (props.topLeftImage) {
         if (!dijit.byId(this.topLeftImage.id)) {
            this.widget.addFragment(this.topLeftImageContainer, props.topLeftImage);
        }
    }

    if (props.topRightImage) {
        if (!dijit.byId(this.topRightImage.id)) {
            this.widget.addFragment(this.topRightImageContainer, props.topRightImage);
        }
    }

    if (props.date) {
        var selDate = this.convertStringToDate(props.date, true);
        if (selDate != null) {
            this.currentValue = selDate;
        }
    }

    // Set close link properties.
    if (props.closeButtonLink) {
        // Set properties.
        props.closeButtonLink.id = this.closeButtonLink.id; // Required for updateFragment().
        props.closeButtonLink.onClick = 
            "dijit.byId('" + this.id + "').toggleCalendar();return false;";
        props.closeButtonLink.onKeyDown = 
            "dijit.byId('" + this.id + "').closeCalendar(event);return false;";       

        // Update/add fragment.
        this.widget.updateFragment(this.closeButtonContainer, props.closeButtonLink);
    }

    // Set decrease link properties.
    if (props.decreaseLink) {
        // Set properties.
        props.decreaseLink.id = this.decreaseLink.id; // Required for updateFragment().
        props.decreaseLink.onClick = 
            "dijit.byId('" + this.id + "').decreaseMonth();return false;";

        // Update/add fragment.
        this.widget.updateFragment(this.previousLinkContainer, props.decreaseLink);
    }

    // Set increase link properties.
    if (props.increaseLink) {
        // Set properties.
        props.increaseLink.id = this.increaseLink.id; // Required for updateFragment().
        props.increaseLink.onClick = 
            "dijit.byId('" + this.id + "').increaseMonth();return false;"

        // Update/add fragment.
        this.widget.updateFragment(this.nextLinkContainer, props.increaseLink);
    }
    
        var minDate = null;
        var maxDate = null;    
    if (props.minDate || props.maxDate) {
        if (props.minDate) {
            
            // Convert the given string to a proper given date format pattern
            // and then store it.
            minDate = this.convertStringToDate(props.minDate);
            if (minDate != null) {
                this.minDate = this.formatDate(minDate.getMonth(), 
                    minDate.getDate(), minDate.getFullYear());
            }
        } 
        
        if (props.maxDate) {
            
            // Convert the given string to a proper given date format pattern
            // and then store it.            
            maxDate = this.convertStringToDate(props.maxDate);      
            if (maxDate != null) {
                this.maxDate = this.formatDate(maxDate.getMonth(), 
                    maxDate.getDate(), maxDate.getFullYear());
            }               
        } 
        
        //Recalculate the year options with new minDate and maxDate values.
        props.yearMenu = this.widget.getDropDownProps({
            id: this.id + ":yearMenu",
            options:this.getYearOptions(minDate.getFullYear(), maxDate.getFullYear()),
            title: this.theme.getMessage("CalendarMonth.selectYear")   
        });  
        
        // update the value of yearMenu
        this.yearMenu = props.yearMenu;                                          
    }
        
    // Set month menu properties
    if (props.monthMenu) {                        
        // Set properties.
        props.monthMenu.id = this.monthMenu.id; // Required for updateFragment().
        props.monthMenu.onChange =
            "dijit.byId('" + this.id + "').updateMonth(false);return false;";
                         
        // Update/add fragment.
        this.widget.updateFragment(this.monthMenuContainer, props.monthMenu);
    }

    // Set year menu properties.
    if (props.yearMenu) {        
        // Set properties.
        props.yearMenu.id = this.yearMenu.id; // Required for updateFragment().
        props.yearMenu.onChange =
            "dijit.byId('" + this.id + "').updateMonth(false);return false;";

        // Update/add fragment.
        this.widget.updateFragment(this.yearMenuContainer, props.yearMenu);
    }

    // Set toggle link properties.
    if (props.disabled != null) {
        this.disabled = new Boolean(props.disabled).valueOf(); 
    }

    // If the popup calendar is still being shown, prevent disabling of the calendar.
    // The widget can only be disabled if the popup calendar is not shown.
    if (props.toggleLink || 
        (props.disabled != null && this.calendarContainer.style.display != "block")) {

        // Ensure property exists so we can call setProps just once.
        if (props.toggleLink == null) {
            props.toggleLink = {}; // Avoid updating all props using "this" keyword.
        }

        // Set properties.
        props.toggleLink.id = this.toggleLink.id; // Required for updateFragment().
        props.toggleLink.disabled = this.disabled;
        props.toggleLink.onClick =
            "dijit.byId('" + this.id + "').toggleCalendar();return false;";

        // Update/add fragment.
        this.widget.updateFragment(this.linkNode, props.toggleLink); 
    }

    // Set more properties.
    this.setCommonProps(this.domNode, props);

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}

/**
 * This function is used to set the value of a select element.
 *
 * @param {Node} select The HTML select element.
 * @param {String} value The selected value.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendar.prototype.setSelectedValue = function(select, value) {
    for (var i = 0; i < select.length; i++) {
        if (select.options[i].value == value) {
            select.selectedIndex = i;
            return true;
        }
    }
    select.selectedIndex = -1;
    return true;
}

/**
 * Process toogle event.
 *
 * @return {boolean} false to cancel JavaScript event.
 */
webui.suntheme.widget.calendar.prototype.toggleCalendar = function() {
    var topic = (this.calendarContainer.style.display != "block")
        ? webui.suntheme.widget.calendar.event.toggle.openTopic
        : webui.suntheme.widget.calendar.event.toggle.closeTopic;

    // Publish an event for other widgets to listen for.
    //
    // Note: This must be done before the calendar is opened so user
    // input can be applied to the current date.
    dojo.publish(webui.suntheme.widget.calendar.event.toggle.openTopic, [{
        id: this.id
    }]);

    // Open the calendar.
    if (this.calendarContainer.style.display != "block") {
        if (webui.suntheme.widget.calendar.activeCalendarId != null) {
            var cal = dijit.byId(webui.suntheme.widget.calendar.activeCalendarId);
            cal.toggleCalendar();
        }
        webui.suntheme.widget.calendar.activeCalendarId = this.id;        
        this.calendarContainer.style.display = "block";
        this.setInitialFocus();
        this.updateMonth(true);    
    } else {
        // Hide the calendar popup
        this.calendarContainer.style.display = "none";
        webui.suntheme.widget.calendar.activeCalendarId = null;
    }

    // Test for IE 
    if (webui.suntheme.browser.isIe5up()) {
        this.ieStackingContextFix();
    }          
    return false;
}

/**
 * This function is used to update the calendar month.
 * It is called when the calendar is opened, the next or previous
 * links are clicked, or the month or year menus are changed.
 *
 * @param {boolean} initialize Flag indicating to initialze the year and month menus
 * with the current value. The value is true only when the calendar is opened.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendar.prototype.updateMonth = function(initialize) {
    
    // Remove all the nodes of <tbody> before cloning its children.
    this.widget.removeChildNodes(this.tbodyContainer);    
    // Add week days
    this.addWeekDays();    
    
    // Add days of the month
    this.addDaysInMonth(this.currentValue, initialize);
    
    return true;     
}
dojo.provide("webui.suntheme.widget.fieldBase");


/**
 * @name webui.suntheme.widget.fieldBase
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for widgets that extend fieldBase.
 * @static
 */
dojo.declare("webui.suntheme.widget.fieldBase", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    disabled: false,
    required: false,
    size: 20,
    valid: true
});

/**
 * Helper function to obtain HTML input element class names.
 *
 * @return {String} The HTML input element class name.
 */
webui.suntheme.widget.fieldBase.prototype.getInputClassName = function() {   
    return null; // Overridden by subclass.
}

/**
 * Returns the HTML input element that makes up the text field.
 *
 * @return {Node} The HTML input element.
 */
webui.suntheme.widget.fieldBase.prototype.getInputElement = function() {
    return this.fieldNode;
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.fieldBase.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);
    
    // Set properties.
    if (this.alt) { props.alt = this.alt; }
    if (this.disabled != null) { props.disabled = this.disabled; }
    if (this.label) { props.label= this.label; }
    if (this.maxLength > 0) { props.maxLength = this.maxLength; }    
    if (this.notify) { props.notify = this.notify; }
    if (this.submitForm != null) { props.submitForm = this.submitForm; }
    if (this.text != null) { props.text = this.text; }
    if (this.title != null) { props.title = this.title; }
    if (this.type) { props.type= this.type; }
    if (this.readOnly != null) { props.readOnly = this.readOnly; }
    if (this.required != null) { props.required = this.required; }
    if (this.size > 0) { props.size = this.size; }
    if (this.style != null) { props.style = this.style; }
    if (this.valid != null) { props.valid = this.valid; }
    
    // After widget has been initialized, get user's input.
    if (this.isInitialized() == true && this.fieldNode.value != null) {
        props.value = this.fieldNode.value;
    } else if (this.value != null) {
        props.value = this.value;
    }
    return props;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.fieldBase.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {
        this.fieldNode.id = this.id + "_field";
        this.fieldNode.name = this.id + "_field";
        this.labelContainer.id = this.id + "_label";
    }
    
    // Set public functions.
    this.domNode.getInputElement = function() { return dijit.byId(this.id).getInputElement(); }
    
    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.fieldBase.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }
    
    // Set properties.
    if (props.submitForm == false || props.submitForm == true ) { 
        // connect the keyPress event
        dojo.connect(this.fieldNode, "onkeypress", this, "submitFormData");
    }
    if (props.maxLength > 0) { this.fieldNode.maxLength = props.maxLength; }
    if (props.size > 0) { this.fieldNode.size = props.size;  }
    if (props.value != null) { this.fieldNode.value = props.value; }
    if (props.title != null) { this.fieldNode.title = props.title; }   
    if (props.disabled != null) { 
        this.fieldNode.disabled = new Boolean(props.disabled).valueOf();
    }
    if (props.readOnly != null) { 
        this.fieldNode.readOnly = new Boolean(props.readOnly).valueOf();
    }
    
    // Set label properties.
    if (props.label || (props.valid != null || props.required != null) && this.label) {
        // Ensure property exists so we can call setProps just once.
        if (props.label == null) {
            props.label = {}; // Avoid updating all props using "this" keyword.
        }
        
        // Set properties.
        props.label.id = this.label.id; // Required for updateFragment().
        props.label.required = this.required;
        props.label.valid = this.valid;
        
        // Update/add fragment.
        this.widget.updateFragment(this.labelContainer, props.label);
    }
    
    // Set HTML input element class name.
    this.fieldNode.className = this.getInputClassName();
    
    // Set more properties.
    this.setCommonProps(this.fieldNode, props);
    this.setEventProps(this.fieldNode, props);
    
    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}

/**
 * Process keyPress events on the field, which enforces/disables 
 * submitForm behavior.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.fieldBase.prototype.submitFormData = function(event) {
    if (event == null) {
        return false;
    }
    if (event.keyCode == event.KEY_ENTER) {               
        if (this.submitForm == false) {
            // Disable form submission.
            if (window.event) {
                event.cancelBubble = true;
                event.returnValue = false;
            } else{
                event.preventDefault();
                event.stopPropagation();
            }
            return false;
        } else {
            // Submit the form                    
            if (event.currentTarget.form) {
                event.currentTarget.form.submit();
            }
        }
    }
    return true;    
}
dojo.provide("webui.suntheme.widget.textField");


/**
 * @name webui.suntheme.widget.textField
 * @extends webui.suntheme.widget.fieldBase
 * @class This class contains functions for the textField widget.
 * @constructor This function is used to construct a textField widget.
 */
dojo.declare("webui.suntheme.widget.textField", webui.suntheme.widget.fieldBase, {
    // Set defaults.
    widgetName: "textField" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.textField.event =
        webui.suntheme.widget.textField.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_textField_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_textField_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_textField_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_textField_event_state_end"
    },

    /**
     * This object contains submit event topics.
     * @ignore
     */
    submit: {
        /** Submit event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_textField_event_submit_begin",

        /** Submit event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_textField_event_submit_end"
    },

    /**
     * This object contains validation event topics.
     * @ignore
     */
    validation: {
        /** Validation event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_textField_event_validation_begin",

        /** Validation event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_textField_event_validation_end"
    }
}

/**
 * Helper function to obtain HTML input element class names.
 *
 * @return {String} The HTML input element class name.
 */
webui.suntheme.widget.textField.prototype.getInputClassName = function() {          
    // Set readOnly style.
    if (this.fieldNode.readOnly) {
        return this.widget.getClassName("TEXT_FIELD_READONLY", "");
    }

    // Apply invalid style.
    var validStyle =  (this.valid == false) 
        ? " " + this.widget.getClassName("TEXT_FIELD_INVALID", "")
        : " " + this.widget.getClassName("TEXT_FIELD_VALID", "");
    
    // Set default style.    
    return (this.disabled == true)
        ? this.widget.getClassName("TEXT_FIELD_DISABLED", "") 
        : this.widget.getClassName("TEXT_FIELD", "") + validStyle;
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.textField.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.autoValidate != null) { props.autoValidate = this.autoValidate; }
    
    return props;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.textField.prototype.postCreate = function () {
    // Set events.
    if (this.autoValidate == true) {
        // Generate the following event ONLY when 'autoValidate' == true.
        dojo.connect(this.fieldNode, "onblur", this, "validate");
    }
    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [accesskey] 
 * @config {boolean} [autoValidate]
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [label]
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {int} [maxLength] 
 * @config {Array} [notify] 
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {boolean} [readOnly] 
 * @config {boolean} [required] 
 * @config {int} [size] 
 * @config {String} [style] Specify style rules inline.
 * @config {boolean} [submitForm]
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [valid]
 * @config {String} [value] Value of input.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.textField.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * Process validation event.
 * <p>
 * This function interprets an event (one of onXXX events, such as onBlur,
 * etc) and extracts data needed for subsequent Ajax request generation. 
 * Specifically, the widget id that has generated the event. If widget
 * id is found, publishBeginEvent is called with extracted data.
 * </p>
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.textField.prototype.validate = function(event) {
    if (event == null) {
        return false;
    }
    // Publish an event for custom AJAX implementations to listen for.
    dojo.publish(webui.suntheme.widget.textField.event.validation.beginTopic, [{
        id: this.id
    }]);
    return true;
}
dojo.provide("webui.suntheme.widget.calendarField");


/**
 * @name webui.suntheme.widget.calendarField
 * @extends webui.suntheme.widget.textField
 * @class This class contains functions for the calendarField widget.
 * @constructor This function is used to construct a calendarField widget.
 */
dojo.declare("webui.suntheme.widget.calendarField", webui.suntheme.widget.textField, {
    // Set defaults.
    widgetName: "calendarField" // Required for theme properties.
});

/**
 * This function is called when a day link is selected from the calendar.
 * It updates the field with the value of the clicked date.
 *
 * @param props Key-Value pairs of properties.
 * @config {String} [id] 
 * @config {String} [date]
 * @return {boolean} false to cancel JavaScript event.
 */
webui.suntheme.widget.calendarField.prototype.dayClicked = function(props) {
    // Check whether the calendar associated with this particular calendarField
    // broadcasted the event.
    if (props.date != null && props.id == this.calendar.id) {
        // Set the selected date on the field.
        this.domNode.setProps({value: props.date});
    }
    return false;
}

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.calendarField.event =
        webui.suntheme.widget.calendarField.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_calendarField_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_calendarField_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_calendarField_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_calendarField_event_state_end"
    }
}

/**
 * This function is used to obtain the outermost HTML element class name.
 * <p>
 * Note: Selectors should be concatinated in order of precedence (e.g., the 
 * user's className property is always appended last).
 * </p>
 * @return {String} The outermost HTML element class name.
 */
webui.suntheme.widget.calendarField.prototype.getClassName = function() {
    // Set default style.
    var className = this.widget.getClassName("CALENDAR_ROOT_TABLE","");

    return (this.className)
        ? className + " " + this.className
        : className;
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.calendarField.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.  
    if (this.align) { props.align = this.align; }
    if (this.calendar) { props.calendar = this.calendar; }  
    if (this.patternHelp) { props.patternHelp = this.patternHelp; }   

    return props;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendarField.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {
        this.inlineHelpNode.id = this.id + "_pattern";
        this.linkContainer.id = this.id + "_linkContainer";
        this.calendarContainer.id = this.id + "_calendarContainer";
    }     

    // If a patternHelp is not specified by the developer
    // try to get one from the themes. If not, the dateFormatPattern
    // will be used as the help.
    if (this.patternHelp == null) {
        var pattern = this.theme.getMessage("calendar.dateFormat");
        var help = this.theme.getMessage("calendar."+pattern);
        if (help != null) {
            this.patternHelp = help;
        } else {
            this.patternHelp = pattern;
        }
    }
    // Set events.

    // Subscribe to the "dayClicked" event present in the calendar widget.
    dojo.subscribe(webui.suntheme.widget.calendar.event.day.selectedTopic,
        this, "dayClicked");
    // Subscribe to the "toggle" event that occurs whenever the calendar is opened.
    dojo.subscribe(webui.suntheme.widget.calendar.event.toggle.openTopic,
        this, "toggleCalendar");
        
    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [alt] Alternate text for image input.
 * @config {String} [align] Alignment of image input.
 * @config {Object} [calendar] 
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [label]
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {Array} [notify] 
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {String} [patternHelp] 
 * @config {boolean} [readOnly] 
 * @config {boolean} [required] 
 * @config {String} [style] Specify style rules inline.
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [valid] 
 * @config {String} [value] Value of input.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendarField.prototype.setProps = function(props, notify) {
    if (props == null) {
        return false;
    }
    
    // If the popup calendar is visible, prevent disabling of the calendar.
    // The widget can only be disabled if the popup calendar is not visible.
    if (props.disabled != null) { 
        var widget = dijit.byId(this.calendar.id); 
        if (widget != null && !(widget.calendarContainer.style.display != "block")) {
            props.disabled = this.disabled;
        }        
    }
    
    // Set remaining properties.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.calendarField.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set disabled.
    if (props.disabled != null) { this.disabled = new Boolean(props.disabled).valueOf(); }

    // Set calendar.
    if (props.calendar || props.disabled != null) {
        // Ensure property exists so we can call setProps just once.
        if (props.calendar == null) {
            props.calendar = {}; // Avoid updating all props using "this" keyword.
        }

        // Set properties.
        props.calendar.id = this.calendar.id; // Required for updateFragment().
        props.calendar.disabled = this.disabled;
        
        // Update/add fragment.
        this.widget.updateFragment(this.calendarContainer, props.calendar); 
    }
    
    // Set date format pattern help.
    if (props.patternHelp) {
        // NOTE: If you set this value manually, text must be HTML escaped.
        this.widget.addFragment(this.inlineHelpNode, props.patternHelp);
    }

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}

/**
 * This function subscribes to the toggleCalendar function of the calendar widget.
 * Whenever the calendar is opened, it updates the value of the calendar with
 * the value present in the field.
 * 
 * @param props Key-Value pairs of properties.
 * @config {String} [id]
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.calendarField.prototype.toggleCalendar = function(props) {   
    if (props.id != null && props.id == this.calendar.id) {
        var widget = dijit.byId(props.id);
        widget.setProps({date: this.getProps().value});
    }
    return true;
}
dojo.provide("webui.suntheme.widget.checkedBase");


/**
 * @name webui.suntheme.widget.checkedBase
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for widgets that extend checkedBase.
 * @static
 */
dojo.declare("webui.suntheme.widget.checkedBase", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    idSuffix: "" // Overridden by subclass
});

/**
 * Helper function to obtain image class names.
 *
 * @return {String} The HTML image element class name.
 */
webui.suntheme.widget.checkedBase.prototype.getImageClassName = function() {
    return null; // Overridden by subclass.
}

/**
 * Helper function to obtain input class names.
 *
 * @return {String} The HTML input element class name.
 */
webui.suntheme.widget.checkedBase.prototype.getInputClassName = function() {
    return null; // Overridden by subclass.
}

/**
 * Returns the HTML input element that makes up the chekcbox.
 *
 * @return {Node} The HTML input element. 
 */
webui.suntheme.widget.checkedBase.prototype.getInputElement = function() {
    return this.inputNode;
}

/**
 * Helper function to obtain label class names.
 *
 * @return {String} The HTML label element class name.
 */
webui.suntheme.widget.checkedBase.prototype.getLabelClassName = function() {
    return null; // Overridden by subclass.
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.checkedBase.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.  
    if (this.align) { props.align = this.align; }
    if (this.disabled != null) { props.disabled = this.disabled; }   
    if (this.image) { props.image = this.image; }
    if (this.label) { props.label = this.label; }
    if (this.name) { props.name = this.name; }        
    if (this.readOnly != null) { props.readOnly = this.readOnly; }
    if (this.value) { props.value = this.value; }

    // After widget has been initialized, get user's input.
    if (this.isInitialized() == true && this.inputNode.checked != null) {
        props.checked = this.inputNode.checked;
    } else if (this.checked != null) {
        props.checked = this.checked;
    }
    return props;
}

/**
 * Helper function to create callback for onClick event.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.checkedBase.prototype.onClickCallback = function(event) {
    if (this.readOnly == true) {
        event.preventDefault();
        return false;
    }

    // If function returns false, we must prevent the request.
    var result = (this.domNode._onclick)
        ? this.domNode._onclick(event) : true;
    if (result == false) {
        event.preventDefault();
        return false;
    }
    return true;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.checkedBase.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {
        this.inputNode.id = this.id + this.idSuffix;
        this.imageContainer.id = this.id + "_imageContainer";
        this.labelContainer.id = this.id + "_labelContainer";

        // If null, use HTML input id.
        if (this.name == null) {
            this.name = this.inputNode.id;
        }
    }

    // Set public functions.
    this.domNode.getInputElement = function() { return dijit.byId(this.id).getInputElement(); }
    
    // Create callback function for onclick event.
    dojo.connect(this.domNode, "onclick", this, "onClickCallback");

    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.checkedBase.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set properties.
    if (props.value) { 
        this.inputNode.value = props.value;
    }
    if (props.readOnly != null) { 
        this.inputNode.readOnly = new Boolean(props.readOnly).valueOf();       
    }
    if (props.disabled != null) {
        this.inputNode.disabled = new Boolean(props.disabled).valueOf();        
    }
    
    // Set HTML input element class name.
    this.inputNode.className = this.getInputClassName();
    
    if (props.name) { 
        this.inputNode.name = props.name;
    }
    
    // A web app devleoper could return false in order to cancel the 
    // submit. Thus, we will handle this event via the onClick call back.
    if (props.onClick) {
        // Set private function scope on DOM node.
        this.domNode._onclick = (typeof props.onClick == 'string')
            ? new Function("event", props.onClick) : props.onClick;

        // Must be cleared before calling setEventProps() below.
        props.onClick = null;
    }
    	
    if (props.checked != null) {
        var checked = new Boolean(props.checked).valueOf();

        // Dynamically setting the checked attribute on IE 6 does not work until
        // the HTML input element has been added to the DOM. As a work around, 
        // we shall use a timeout to set the property during initialization.
        if (this.isInitialized() != true &&
                webui.suntheme.browser.isIe()) {
            var _id = this.id;
            setTimeout(function() {
                // New literals are created every time this function
                // is called, and it's saved by closure magic.
                var widget = dijit.byId(_id);
                widget.inputNode.checked = checked;
            }, 0); // (n) milliseconds delay.
        } else {
            this.inputNode.checked = checked;
        }
    }

    // Set image properties.
    if (props.image || props.disabled != null && this.image) {     
        // Ensure property exists so we can call setProps just once.
        if (props.image == null) {
            props.image = {}; // Avoid updating all props using "this" keyword.
        }

        // Set properties.
        props.image.id = this.image.id; // Required for updateWidget.
        props.image.className = this.getImageClassName();

        // Update/add fragment.
        this.widget.updateFragment(this.imageContainer, props.image);
    } 

    // Set label properties.
    if (props.label || props.disabled != null && this.label) {     
        // Ensure property exists so we can call setProps just once.
        if (props.label == null) {
            props.label = {}; // Avoid updating all props using "this" keyword.
        }

        // Set properties.
        props.label.id = this.label.id; // Required for updateFragment().
        props.label.className = this.getLabelClassName();

        // Update/add fragment.
        this.widget.updateFragment(this.labelContainer, props.label);
    }

    // Set more properties.
    this.setCommonProps(this.inputNode, props);
    this.setEventProps(this.inputNode, props);

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.checkbox");


/**
 * @name webui.suntheme.widget.checkbox
 * @extends webui.suntheme.widget.checkedBase
 * @class This class contains functions for the checkbox widget.
 * @constructor This function is used to construct a checkbox widget.
 */
dojo.declare("webui.suntheme.widget.checkbox", webui.suntheme.widget.checkedBase, {
    // Set defaults.
    idSuffix: "_cb",
    widgetName: "checkbox" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.checkbox.event =
        webui.suntheme.widget.checkbox.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_checkbox_event_refresh_begin",

        /** event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_checkbox_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_checkbox_event_state_begin",

        /** event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_checkbox_event_state_end"
    },

    /**
     * This object contains submit event topics.
     * @ignore
     */
    submit: {
        /** event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_checkbox_event_submit_begin",

        /** event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_checkbox_event_submit_end"
    }
}

/**
 * This function is used to obtain the outermost HTML element class name.
 * <p>
 * Note: Selectors should be concatinated in order of precedence (e.g., the 
 * user's className property is always appended last).
 * </p>
 * @return {String} The outermost HTML element class name.
 */
webui.suntheme.widget.checkbox.prototype.getClassName = function() {
    // Set default style.
    var className = (this.disabled == true)
        ? this.widget.getClassName("CHECKBOX_SPAN_DISABLED", "")
        : this.widget.getClassName("CHECKBOX_SPAN", ""); 

    return (this.className)
        ? className + " " + this.className
        : className;
}

/**
 * Helper function to obtain image class names.
 *
 * @return {String} The HTML image element class name.
 */
webui.suntheme.widget.checkbox.prototype.getImageClassName = function() {
    return (this.disabled == true)
        ? this.widget.getClassName("CHECKBOX_IMAGE_DISABLED", "")
        : this.widget.getClassName("CHECKBOX_IMAGE", "");  
}

/**
 * Helper function to obtain input class names.
 *
 * @return {String} The HTML input element class name.
 */
webui.suntheme.widget.checkbox.prototype.getInputClassName = function() {
    // readOnly style.
    if (this.readOnly == true) {
        return this.widget.getClassName("CHECKBOX_READONLY", "");        
    }

    // disabled style.
    return (this.disabled == true)
        ? this.widget.getClassName("CHECKBOX_DISABLED", "")
        : this.widget.getClassName("CHECKBOX", "");  
}

/**
 * Helper function to obtain label class names.
 *
 * @return {String} The HTML label element class name.
 */
webui.suntheme.widget.checkbox.prototype.getLabelClassName = function() {
    return (this.disabled == true)
        ? this.widget.getClassName("CHECKBOX_LABEL_DISABLED", "")
        : this.widget.getClassName("CHECKBOX_LABEL", "");  
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [alt] Alternate text for image input.
 * @config {String} [align] Alignment of image input.
 * @config {boolean} [checked] 
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {Object} [image] 
 * @config {String} [label] 
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {String} [name] 
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {String} [onSelect] 
 * @config {boolean} [readOnly] 
 * @config {String} [style] Specify style rules inline.
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {String} [value] Value of input.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.checkbox.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}
dojo.provide("webui.suntheme.widget.checkedGroupBase");


/**
 * @name webui.suntheme.widget.checkedGroupBase
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for widgets that extend checkedGroupBase.
 * @static
 */
dojo.declare("webui.suntheme.widget.checkedGroupBase", webui.suntheme.widget.widgetBase);

/**
 * Helper function to add elements with Object literals.
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {boolean} [disabled] 
 * @config {Array} [columns] 
 * @config {Array} [contents] 
 * @config {Object} [label]
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.checkedGroupBase.prototype.addContents = function(props) {   
    if (props == null) {
        return false;
    }
    
    if (props.contents) {
        this.widget.removeChildNodes(this.tbodyContainer);
        var rowContainerCloneNode = this.rowContainer.cloneNode(false);
        this.tbodyContainer.appendChild(rowContainerCloneNode);
        if (props.label) {
            var rowNodeClone = this.rowNode.cloneNode(false);
            rowContainerCloneNode.appendChild(rowNodeClone);
            var labelContainerClone = this.labelContainer.cloneNode(false);
            rowNodeClone.appendChild(labelContainerClone);
            this.widget.addFragment(labelContainerClone, props.label, "last");            
              
        }
        var itemN = 0;
        var length = props.contents.length;
        var columns = (props.columns <= 0) ? 1 : props.columns;
        var rows = (length + (columns-1))/columns;
        var propsdisabledvalue = props.disabled == null ? false : props.disabled;
        for (var row = 0; row <= rows; row++) {
            for (var column = 0; column < columns; column++) {
                if (itemN < length) {
                    // Clone < td> node.
                    var contentsRowNodeClone = this.contentsRowNode.cloneNode(false);
                    rowContainerCloneNode.appendChild(contentsRowNodeClone);
                    // Set disabled.                   
                    props.contents[itemN].disabled = propsdisabledvalue;
                   
                    // Add child to the group.
                    this.widget.addFragment(contentsRowNodeClone, props.contents[itemN], "last");
                    itemN++;
                }
            }
            if (row + 1 <= rows) {
                rowContainerCloneNode = this.rowContainer.cloneNode(false);
                this.tbodyContainer.appendChild(rowContainerCloneNode);
                // This check is required here. Else the elements won't be
                // aligned properly when there is no label.
                if (props.label != null) {
                    var contentsRowNodeClone = this.contentsRowNode.cloneNode(false);
                    rowContainerCloneNode.appendChild(contentsRowNodeClone);
                }
              
            }
        }
    } else {
        // Update the disabled property client side
        if (props.disabled != null && this.contents) {
            for (var i = 0; i < this.contents.length; i++) {
                var contentWidget = dijit.byId(this.contents[i].id);
                if (contentWidget) {
                    contentWidget.setProps({disabled: props.disabled});
                }
            }
        }
    }
    return true;
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.checkedGroupBase.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.     
    if (this.columns) { props.columns = this.columns; }
    if (this.contents) { props.contents = this.contents; }    
    if (this.disabled != null) { props.disabled = this.disabled; }   
    if (this.id) { props.id = this.id; }
    if (this.label) { props.label = this.label; }    
    if (this.name) { props.name = this.name; }
    if (this.readOnly != null) { props.readOnly = this.readOnly; }  

    return props;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.checkedGroupBase.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {                    
        this.contentsRowNode.id = this.id + "_contentsRowNode";
        this.divContainer.id = this.id + "_divContainer";
        this.labelContainer.id = this.id + "_labelContainer";                            
        this.rowContainer.id = this.id + "_rowContainer";
        this.rowNode.id = this.id + "_rowNode";
        this.tableContainer.id = this.id + "_tableContainer";   
        this.tbodyContainer.id = this.id + "_tbodyContainer";     
    }

    // Show label.
    if (this.label) {
        webui.suntheme.common.setVisibleElement(this.rowNode, true);
    }
    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [align] Alignment of image input.
 * @config {String} [className] CSS selector.
 * @config {int} [columns] 
 * @config {Array} [contents] 
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [label]
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {String} [name] 
 * @config {boolean} [readOnly] Set button as primary if true.
 * @config {String} [style] Specify style rules inline.
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.checkedGroupBase.prototype.setProps = function(props, notify) {
    if (props == null) {
        return false;
    }

    // Replace contents -- do not extend.
    if (props.contents) {
        this.contents = null;
    }

    // Extend widget object for later updates.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.checkedGroupBase.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set label properties.
    if (props.label) {     
        // Set properties.
        props.label.id = this.label.id; // Required for updateFragment().

        // Update/add fragment.
        this.widget.updateFragment(this.labelContainer, props.label);
    }

    // Set contents.    
    if (props.contents || props.disabled != null) {              
        this.addContents(props);   
    }

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.checkboxGroup");


/**
 * @name webui.suntheme.widget.checkboxGroup
 * @extends webui.suntheme.widget.checkedGroupBase
 * @class This class contains functions for the checkboxGroup widget.
 * @constructor This function is used to construct a checkboxGroup widget.
 */
dojo.declare("webui.suntheme.widget.checkboxGroup", webui.suntheme.widget.checkedGroupBase, {
    // Set defaults.
    widgetName: "checkboxGroup" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.checkboxGroup.event =
        webui.suntheme.widget.checkboxGroup.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_checkboxGroup_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_checkboxGroup_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_checkboxGroup_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_checkboxGroup_event_state_end"
    }
}

/**
 * This function is used to obtain the outermost HTML element class name.
 * <p>
 * Note: Selectors should be concatinated in order of precedence (e.g., the 
 * user's className property is always appended last).
 * </p>
 * @return {String} The outermost HTML element class name.
 */
webui.suntheme.widget.checkboxGroup.prototype.getClassName = function() {
    // Set default style.
    var className = (this.columns > 1)
        ? this.widget.getClassName("CBGRP_HORIZ", "")
        : this.widget.getClassName("CBGRP_VERT", "");

    return (this.className)
        ? className + " " + this.className
        : className;
}
dojo.provide("webui.suntheme.widget.selectBase");


/**
 * @name webui.suntheme.widget.selectBase
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for widgets that extend selectBase.
 * @static
 */
dojo.declare("webui.suntheme.widget.selectBase", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    labelOnTop: false,
    titleOptionLabel: "" // Overridden by subclass.
});

/**
 * Helper function to add option and optgroup elements to the HTML select element.
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {Array} [options]
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.selectBase.prototype.addOptions = function(props) {
    var numChildNodes = this.listContainer.options.length;

    // Start with a clean slate.
    //
    // Note: this has to be done. Otherwise, you'll get blank entries in the 
    // drop down for the original empty dojo attach point nodes.
    if (!this.alreadyRemoved) {
        this.listContainer.removeChild(this.optionNode); 
        this.optionGroupContainer.removeChild(this.memberOptionNode);
        this.listContainer.removeChild(this.optionGroupContainer);
        this.alreadyRemoved = true;
    }

    // Cleaning up the old options
    while (this.listContainer.firstChild) {
        this.listContainer.removeChild(this.listContainer.firstChild);
    }

    var thisNode;
    for (var i = 0; i < props.options.length; i++) {
        var pOption = props.options[i];
        if (pOption.group == false) {
            // For some reason, ie is prone to painting problems (esp. after a 
            // style change) when using the DOM to populate options, but 
            // apparently not when the options are in a group
            
            if (webui.suntheme.browser.isIe()) {
                thisNode = new Option();
            } else {
                thisNode = this.optionNode.cloneNode(true);
            }
            
            // Set the properties on this option element
            this.setOptionProps(thisNode, pOption);
            
            // Append this option node to the select
            if (webui.suntheme.browser.isIe()) {
                var idx = this.listContainer.options.length;
                var isSelected = thisNode.selected;
                this.listContainer.options[idx] = thisNode;
                //explicitly set the selected property again!
                //this is necessary to work around a defect in some versions of IE6
                this.listContainer.options[idx].selected = isSelected;
            } else {
                this.listContainer.appendChild(thisNode);
            }
        } else { // group option optgroup
            thisNode = this.optionGroupContainer.cloneNode(true);
            
            // Set the properties on this optgroup element
            this.setGroupOptionProps(thisNode, pOption);
            
            // Append this optgroup node to the select
            this.listContainer.appendChild(thisNode);

            // Add the option elements to this group
            var thisSubNode;
            for (var ix = 0; ix < pOption.options.length; ix++) {
                thisSubNode = this.memberOptionNode.cloneNode(true);
                this.setOptionProps(thisSubNode, pOption.options[ix]);
                thisNode.appendChild(thisSubNode); 
            }
        }
    }
    return true;
}

/**
 * Helper function called by onChange event to set the selected and disabled
 * styles.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.selectBase.prototype.changed = function() { 
    var options = this.listContainer.options;

    if (webui.suntheme.browser.isIe()) { 
        for (var i = 0; i < options.length; ++i) {
            if (options[i].disabled == true && options[i].selected == true) {
                if (this.listContainer.multiple == true) {
                    options[i].selected = false;
                } else {
                    this.listContainer.selectedIndex = -1;
                }
            }
        }  
    }        
    for (var i = 0; i < options.length; ++i) { 
        options[i].className = this.getOptionClassName(options[i]);
    }
    return true; 
}

/**
 * Helper function to obtain class name for the option element
 *
 * @param {Object} option Key-Value pairs of properties.
 * @return {String} The HTML option element class name.
 */
webui.suntheme.widget.selectBase.prototype.getOptionClassName = function(option) {
    // Set default style.
    if (option.separator && option.separator == true) {
        return this.optionSeparatorClassName;
    } else if (option.group && option.group == true) {
        return this.optionGroupClassName;
    } else if (option.disabled && option.disabled == true) {
        return this.optionDisabledClassName;
    } else if (option.selected && option.selected == true) {
        return this.optionSelectedClassName;
    } else {
        return this.optionClassName;
    }
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.selectBase.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Get properties.
    if (this.labelOnTop != null) { props.labelOnTop = this.labelOnTop; }
    if (this.disabled != null) { props.disabled = this.disabled; }
    if (this.label ) { props.label = this.label; }
    if (this.options ) { props.options = this.options; }

    return props;
}

/**
 * Helper function to obtain class name for the HTML select element.
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {boolean} [disabled] true if disabled.
 * @return {String} The HTML select element class name.
 */
webui.suntheme.widget.selectBase.prototype.getSelectClassName = function(props) {    
    return (props.disabled == true)
        ? this.selectDisabledClassName
        : this.selectClassName;
}

/**
 * Returns the HTML select element that makes up the listbox.
 *
 * @return {Node} The HTML select element.
 */
webui.suntheme.widget.selectBase.prototype.getSelectElement = function() {
    return this.listContainer;
}

/**
 * To get the label of the first selected option on the listbox. 
 * If no option is selected, this function returns null.
 * 
 * @return The label of the selected option, or null if none is selected. 
 * @return {String} The label of the selected option.
 */
webui.suntheme.widget.selectBase.prototype.getSelectedLabel = function() { 
    var index = this.listContainer.selectedIndex; 

    if (index == -1) { 
        return null; 
    } else { 
        return this.options[index].label;
    }
}

/**
 * To get the value of the first selected option on the listbox. 
 * If no option is selected, this function returns null. 
 *
 * @return {String} The selected option value or null if none is selected. 
 */
webui.suntheme.widget.selectBase.prototype.getSelectedValue = function() { 
    var index = this.listContainer.selectedIndex; 
    if (index == -1) { 
        return null; 
    } else { 
        return this.listContainer.options[index].value; 
    }
}


/**
 * Helper function to create callback for onChange event.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.selectBase.prototype.onChangeCallback = function(event) {
    if (this.disabled == true) {
        return false;
    }

    // If function returns false, we must prevent the auto-submit.
    var result = (this.listContainer._onchange)
        ? this.listContainer._onchange(event) : true;
    if (result == false) {
        return false;
    }

    // Set style classes.
    return this.changed();
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.selectBase.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {
        this.labelContainer.id = this.id + "_label";
        this.listContainer.id = this.id + "_list";
    }

    // Set public functions.
    this.domNode.getSelectedValue = function() { return dijit.byId(this.id).getSelectedValue(); }
    this.domNode.getSelectedLabel = function() { return dijit.byId(this.id).getSelectedLabel(); }
    this.domNode.getSelectElement = function() { return dijit.byId(this.id).getSelectElement(); }

    // Set events.
    dojo.connect(this.listContainer, "onchange", this, "onChangeCallback");

    return this.inherited("postCreate", arguments);
}

/**
 * Helper function to set properties on optgroup element.
 *
 * @param {Node} element The optgroup DOM node
 * @param {Object} option Key-Value pairs of properties for the optgroup node.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.selectBase.prototype.setGroupOptionProps = function(element, option) {
    element.className = this.getOptionClassName(option);
    element.label = option.label;
  
    if (option.disabled != null) {
        element.disabled = new Boolean(option.disabled).valueOf();
    }
    return true;
}

/**
 * Helpper function to set properties on the option element.
 *
 * @param {Node} element The option DOM node
 * @param {Object} option Key-Value pairs of properties for the option node.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.selectBase.prototype.setOptionProps = function(element, option) {
    element.value = option.value;
    element.className = this.getOptionClassName(option);

    var textToUse = null;
    if (option.isTitle == true) {
       // Prepend and append long dashes with the title label
       textToUse = this.theme.getMessage(this.titleOptionLabel,
           [option.label]).unescapeHTML(); // Prototype method.
    } else {
       textToUse = option.label;
    }

    // If option.escape is true, we want the text to be displayed literally. To 
    // achieve this behavior, do nothing.
    //
    // If option.escape is false, we want any special sequences in the text 
    // (e.g., "&nbsp;") to be displayed as evaluated (i.e., unescaped).
    if (new Boolean(option.escape).valueOf() == false) {
        element.text = textToUse.unescapeHTML(); // Prototype method.
    } else {
        element.text = textToUse;
    }

    if (option.selected != null) {
        element.selected = new Boolean(option.selected).valueOf();
    }
    if (option.disabled != null) {
        element.disabled = new Boolean(option.disabled).valueOf();
    }
    return true;
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.selectBase.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // A web app devleoper could return false in order to cancel the 
    // auto-submit. Thus, we will handle this event via the onChange call back.
    if (props.onChange) {
        // Set private function scope on DOM node.
        this.listContainer._onchange = (typeof props.onChange == 'string')            
            ? new Function("event", props.onChange) : props.onChange;

        // Must be cleared before calling setEventProps() below.
        props.onChange = null;
    }

    // Set the properties specific to the select element
    this.setSelectProps(this.listContainer, props);

    // Add option and optgroup elements in the select element
    if (props.options) {
        this.addOptions(props);
    }

    // Set label properties.
    if (props.label) {
        // Set properties.
        props.label.id = this.label.id; // Required for updateFragment().

        // Update/add fragment.
        this.widget.updateFragment(this.labelContainer, props.label);

        // Remove line break -- required for IE & cannot be updated once set.
        if (new Boolean(this.labelOnTop).valueOf() == true) {
            webui.suntheme.common.setVisibleElement(this.brContainer, true);
        }
    }

    // Set more properties.
    this.setCommonProps(this.listContainer, props);
    this.setEventProps(this.listContainer, props);

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}

/**
 * Helper function to set properties specific to the HTML select element
 *
 * @param {Node} selectNode The HTML select element.
 * @param {Object} props Key-Value pairs of properties.
 * @config {boolean} [disabled]
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.selectBase.prototype.setSelectProps = function(selectNode, props) {
    selectNode.name = selectNode.id;
    if (props.disabled != null) {
        selectNode.disabled = new Boolean(props.disabled).valueOf();
        selectNode.className = this.getSelectClassName(props);
    }
    return true;
}
dojo.provide("webui.suntheme.widget.dropDown");


/**
 * @name webui.suntheme.widget.dropDown
 * @extends webui.suntheme.widget.selectBase
 * @class This class contains functions for the dropDown widget.
 * @constructor This function is used to construct a dropDown widget.
 */
dojo.declare("webui.suntheme.widget.dropDown", webui.suntheme.widget.selectBase, {
    // Set defaults.
    submitForm: false,
    titleOptionLabel: "DropDown.titleOptionLabel",
    widgetName: "dropDown" // Required for theme properties.
});

/**
 * Helper function called by onChange event to set the proper
 * selected, and disabled styles.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.dropDown.prototype.changed = function() {
    if (this.submitForm != true) {
        // Drop down changed.
        return this.inherited("changed", arguments);
    } else {
        // Jump drop down changed.
        var jumpDropdown = this.listContainer; 

        // Find the <form> for this drop down
        var form = jumpDropdown.form; 

        if (typeof form != "undefined" && form != null) { 
            this.submitterHiddenNode.value = "true";

            // Set style classes.
            var options = jumpDropdown.options;
            for (var i = 0; i < options.length; i++) { 
                options[i].className = this.getOptionClassName(options[i]);
            }
            form.submit();
        }
    }
    return true; 
}

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.dropDown.event =
        webui.suntheme.widget.dropDown.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_dropDown_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_dropDown_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_dropDown_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_dropDown_event_state_end"
    },

    /**
     * This object contains submit event topics.
     * @ignore
     */
    submit: {
        /** Submit event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_dropDown_event_submit_begin",

        /** Submit event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_dropDown_event_submit_end"
    }
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.dropDown.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Get properties.
    if (this.submitForm != null) { props.submitForm = this.submitForm; }

    return props;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.dropDown.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {
        this.submitterHiddenNode.id = this.id + "_submitter";
    }
    
    // Set style classes
    if (this.submitForm) {
        if (new Boolean(this.submitForm).valueOf() == true) {
            this.selectClassName = this.widget.getClassName("MENU_JUMP");
            this.selectDisabledClassName = this.widget.getClassName("MENU_JUMP_DISABLED");
            this.optionClassName = this.widget.getClassName("MENU_JUMP_OPTION");
            this.optionDisabledClassName = this.widget.getClassName("MENU_JUMPOPTION_DISABLED");
            this.optionGroupClassName = this.widget.getClassName("MENU_JUMP_OPTION_GROUP");
            this.optionSelectedClassName = this.widget.getClassName("MENU_JUMP_OPTION_SELECTED");
            this.optionSeparatorClassName = this.widget.getClassName("MENU_JUMP_OPTION_SEPARATOR");
        } else {
            this.selectClassName = this.widget.getClassName("MENU_STANDARD");
            this.selectDisabledClassName = this.widget.getClassName("MENU_STANDARD_DISABLED");
            this.optionClassName = this.widget.getClassName("MENU_STANDARD_OPTION");
            this.optionDisabledClassName = this.widget.getClassName("MENU_STANDARD_DISABLED");
            this.optionGroupClassName = this.widget.getClassName("MENU_STANDARD_OPTION_GROUP");
            this.optionSelectedClassName = this.widget.getClassName("MENU_STANDARD_OPTION_SELECTED");
            this.optionSeparatorClassName = this.widget.getClassName("MENU_STANDARD_OPTION_SEPARATOR");
        }
    }
    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [label] 
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {boolean} [multiple] 
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onChange] 
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {String} [onSelect]
 * @config {Array} [options] 
 * @config {int} [size] 
 * @config {String} [style] Specify style rules inline.
 * @config {boolean} [submitForm] 
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.dropDown.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.dropDown.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Add attributes to the hidden input for jump drop down.
    if (props.submitForm != null && props.submitForm == true) {
        this.submitterHiddenNode.name = this.submitterHiddenNode.id;
        this.submitterHiddenNode.value = "false";
    }

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.editableField");


/**
 * @name webui.suntheme.widget.editableField
 * @extends webui.suntheme.widget.textField
 * @class This class contains functions for the editableField widget.
 * @constructor This function is used to construct a editableField widget.
 */
dojo.declare("webui.suntheme.widget.editableField", webui.suntheme.widget.textField, {
    // Set defaults.
    edit: false,
    widgetName: "editableField" // Required for theme properties.
});
   
/**
 * Helper function to disable edit mode.
 *
 * This function will commit or rollback the changes made in the field, and
 * setup appropriate style on the field.
 *
 * @param {boolean} acceptChanges Optional parameter. 
 * <p>
 * If true, the entered data (fieldNode value) will be commited through the Ajax
 * submit() request. 
 * </p><p>
 * If false, the entered data (fieldNode value) will be rolled back to previos 
 * state (value is saved before field enters editable state).
 * </p><p>
 * If not specified, no changes to the field value will made, and only styles 
 * will be modified.
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.editableField.prototype.disableEdit = function(acceptChanges) {
    if (acceptChanges == true) {
        // If savedValue does not exist, we have not edited the field yet
        if (this.autoSave == true && this.savedValue && 
                this.savedValue != this.fieldNode.value) {
            this.submit();
        }
    } else if (acceptChanges == false) {
        if (this.savedValue)   {
            this.fieldNode.value = this.savedValue;
        }
    }
    this.edit = false;
    this.savedValue = null;
    this.fieldNode.className = this.getInputClassName();   
    this.fieldNode.readOnly = true;
    return true;
}

/**
 * Helper function to enable edit mode.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.editableField.prototype.enableEdit = function() {
    // Save the current value.
    this.savedValue = this.fieldNode.value;
        
    this.edit = true;
    this.fieldNode.className = this.getInputClassName();   
    this.fieldNode.readOnly = false;
    this.fieldNode.focus(); // In case function has been called programmatically, not by event.
    this.fieldNode.select();
    return true;
}

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.editableField.event =
        webui.suntheme.widget.editableField.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_editableField_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_editableField_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_editableField_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_editableField_event_state_end"
    },

    /**
     * This object contains submit event topics.
     * @ignore
     */
    submit: {
        /** Submit event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_editableField_event_submit_begin",

        /** Submit event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_editableField_event_submit_end"
    }
}

/**
 * Helper function to obtain HTML input element class names.
 *
 * @return {String} The HTML input element class name.
 */
webui.suntheme.widget.editableField.prototype.getInputClassName = function() {    
    // Set default style.
    if (this.disabled == true) {
        return  this.widget.getClassName("EDITABLE_FIELD_DISABLED","");
    }

    // Apply invalid style.
    var validStyle =  (this.valid == false) 
        ? " " + this.widget.getClassName("EDITABLE_FIELD_INVALID","")
        : ""; 

    return (this.edit == true)
        ? this.widget.getClassName("EDITABLE_FIELD_EDIT","") + validStyle
        : this.widget.getClassName("EDITABLE_FIELD_DEFAULT","") + validStyle;
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.editableField.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.autoSave!= null) { props.autoSave = this.autoSave; }

    return props;
}

/**
 * Process user gesture events on the field, such as dblclick, onblur, 
 * onkeyup, etc.
 *
 * @param {Event} event The JavaScript event
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.editableField.prototype.onEditCallback = function(event) {
    if (event == null) {
        return false;
    }
    if (event.type == "dblclick") {
        this.enableEdit();
        return true;
    }
    if (event.type == "blur") {
        this.disableEdit(true);
        return true;
    }
    if (event.type == "keyup") {
        if (this.edit == false) {
            // Currently in readOnly state.
            // Allow <SPACE> key.
            if (event.keyCode == 32) {
                this.enableEdit();
            }
        } else {
            // Currently in edit state.
            if (event.keyCode == 27) this.disableEdit(false); // ESC
        }
        // Otherwise do not do anything.
        return true;
    }
    return true;    
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.editableField.prototype.postCreate = function () {
    // Set Initial readOnly state.
    this.fieldNode.readOnly = true;

    // Set events.
    dojo.connect(this.fieldNode, "ondblclick", this, "onEditCallback");
    dojo.connect(this.fieldNode, "onblur", this, "onEditCallback");
    dojo.connect(this.fieldNode, "onkeyup", this, "onEditCallback");

    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [accesskey] 
 * @config {boolean} [autoSave]
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [escape] HTML escape button text (default).
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [label]
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {int} [maxLength] 
 * @config {Array} [notify]
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {boolean} [required] 
 * @config {int} [size]
 * @config {String} [style] Specify style rules inline.
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {String} [valid] Value of input.
 * @config {String} [value] Value of input.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.editableField.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.editableField.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Explicitly provided readOnly property must be ignored.
    props.readOnly = null;

    // Set properties.
    if (props.autoSave != null) { this.autoSave = props.autoSave; }

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.hiddenField");


/**
 * @name webui.suntheme.widget.hiddenField
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for the hiddenField widget.
 * @constructor This function is used to construct a hiddenField widget.
 */
dojo.declare("webui.suntheme.widget.hiddenField", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    disabled: false,
    widgetName: "hiddenField" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.hiddenField.event =
        webui.suntheme.widget.hiddenField.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_hiddenField_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_hiddenField_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_hiddenField_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_hiddenField_event_state_end"
    },

    /**
     * This object contains submit event topics.
     * @ignore
     */
    submit: {
        /** Submit event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_hiddenField_event_submit_begin",

        /** Submit event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_hiddenField_event_submit_end"
    }
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.hiddenField.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.disabled != null) { props.disabled = this.disabled; }
    if (this.name) { props.name = this.name; }
    if (this.value) { props.value = this.value; }

    return props;
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [name]
 * @config {String} [value] Value of input.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.hiddenField.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.hiddenField.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set properties.
    if (props.name) { this.domNode.name = props.name; }
    if (props.value) { this.domNode.value = props.value; }
    if (props.disabled != null) { 
        this.domNode.disabled = new Boolean(props.disabled).valueOf();
    }

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.hyperlink");


/**
 * @name webui.suntheme.widget.hyperlink
 * @extends webui.suntheme.widget.anchorBase
 * @class This class contains functions for the hyperlink widget.
 * @constructor This function is used to construct a hyperlink widget.
 */
dojo.declare("webui.suntheme.widget.hyperlink", webui.suntheme.widget.anchorBase, {
    // Set defaults.
    widgetName: "hyperlink" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.hyperlink.event =
        webui.suntheme.widget.hyperlink.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_hyperlink_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_hyperlink_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_hyperlink_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_hyperlink_event_state_end"
    }
}

/**
 * This function is used to obtain the outermost HTML element class name.
 * <p>
 * Note: Selectors should be concatinated in order of precedence (e.g., the 
 * user's className property is always appended last).
 * </p>
 * @return {String} The outermost HTML element class name.
 */
webui.suntheme.widget.hyperlink.prototype.getClassName = function() {
    // Set default style.
    var className = (this.disabled == true)
        ? this.widget.getClassName("HYPERLINK_DISABLED","")
        : this.widget.getClassName("HYPERLINK","");

    return (this.className)
        ? className + " " + this.className
        : className;
}

/**
 * Helper function to create callback for onClick event.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.hyperlink.prototype.onClickCallback = function(event) {
    if (this.disabled == true) {
        event.preventDefault();
        return false;
    }

    // If function returns false, we must prevent the submit.
    var result = (this.domNode._onclick)
        ? this.domNode._onclick(event) : true;
    if (result == false) {
        event.preventDefault();
        return false;
    }
    if (this.href) {
        return false;
    }
    event.preventDefault();
    
    // If a form id isnt provided, use the utility function to
    // obtain the form id.
    if (this.formId == null) {
        var form = this.widget.getForm(this.domNode);
        this.formId = (form) ? form.id : null;
    }
    return this.submitFormData(this.formId, this.params);
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.hyperlink.prototype.postCreate = function () {
    // If the href attribute does not exist, set "#" as the default value of the
    // DOM node.
    this.domNode.href = "#";

    // Create callback function for onClick event.
    dojo.connect(this.domNode, "onclick", this, "onClickCallback");

    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [accessKey]
 * @config {String} [charset]
 * @config {String} [className] CSS selector.
 * @config {Array} [contents]
 * @config {String} [coords]
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [formId] The id of the HTML form element.
 * @config {String} [href]
 * @config {String} [hrefLang]
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {String} [name] 
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {Array} [params] The parameters to be passed during request.
 * @config {String} [rel]
 * @config {String} [rev]
 * @config {String} [shape]
 * @config {String} [style] Specify style rules inline.
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.hyperlink.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function submits the hyperlink if the hyperlink is enabled.
 *
 * @param {String} formId The id of the HTML form element.
 * @param {Array} params The parameters to be passed during request.
 * @return {boolean} false to cancel the JavaScript event.
 */
webui.suntheme.widget.hyperlink.prototype.submitFormData = function (formId, params) {
    var theForm = document.getElementById(formId);
    var oldTarget = theForm.target;
    var oldAction = theForm.action;

    // Obtain HTML element for tab and common task components.
    var link = document.getElementById(this.id);
    if (link == null) {
        link = this.domNode;
    }

    // Set new action URL.
    theForm.action += "?" + link.id + "_submittedLink=" + link.id;               

    // Set new target.
    if (link.target && link.target.length > 0) {
        theForm.target = link.target;
    } else {
        theForm.target = "_self";
    }

    // Append query params to new action URL.
    if (params != null) {
        var x;
        for (var i = 0; i < params.length; i++) {
            x = params[i];
            theForm.action += "&" + params[i] + "=" + params[i + 1]; 
            i++;
        }
    }

    // Submit form.
    theForm.submit(); 

    // Restore target and action URL.
    if (link.target != null) {
        theForm.target = oldTarget;
        theForm.action = oldAction;
    }
    return false;        
}
dojo.provide("webui.suntheme.widget.image");


/**
 * @name webui.suntheme.widget.image
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for the image widget.
 * @constructor This function is used to construct a image widget.
 */
dojo.declare("webui.suntheme.widget.image", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    border: 0,
    widgetName: "image" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.image.event =
        webui.suntheme.widget.image.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_image_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_image_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_image_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_image_event_state_end"
    }
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.image.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.alt) { props.alt = this.alt; }
    if (this.icon) { props.icon = this.icon; }
    if (this.align) { props.align = this.align; }
    if (this.border != null) { props.border = this.border; }
    if (this.height) { props.height = this.height; }
    if (this.hspace) { props.hspace = this.hspace; }
    if (this.longDesc) { props.longDesc = this.longDesc; }
    if (this.src) { props.src = this.src; }
    if (this.vspace) { props.vspace = this.vspace; }
    if (this.width) { props.width = this.width; }

    return props;
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [alt] Alternate text for image input.
 * @config {String} [align] Alignment of image input.
 * @config {String} [border]
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {String} [height] 
 * @config {String} [hspace] 
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {String} [longDesc] 
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {String} [src] 
 * @config {String} [style] Specify style rules inline.
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [visible] Hide or show element.
 * @config {String} [vspace]
 * @config {String} [width]
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.image.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.image.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set properties.
    if (props.icon != null) {
        props = this.widget.getImageProps(props.icon, props);
    }    
    if (props.alt) { this.domNode.alt = props.alt; }
    if (props.align) { this.domNode.align = props.align; }
    if (props.border != null) { this.domNode.border = props.border; }
    if (props.height) { this.domNode.height = props.height; }
    if (props.hspace) { this.domNode.hspace = props.hspace; }
    if (props.longDesc) { this.domNode.longDesc = props.longDesc; }
    if (props.src) { this.domNode.src = props.src; }
    if (props.vspace) { this.domNode.vspace = props.vspace; }
    if (props.width) { this.domNode.width = props.width; }

    // Set more properties.
    this.setCommonProps(this.domNode, props);
    this.setEventProps(this.domNode, props);

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.imageButton");


/**
 * @name webui.suntheme.widget.imageButton
 * @extends webui.suntheme.widget.button
 * @class This class contains functions for the imageButton widget.
 * @constructor This function is used to construct a imageButton widget.
 */
dojo.declare("webui.suntheme.widget.imageButton", webui.suntheme.widget.button, {
    // Set defaults.
    widgetName: "imageButton"  // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.imageButton.event =
        webui.suntheme.widget.imageButton.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_imageButton_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_imageButton_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_imageButton_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_imageButton_event_state_end"
    }
}

/**
 * This function is used to obtain the outermost HTML element class name.
 * <p>
 * Note: Selectors should be concatinated in order of precedence (e.g., the 
 * user's className property is always appended last).
 * </p>
 * @return {String} The outermost HTML element class name.
 */
webui.suntheme.widget.imageButton.prototype.getClassName = function() {
    // If it is an image button, only the BUTTON3 selectors are used.
    // Note that the "mini" and "primary" values can still be set but
    // have no effect on image buttons by policy, vs by theme.
    var key = (this.disabled == true)
	? "BUTTON3_DISABLED"
	: "BUTTON3";

    var className = this.widget.getClassName(key, "");
    return (this.className)
        ? className + " " + this.className
        : className;
}

/**
 * This function is used to obtain the outermost HTML element class name during
 * an onFocus or onMouseOver event.
 * <p>
 * Note: Selectors should be concatinated in order of precedence (e.g., the 
 * user's className property is always appended last).
 * </p>
 * @return {String} The outermost HTML element class name.
 */
webui.suntheme.widget.imageButton.prototype.getHoverClassName = function() {
    // If it is an image button, only the BUTTON3 selectors are used.
    // Note that the "mini" and "primary" values can still be set but
    // have no effect on image buttons by policy, vs by theme.
    var key = "BUTTON3_HOVER";
    var className = this.widget.getClassName(key, "");
    return (this.className)
        ? className + " " + this.className
        : className;
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.imageButton.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.src) { props.src = this.src; }

    return props;
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [alt] Alternate text for image input.
 * @config {String} [align] Alignment of image input.
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [escape] HTML escape button text (default).
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {String} [src] Source for image.
 * @config {String} [style] Specify style rules inline.
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {String} [value] Value of input.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 */
webui.suntheme.widget.imageButton.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.imageButton.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set properties.
    if (props.src) { this.domNode.src = props.src; }

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.imageHyperlink");


/**
 * @name webui.suntheme.widget.imageHyperlink
 * @extends webui.suntheme.widget.hyperlink
 * @class This class contains functions for the imageHyperlink widget.
 * @constructor This function is used to construct a imageHyperlink widget.
 */
dojo.declare("webui.suntheme.widget.imageHyperlink", webui.suntheme.widget.hyperlink, {
    // Set defaults.
    widgetName: "imageHyperlink" // Required for theme properties.
});

/**
 * Helper function to add children.
 *
 * @param props Key-Value pairs of properties.
 * @config {Array} [contents] The contents of the anchor body.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.imageHyperlink.prototype.addContents = function(props) {
    if (props.contents == null) {
        return false;
    }

    // Remove child nodes.
    this.widget.removeChildNodes(this.leftContentsContainer);
    this.widget.removeChildNodes(this.rightContentsContainer);

    // Add contents.
    for (i = 0; i <props.contents.length; i++) {
        this.widget.addFragment(this.leftContentsContainer, props.contents[i], "last");
        this.widget.addFragment(this.rightContentsContainer, props.contents[i], "last");
    }
    return true;
}

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.imageHyperlink.event =
        webui.suntheme.widget.imageHyperlink.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_imageHyperlink_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_imageHyperlink_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_imageHyperlink_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_imageHyperlink_event_state_end"
    }
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.imageHyperlink.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.enabledImage) { props.enabledImage = this.enabledImage; }
    if (this.disabledImage) { props.disabledImage = this.disabledImage; }
    if (this.imagePosition) { props.imagePosition = this.imagePosition; }

    return props;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.imageHyperlink.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {
        this.enabledImageContainer.id = this.id + "_enabled";
        this.disabledImageContainer.id = this.id + "_disabled";
        this.leftContentsContainer.id = this.id + "_leftContents";
        this.rightContentsContainer.id = this.id + "_rightContents";
    }
    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [accessKey]
 * @config {String} [charset]
 * @config {String} [className] CSS selector.
 * @config {Array} [contents]
 * @config {String} [coords]
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {Object} [disabledImage]
 * @config {Object} [enabledImage]
 * @config {String} [href]
 * @config {String} [hrefLang]
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [imagePosition]
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {String} [rel]
 * @config {String} [rev]
 * @config {String} [shape]
 * @config {String} [style] Specify style rules inline.
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.imageHyperlink.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.imageHyperlink.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Show en/disabled images.
    if (props.disabled != null) {
        var disabled = new Boolean(props.disabled).valueOf();

        // We need to hide/show images only when the disabed image is specified.
        if (this.disabledImage) { 
            webui.suntheme.common.setVisibleElement(this.enabledImageContainer, !disabled);
            webui.suntheme.common.setVisibleElement(this.disabledImageContainer, disabled);
        }
    }

    // Add enabled image.
    if (props.enabledImage) {
        this.widget.addFragment(this.enabledImageContainer, props.enabledImage);
    }

    // Add disabled image.
    if (props.disabledImage) {
        this.widget.addFragment(this.disabledImageContainer, props.disabledImage);
    }

    // Set image position.
    if (props.imagePosition) {
        var left = (props.imagePosition == "left") 
        webui.suntheme.common.setVisibleElement(this.leftContentsContainer, !left);
        webui.suntheme.common.setVisibleElement(this.rightContentsContainer, left);    
    }

    // Add contents.
    this.addContents(props);

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.jsfx.common");

/**
 * @class This class contains functions to obtain data asynchronously using JSF
 * Extensions as the underlying transfer protocol.
 * @static
 */
webui.suntheme.widget.jsfx.common = {
    /**
     * This function is used to process refresh events with Object literals.
     *
     * @param props Key-Value pairs of properties.
     * @config {String} [id] The HTML element Id.
     * @config {String} [endTopic] The event topic to publish.
     * @config {String} [execute] The string containing a comma separated list 
     * of client ids against which the execute portion of the request 
     * processing lifecycle must be run.
     * @return {boolean} true if successful; otherwise, false.
     */
    processRefreshEvent: function(props) {
        if (props == null) {
            return false;
        }

        // Dynamic Faces requires a DOM node as the source property.
        var domNode = document.getElementById(props.id);

        // Generate AJAX request using the JSF Extensions library.
        DynaFaces.fireAjaxTransaction(
            (domNode) ? domNode : document.forms[0], {
            execute: (props.execute) ? props.execute : "none",
            render: props.id,
            replaceElement: webui.suntheme.widget.jsfx.common.refreshCallback,
            xjson: {
                id: props.id,
                endTopic: props.endTopic,
                event: "refresh"
            }
        });
        return true;
    },

    /**
     * This function is used to process state change events with Object literals.
     *
     * @param props Key-Value pairs of properties.
     * @config {String} [id] The HTML element Id.
     * @config {String} [endTopic] The event topic to publish.
     * @config {Object} [props] Key-Value pairs of widget properties to update.
     * @return {boolean} true if successful; otherwise, false.
     */
    processStateEvent: function(props) {
        if (props == null) {
            return false;
        }

        // Dynamic Faces requires a DOM node as the source property.
        var domNode = document.getElementById(props.id);

        // Generate AJAX request using the JSF Extensions library.
        DynaFaces.fireAjaxTransaction(
            (domNode) ? domNode : document.forms[0], {
            render: props.id,
            replaceElement: webui.suntheme.widget.jsfx.common.stateCallback,
            xjson: {
                id: props.id,
                endTopic: props.endTopic,
                event: "state",
                props: props.props // Widget properties to update.
            }
        });
        return true;
    },

    /**
     * This function is used to process submit events with Object literals.
     *
     * @param props Key-Value pairs of properties.
     * @config {String} [id] The HTML element Id.
     * @config {String} [endTopic] The event topic to publish.
     * @config {String} [execute] The string containing a comma separated list 
     * of client ids against which the execute portion of the request 
     * processing lifecycle must be run.
     * @return {boolean} true if successful; otherwise, false.
     */
    processSubmitEvent: function(props) {
        if (props == null) {
            return false;
        }

        // Dynamic Faces requires a DOM node as the source property.
        var domNode = document.getElementById(props.id);

        // Generate AJAX request using the JSF Extensions library.
        DynaFaces.fireAjaxTransaction(
            (domNode) ? domNode : document.forms[0], {
            execute: (props.execute) ? props.execute : props.id,
            render: props.id,
            replaceElement: webui.suntheme.widget.jsfx.common.submitCallback,
            xjson: {
                id: props.id,
                endTopic: props.endTopic,
                event: "submit"
            }
        });
        return true;
    }, 
   
    /**
     * This function is used to refresh widgets.
     *
     * @param {String} elementId The HTML element Id.
     * @param {String} content The content returned by the AJAX response.
     * @param {Object} closure The closure argument provided to DynaFaces.fireAjaxTransaction.
     * @param {Object} xjson The xjson argument provided to DynaFaces.fireAjaxTransaction.
     * @return {boolean} true if successful; otherwise, false.
     */
    refreshCallback: function(id, content, closure, xjson) {
        if (id == null || content == null) {
            return false;
        }

        // Parse JSON text.
        var props = JSON.parse(content);

        // Add rows.
        var widget = dijit.byId(id);
        widget.setProps(props);

        // Publish an event for custom AJAX implementations to listen for.
        if (xjson.endTopic) {
            dojo.publish(xjson.endTopic, [props]);
        }
        return true;
    },

    /**
     * This function is a callback to respond to the end of state request.
     * It will only publish submit end event without updating the widget itself.
     *
     * @param {String} elementId The HTML element Id.
     * @param {String} content The content returned by the AJAX response.
     * @param {Object} closure The closure argument provided to DynaFaces.fireAjaxTransaction.
     * @param {Object} xjson The xjson argument provided to DynaFaces.fireAjaxTransaction.
     * @return {boolean} true if successful; otherwise, false.
     */
    stateCallback: function(id, content, closure, xjson) {
        if (id == null || content == null) {
            return false;
        }

        // Parse JSON text.
        var props = JSON.parse(content);
            
        // Publish an event for custom AJAX implementations to listen for.
        if (xjson.endTopic) {
            dojo.publish(xjson.endTopic, [props]);
        }
        return true;
    },

    /**
     * This function is a callback to respond to the end of submit request.
     * It will only publish submit end event without updating the widget itself.
     *
     * @param {String} elementId The HTML element Id.
     * @param {String} content The content returned by the AJAX response.
     * @param {Object} closure The closure argument provided to DynaFaces.fireAjaxTransaction.
     * @param {Object} xjson The xjson argument provided to DynaFaces.fireAjaxTransaction.
     * @return {boolean} true if successful; otherwise, false.
     */
    submitCallback: function(id, content, closure, xjson) {
        if (id == null || content == null) {
            return false;
        }

        // Parse JSON text.
        var props = JSON.parse(content);
            
        // Publish an event for custom AJAX implementations to listen for.
        if (xjson.endTopic) {
            dojo.publish(xjson.endTopic, [props]);
        }
        return true;
    }
}
dojo.provide("webui.suntheme.widget.jsfx.accordion");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.accordion.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.jsfx.accordionTab");


/**
 * @class This class contains functions to obtain data asynchronously using JSF
 * Extensions as the underlying transfer protocol.
 * @static
 */
webui.suntheme.widget.jsfx.accordionTab = {
    /**
     * This function is used to load a tab content asynchronously.
     *
     * @param props Key-Value pairs of properties.
     * @config {String} [id] The HTML element Id.
     * @return {boolean} true if successful; otherwise, false.
     */
    processLoadContentEvent: function(props) {
        if (props == null) {
            return false;
        }

        // Dynamic Faces requires a DOM node as the source property.
        var domNode = document.getElementById(props.id);

        // Generate AJAX request using the JSF Extensions library.
        new DynaFaces.fireAjaxTransaction(
            (domNode) ? domNode : document.forms[0], {
            execute: props.id, // Need to decode hidden field.
            render: props.id,
            replaceElement: webui.suntheme.widget.jsfx.progressBar.loadContentCallback,
            xjson: {
                id: props.id,
                event: "loadContent"
            }
        });
        return true;
    },

    /**
     * This function is used to update tab with the loaded content.
     *
     * @param {String} elementId The HTML element Id.
     * @param {String} content The content returned by the AJAX response.
     * @param {Object} closure The closure argument provided to DynaFaces.fireAjaxTransaction.
     * @param {Object} xjson The xjson argument provided to DynaFaces.fireAjaxTransaction.
     * @return {boolean} true if successful; otherwise, false.
     */
    loadContentCallback: function(id, content, closure, xjson) {
        if (id == null || content == null) {
            return false;
        }

        // Parse JSON text.
        var json = JSON.parse(content);

        // Set progress.
        var widget = dijit.byId(id);
        widget.setProps(json);

        // Publish an event for custom AJAX implementations to listen for.
        dojo.publish(webui.suntheme.widget.accordionTab.event.load.endTopic, [json]);
        return true;
    }
}

// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.accordionTab.event.load.beginTopic,
    webui.suntheme.widget.jsfx.accordionTab, "processLoadContentEvent");
dojo.subscribe(webui.suntheme.widget.accordionTab.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.jsfx.alarm");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.alarm.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.jsfx.alert");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.alert.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.jsfx.anchor");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.anchor.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.jsfx.bubble");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.bubble.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.jsfx.button");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.button.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.jsfx.calendar");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.calendar.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.jsfx.calendarField");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.calendarField.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.jsfx.checkbox");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.checkbox.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.subscribe(webui.suntheme.widget.checkbox.event.submit.beginTopic,
    webui.suntheme.widget.jsfx.common, "processSubmitEvent");
dojo.provide("webui.suntheme.widget.jsfx.checkboxGroup");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.checkboxGroup.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.jsfx.dropDown");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.dropDown.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.subscribe(webui.suntheme.widget.dropDown.event.submit.beginTopic,
    webui.suntheme.widget.jsfx.common, "processSubmitEvent");
dojo.provide("webui.suntheme.widget.jsfx.editableField");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.editableField.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.subscribe(webui.suntheme.widget.editableField.event.submit.beginTopic,
    webui.suntheme.widget.jsfx.common, "processSubmitEvent");
dojo.provide("webui.suntheme.widget.jsfx.hiddenField");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.hiddenField.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.subscribe(webui.suntheme.widget.hiddenField.event.submit.beginTopic,
    webui.suntheme.widget.jsfx.common, "processSubmitEvent");
dojo.provide("webui.suntheme.widget.jsfx.hyperlink");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.hyperlink.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.jsfx.image");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.image.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.jsfx.imageButton");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.imageButton.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.jsfx.imageHyperlink");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.imageHyperlink.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.label");


/**
 * @name webui.suntheme.widget.label
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for the label widget.
 * @constructor This function is used to construct a label widget.
 */
dojo.declare("webui.suntheme.widget.label", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    level: 2,
    required: false,
    valid: true,
    widgetName: "label" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.label.event =
        webui.suntheme.widget.label.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_label_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_label_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_label_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_label_event_state_end"
    }
}

/**
 * This function is used to obtain the outermost HTML element class name.
 * <p>
 * Note: Selectors should be concatinated in order of precedence (e.g., the 
 * user's className property is always appended last).
 * </p>
 * @return {String} The outermost HTML element class name.
 */
webui.suntheme.widget.label.prototype.getClassName = function() {
    var key = "LABEL_LEVEL_TWO_TEXT";

    if (this.valid == false) {
        key = "CONTENT_ERROR_LABEL_TEXT";
    } else if (this.level == 1) {
        key = "LABEL_LEVEL_ONE_TEXT";
    } else if (this.level == 3) {
        key = "LABEL_LEVEL_THREE_TEXT";
    }

    // Get theme property.
    var className = this.theme.getClassName(key);
    if (className == null || className.length == 0) {
	return this.className;
    }
    return (this.className)
        ? className + " " + this.className
        : className;
}

/**
 * This function is used to process notification events with Object
 * literals.
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [detail] Message detail text.
 * @config {boolean} [valid] Flag indicating validation state.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.label.prototype.notify = function(props) {
    if (props == null) {
        return false;
    }
    return this.setProps({
        valid: props.valid,
        errorImage: {
            title: props.detail
        }
    });
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.label.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.contents) { props.contents = this.contents; }
    if (this.errorImage) { props.errorImage = this.errorImage; }
    if (this.htmlFor) { props.htmlFor = this.htmlFor; }
    if (this.level != null) { props.level = this.level; }
    if (this.required != null) { props.required = this.required; }
    if (this.requiredImage) { props.requiredImage = this.requiredImage; }
    if (this.valid != null) { props.valid = this.valid; }
    if (this.value) { props.value = this.value; }

    return props;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.label.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {
        this.contentsContainer.id = this.id + "_contentsContainer";
        this.errorImageContainer.id = this.id + "_errorImageContainer";
        this.requiredImageContainer.id = this.id + "_requiredImageContainer";
        this.valueContainer.id = this.id + "_valueContainer";
    }

    // If errorImage or requiredImage are null, create images from the theme.
    // When the _setProps() function is called, image widgets will be
    // instantiated via the props param. 
    if (this.errorImage == null) {
	this.errorImage = this.widget.getImageProps("LABEL_INVALID_ICON", {
            id: this.id + "_error"
        });
    }
    if (this.requiredImage == null) {
	this.requiredImage = this.widget.getImageProps("LABEL_REQUIRED_ICON", {
            id: this.id + "_required"
        });
    }
    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [accesskey]
 * @config {String} [className] CSS selector.
 * @config {String} [contents] 
 * @config {String} [dir] Specifies the directionality of text.
 * @config {Object} [errorImage] 
 * @config {String} [htmlFor] 
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {int} [level] 
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {boolean} [primary] Set button as primary if true.
 * @config {boolean} [required]
 * @config {Object} [requiredImage]
 * @config {String} [style] Specify style rules inline.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [valid]
 * @config {String} [value] Value of input.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.label.prototype.setProps = function(props, notify) {
    if (props == null) {
        return false;
    }

    // Replace contents -- do not extend.
    if (props.contents) {
        this.contents = null;
    }

    // Extend widget object for later updates.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.label.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set properties.
    if (props.htmlFor) { this.domNode.htmlFor = props.htmlFor; }
    if (props.valid != null) { this.valid = new Boolean(props.valid).valueOf(); }
    if (props.required != null) { this.required = new Boolean(props.required).valueOf(); }
    if (props.value) { this.widget.addFragment(this.valueContainer, props.value); }

    // Set error image properties.
    if (props.errorImage || props.valid != null) {
        // Ensure property exists so we can call setProps just once.
        if (props.errorImage == null) {
            props.errorImage = {}; // Avoid updating all props using "this" keyword.
        }

        // Set properties.
        props.errorImage.id = this.errorImage.id; // Required for updateFragment().
        props.errorImage.visible = !this.valid;

        // Update/add fragment.
        this.widget.updateFragment(this.errorImageContainer, props.errorImage);
    }

    // Set required image properties.
    if (props.requiredImage || props.required != null) {
        // Ensure property exists so we can call setProps just once.
        if (props.requiredImage == null) {
            props.requiredImage = {}; // Avoid updating all props using "this" keyword.
        }

        // Set properties.
        props.requiredImage.id = this.requiredImage.id; // Required for updateFragment().
        props.requiredImage.visible = this.required;

        // Update/add fragment.
        this.widget.updateFragment(this.requiredImageContainer, props.requiredImage);
    }

    // Set contents.
    if (props.contents) {
        // Remove child nodes.
        this.widget.removeChildNodes(this.contentsContainer);

	for (var i = 0; i < props.contents.length; i++) {
            this.widget.addFragment(this.contentsContainer, props.contents[i], "last");
        }
    }

    // Set more properties.
    this.setCommonProps(this.domNode, props);
    this.setEventProps(this.domNode, props);

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.jsfx.label");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.label.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.listbox");


/**
 * @name webui.suntheme.widget.listbox
 * @extends webui.suntheme.widget.selectBase
 * @class This class contains functions for the listbox widget.
 * @constructor This function is used to construct a listbox widget.
 */
dojo.declare("webui.suntheme.widget.listbox", webui.suntheme.widget.selectBase, {
    // Set defaults.
    monospace: false,
    multiple: false,
    size: 12,
    titleOptionLabel: "ListSelector.titleOptionLabel",
    widgetName: "listbox" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.listbox.event =
        webui.suntheme.widget.listbox.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_listbox_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_listbox_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_listbox_event_state_begin",

        /** event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_listbox_event_state_end"
    },

    /**
     * This object contains submit event topics.
     * @ignore
     */
    submit: {
        /** event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_listbox_event_submit_begin",

        /** event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_listbox_event_submit_end"
    }
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.listbox.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Get properties.
    if (this.size != null) { props.size = this.size; }
    if (this.multiple != null) { props.multiple = this.multiple; }
    if (this.monospace != null) { props.monospace = this.monospace; }

    return props;
}

/**
 * Helper function to obtain class name for the HTML select element.
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {boolean} [disabled]
 * @config {boolean} [monospace]
 * @return {String} The HTML select element class name.
 */
webui.suntheme.widget.listbox.prototype.getSelectClassName = function(props) {    
    // Set default style.
    if (props.monospace == true) {
        return (props.disabled == true)
            ? this.selectMonospaceDisabledClassName
            : this.selectMonospaceClassName;
    }
    return this.inherited("getSelectClassName", arguments);
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.listbox.prototype.postCreate = function () {
    // Set style classes.
    this.selectClassName = this.widget.getClassName("LIST");
    this.selectDisabledClassName = this.widget.getClassName("LIST_DISABLED");
    this.selectMonospaceClassName = this.widget.getClassName("LIST_MONOSPACE");
    this.selectMonospaceDisabledClassName = this.widget.getClassName("LIST_MONOSPACE_DISABLED");
    this.optionClassName = this.widget.getClassName("LIST_OPTION");
    this.optionDisabledClassName = this.widget.getClassName("LIST_OPTION_DISABLED");
    this.optionGroupClassName = this.widget.getClassName("LIST_OPTION_GROUP");
    this.optionSelectedClassName = this.widget.getClassName("LIST_OPTION_SELECTED");
    this.optionSeparatorClassName = this.widget.getClassName("LIST_OPTION_SEPARATOR");

    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [label] 
 * @config {String} [labelOnTop]
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {boolean} [multiple] 
 * @config {boolean} [monospace] 
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onChange] 
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {String} [onSelect]
 * @config {Array} [options] 
 * @config {int} [size] 
 * @config {String} [style] Specify style rules inline. 
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.listbox.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * Helper function to set properties specific to the HTML select element
 *
 * @param {Node} selectNode The HTML select element.
 * @param {Object} props Key-Value pairs of properties.
 * @config {boolean} [disabled]
 * @config {boolean} [monospace]  
 * @config {boolean} [multiple] 
 * @config {boolean} [size]
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.listbox.prototype.setSelectProps = function(selectNode, props) {
    if (props.size != null) {
        selectNode.size = (props.size < 1) ? 12 : props.size;  
    }
    if (props.multiple != null) {
        selectNode.multiple = new Boolean(props.multiple).valueOf();
    }
    return this.inherited("setSelectProps", arguments);
}
dojo.provide("webui.suntheme.widget.jsfx.listbox");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.listbox.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.subscribe(webui.suntheme.widget.listbox.event.submit.beginTopic,
    webui.suntheme.widget.jsfx.common, "processSubmitEvent");
dojo.provide("webui.suntheme.widget.menuBase");


/**
 * @name webui.suntheme.widget.menuBase
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for widgets that extend menuBase.
 * @static
 */
dojo.declare("webui.suntheme.widget.menuBase", webui.suntheme.widget.widgetBase);

/**
 * Add the specified options to the dom element.
 *
 * @param {Node} menuNode The node to which the menu items are to be added.
 * @param {Object} props Key-Value pairs of properties.
 * @config {Array} [options] 
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.menuBase.prototype.addOptions = function(menuNode, props) {
    var groupNode, optionNode, separator, sepNode;
    for (var i = 0; i < props.options.length; i++) {
        
        // create an li node which will represent an option element.
        optionNode = this.optionContainer.cloneNode(false);   
        optionNode.id = this.id + "_" + props.options[i].label + "_container";                
        this.setOptionNodeProps(optionNode, props.options[i], i);

        // Append the li element to the menu element.        
        menuNode.appendChild(optionNode);
        if (props.options[i].group == true) {
            optionNode.group = true;
            groupNode = this.groupOptionContainer.cloneNode(false);
            menuNode.appendChild(groupNode);
            this.addOptions(groupNode, props.options[i]);
        }
        
        if (props.options[i].separator == true) {
            separator = this.menuSeparatorContainer.cloneNode(true);
            if (webui.suntheme.browser.isIe5up()) {
                var sep = this.menuSeparator.cloneNode(true);
                separator.appendChild(sep);
            }
            webui.suntheme.common.setVisibleElement(separator, true);             
            menuNode.appendChild(separator);
        }
    }
    return true;    
}

/**
 * The callback function for clicking on a menu item.
 *
 * @param {String} optionId The id of the option element that is clicked.
 * @return {Function} The callback function.
 */
webui.suntheme.widget.menuBase.prototype.createOnClickCallback = function(optionId) {
    var _id = this.id;

    /**
     * New literals are created every time this function is called, and it's 
     * saved by closure magic.
     *
     * @param {Event} event The JavaScript event.
     */
    return function(event) {
        // Get hold of the particular option element.
        var elem = document.getElementById(optionId);
        if (elem == null) {
            return;
        }        

        // Get hold of the menu element.
        var widget = dijit.byId(_id);                
        var val = elem.selectValue;
        var dis = elem.disabled;       
        var group = elem.group;
        
        // process the action only if the menu is not disabled and the selected
        // option is not a group header.
        if (!dis && !group) {
            widget.processOnClickEvent(val);
        }         
    }        
}

/**
 * The callback function for key press on a menu item.
 *
 * @param {String} nodeId The id of the option element that is clicked
 * @return {Function} The callback function. 
 */
webui.suntheme.widget.menuBase.prototype.createOnKeyDownCallBack = function(nodeId) {
    if (nodeId == null) {
        return;
    }
    
    var id = this.id;
    var _widget = this.widget;
    return function(event) {
       var elem = document.getElementById(nodeId);
       if (elem == null) {
          return;
       }
        var widget = dijit.byId(id);

        // If the menu is not visible, we do not need to capture
        // key press events.
        if (!webui.suntheme.common.isVisibleElement(widget.domNode)) {
            return;
        }
        event = _widget.getEvent(event);
        var keyCode = _widget.getKeyCode(event);
        
        // if onkeypress returns false, we do not traverse the menu.
        var keyPressResult = true;
        if (this._onkeypress) {
            keyPressResult = (this._onkeypress) ? this._onkeypress() : true;
        }        
        
        if (keyPressResult != false) {
            widget.traverseMenu(keyCode, event, nodeId);
        }
        return true;
    }
}

/**
 * Handles the on mouse out for each menuitem.
 *
 * @param {Node} menuItem The DOM node associated with the menu item.
 * @return {Function} The callback function.
 */
webui.suntheme.widget.menuBase.prototype.createOnMouseOutCallBack = function(menuItem) {
    if (menuItem == null) {
        return null;
    }
    var _id = this.id;
 
    /**
     * New literals are created every time this function is called, and it's 
     * saved by closure magic.
     *
     * @param {Event} event The JavaScript event.
     */
    return function(event) {
        var widget = dijit.byId(_id);
        menuItem.className = widget.theme.getClassName("MENU_GROUP_CONTAINER");
    }
}

/**
 * Handles the on mouse over for each menuitem.
 *
 * @param {Node} menuItem The DOM node associated with the menu item.
 * @return {Function} The callback function.
 */
webui.suntheme.widget.menuBase.prototype.createOnMouseOverCallBack = function(menuItem) {
    if (menuItem == null) {
        return null;
    }
    var _id = this.id;

    /**
     * New literals are created every time this function is called, and it's 
     * saved by closure magic.
     *
     * @param {Event} event The JavaScript event.
     */
    return function(event) {
        var widget = dijit.byId(_id);
        menuItem.className = menuItem.className + " " + 
                    widget.theme.getClassName("MENU_ITEM_HOVER");            
            if (widget != null) {
                
                //Mozilla browser (not firefox/seamonkey) do not support focus/blur
                // for divs                
                if (document.getElementById(widget.menuId[widget.focusPosition]).blur) {
                    document.getElementById(widget.menuId[widget.focusPosition]).blur();
                }                
                if (!(menuItem.id == widget.menuId[widget.focusPosition])) {
                    document.getElementById(widget.menuId[widget.focusPosition]).className =
                        widget.theme.getClassName("MENU_GROUP_CONTAINER");
                }
            }
            if (webui.suntheme.browser.isIe5up() ) {
                menuItem.className = menuItem.className + " " + 
                    widget.theme.getClassName("MENU_ITEM_HOVER");            
            }
    }
}

/**
 * Calculate the maximum width of menu to be set.
 * If a menu if at all contains a group, then set the containsGroup
 * attribute to true. This will help in adding an extra pixel width
 * while assigning the width of th menu. This is to account for the
 * indentation of the menu.
 *
 * @param {Array} props 
 * @return {int} The max menu width.
 */
webui.suntheme.widget.menuBase.prototype.getMaxWidth = function(props) {
    var menuWidth = 0;
    var maxWidth = 0;
    for (var i = 0; i < props.length; i++) {
         menuWidth = props[i].label.length;
         if (menuWidth > maxWidth) {
            maxWidth = menuWidth;
         }
         if (props[i].image != null) {
            this.hasImage = true;
         }
         if (props[i].group) {
            this.containsGroup = true;
            maxWidth = this.getMaxWidth(props[i].options);
         }
    }
    return maxWidth;
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.menuBase.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    if (this.options) { props.options = this.options; }
    if (this.formId) { props.formId = this.formId; }
    if (this.submitForm) { props.submitForm = this.submitForm; }  

    return props;
}

/**
 * Returns the currently selected item in the menu.
 *
 * @return {String} The selected item.
 */
webui.suntheme.widget.menuBase.prototype.getSelectedValue = function() {
    if (this.clickedItem) {
        return this.clickedItem;
    } else {
        return null;
    }
}

/**
 * This function is used to obtain the outermost HTML element style.
 * <p>
 * Note: Styles should be concatinated in order of precedence (e.g., the 
 * user's style property is always appended last).
 * </p>
 * @return {String} The outermost HTML element style.
 */
webui.suntheme.widget.menuBase.prototype.getStyle = function() {
    var style = "width:" + this.maxWidth + "em;";

    // Since the style has to be recalculated each and every time the options
    // are changed, you cannot rely on the the existing style on the dom element
    // to see whether style was already defined. Hence use a separate variable
    // which sets itself to true if the widget sets the width/
    if (!this.widthSet) {
    	var st = this.style;
	var reg = ";?[\\t]*width[\\t]*:";
	if (st != null) {
	    var res = st.match(reg);

            // Return user's style if width is set already.
            if (this.style && res != null) {
                return this.style;
            }
	}
    }
    this.widthSet = true;
    return (this.style)
        ? style + this.style
        : style;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.menuBase.prototype.postCreate = function () {
    // Set public functions.
    this.domNode.getSelectedValue = function(props, optionNode) { return dijit.byId(this.id).getSelectedValue(); }
    this.focusPosition = 0;        
    return this.inherited("postCreate", arguments);
}

/**
 * Process the enter key press event.Evaluvate the keyPress/keyDown (for non-IE/IE browsers)
 * and traverse through the menu. Also, if onChange is specified, evaluvate that and 
 * submit the form if submitForm is specified to true.
 *
 * @param (String) value The "value" of the selected option. 
 * @return {boolean} true The enter key press event completed successfully
 */
webui.suntheme.widget.menuBase.prototype.processEnterKeyPressEvent = function(value) {
    var changeResult = true;

    // Check whether the selected value is different than the one previously selected
    var bool = (value == this.getSelectedValue());
    this.setSelectedValue(value);

    if (this._onchange && !bool) {    
        // If function returns false, we must prevent the request.       
        changeResult = (this._onchange) ? this._onchange() : true;
    }
    
    // Functions may sometime return without a value in which case the value
    // of the boolean variable may become undefined. 
    if (changeResult != false) {
        if (this.submitForm) {
            this.submitFormData();
        }  
    }
    return true;
}    
    
/**
 * This function executes the onchange and onclick event handlers if provided by 
 * the developer. It then either submits the form if submitForm attribute is 
 * specified to true.
 *
 * @param {String} value The selected value.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.menuBase.prototype.processOnClickEvent = function(value) {
    var clickResult = true;
    var changeResult = true;
    
    //Check if the selected value has changed from the previous selection.
    var bool = (value == this.getSelectedValue());
    this.setSelectedValue(value);

    if (this._onclick) {
        clickResult = (this._onclick) ? this._onclick() : true;
    }
    if (this._onchange && !bool) {    
        // If function returns false, we must prevent the request.       
        changeResult = (this._onchange) ? this._onchange() : true;
    }

    // Functions may sometime return without a value in which case the value
    // of the boolean variable may become undefined. 
    if (clickResult != false && changeResult != false) {
        if (this.submitForm) {
            this.submitFormData();
        }  
    }
    return true;
}

/**
 * Set the appropriate class name on the menu item container.
 *
 * @param {Node} menuItemContainer The container for the menu item.
 * @param {Object} props Key-Value pairs of properties.
 * @config {boolean} [disabled] 
 * @config {boolean} [group]
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.menuBase.prototype.setMenuNodeClassName = function(
        menuItemContainer, props) {        
    if (new Boolean(props.group).valueOf() == true) {
        menuItemContainer.className = this.theme.getClassName("MENU_GROUP_HEADER");
    } else if (new Boolean(props.disabled).valueOf() == true) {
        menuItemContainer.className = this.theme.getClassName("MENU_ITEM_DISABLED");
    } else {
        menuItemContainer.className = this.theme.getClassName("MENU_GROUP_CONTAINER");        

        // Whenever mouse over/out happens, focus must be set on the menu accordingly.
        // Apply an hack for IE for mouse hover on the div element since div:hover type
        // of css declarations do not seem to work. onmouseover and onmouseout events
        // are attached with the div element and a style class is applied each time a
        // mouseover happens. This style represents the "hover" class.
        // Note that the "this" in these functions represent the menuItem's "div" element
        // and not the "menu" widget element.
            dojo.connect(menuItemContainer, "onmouseover",
                this.createOnMouseOverCallBack(menuItemContainer));
            dojo.connect(menuItemContainer, "onmouseout",
                this.createOnMouseOutCallBack(menuItemContainer));

    }
    return true;
}

/**
 * Helper function to set the properties of an option item. This is invoked
 * by the addOptions function call.
 *
 * @param optionNode The node for which the option is to be added.
 * @param {Object} props Key-Value pairs of properties.
 * @param {String} number The position of the option item in the menu.
 * @config {boolean} [disabled] 
 * @config {boolean} [escape]
 * @config {Object} [image]
 * @config {String} [label]
 * @config {String} [title]
 * @config {String} [value]
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.menuBase.prototype.setOptionNodeProps = function(optionNode, props, number) {
    optionNode.id = this.id + "_" + props.value + "_container";
    var menuItemContainer = this.menuItemContainer.cloneNode(false);
    menuItemContainer.id = optionNode.id + "_label";

    // depending on the kind of node, assign the appropriate style
    // for the menu node.
    this.setMenuNodeClassName(menuItemContainer, props);
    optionNode.appendChild(menuItemContainer);
    
    // valueNode contains a div element which will hold the option.
    var valueNode = this.menuItemNode.cloneNode(false);  
    valueNode.id = this.id + "_" + props.value;

    if (!(new Boolean(props.group).valueOf() == true) && 
            !(new Boolean(props.disabled).valueOf() == true)) {
        this.menuId[this.menuItemCount++] = menuItemContainer.id;   
    }

    menuItemContainer.tabIndex = -1;
    valueNode.tabIndex = -1;

    menuItemContainer.selectValue = props.value;
    menuItemContainer.disabled = props.disabled;
    menuItemContainer.group = props.group;
    menuItemContainer.title = props.label;
    valueNode.title = props.label;
    
    if (valueNode.setAttributeNS) {
        valueNode.setAttributeNS(
            "http://www.w3.org/2005/07/aaa", "posinset", number);
    }

    if (valueNode.setAttributeNS) {
        valueNode.setAttributeNS(
            "http://www.w3.org/2005/07/aaa", "disabled", props.disabled);
    }
        
    //Create callback function for onkeydown event.
    dojo.connect(menuItemContainer, "onkeydown", 
        this.createOnKeyDownCallBack(menuItemContainer.id));         
        
     //Create callback function for onClick event.
     dojo.connect(menuItemContainer, "onclick",
        this.createOnClickCallback(menuItemContainer.id));
        
    // Set label value.
    if (props.label) {
        this.widget.addFragment(valueNode, props.label, "last", props.escape);
    }    

    // Set title.
    if (props.title) {
        optionNode.title = props.title;
    }
    
    // By default have the no image container cloned and kept
    // If an image is present, then replace that with the span
    // placeholder for the image.
    if (new Boolean(this.hasImage).valueOf() == true) {
        var imageNode = this.menuItemNoImageContainer.cloneNode(false);
        if (props.image != null) {
            // Add the widget
            imageNode = this.menuItemImageContainer.cloneNode(false);
            props.image.className = this.theme.getClassName("MENU_ITEM_IMAGE");
            this.widget.addFragment(imageNode, props.image);
        } 
        menuItemContainer.appendChild(imageNode);
    }
    
    // Append the placeholder image node.
    menuItemContainer.appendChild(this.menuItemSubMenu.cloneNode(false));

    // Append the div element to the li element.           
    menuItemContainer.appendChild(valueNode);
    return true;
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [accessKey] 
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {String} [formId] 
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {Array} [options] 
 * @config {boolean} [primary] Set button as primary if true.
 * @config {String} [style] Specify style rules inline.
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.menuBase.prototype.setProps = function(props, notify) {
    if (props == null) {
        return false;
    }
    
    // Replace options -- do not extend.
    if (props.options) {
        this.options = null;
    }

    // Extend widget object for later updates.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.menuBase.prototype._setProps = function(props){
    if (props == null) {
        return false;
    }

    // A web app devleoper could return false in order to cancel the 
    // submit. Thus, we will handle this event via the onClick call back.
    if (props.onChange) {
        // Set private function scope on widget.
        this._onchange = (typeof props.onChange == 'string')
            ? new Function(props.onChange) : props.onChange;

        // Must be cleared before calling setJavaScriptProps() below.
        props.onChange = null;
    }

    // A web app devleoper could return false in order to cancel the 
    // submit. Thus, we will handle this event via the onClick call back.
    if (props.onClick) {
        
        // Set private function scope on widget.
        this._onclick = (typeof props.onClick == 'string')
            ? new Function(props.onClick) : props.onClick;

        // Must be cleared before calling setJavaScriptProps() below.
        props.onClick = null;
    }
    
    // Add options
    if (props.options) {
        
        // Try to set the width of the menu here.
        this.maxWidth = this.getMaxWidth(props.options);        
        
        // Account for image width if one exists. This property can be got from the
	// theme
        if (this.hasImage) {
            var placeHolderWidth = parseFloat(this.theme.getMessage("Menu.placeHolderImageWidth"));
            this.maxWidth += placeHolderWidth; 
        }       
   
        // If an menuGroup exists, then add one character width. Otherwise menu
        // does not scale properly
        if (this.containsGroup) {
            this.maxWidth += 1;
        }
             
        this.widget.removeChildNodes(this.outerMenuContainer);
        this.menuId = [];
        this.menuItemCount = 0;
        
        // Clone the menu node and add it to the outer container.
        var menuNode = this.groupOptionContainer.cloneNode(false);
        menuNode.className = this.theme.getClassName("MENU_CONTAINER");
        this.outerMenuContainer.appendChild(menuNode);         
        this.addOptions(menuNode, props);
    }

    // Need to redo style calculation if style or options
    // have been specified.
    if (props.style || props.options) {
        props.style = this.getStyle();
    }

    // Set more properties.
    this.setCommonProps(this.domNode, props);
    this.setEventProps(this.domNode, props);
    
    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}

/**
 * Set the selected item on the widget.
 *
 * @param {String} item The selected value.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.menuBase.prototype.setSelectedValue = function(item) {
    this.clickedItem = item;
    return true;
}

/**
 * Submits the form. Appends the value of the selected item in the request url.
 *
 * @return {boolean} false to cancel the JavaScript event.
 */
webui.suntheme.widget.menuBase.prototype.submitFormData = function () {
    var theForm = document.getElementById(this.formId);
    var oldAction = theForm.action;
    var oldTarget = theForm.target;
       
    // Specify which option in the menu was clicked.
    theForm.action += "?" + this.id + "_submittedValue=" + this.getSelectedValue();
    theForm.target = "_self";
    theForm.submit();     
    theForm.action = oldAction;
    theForm.target = oldTarget;
    return false;
}

/**
 * This function takes care of traversing through the menu items depending
 * on which key is pressed.
 *
 * @param (String) keyCode The valye of the key which was pressed
 * @param (Event) event The key press event.
 * @param (String) nodeId The id of the menu item. 
 * @return {boolean} true Propagate the javascript event
 */
webui.suntheme.widget.menuBase.prototype.traverseMenu = function(keyCode, event, nodeId) {
    var arr = this.menuId;
    var elem = document.getElementById(nodeId);
    var focusElem = document.getElementById(arr[this.focusPosition]);
    
    if (focusElem.blur) {
        focusElem.blur();
    }
    // Operations to be performed if the arrow keys are pressed.
    if (keyCode >= 37 && keyCode <= 40) {
        
        // Check whether up arrow was pressed.
        if (keyCode == 38) {
            focusElem.className = this.theme.getClassName("MENU_GROUP_CONTAINER");
            this.focusPosition--;
            if (this.focusPosition < 0) {
                this.focusPosition = arr.length-1;
            }
            focusElem = document.getElementById(arr[this.focusPosition]); 

        // Check whether down arrow was pressed
        } else if (keyCode == 40) {
            focusElem.className = 
                this.theme.getClassName("MENU_GROUP_CONTAINER");
            this.focusPosition++;
            if (this.focusPosition == arr.length) {
                this.focusPosition = 0;
            }
            focusElem = document.getElementById(arr[this.focusPosition]);
        }   
        
    if (focusElem.focus) {
        focusElem.focus();
    }        
        focusElem.className = focusElem.className + " "+
            this.theme.getClassName("MENU_FOCUS");           
    // Check if enter key was pressed    
    } else if(keyCode == 13){
        focusElem.className =
            this.theme.getClassName("MENU_GROUP_CONTAINER");
        var val = elem.selectValue;
        this.processEnterKeyPressEvent(val);
       
    }    
    if (webui.suntheme.browser.isIe5up()) {
        window. event.cancelBubble = true;
        window.event.returnValue = false;
    } else {
        event.stopPropagation();
        event.preventDefault();
    }
    return true;
}
dojo.provide("webui.suntheme.widget.popupMenu");


/**
 * @name webui.suntheme.widget.popupMenu
 * @extends webui.suntheme.widget.menuBase
 * @class This class contains functions for the popupMenu widget.
 * @constructor This function is used to construct a popupMenu widget.
 */
dojo.declare("webui.suntheme.widget.popupMenu", webui.suntheme.widget.menuBase, {
    // Set defaults.
    widgetName: "popupMenu" // Required for theme properties.
});

/**
 * Close the menu. Sets the visibility to false.
 *
 * @return {boolean} false to cancel the JavaScript event.
 */
webui.suntheme.widget.popupMenu.prototype.close = function() {
    if (webui.suntheme.common.isVisibleElement(this.domNode)) {
        if (webui.suntheme.widget.popupMenu.activeMenuId) {
            webui.suntheme.widget.popupMenu.activeMenuId = null;
        }
        if (this.target != null) {
            if (this.target.focus) {
                this.target.focus();
            }   
        }        
        return this.setProps({visible: false});
    }
    return false;    
}

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.popupMenu.event =
        webui.suntheme.widget.popupMenu.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_popupMenu_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_popupMenu_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_popupMenu_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_popupMenu_event_state_end"
    },

    /**
     * This object contains submit event topics.
     * @ignore
     */
    submit: {
        /** Submit event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_popupMenu_event_submit_begin",

        /** Submit event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_popupMenu_event_submit_end"
    }
}

/**
 * Helper function to create callback to close menu.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.popupMenu.prototype.onCloseMenuCallBack = function(event) {
    // Capture the click and see whether it falls within the boundary of the menu
    // if so do not close the menu.
    var evt = (event) 
        ? event : ((window.event) 
            ? window.event : null);

    var target = (evt.target) 
        ? evt.target 
        : ((evt.srcElement) 
            ? evt.srcElement : null);
        
    // If key pressed and it's NOT the escape key, do NOT cancel.
    if ((evt.type == "keydown") && (evt.keyCode != 27)) {
        return false;
    }
        
    // If the event occured on the menu, do NOT cancel.
    // Instead we let the event propagate to the MenuItem handler.
    // Cannot use 
    while (target != null) {
        if (target.className == "Menu_sun4") {
            return false;
        }
        target = target.parentNode;
    }

    // The above will not catch events on IE which occur on menuitem seperators
    // or empty space between menuitems.
    var menuLeft = this.domNode.offsetLeft;        
    var menuTop = this.domNode.offsetTop;        
    var tmp;

    var menuRight = menuLeft + this.domNode.offsetWidth - this.rightShadow;
    var menuBottom = menuTop + this.domNode.offsetHeight - this.bottomShadow;

    // Having problems with document.body.scrollTop/scrollLeft in firefox.
    // It always seems to return 0. But window.pageXOffset works fine.
    if (window.pageXOffset || window.pageYOffset) {
        var eventX = evt.clientX + window.pageXOffset;
        var eventY = evt.clientY + window.pageYOffset;
    } else if (document.documentElement.scrollLeft ||
            document.documentElement.scrollTop){
        var eventX = evt.clientX + document.documentElement.scrollLeft;
        var eventY = evt.clientY + document.documentElement.scrollTop;
    } else {
        var eventX = evt.clientX + document.body.scrollLeft;
        var eventY = evt.clientY + document.body.scrollTop;
    }
    if ((eventX >= menuLeft) && (eventX <= menuRight) && (eventY >= menuTop) && 
            (eventY <= menuBottom)) {
        return;
    }
    if ((evt.type == "keydown" && evt.keyCode == 27) || evt.type == "click") {
        this.close();
    }
    return true;
}

/**
 * Use this function to make the menu visible. It takes an event parameter
 * as an argument.It calculates the position where the menu is to be displayed
 * at if one is not already provided by the developer.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.popupMenu.prototype.open = function(event) {    
    var evt = this.widget.getEvent(event);
    var keyCode = this.widget.getKeyCode(evt);
    if(evt.type == "keydown" || evt.type == "keypress") {

        if (!(evt.shiftKey && keyCode == 121)) {
            return false;
        }

        if (webui.suntheme.browser.isIe5up()) {
            window.event.cancelBubble = true;
            window.event.returnValue = false;
        } else {
            evt.stopPropagation();
            evt.preventDefault();
        }         
     }
         
    // Only one menu can be open at a time. Hence, close the previous menu.
    var widget = dijit.byId(webui.suntheme.widget.popupMenu.activeMenuId);
    if (widget) {
        widget.close();
    }
    webui.suntheme.widget.popupMenu.activeMenuId = this.id;

    var evt = (event) 
        ? event : ((window.event) 
            ? window.event : null);

    // Note: Must test if event is null. Otherwise, pressing enter key while
    // link has focus generates an error on IE.
    if (evt) {
        evt.cancelBubble = true;
    }

    // If menu already rendered, do nothing.
    if (webui.suntheme.common.isVisibleElement(this.domNode)) {
        return false;
    }
        
    // Check if developer defined styles are set on the widget.
    if (this.style != null) {
        // Mozilla browsers will tell us which styles are set.  If they're not
        // in the list, then the styles appear to be undefined.
        if (this.domNode.style.length != null) {
            for (var i = 0; i < this.domNode.style.length; i++) {
                    var x = this.domNode.style[i];
                if (this.domNode.style[i] == "top")
                    this.top = this.domNode.style.top;
                if (this.domNode.style[i] == "left")
                    this.left = this.domNode.style.left;
            }
        } else {
            // For IE, simply query the style attributes.
            if (this.style.top != "")
                this.top = this.domNode.style.top;
            if (this.domNode.style.left != "")
                this.left = this.domNode.style.left;
        }    
    }

    // Fix: Setting the menu visible here causes flashing. The menu is shown in
    // an old location until moved to the new location in the page.

    // Show the menu. Must do this here, else target properties referenced
    // below will not be valid.
    this.setProps({visible: true});
      
    // If specific positioning specified, then simply use it.  This means
    // no provisions are made to guarantee the menu renders in the viewable area.
    if ((this.top != null) && (this.left != null)) {
        this.domNode.style.left = this.left;
        this.domNode.style.top = this.top;
    } else {
        if (evt == null) {
            return false;
        }
        // No positioning specified, so we calculate the optimal position to guarantee
        // menu is fully viewable.
        // Get the absolute position of the target.
        var target = (evt.target) 
            ? evt.target 
            : ((evt.srcElement) 
                ? evt.srcElement : null);
        var absPos = this.widget.getPosition(target);
        var targetLeft = absPos[0];
        var targetTop = absPos[1];

        // Assume default horizontal position is to align the left edge of the menu with the
        // left edge of the target.
        var menuLeft = targetLeft + this.rightShadow;

        // But can be overridden to align right edges.
        // Check if right edge of menu exceeds page boundary.
        var rightEdge = menuLeft + this.domNode.offsetWidth;
        var pageWidth = this.widget.getPageWidth();
        if (rightEdge > pageWidth) {
            // Shift menu left just enough to bring it into view.
            menuLeft -= (rightEdge - pageWidth);
        }
        
        // Shift menu to account for horizontal scrolling.
        if ((window.pageXOffset != null) && (window.pageXOffset > 0)) {
            menuLeft += window.pageXOffset;
        }
        if ((document.body.scrollLeft != null) && (document.body.scrollLeft > 0)) {
            menuLeft += document.body.scrollLeft;
        }
        if ((document.documentElement.scrollLeft != null) && 
            (document.documentElement.scrollLeft > 0)) {
            menuLeft += document.documentElement.scrollLeft;        
        }

        // If left edge of menu crosses left page boundary, then
        // shift menu right just enough to bring it into view.
        if (menuLeft < 0) {
            menuLeft = 0;
        }

        // Assume default vertical position is to position menu below target.
        var menuTop = targetTop + target.offsetHeight + this.bottomShadow;
        
        // Shift menu to account for vertical scrolling.
        if ((window.pageYOffset != null) && (window.pageYOffset > 0)) {
            menuTop += window.pageYOffset;
        }
        if ((document.body.scrollTop != null) && (document.body.scrollTop > 0)) {
            menuTop += document.body.scrollTop;
        }
        if ((document.documentElement.scrollTop != null) &&
        (document.documentElement.scrollTop > 0)) {
            menuTop += document.documentElement.scrollTop;
        }

        // Check if bottom edge of menu exceeds page boundary.
        var bottomEdge = menuTop + this.domNode.offsetHeight - this.bottomShadow;
        if (bottomEdge > this.widget.getPageHeight()) {
            // Shift menu to top of target.
            menuTop = targetTop - this.domNode.offsetHeight;

            // If top edge of menu cross top page boundary, then
            // reposition menu back to below target.
            // User will need to use scrollbars to position menu into view.
            if (menuTop <= 0) {
                menuTop = targetTop + target.offsetHeight - this.bottomShadow;
            }
        }

        // Set new menu position.
        this.domNode.style.left = menuLeft + "px";
        this.domNode.style.top = menuTop + "px";
    }

    // Keep track of the element that opened the popup menu.
    // When the menu is closed, the focus is set back on this element.
    if (evt.target) {
        this.target = evt.target;
    } else if (evt.srcElement) {
        this.target = evt.srcElement;
    }
    if (this.target.blur) {
        this.target.blur();
    }
    
    // Always set the focus on the first element of the menu.
    if (this.focusPosition > 0) {
        var menuNode = document.getElementById(this.menuId[this.focusPosition]); 
        if (menuNode) {
            menuNode.className = this.theme.getClassName("MENU_GROUP_CONTAINER");
        }
    }
    this.focusPosition = 0;
    menuNode = document.getElementById(this.menuId[0]);
    menuNode.className = menuNode.className + " " + 
        this.theme.getClassName("MENU_FOCUS");  
        
    if (menuNode.focus) {
        menuNode.focus();
    }
    return true;        
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.popupMenu.prototype.postCreate = function () {
    // Set public functions.
    this.domNode.open = function(event) { return dijit.byId(this.id).open(event); }
    this.domNode.close = function() { return dijit.byId(this.id).close(); }

    // Set events.s
    dojo.connect(document, "onclick", this, "onCloseMenuCallBack"); 
            
    // escape key should also close menu.
    dojo.connect(document, "onkeydown", this, "onCloseMenuCallBack");  

    // Default widths of the drop shadow on each side of the menu.  These MUST 
    // be in pixel units and MUST match the absolute values of the left/top 
    // styles of the "Menu" style class in the CSS.
    this.rightShadow = parseFloat(this.theme.getMessage("Menu.rightShadow"));
    this.bottomShadow = parseFloat(this.theme.getMessage("Menu.bottomShadow"));
    this.shadowContainer.className = this.theme.getClassName("MENU_SHADOW_CONTAINER"); 

    return this.inherited("postCreate", arguments);
}

/**
 * Override the "super class" processKeyPressEvent functionality and close the menu.
 *
 * @param (String) value The "value" of the selected option.  
 * @return {boolean} true The enter key press event completed successfully 
 */
webui.suntheme.widget.popupMenu.prototype.processEnterKeyPressEvent = function(value) {
    this.inherited("processEnterKeyPressEvent", arguments);
    this.close();
    return true;
}

/**
 * Override the "super class" processOnClickEvent functionality and close the menu.
 *
 * @param {String} value The selected value.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.popupMenu.prototype.processOnClickEvent = function(value) {
    this.inherited("processOnClickEvent", arguments);
    this.close();
    return true;
}

/**
 * Traverse through the menu items. This overrides the superclass implementation
 * and handles escape/tab/page up/page down/home/end key press events.
 *
 * @param (String) keyCode The valye of the key which was pressed
 * @param (Event) event The key press event.
 * @param (String) nodeId The id of the menu item. 
 * @return {boolean} true Propagate the javascript event 
 */
webui.suntheme.widget.popupMenu.prototype.traverseMenu = function(keyCode, event, nodeId) {
    
    // Handle the escape key and tab key press
    if (keyCode == 27 || keyCode == 9) {
        var focusElem = document.getElementById(this.menuId[this.focusPosition]);
        focusElem.className = this.theme.getClassName("MENU_GROUP_CONTAINER");        
        this.close();
        return true;
    } else if(keyCode >= 33 && keyCode <= 36) {
        focusElem = document.getElementById(this.menuId[this.focusPosition]);        
        focusElem.className = this.theme.getClassName("MENU_GROUP_CONTAINER");
        
        // Handle the home and page Up keys. Focus is set on the first element.
        if (keyCode == 33 || keyCode == 36) {
            this.focusPosition = 0;
            focusElem = document.getElementById(this.menuId[this.focusPosition]);        
        }
        
        // Handle Page Down and End keys. Focus is set on the last element.
        if (keyCode == 34 || keyCode == 35) {
            this.focusPosition = this.menuId.length - 1;
            focusElem = document.getElementById(this.menuId[this.focusPosition]);        
        }
        if (focusElem.focus) {
            focusElem.focus();
        }                        
        focusElem.className = focusElem.className + " " +
            this.theme.getClassName("MENU_FOCUS"); 
        if (webui.suntheme.browser.isIe5up()) {
            window. event.cancelBubble = true;
            window.event.returnValue = false;
        } else {
            event.stopPropagation();
            event.preventDefault();
        }   
        return true;                 
    }    
    this.inherited("traverseMenu", arguments);
    return true;
}

/**
 * Process submit event.
 *
 * @param {String} execute The string containing a comma separated list 
 * of client ids against which the execute portion of the request 
 * processing lifecycle must be run.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.popupMenu.prototype.submit = function(execute) {
    // Publish an event for custom AJAX implementations to listen for.
    dojo.publish(webui.suntheme.widget.popupMenu.event.submit.beginTopic, [{
        id: this.id,
        execute: execute,
        value: this.getSelectedValue(),
        endTopic: webui.suntheme.widget.popupMenu.event.submit.endTopic
    }]);
    return true;
}
dojo.provide("webui.suntheme.widget.jsfx.popupMenu");


/**
 * @class This class contains functions to obtain data asynchronously using JSF
 * Extensions as the underlying transfer protocol.
 * @static
 */
webui.suntheme.widget.jsfx.popupMenu = {
    /**
     * This function is used to process submit events with Object literals. 
     * <p>
     * Note: Cannot use the processSubmitEvent() function in the common.js file
     * as we need an extra attribute called value to be submitted for every request.
     * </p>
     *
     * @param props Key-Value pairs of properties.
     * @config {String} [id] The HTML element Id.
     * @config {String} [endTopic] The event topic to publish.
     * @config {String} [execute] The string containing a comma separated list 
     * of client ids against which the execute portion of the request 
     * processing lifecycle must be run.
     * @config {String} [value] The selected menu option value.
     * @return {boolean} true if successful; otherwise, false.
     */
    processSubmitEvent: function(props) {
        if (props == null) {
            return false;
        }

        // Dynamic Faces requires a DOM node as the source property.
        var domNode = document.getElementById(props.id);

        // Generate AJAX request using the JSF Extensions library.
        DynaFaces.fireAjaxTransaction(
            (domNode) ? domNode : document.forms[0], {
            execute: (props.execute) ? props.execute : props.id,
            render: props.id,
            replaceElement: webui.suntheme.widget.jsfx.common.submitCallback,
            xjson: {
                id: props.id,
                endTopic: props.endTopic,
                value: props.value,
                event: "submit"
            }
        });
        return true;
    }
}

// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.popupMenu.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.subscribe(webui.suntheme.widget.popupMenu.event.submit.beginTopic,
    webui.suntheme.widget.jsfx.popupMenu, "processSubmitEvent");
dojo.provide("webui.suntheme.widget.progressBar");


/**
 * @name webui.suntheme.widget.progressBar
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for the progressBar widget.
 * @constructor This function is used to construct a progressBar widget.
 */
dojo.declare("webui.suntheme.widget.progressBar", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    percentChar: "%",
    progress: 0,
    type: "DETERMINATE",
    busy: "BUSY",
    canceled: "canceled",
    completed: "completed",
    determinate: "DETERMINATE",
    failed: "failed",
    indeterminate: "INDETERMINATE",
    notstarted: "not_started",
    paused: "paused",
    resumed: "resumed",
    stopped: "stopped",
    widgetName: "progressBar"
});

/**
 * This function handles cancel progressBar event.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.cancel = function() {
    clearTimeout(this.timeoutId);

    this.hiddenFieldNode.value = this.canceled;
    if (this.type == this.determinate) {
        this.innerBarContainer.style.width = "0%";
    }
    return this.updateProgress();
}

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.progressBar.event =
        webui.suntheme.widget.progressBar.prototype.event = {
    /**
     * This closure is used to publish progress events.
     * @ignore
     */
    progress: {
        /** Progress event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_progressBar_event_progress_begin",

        /** Progress event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_progressBar_event_progress_end"
    },

    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_progressBar_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_progressBar_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_progressBar_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_progressBar_event_state_end"
    }
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.progressBar.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.height) { props.height = this.height; }
    if (this.width) { props.width = this.width; }
    if (this.bottomText) { props.bottomText = this.bottomText; }
    if (this.busyImage != null) { props.busyImage = this.busyImage; }
    if (this.failedStateText != null) { props.failedStateText = this.failedStateText; }
    if (this.id) { props.id = this.id; }
    if (this.log != null) { props.log = this.log; }
    if (this.logId) { props.logId = this.logId; }
    if (this.logMessage) { props.logMessage = this.logMessage; }
    if (this.overlayAnimation != null) { props.overlayAnimation = this.overlayAnimation; }
    if (this.percentChar) { props.percentChar = this.percentChar; }
    if (this.progress != null) { props.progress = this.progress; }
    if (this.progressImageUrl) { props.progressImageUrl = this.progressImageUrl; }
    if (this.progressControlBottom != null) { props.progressControlBottom = this.progressControlBottom; }
    if (this.progressControlRight != null) { props.progressControlRight = this.progressControlRight; }
    if (this.refreshRate) { props.refreshRate = this.refreshRate; }
    if (this.taskState != null) { props.taskState = this.taskState; }
    if (this.toolTip) { props.toolTip = this.toolTip; }
    if (this.topText) { props.topText = this.topText; }
    if (this.type) { props.type = this.type; }

    return props;
}

/**
 * This method displays the Bottom Controls if it was hidden.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.isBottomControlVisible = function() {
    return webui.suntheme.common.isVisibleElement(this.bottomControlsContainer);
}

/**
 * This method displays the failed state message and icon if it was hidden.
 *
* @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.isFailedStateMessageVisible = function() {
    return webui.suntheme.common.isVisibleElement(this.failedStateContainer);
}

/**
 * This method displays the log message if it was hidden.
 *
* @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.isLogMsgVisible = function() {
    return webui.suntheme.common.isVisibleElement(this.logContainer);
}

/**
 * This method displays the operation text if it was hidden.
 *
* @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.isOperationTextVisible = function() {
    return webui.suntheme.common.isVisibleElement(this.topTextContainer);
}

/**
 * This method displays the ProgressBar Container if it was hidden.
 *
* @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.isProgressBarContainerVisible = function() {
    return webui.suntheme.common.isVisibleElement(this.barContainer);
}

/**
 * This method displays the ProgressBar if it was hidden.
 *
* @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.isProgressBarVisible = function() {
    return webui.suntheme.common.isVisibleElement(this); 
}

/**
 * This method displays the Right Controls if it was hidden.
 *
* @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.isRightControlVisible = function() {
    return webui.suntheme.common.isVisibleElement(this.rightControlsContainer);
}

/**
 * This method displays the status text if it was hidden.
 *
* @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.isStatusTextVisible = function() {
    return webui.suntheme.common.isVisibleElement(this.bottomTextContainer);
}

/**
 * This function handles pause button event.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.pause = function() {
    clearTimeout(this.timeoutId);

    this.hiddenFieldNode.value = this.paused;
    if (this.type == this.indeterminate) {
        this.innerBarContainer.className =
            this.theme.getClassName("PROGRESSBAR_INDETERMINATE_PAUSED");
    }
    return this.updateProgress();
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {
        this.barContainer.id = this.id + "_barContainer";
        this.bottomControlsContainer.id = this.id + "_bottomControlsContainer";
        this.bottomTextContainer.id = this.id + "_bottomTextContainer"; 
        this.failedStateContainer.id = this.id + "_failedStateContainer";
        this.failedLabelContainer.id = this.id + "_failedLabelContainer";
        this.hiddenFieldNode.id = this.id + "_" + "controlType";
        this.hiddenFieldNode.name = this.hiddenFieldNode.id;
        this.innerBarContainer.id = this.id + "_innerBarContainer";
        this.innerBarOverlayContainer.id = this.id + "_innerBarOverlayContainer";
        this.logContainer.id = this.id + "_logContainer";
        this.rightControlsContainer.id = this.id + "_rightControlsContainer";
        this.topTextContainer.id = this.id + "_topTextContainer"; 
    }

    // Set public functions
    this.domNode.cancel = function() { return dijit.byId(this.id).cancel(); }
    this.domNode.isBottomControlVisible = function() { return dijit.byId(this.id).isBottomControlVisible(); }
    this.domNode.isFailedStateMessageVisible = function() { return dijit.byId(this.id).isFailedStateMessageVisible(); }
    this.domNode.isLogMsgVisible = function() { return dijit.byId(this.id).isLogMsgVisible(); }
    this.domNode.isOperationTextVisible = function() { return dijit.byId(this.id).isOperationTextVisible(); }
    this.domNode.isProgressBarContainerVisible = function() { return dijit.byId(this.id).isProgressBarContainerVisible(); }
    this.domNode.isProgressBarVisible = function() { return dijit.byId(this.id).isProgressBarVisible(); }
    this.domNode.isRightControlVisible = function() { return dijit.byId(this.id).isRightControlVisible(); }
    this.domNode.isStatusTextVisible = function() { return dijit.byId(this.id).isStatusTextVisible(); }
    this.domNode.pause = function() { return dijit.byId(this.id).pause(); }
    this.domNode.resume = function() { return dijit.byId(this.id).resume(); }
    this.domNode.stop = function() { return dijit.byId(this.id).stop(); }
    this.domNode.setOnCancel = function(func) { return dijit.byId(this.id).setOnCancel(func); }
    this.domNode.setOnComplete = function(func) { return dijit.byId(this.id).setOnComplete(func); }
    this.domNode.setOnFail = function(func) { return dijit.byId(this.id).setOnFail(func); }
    this.domNode.setBottomControlVisible = function(show) { return dijit.byId(this.id).setBottomControlVisible(show); }
    this.domNode.setFailedStateMessageVisible = function(show) { return dijit.byId(this.id).setFailedStateMessageVisible(show); }
    this.domNode.setLogMsgVisible = function(show) { return dijit.byId(this.id).setLogMsgVisible(show); }
    this.domNode.setOperationTextVisible = function(show) { return dijit.byId(this.id).setOperationTextVisible(show); }
    this.domNode.setProgressBarContainerVisible = function(show) { return dijit.byId(this.id).setProgressBarContainerVisible(show); }
    this.domNode.setProgressBarVisible = function(show) { return dijit.byId(this.id).setProgressBarVisible(show); }
    this.domNode.setRightControlVisible = function(show) { return dijit.byId(this.id).setRightControlVisible(show); }
    this.domNode.setStatusTextVisible = function(show) { return dijit.byId(this.id).setStatusTextVisible(show); }

    if (this.busyImage == null) {
	this.busyImage = this.widget.getImageProps("PROGRESS_BUSY", {
            id: this.id + "_busy"
        });
    }
    return this.inherited("postCreate", arguments);
}

/**
 * This function handles resume button event.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.resume = function() {
    clearTimeout(this.timeoutId);

    this.hiddenFieldNode.value = this.resumed;
    if (this.type == this.indeterminate) {
        this.innerBarContainer.className = 
            this.theme.getClassName("PROGRESSBAR_INDETERMINATE");
            
    }
    return this.updateProgress();
}

/**
 * This method hides the Bottom Control.
 *
 * @param {boolean} show true to show the element, false to hide the element.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.setBottomControlVisible = function(show) {
    if (show == null) {
        return false;
    }
    webui.suntheme.common.setVisibleElement(this.bottomControlsContainer, show);
    return true;
}

/**
 * This method hides the failed state message and icon area.
 *
 * @param {boolean} show true to show the element, false to hide the element.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.setFailedStateMessageVisible = function(show) {
    if (show == null) {
        return false;
    }
    webui.suntheme.common.setVisibleElement(this.failedStateContainer, show);
    return true;
}

/**
 * This method hides the log message area.
 *
 * @param {boolean} show true to show the element, false to hide the element.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.setLogMsgVisible = function(show) {
    if (show == null) {
        return false;
    }
    webui.suntheme.common.setVisibleElement(this.logContainer, show);
    return true;
}

/**
 * This function invokes developer define function for cancel event.
 * 
 * @param {Function} func The JavaScript function.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.setOnCancel = function(func) {
    if (func) {
        this.funcCanceled = func;
    }
    return true;
}

/**
 * This function invokes developer define function for complete event.
 * 
 * @param {Function} func The JavaScript function.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.setOnComplete = function(func) {
    if (func) {
        this.funcComplete = func;
    }
    return true;
}

/**
 * This function invokes developer define function for failed event.
 * 
 * @param {Function} func The JavaScript function.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.setOnFail = function(func) {
    if (func) {
        this.funcFailed = func;
    }
    return true;
}

/**
 * This method hides the operation text.
 *
 * @param {boolean} show true to show the element, false to hide the element.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.setOperationTextVisible = function(show) {
    if (show == null) {
        return false;
    }
    webui.suntheme.common.setVisibleElement(this.topTextContainer, show);
    return true;
}

/**
 * This function is used to set progress with Object literals.
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [failedStateText]
 * @config {String} [logMessage]
 * @config {int} [progress]
 * @config {String} [status]
 * @config {String} [taskState]
 * @config {String} [topText]
 * @config {String} [type]
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.setProgress = function(props) {
    if (props == null) {
        return false;
    }
      
    // Adjust max value.
    if (props.progress > 99 
            || props.taskState == this.completed) {
        props.progress = 100;
    }

    // Save properties for later updates.
    this.widget.extend(this, props);    

    // Set status.
    if (props.status) {
        this.widget.addFragment(this.bottomTextContainer, props.status);
    }

    // If top text doesnt get change, dont update.
    if (props.topText) {
        if (props.topText != this.topText) {
            this.widget.addFragment(this.topTextContainer, props.topText);
        }
    }

    // Update log messages.
    if (this.type == this.determinate) { 
        if (props.progress != null && props.progress >= 0 ) {
            this.innerBarContainer.style.width = props.progress + '%';
        }

        if (props.logMessage) {
            var field = dijit.byId(this.logId).getInputElement();
            if (field != null) {
                field.value = (field.value)
                   ? field.value + props.logMessage + "\n"
                   : props.logMessage + "\n";
            }
        }

        // Add overlay text.
        if (this.overlayAnimation == true) {
            // NOTE: If you set this value manually, text must be HTML escaped.
            this.widget.addFragment(this.innerBarOverlayContainer, props.progress + "%");
        }
    } 

    // Failed state.
    if (props.taskState == this.failed) {
        clearTimeout(this.timeoutId);
        this.widget.sleep(1000);
        this.setProgressBarContainerVisible(false);
        this.setBottomControlVisible(false);
        this.setRightControlVisible(false);
        this.setLogMsgVisible(false);

        if (props.failedStateText != null) {
            // NOTE: If you set this value manually, text must be HTML escaped.
            this.widget.addFragment(this.failedLabelContainer,
                props.failedStateText + " " + props.progress + this.percentChar);

            webui.suntheme.common.setVisibleElement(this.failedLabelContainer, true);
            webui.suntheme.common.setVisibleElement(this.failedStateContainer, true);
        }
        if (this.funcFailed != null) {
            (this.funcFailed)();
        }
        return true;
    }

    // Cancel state.
    if (props.taskState == this.canceled) {
        clearTimeout(this.timeoutId);
        this.widget.sleep(1000);
        this.setOperationTextVisible(false);
        this.setStatusTextVisible(false);
        this.setProgressBarContainerVisible(false);
        this.setBottomControlVisible(false);
        this.setRightControlVisible(false);
        this.setLogMsgVisible(false);

        if (this.type == this.determinate) {
            this.innerBarContainer.style.width = "0%";
        }
        if (this.funcCanceled != null) {
           (this.funcCanceled)(); 
        }
        return true;    
    }

    // paused state
    if (props.taskState == this.paused) {
        clearTimeout(this.timeoutId);
        return true;
    }

    // stopped state
    if (props.taskState == this.stopped) {
        clearTimeout(this.timeoutId);
        return true;
    }

    if (props.progress > 99 
            || props.taskState == this.completed) {
        clearTimeout(this.timeoutId);
        if (this.type == this.indeterminate) {
            this.innerBarContainer.className =
                this.theme.getClassName("PROGRESSBAR_INDETERMINATE_PAUSED");
        }
        if (this.type == this.busy) {
            this.setProgressBarContainerVisible(false);
        }
        if (this.funcComplete != null) {
           (this.funcComplete)(); 
        }
    }

    // Set progress for A11Y.
    if (props.progress) {
        if (this.bottomTextContainer.setAttributeNS) {
            this.bottomTextContainer.setAttributeNS("http://www.w3.org/2005/07/aaa",
                "valuenow", props.progress);
        }
    }
    return true;
}

/**
 * This method hides the ProgressBar Container.
 *
 * @param {boolean} show true to show the element, false to hide the element.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.setProgressBarContainerVisible = function(show) {
    if (show == null) {
        return false;
    }

    if (show == false) {
        this.barContainer.style.display = "none";
    } else {
        this.barContainer.style.display = '';
    }
    return true; 
}

/**
 * This method hides the ProgressBar.
 *
 * @param {boolean} show true to show the element, false to hide the element.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.setProgressBarVisible = function(show) {
    if (show == null) {
        return false;
    }
    webui.suntheme.common.setVisibleElement(this, show);
    return true; 
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [bottomText] 
 * @config {Object} [busyImage] 
 * @config {String} [failedStateText]
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [logId] 
 * @config {boolean} [logMessage] 
 * @config {String} [overlayAnimation] 
 * @config {String} [percentChar] 
 * @config {int} [progress] 
 * @config {String} [progressImageUrl] 
 * @config {String} [progressControlBottom]
 * @config {String} [progressControlRight] 
 * @config {int} [refreshRate] 
 * @config {String} [taskState]
 * @config {String} [toolTip] 
 * @config {String} [topText] 
 * @config {String} [type] 
 * @config {boolean} [visible] Hide or show element.
 * @config {int} [width] 
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.progressBar.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set tool tip.
    if (props.toolTip) {
        this.barContainer.title = props.toolTip;
    }

    // Add top text.
    if (props.topText) {
        this.widget.addFragment(this.topTextContainer, props.topText); 
        webui.suntheme.common.setVisibleElement(this.topTextContainer, true);
    }

    // Add bottom text.
    if (props.bottomText) {
        this.widget.addFragment(this.bottomTextContainer, props.bottomText);
        webui.suntheme.common.setVisibleElement(this.bottomTextContainer, true);
    }

    if (props.type == this.determinate 
            || props.type == this.indeterminate) {
        // Set style class.
        this.barContainer.className =
            this.theme.getClassName("PROGRESSBAR_CONTAINER");

        // Set height.
        if (props.height != null && props.height > 0) {
            this.barContainer.style.height = props.height + "px;"; 
            this.innerBarContainer.style.height = props.height + "px;";
        }

        // Set width.
        if (props.width != null && props.width > 0) {
            this.barContainer.style.width = props.width + "px;";
        }

        // Add right controls.
        if (props.progressControlRight != null) {
            this.widget.addFragment(this.rightControlsContainer, props.progressControlRight);
            webui.suntheme.common.setVisibleElement(this.rightControlsContainer, true);
        }

        // Add bottom controls.
        if (props.progressControlBottom != null) {
            this.widget.addFragment(this.bottomControlsContainer, props.progressControlBottom);
            webui.suntheme.common.setVisibleElement(this.bottomControlsContainer, true);
        }
    }

    if (props.type == this.determinate) {
        // Set style class.
        this.innerBarContainer.className =
            this.theme.getClassName("PROGRESSBAR_DETERMINATE");

        // Set width.
        if (this.progress != null && this.progress >= 0) {
            this.innerBarContainer.style.width = this.progress + '%';
        }    

        // Add overlay.
        if (props.overlayAnimation == true) {
            // NOTE: If you set this value manually, text must be HTML escaped.
            this.widget.addFragment(this.innerBarOverlayContainer, this.progress + "%");
            webui.suntheme.common.setVisibleElement(this.innerBarOverlayContainer, true);
        }

        // Add log.
        if (props.log != null && props.overlayAnimation == false) { 
            this.widget.addFragment(this.logContainer, props.log);
            webui.suntheme.common.setVisibleElement(this.logContainer, true);
        }  
    } else if (props.type == this.indeterminate) {
        // Set style class.
        this.barContainer.className = 
            this.theme.getClassName("PROGRESSBAR_CONTAINER");
        this.innerBarContainer.className = 
            this.theme.getClassName("PROGRESSBAR_INDETERMINATE");
    } else if (props.type == this.busy) {
        // Add busy image.
        if (props.busyImage) {
            if (props.width > 0) {
                props.busyImage.width = props.width;
            } 
            if (props.height > 0) {
                props.busyImage.height = props.height;
            }
            this.widget.addFragment(this.busyImageContainer, props.busyImage);
            webui.suntheme.common.setVisibleElement(this.busyImageContainer, true);
        }
    }

    // Set developer specified image.
    if (props.progressImageUrl != null ) {
        this.innerBarContainer.style.backgroundImage = 'url(' + props.progressImageUrl + ')';
    }

    // Set A11Y properties.
    if (props.progress != null) {
        if (this.bottomTextContainer.setAttributeNS) {
            this.bottomTextContainer.setAttributeNS(
                "http://www.w3.org/2005/07/aaa", "valuenow", this.progress);
        }
    }

    // Set more properties.
    this.setCommonProps(this.domNode, props);

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}

/**
 * This method hides the Right Control.
 *
 * @param {boolean} show true to show the element, false to hide the element.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.setRightControlVisible = function(show) {
    if (show == null) {
        return false;
    }
    webui.suntheme.common.setVisibleElement(this.rightControlsContainer, show);
    return true;
}

/**
 * This method hides the status text.
 *
 * @param {boolean} show true to show the element, false to hide the element.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.setStatusTextVisible = function(show) {
    if (show == null) {
        return false;
    }
    webui.suntheme.common.setVisibleElement(this.bottomTextContainer, show);
    return true;
}

/**
 * This function is used to "start" the widget, after the widget has been
 * instantiated.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.startup = function () {
    if (this._started) {
        return false;
    }
    // Start a timer used to periodically publish progress events.
    this.updateProgress();
    
    return this.inherited("startup", arguments);
}

/**
 * This function handles stop button event.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.stop = function() {
    clearTimeout(this.timeoutId);

    this.hiddenFieldNode.value = this.stopped;
    if (this.type == this.indeterminate) {
        this.innerBarIdContainer.className =
            this.theme.getClassName("PROGRESSBAR_INDETERMINATE_PAUSED");
    }
    return this.updateProgress();
}

/**
 * Process progress event.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.progressBar.prototype.updateProgress = function() {
    // Publish event.
    if (this.refreshRate > 0) {
        // Publish an event for custom AJAX implementations to listen for.
        dojo.publish(webui.suntheme.widget.progressBar.event.progress.beginTopic, [{
            id: this.id
        }]);
    }

    // Create a call back function to periodically publish progress events.
    var _id = this.id;
    this.timeoutId = setTimeout(function() {
        // New literals are created every time this function is called, and it's 
        // saved by closure magic.
        dijit.byId(_id).updateProgress();
    }, this.refreshRate);
    return true;
}
dojo.provide("webui.suntheme.widget.jsfx.progressBar");


/**
 * @class This class contains functions to obtain data asynchronously using JSF
 * Extensions as the underlying transfer protocol.
 * @static
 */
webui.suntheme.widget.jsfx.progressBar =  {
    /**
     * This function is used to process progress events with Object literals.
     *
     * @param props Key-Value pairs of properties.
     * @config {String} [id] The HTML element Id.
     * @return {boolean} true if successful; otherwise, false.
     */
    processProgressEvent: function(props) {
        if (props == null) {
            return false;
        }

        // Dynamic Faces requires a DOM node as the source property.
        var domNode = document.getElementById(props.id);

        // Generate AJAX request using the JSF Extensions library.
        DynaFaces.fireAjaxTransaction(
            (domNode) ? domNode : document.forms[0], {
            execute: props.id, // Need to decode hidden field.
            render: props.id,
            replaceElement: webui.suntheme.widget.jsfx.progressBar.progressCallback,
            xjson: {
                id: props.id,
                event: "progress"
            }
        });

        return true;
    },

    /**
     * This function is used to update progress.
     *
     * @param {String} elementId The HTML element Id.
     * @param {String} content The content returned by the AJAX response.
     * @param {Object} closure The closure argument provided to DynaFaces.fireAjaxTransaction.
     * @param {Object} xjson The xjson argument provided to DynaFaces.fireAjaxTransaction.
     * @return {boolean} true if successful; otherwise, false.
     */
    progressCallback: function(id, content, closure, xjson) {
        if (id == null || content == null) {
            return false;
        }

        // Parse JSON text.
        var props = JSON.parse(content);

        // Set progress.
        var widget = dijit.byId(id);
        widget.setProgress({
            failedStateText : props.failedStateText,
            logMessage : props.logMessage,
            progress : props.progress,
            status: props.status,
            taskState : props.taskState,
            topText : props.topText
        });

        // Publish an event for custom AJAX implementations to listen for.
        dojo.publish(
            webui.suntheme.widget.progressBar.event.progress.endTopic, [props]);
        return true;
    }
}

// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.progressBar.event.progress.beginTopic,
    webui.suntheme.widget.jsfx.progressBar, "processProgressEvent");
dojo.subscribe(webui.suntheme.widget.progressBar.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.radioButton");


/**
 * @name webui.suntheme.widget.radioButton
 * @extends webui.suntheme.widget.checkedBase
 * @class This class contains functions for the radioButton widget.
 * @constructor This function is used to construct a radioButton widget.
 */
dojo.declare("webui.suntheme.widget.radioButton", webui.suntheme.widget.checkedBase, {
    // Set defaults.
    idSuffix: "_rb",
    widgetName: "radioButton" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.radioButton.event =
        webui.suntheme.widget.radioButton.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_radioButton_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_radioButton_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_radioButton_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_radioButton_event_state_end"
    },

    /**
     * This object contains submit event topics.
     * @ignore
     */
    submit: {
        /** Submit event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_radioButton_event_submit_begin",

        /** Submit event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_radioButton_event_submit_end"
    }
}

/**
 * This function is used to obtain the outermost HTML element class name.
 * <p>
 * Note: Selectors should be concatinated in order of precedence (e.g., the 
 * user's className property is always appended last).
 * </p>
 * @return {String} The outermost HTML element class name.
 */
webui.suntheme.widget.radioButton.prototype.getClassName = function() {
    // Set default style.
    var className = (this.disabled == true)
        ? this.widget.getClassName("RADIOBUTTON_SPAN_DISABLED", "")
        : this.widget.getClassName("RADIOBUTTON_SPAN", "");

    return (this.className)
        ? className + " " + this.className
        : className;
}

/**
 * Helper function to obtain image class names.
 *
 * @return {String} The HTML image element class name.
 */
webui.suntheme.widget.radioButton.prototype.getImageClassName = function() {
    return (this.disabled == true)
        ? this.widget.getClassName("RADIOBUTTON_IMAGE_DISABLED", "")
        : this.widget.getClassName("RADIOBUTTON_IMAGE", "");  
}

/**
 * Helper function to obtain input class names.
 *
 * @return {String} The HTML input element class name.
 */
webui.suntheme.widget.radioButton.prototype.getInputClassName = function() {
    // Set readOnly style.
    if (this.readOnly == true) {
        return this.widget.getClassName("RADIOBUTTON_READONLY", "");
    }

    // Disabled style.
    return (this.disabled == true)
        ? this.widget.getClassName("RADIOBUTTON_DISABLED", "")
        : this.widget.getClassName("RADIOBUTTON", "");  
}

/**
 * Helper function to obtain label class names.
 *
 * @return {String} The HTML label element class name.
 */
webui.suntheme.widget.radioButton.prototype.getLabelClassName = function() {
    return (this.disabled == true)
        ? this.widget.getClassName("RADIOBUTTON_LABEL_DISABLED", "")
        : this.widget.getClassName("RADIOBUTTON_LABEL", "");  
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [alt] Alternate text for image input.
 * @config {String} [align] Alignment of image input.
 * @config {boolean} [checked] 
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {Object} [image] 
 * @config {String} [label] 
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {String} [name] 
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {String} [onSelect] 
 * @config {boolean} [readOnly] 
 * @config {String} [style] Specify style rules inline.
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {String} [value] Value of input.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.radioButton.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.radioButton.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    if (props.name) {
        // IE does not support the name attribute being set dynamically as 
        // documented at:
        //
        // http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/name_2.asp
        //
        // In order to create an HTML element with a name attribute, the name
        // and value must be provided when using the inner HTML property or the
        // document.createElement() function. As a work around, we shall set the
        // attribute via the HTML template using name="${this.name}". In order
        // to obtain the correct value, the name property must be provided to 
        // the widget. Although we're resetting the name below, as the default,
        // this has no affect on IE. 
        this.inputNode.name = props.name;
    }

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.jsfx.radioButton");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.radioButton.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.subscribe(webui.suntheme.widget.radioButton.event.submit.beginTopic,
    webui.suntheme.widget.jsfx.common, "processSubmitEvent");
dojo.provide("webui.suntheme.widget.radioButtonGroup");


/**
 * @name webui.suntheme.widget.radioButtonGroup
 * @extends webui.suntheme.widget.checkedGroupBase
 * @class This class contains functions for the radioButtonGroup widget.
 * @constructor This function is used to construct a radioButtonGroup widget.
 */
dojo.declare("webui.suntheme.widget.radioButtonGroup", webui.suntheme.widget.checkedGroupBase, {
    // Set defaults.
    widgetName: "radioButtonGroup" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.radioButtonGroup.event =
        webui.suntheme.widget.radioButtonGroup.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_radioButtonGroup_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */        
        endTopic: "webui_suntheme_widget_radioButtonGroup_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_radioButtonGroup_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_radioButtonGroup_event_state_end"
    }
}

/**
 * This function is used to obtain the outermost HTML element class name.
 * <p>
 * Note: Selectors should be concatinated in order of precedence (e.g., the 
 * user's className property is always appended last).
 * </p>
 * @return {String} The outermost HTML element class name.
 */
webui.suntheme.widget.radioButtonGroup.prototype.getClassName = function() {
    // Set default style.
    var className = (this.columns > 1)
        ? this.widget.getClassName("RBGRP_HORIZ", "")
        : this.widget.getClassName("RBGRP_VERT", "");

    return (this.className)
        ? className + " " + this.className
        : className;
}
dojo.provide("webui.suntheme.widget.jsfx.radioButtonGroup");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.radioButtonGroup.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.resetButton");


/**
 * @name webui.suntheme.widget.resetButton
 * @extends webui.suntheme.widget.button
 * @class This class contains functions for the resetButton widget.
 * @constructor This function is used to construct a resetButton widget.
 */
dojo.declare("webui.suntheme.widget.resetButton", webui.suntheme.widget.button, {
    // Set defaults.
    widgetName: "resetButton"  // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.resetButton.event =
        webui.suntheme.widget.resetButton.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_resetButton_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_resetButton_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_resetButton_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_resetButton_event_state_end"
    }
}
dojo.provide("webui.suntheme.widget.jsfx.resetButton");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.resetButton.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.staticText");


/**
 * @name webui.suntheme.widget.staticText
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for the staticText widget.
 * @constructor This function is used to construct a staticText widget.
 */
dojo.declare("webui.suntheme.widget.staticText", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    escape: true,
    widgetName: "staticText" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.staticText.event =
        webui.suntheme.widget.staticText.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_staticText_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_staticText_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_staticText_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_staticText_event_state_end"
    }
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.staticText.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.escape) { props.escape = this.escape; }
    if (this.value) { props.value = this.value; }

    return props;
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {String} [escape] HTML escape value (default).
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {String} [style] Specify style rules inline.
 * @config {String} [title] Provides a title for element.
 * @config {String} [value] Value of input.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.staticText.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.staticText.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }
      
    // Set text value.
    if (props.value) {
        // NOTE: If you set this value manually, text must be HTML escaped.
        this.widget.addFragment(this.domNode, props.value, null, this.escape);
    }

    // Set more properties.
    this.setCommonProps(this.domNode, props);
    this.setEventProps(this.domNode, props);

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.jsfx.staticText");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.staticText.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.table2");


/**
 * @name webui.suntheme.widget.table2
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for the table2 widget.
 * @constructor This function is used to construct a table2 widget.
 */
dojo.declare("webui.suntheme.widget.table2", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    widgetName: "table2" // Required for theme properties.
});

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.table2.event =
        webui.suntheme.widget.table2.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_table2_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_table2_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_table2_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_table2_event_state_end"
    }
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.table2.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.actions) { props.actions = this.actions; }
    if (this.align) { props.align = this.align; }
    if (this.bgColor) { props.bgColor = this.bgColor; }
    if (this.border) { props.border = this.border; }
    if (this.caption) { props.caption = this.caption; }
    if (this.cellpadding) { props.cellpadding = this.cellpadding; }
    if (this.cellspacing) { props.cellspacing = this.cellspacing; }
    if (this.filterText) { props.filterText = this.filterText; }
    if (this.frame) { props.frame = this.frame; }
    if (this.rowGroups) { props.rowGroups = this.rowGroups; }
    if (this.rules) { props.rules = this.rules; }
    if (this.summary) { props.summary = this.summary; }
    if (this.width) { props.width = this.width; }

    return props;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.table2.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {
        this.actionsContainer.id = this.id + "_actionsContainer";
        this.actionsNode.id = this.id + "_actionsNode";
        this.controlsNode.id = this.id + "_controlsNode";
        this.filterPanelContainer.id = this.id + "_filterPanelContainer";
        this.preferencesPanelContainer.id = this.id + "_preferencesPanelContainer";
        this.sortPanelContainer.id = this.id + "_sortPanelContainer";
        this.rowGroupsContainer.id = this.id + "_rowGroupsContainer";
        this.captionContainer.id = this.id + "_captionContainer";
    }
    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {Object} [actions] 
 * @config {String} [align] Alignment of image input.
 * @config {String} [bgColor]
 * @config {String} [border]
 * @config {String} [caption]
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {String} [frame] 
 * @config {String} [filterText] 
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {Array} [rowGroups] 
 * @config {String} [rules] 
 * @config {String} [style] Specify style rules inline.
 * @config {String} [summary]
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [visible] Hide or show element.
 * @config {String} [width]
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.table2.prototype.setProps = function(props, notify) {
    if (props == null) {
        return false;
    }

    // Replace actions -- do not extend.
    if (props.actions) {
        this.actions = null;
    }

    // Replace rows -- do not extend.
    if (props.rowGroups) {
        this.rowGroups = null;
    }

    // Extend widget object for later updates.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.table2.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // To do: Add tabIndex to subwidgets, but not table, tr, or td tags.
    props.tabIndex = null;

    // Set properties.
    if (props.align) { this.domNode.align = props.align; }
    if (props.width) { this.domNode.style.width = props.width; }

    // Add actions.
    if (props.actions) {
        this.widget.addFragment(this.actionsNode, props.actions);
        webui.suntheme.common.setVisibleElement(this.actionsContainer, true);
    }

    // Add caption.
    if (props.caption || props.filterText && this.caption) {       
        var filterText = null;
        if (props.filterText) {
            filterText = this.theme.getMessage("table.title.filterApplied", [
                props.filterText
            ]);
        }

        // To do: Create a new title message.
        
        this.widget.addFragment(this.captionContainer, (filterText) 
            ? props.caption + filterText : props.caption);
        webui.suntheme.common.setVisibleElement(this.captionContainer, true);
    }

    // Add row groups.
    if (props.rowGroups) {
        // Remove child nodes.
        this.widget.removeChildNodes(this.rowGroupsContainer);
 
        // Add row group.
        for (var i = 0; i < props.rowGroups.length; i++) {
            // Set properties that must be applied to each HTML table element.
            props.rowGroups[i]._table = {
                bgColor: props.bgColor,
                border: props.border,
                cellpadding: props.cellpadding,
                cellspacing: props.cellspacing,
                frame: props.frame,
                summary: props.summary
            }
            this.widget.addFragment(this.rowGroupsContainer, props.rowGroups[i], "last");
        }
        //this.bgContainer.style.height = '100%'; // For IE?
    }

    // Set more properties.
    this.setCommonProps(this.domNode, props);
    this.setEventProps(this.domNode, props);

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.jsfx.table2");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.table2.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.provide("webui.suntheme.widget.table2RowGroup");


/**
 * @name webui.suntheme.widget.table2RowGroup
 * @extends webui.suntheme.widget.widgetBase
 * @class This class contains functions for the table2RowGroup widget.
 * @constructor This function is used to construct a table2RowGroup widget.
 */
dojo.declare("webui.suntheme.widget.table2RowGroup", webui.suntheme.widget.widgetBase, {
    // Set defaults.
    currentRow: 0, // Current row in view.
    first: 0, // Index used to obtain rows.
    widgetName: "table2RowGroup" // Required for theme properties.
});

/**
 * This function is used to set column headers and footers.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.table2RowGroup.prototype.addColumns = function() {
    // Clear column headers/footers.
    this.widget.removeChildNodes(this.thead);
    this.widget.removeChildNodes(this.tfoot);

    // Clone dojo attach points.
    var headerRowClone = this.colHeaderRow.cloneNode(false);
    var footerRowClone = this.colFooterRow.cloneNode(false);

    // Append row nodes.
    this.thead.appendChild(headerRowClone);
    this.tfoot.appendChild(footerRowClone);

    // Append cell nodes.
    for (var i = 0; i < this.columns.length; i++) {
        var col = this.columns[i];
        var headerCellClone = this.colHeaderCell.cloneNode(true);
        var footerCellClone = this.colFooterCell.cloneNode(true);

        // Set properties.
        headerCellClone.id = col.id + "_colHeader";
        footerCellClone.id = col.id + "_colFooter";
        if (col.width) {
            headerCellClone.style.width = col.width;
            footerCellClone.style.width = col.width;
        }

        // Add text.
        if (col.headerText) {
            // StaticText widget adds span to match styles.
            //
            // To do: Create utility to help create client-side widgets.
            this.widget.addFragment(headerCellClone,
                this.widget.getWidgetProps("staticText", {
                    id: headerCellClone.id + "Text",
                    value: col.headerText
                }));
            headerVisible = true;
        }
        if (col.footerText) {
            // StaticText widget adds span to match styles.
            //
            // To do: Create utility to help create client-side widgets.
            this.widget.addFragment(footerCellClone,
                this.widget.getWidgetProps("staticText", {
                    id: footerCellClone.id + "Text",
                    value: col.footerText
                }));
            footerVisible = true;
        }

        // Append nodes.
        headerRowClone.appendChild(headerCellClone);
        footerRowClone.appendChild(footerCellClone);

        // Set colspan.
        this.groupHeaderCell.colSpan = this.columns.length;
    }
    return true;
}

/**
 * This function is used to add rows using the gieven array. Each row contains
 * an array of columns which holds table data.
 *
 * @param {Array} rows An array of rows.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.table2RowGroup.prototype.addRows = function(rows) {
    if (rows == null) {
        return false;
    }

    // Get properties.
    var props = this.getProps();

    // Get className properties to alternate between rows.
    var classNames = (this.className) ? this.className.split(",") : null;          

    // For each row found, clone the tableDataRow attach point.
    for (var i = 0; i < rows.length; i++) {
        var cols = rows[i]; // Get columns.
        var rowId = this.id + ":" + (this.first + i); // Get row id.

        // Clone table data row without cells.
        var rowClone = this.tableDataRow.cloneNode(false);
        this.tbody.appendChild(rowClone);

        // Set properties.
        props.id = rowId;

        // Set className.
        if (classNames) {
            props.className = classNames[i % classNames.length];
        }

        this.setCommonProps(rowClone, props);
        this.setEventProps(rowClone, props);
        this.setCoreProps(rowClone, props);

        // For each column found, clone the tableDataCell attach point.
        for (var k = 0; k < cols.length; k++) {
            var col = this.columns[k]; // Get current column.
            var colId = col.id.replace(this.id, rowId); // Get col id.

            // Clone node.
            var cellClone = this.tableDataCell.cloneNode(true);
            rowClone.appendChild(cellClone);

            // Set properties.
            this.setColumnProps(cellClone, col);
            cellClone.id = colId; // Override id set by setCoreProps.

            // Add cell data.
            this.widget.addFragment(cellClone, cols[k], "last");
        }

        // Save row for destroy() function.
        if (this.first > 0) {
            this.rows[this.rows.length] = rows[i];
        }
    }

    // Set first row value.
    this.first += rows.length;

    // Adjust layout.
    var _id = this.id;
    setTimeout(function() {
        // New literals are created every time this function is called, and it's 
        // saved by closure magic.
        dijit.byId(_id).resize();
    }, 0);

    return true;
}

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.table2RowGroup.event =
        webui.suntheme.widget.table2RowGroup.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_table2RowGroup_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_table2RowGroup_event_refresh_end"
    },

    /**
     * This closure is used to process scroll events.
     * @ignore
     */
    scroll: {
        /** Scroll event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_table2RowGroup_event_scroll_begin",

        /** Scroll event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_table2RowGroup_event_scroll_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_table2RowGroup_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_table2RowGroup_event_state_end"
    }
}

/**
 * This function is used to get widget properties. Please see the
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.table2RowGroup.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);

    // Set properties.
    if (this.align) { props.align = this.align; }
    if (this.bgColor) { props.bgColor = this.bgColor; }
//    if (this.char) { props.char = this.char; } // To do: Rename -- keyword is reserved.
    if (this.charOff) { props.charOff = this.charOff; }
    if (this.columns) { props.columns = this.columns; }
    if (this.first) { props.first = this.first; }
    if (this.headerText) { props.headerText = this.headerText; }
    if (this.height) { props.height = this.height; }
    if (this.maxRows) { props.maxRows = this.maxRows; }
    if (this.rows) { props.rows = this.rows; }
    if (this.totalRows) { props.totalRows = this.totalRows; }
    if (this.valign) { props.valign = this.valign; }

    return props;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.table2RowGroup.prototype.postCreate = function () {
    // Set ids.
    if (this.id) {
        this.colFooterRow.id = this.id + "_colFooterRow";
        this.colFooterCell.id = this.id + "_colFooterCell";
        this.colHeaderRow.id = this.id + "_colHeaderRow";
        this.colHeaderCell.id = this.id + "_colHeaderCell";
        this.groupHeaderControls.id = this.id + "_groupHeaderControls";
        this.groupHeaderText.id = this.id + "_groupHeaderText";        
        this.paginationControls.id = this.id + "_paginationControls";
        this.rowsText.id = this.id + "_rowsText";
        this.table.id = this.id + "_table";
        this.tableContainer.id = this.id + "_tableContainer";
        this.tableDataRow.id = this.id + "_tableDataRow";
        this.tableDataCell.id = this.id + "_tableDataCell";
        this.tbody.id = this.id + "_tbody";
        this.tfoot.id = this.id + "_tfoot";
        this.thead.id = this.id + "_thead";
    }

    // Set events.
    dojo.connect(this.tableContainer, "onscroll", this, "scroll");

    // Resize hack for Moz/Firefox.
    if (webui.suntheme.browser.isNav()) {
        dojo.connect(window, "onresize", this, "resize");
    }
    return this.inherited("postCreate", arguments);
}

/**
 * Process resize event.
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.table2RowGroup.prototype.resize = function() {
    // Update rows text.
    this.updateRowsText();

    // Get height offset of each row.
    var offset = 0;
    for (var i = this.currentRow; i < this.currentRow + this.maxRows; i++) {
        var tableDataRow = document.getElementById(this.id + ":" + i);
        if (tableDataRow != null) {
            offset += tableDataRow.offsetHeight;
        } else {
            break;
        }
    }

    // Set the scrollable height.
    if (offset > 0) {
        this.tableContainer.style.height = offset + "px";
    }

    // Set width of each column header & footer.
    var rowId = this.id + ":0"; // ID of first row.
    for (var i = 0; i < this.columns.length; i++) {
        var col = this.columns[i]; // Get default column props.
        var colId = col.id.replace(this.id, rowId);

        // Get row node.
        var tableDataCell = document.getElementById(colId);
        if (tableDataCell == null) {
            continue;
        }

        // Get nodes.
        var colHeaderCell = document.getElementById(col.id + "_colHeader");
        var colFooterCell = document.getElementById(col.id + "_colFooter");

        // Set width.
        if (colHeaderCell) {
            // Column width plus offset for border.
            colHeaderCell.style.width = (tableDataCell.offsetWidth - 1) + "px";
        }
        if (colFooterCell) {
            // Column width plus offset for border.
            colFooterCell.style.width = (tableDataCell.offsetWidth - 1) + "px";
        }
    }

    // Update header & footer position.
    var colHeaderRow = document.getElementById(this.id + "_colHeaderRow");
    var colFooterRow = document.getElementById(this.id + "_colFooterRow");

    var headerHeight = (colHeaderRow) ? colHeaderRow.offsetHeight : 0;
    var footerHeight = (colFooterRow) ? colFooterRow.offsetHeight : 0;

    this.tableContainer.style.marginTop = (headerHeight - 1) + 'px';
    this.tableContainer.style.marginBottom = footerHeight + 'px';

    if (colHeaderRow) {
        if (webui.suntheme.browser.isIe7()) {
            // IE 7 does not account for the column header.
            colHeaderRow.style.top = (this.tableContainer.offsetTop + 2) + 'px';
        } else {
            // Column header height plus offset for border.
            colHeaderRow.style.top = (this.tableContainer.offsetTop - headerHeight + 1) + 'px';
        }
    }
    if (colFooterRow) {
        if (webui.suntheme.browser.isIe7()) {
            // IE 7 does not account for the column footer.
            colFooterRow.style.top = (this.tableContainer.offsetTop + 
                this.tableContainer.offsetHeight + headerHeight + 2) + 'px';
        } else {
            // Column footer height plus offset for border.
            colFooterRow.style.top = (this.tableContainer.offsetTop + 
                this.tableContainer.offsetHeight - 1) + 'px';
        }
    }
    return true;
}

/**
 * This function is used to set column properties with Object literals.
 *
 * @param {Node} domNode The DOM node to assign properties to.
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [abbr]
 * @config {String} [axis]
 * @config {String} [bgColor]
 * @config {String} [char]
 * @config {String} [charOff]
 * @config {String} [className] CSS selector.
 * @config {int} [colspan]
 * @config {String} [dir] Specifies the directionality of text.
 * @config {String} [height]
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {boolean} [noWrap] 
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {int} [rowSpan] 
 * @config {String} [scope] 
 * @config {String} [style] Specify style rules inline.
 * @config {String} [title] Provides a title for element.
 * @config {String} [valign] 
 * @config {boolean} [visible] Hide or show element.
 * @config {String} [width]
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.table2RowGroup.prototype.setColumnProps = function(domNode, props) {
    // Set properties.
    if (this.abbr) { domNode.abbr = this.abbr; }
    if (this.axis) { domNode.axis = this.axis; }
    if (this.bgColor) { domNode.bgColor = this.bgColor; }
//    if (this.char) { domNode.char = this.char; } // To do: Rename -- keyword is reserved.
    if (this.charOff) { domNode.charoff = this.charOff; }
    if (this.thisspan) { domNode.colspan = this.colspan; }
    if (this.headers) { domNode.headers = this.headers; }
    if (this.height) { domNode.height = this.height; }
    if (this.noWrap) { domNode.nowrap = "nowrap"; }
    if (this.rowSpan) { domNode.rowspan = this.rowSpan; }
    if (this.scope) { domNode.scope = this.scope; }
    if (this.valign) { domNode.valign = this.valign; }
    if (this.width) { domNode.width = this.width; }

    // Set more properties.
    this.setCommonProps(domNode, props);
    this.setEventProps(domNode, props);
    this.setCoreProps(domNode, props);

    return true;
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [align] Alignment of image input.
 * @config {String} [bgColor]
 * @config {String} [char]
 * @config {String} [charOff]
 * @config {String} [className] CSS selector.
 * @config {Array} [columns]
 * @config {String} [dir] Specifies the directionality of text.
 * @config {int} [first]
 * @config {String} [headerText]
 * @config {int} [height]
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {int} [maxRows] 
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {Array} [rows] 
 * @config {String} [style] Specify style rules inline.
 * @config {String} [title] Provides a title for element.
 * @config {int} [totalRows] 
 * @config {String} [valign] 
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.table2RowGroup.prototype.setProps = function(props, notify) {
    if (props == null) {
        return false;
    }

    // Replace columns -- do not extend.
    if (props.columns) {
        this.columns = null;
    }

    // Replace rows -- do not extend.
    if (props.rows) {
        this.rows = null;
    }

    // Extend widget object for later updates.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.table2RowGroup.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set properties.
    if (props.id) { this.domNode.id = props.id; }

    // Set private properties for table widget.
    if (props._table) {
        if (props._table.bgColor) { this.table.bgColor = props._table.bgColor; }
        if (props._table.border) { this.table.border = props._table.border; }
        if (props._table.cellpadding) { this.table.cellpadding = props._table.cellpadding; }
        if (props._table.cellspacing) { this.table.cellspacing = props._table.cellspacing; }
        if (props._table.frame) { this.table.frame = props._table.frame; }
        if (props._table.rules) { this.table.rules = props._table.rules; }
        if (props._table.summary) { this.table.summary = props._table.summary; }
    }

    // Add header.
    if (props.headerText) {
        this.widget.addFragment(this.groupHeaderText, props.headerText);
        webui.suntheme.common.setVisibleElement(this.groupHeaderContainer, true);
    }

    // Set columns.
    if (props.columns && this.refreshCols != false) {
        this.addColumns();

        // To Do: Cannot refresh column headers/footers due to poor CSS styles.
        this.refreshCols = false;
    }

    // Add rows.
    if (props.rows) {
        this.first = 0; // Reset index used to obtain rows.
        this.currentRow = 0; // Reset current row in view.

        // Clear rows.
        this.widget.removeChildNodes(this.tbody);
        this.addRows(props.rows);
    }
   
    // Cannot call "superclass" here because properties are set on each row.
    return true;
}

/**
 * Process scroll event.
 *
 * @param {Event} event The JavaScript event.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.table2RowGroup.prototype.scroll = function(event) {
    // Publish event to retrieve data.
    if (this.first < this.totalRows
            && this.currentRow % this.maxRows == 0) {
        // Publish an event for custom AJAX implementations to listen for.
        dojo.publish(webui.suntheme.widget.table2RowGroup.event.scroll.beginTopic, [{
            id: this.id,
            first: this.first
        }]);
    }
       
    // Set current row based on scroll position and row offset.
    var first = 0; // First row in range.
    var last = Math.min(this.totalRows,
        this.first + this.maxRows) - 1; // Last row in range.
    var scrollTop = this.tableContainer.scrollTop + 1; // Scroll position plus offset for border.

    while (first < last) {
        var mid = Math.floor((first + last) / 2); // Index of midpoint.
        var tableDataRow = document.getElementById(this.id + ":" + mid);
        if (tableDataRow == null) {
            break;
        }
        // Test if scroll position matches row offset.
        if (scrollTop < tableDataRow.offsetTop) {
            last = mid; // Search left half.
        } else if (scrollTop >= tableDataRow.offsetTop) {
            first = mid + 1; // Search right half.
        }
    }
    this.currentRow = Math.max(0, first - 1);

    // Set rows text.
    return this.updateRowsText();
}

/**
 * This function is used to set rows text (e.g., "1 - 5 of 20").
 *
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.table2RowGroup.prototype.updateRowsText = function() {
    // Add title augment.
    var firstRow = this.currentRow + 1;
    var lastRow = Math.min(this.totalRows, this.currentRow + this.maxRows);

    // To do: Need to create a new rows message.

    // NOTE: If you set this value manually, text must be HTML escaped.
    var msg = this.theme.getMessage("table.title.paginated", [
        "", 
        firstRow, 
        lastRow, 
        this.totalRows, 
        ""
    ]);

    // "Items: " + firstRow + " - " + lastRow + " of " + this.totalRows);
    if (msg) {
        this.widget.addFragment(this.rowsText, msg);
    }
    return true;
}
dojo.provide("webui.suntheme.widget.jsfx.table2RowGroup");


/**
 * @class This class contains functions to obtain data asynchronously using JSF
 * Extensions as the underlying transfer protocol.
 * @static
 */
webui.suntheme.widget.jsfx.table2RowGroup = {
    /**
     * This function is used to process scroll events with Object literals.
     *
     * @param props Key-Value pairs of properties.
     * @config {String} [id] The HTML element Id.
     * @config {int} [row] The first row to be rendered.
     * @return {boolean} true if successful; otherwise, false.
     */
    processScrollEvent: function(props) {
        if (props == null) {
            return false;
        }

        // Dynamic Faces requires a DOM node as the source property.
        var domNode = document.getElementById(props.id);

        // Generate AJAX request using the JSF Extensions library.
        DynaFaces.fireAjaxTransaction(
            (domNode) ? domNode : document.forms[0], {
            execute: "none",
            render: props.id,
            replaceElement: webui.suntheme.widget.jsfx.table2RowGroup.scrollCallback,
            xjson: {
                id: props.id,
                event: "scroll",
                first: props.first
            }
        });
        return true;
    },

    /**
     * This function is used to update widgets.
     *
     * @param {String} elementId The HTML element Id.
     * @param {String} content The content returned by the AJAX response.
     * @param {Object} closure The closure argument provided to DynaFaces.fireAjaxTransaction.
     * @param {Object} xjson The xjson argument provided to DynaFaces.fireAjaxTransaction.
     * @return {boolean} true if successful; otherwise, false.
     */
    scrollCallback: function(id, content, closure, xjson) {
        if (id == null || content == null) {
            return false;
        }

        // Parse JSON text.
        var props = JSON.parse(content);

        // Reject duplicate AJAX requests.
        var widget = dijit.byId(id);
        if (widget.first != xjson.first) {
            return;
        }

        // Add rows.
        widget.addRows(props.rows);

        // Publish an event for custom AJAX implementations to listen for.
        dojo.publish(
            webui.suntheme.widget.table2RowGroup.event.scroll.endTopic, [props]);
        return true;
    }
}

// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.table2RowGroup.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.subscribe(webui.suntheme.widget.table2RowGroup.event.scroll.beginTopic,
    webui.suntheme.widget.jsfx.table2RowGroup, "processScrollEvent");
dojo.provide("webui.suntheme.widget.textArea");


/**
 * @name webui.suntheme.widget.textArea
 * @extends webui.suntheme.widget.textField
 * @class This class contains functions for the textArea widget.
 * @constructor This function is used to construct a textArea widget.
 */
dojo.declare("webui.suntheme.widget.textArea", webui.suntheme.widget.textField, {
    // Set defaults.
    autoSave: 0,
    cols: 20,
    rows: 3,
    widgetName: "textArea" // Required for theme properties.
});

/**
 * Helper function to create callback for timer event.
 *
 * @return {Function} The callback function.
 */
webui.suntheme.widget.textArea.prototype.createSubmitCallback = function() {
    var _id = this.id;

    // New literals are created every time this function
    // is called, and it's saved by closure magic.
    return function(event) { 
        var widget = dijit.byId(_id);
        if (widget == null) {
            return false;
        }
        //Create a submit request only if field has been modified
        if (widget.lastSaved != widget.fieldNode.value) {
            widget.lastSaved = widget.fieldNode.value;
            widget.submit();
        }
        return true;
    };
}

/**
 * This object contains event topics.
 * <p>
 * Note: Event topics must be prototyped for inherited functions. However, these
 * topics must also be available statically so that developers may subscribe to
 * events.
 * </p>
 * @ignore
 */
webui.suntheme.widget.textArea.event =
        webui.suntheme.widget.textArea.prototype.event = {
    /**
     * This object contains refresh event topics.
     * @ignore
     */
    refresh: {
        /** Refresh event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_textArea_event_refresh_begin",

        /** Refresh event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_textArea_event_refresh_end"
    },

    /**
     * This object contains state event topics.
     * @ignore
     */
    state: {
        /** State event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_textArea_event_state_begin",

        /** State event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_textArea_event_state_end"
    },

    /**
     * This object contains submit event topics.
     * @ignore
     */
    submit: {
        /** Submit event topic for custom AJAX implementations to listen for. */
        beginTopic: "webui_suntheme_widget_textArea_event_submit_begin",

        /** Submit event topic for custom AJAX implementations to listen for. */
        endTopic: "webui_suntheme_widget_textArea_event_submit_end"
    }
}

/**
 * Helper function to obtain HTML input element class names.
 *
 * @return {String} The HTML input element class name.
 */
webui.suntheme.widget.textArea.prototype.getInputClassName = function() {
    // Set readOnly style
    if (this.fieldNode.readOnly) {
        return this.widget.getClassName("TEXT_AREA_READONLY", "");
    }

    // Apply invalid style.
    var validStyle =  (this.valid == false) 
        ? " " + this.widget.getClassName("TEXT_AREA_INVALID", "")
        : " " + this.widget.getClassName("TEXT_AREA_VALID", "");

    // Set default style.    
    return (this.disabled == true)
        ? this.widget.getClassName("TEXT_AREA_DISABLED", "") 
        : this.widget.getClassName("TEXT_AREA", "") + validStyle;    
}

/**
 * This function is used to get widget properties. Please see the 
 * setProps() function for a list of supported properties.
 *
 * @return {Object} Key-Value pairs of properties.
 */
webui.suntheme.widget.textArea.prototype.getProps = function() {
    var props = this.inherited("getProps", arguments);
    
    // Set properties.
    if (this.cols > 0 ) { props.cols = this.cols; }
    if (this.rows > 0) { props.rows = this.rows; }
    if (this.autoSave > 0 ) { props.autoSave = this.autoSave; }
   
    return props;
}

/**
 * This function is used to fill in remaining template properties, after the
 * buildRendering() function has been processed.
 * <p>
 * Note: Unlike Dojo 0.4, the DOM nodes don't exist in the document, yet. 
 * </p>
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.textArea.prototype.postCreate = function () {
    // Set events.                
    if (this.autoSave > 0) {
        this.autoSaveTimerId = setInterval(this.createSubmitCallback(), 
            this.autoSave);  
    }
    return this.inherited("postCreate", arguments);
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [accesskey] 
 * @config {boolean} [autoSave] 
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [label]
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {boolean} [readOnly] 
 * @config {boolean} [required] 
 * @config {int} [rows] 
 * @config {String} [style] Specify style rules inline.
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [valid]
 * @config {String} [value] Value of input.
 * @config {boolean} [visible] Hide or show element.
 * @param {boolean} notify Publish an event for custom AJAX implementations to listen for.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.textArea.prototype.setProps = function(props, notify) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}

/**
 * This function is used to set widget properties. Please see the setProps() 
 * function for a list of supported properties.
 * <p>
 * Note: This function should only be invoked through setProps().
 * </p>
 * @param {Object} props Key-Value pairs of properties.
 * @return {boolean} true if successful; otherwise, false.
 * @private
 */
webui.suntheme.widget.textArea.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }

    // Set properties.   
    if (props.cols > 0) { this.fieldNode.cols = props.cols; }
    if (props.rows > 0) { this.fieldNode.rows = props.rows; }
    
    // Cancel autosave if it has been changed to <=0
    if (props.autoSave <= 0 && this.autoSaveTimerId && this.autoSaveTimerId != null ) {
        clearTimeout(this.autoSaveTimerId);
        this.autoSaveTimerId = null;
    }

    // Set label className -- must be set before calling superclass.
    if (props.label) {
        props.label.className = (props.label.className)
            ? this.widget.getClassName("TEXT_AREA_TOPLABELALIGN", "")  + " " + props.label.className
            : this.widget.getClassName("TEXT_AREA_TOPLABELALIGN", "") ;
    }

    // Set remaining properties.
    return this.inherited("_setProps", arguments);
}
dojo.provide("webui.suntheme.widget.jsfx.textArea");


// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.textArea.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.subscribe(webui.suntheme.widget.textArea.event.submit.beginTopic,
    webui.suntheme.widget.jsfx.common, "processSubmitEvent");
dojo.provide("webui.suntheme.widget.jsfx.textField");


/**
 * @class This class contains functions to obtain data asynchronously using JSF
 * Extensions as the underlying transfer protocol.
 * @static
 */
webui.suntheme.widget.jsfx.textField = {
    /**
     * This function is used to process validation events with Object literals.
     *
     * @param props Key-Value pairs of properties.
     * @config {String} [id] The HTML element Id.
     * @return {boolean} true if successful; otherwise, false.
     */
    processValidationEvent: function(props) {
        if (props == null) {
            return false;
        }
        
        // Dynamic Faces requires a DOM node as the source property.
        var domNode = document.getElementById(props.id);
        
        // Generate AJAX request using the JSF Extensions library.
        DynaFaces.fireAjaxTransaction(
            (domNode) ? domNode : document.forms[0], {
            execute: props.id,
            render: props.id,
            replaceElement: webui.suntheme.widget.jsfx.textField.validationCallback,
            xjson: {
                id : props.id,
                event: "validate"
            }
        });
        return true;
    },  

    /**
     * This function is used to update widgets.
     *
     * @param {String} elementId The HTML element Id.
     * @param {String} content The content returned by the AJAX response.
     * @param {Object} closure The closure argument provided to DynaFaces.fireAjaxTransaction.
     * @param {Object} xjson The xjson argument provided to DynaFaces.fireAjaxTransaction.
     * @return {boolean} true if successful; otherwise, false.
     */
    validationCallback: function(elementId, content, closure, xjson) {
        if (elementId == null || content == null) {
            return false;
        }
        
        // Parse JSON text.
        var props = JSON.parse(content);

        // Update text field.
        var widget = dijit.byId(elementId);
        widget.setProps({
            valid: props.valid,
            errorImage: {
                title: props.detail
            }
        });

        // Notify decoupled widgets.
        if (widget.notify) {
            // Update each given client ID.
            for (var i = 0; i < widget.notify.length; i++) {
                // Get widget associated with client ID.
                var curWidget = dijit.byId(widget.notify[i]);
                if (curWidget && typeof curWidget.notify == "function") {
                    curWidget.notify(props);
                }
            }
        }

        // Publish an event for custom AJAX implementations to listen for.
        dojo.publish(
            webui.suntheme.widget.textField.event.validation.endTopic, [props]);
        return true;
    }
}

// Listen for Dojo Widget events.
dojo.subscribe(webui.suntheme.widget.textField.event.refresh.beginTopic,
    webui.suntheme.widget.jsfx.common, "processRefreshEvent");
dojo.subscribe(webui.suntheme.widget.textField.event.state.beginTopic,
    webui.suntheme.widget.jsfx.common, "processStateEvent");
dojo.subscribe(webui.suntheme.widget.textField.event.submit.beginTopic,
    webui.suntheme.widget.jsfx.common, "processSubmitEvent");
dojo.subscribe(webui.suntheme.widget.textField.event.validation.beginTopic,
    webui.suntheme.widget.jsfx.textField, "processValidationEvent");
dojo.provide("webui.suntheme.widget.passwordField");


/**
 * @name webui.suntheme.widget.passwordField
 * @extends webui.suntheme.widget.fieldBase
 * @class This class contains functions for the passwordField widget.
 * @constructor This function is used to construct a passwordField widget.
 */
dojo.declare("webui.suntheme.widget.passwordField", webui.suntheme.widget.fieldBase, {
    // Set defaults.
    widgetName: "passwordField" // Required for theme properties.
});

/**
 * Helper function to obtain HTML input element class names.
 *
 * @return {String} The HTML input element class name.
 */
webui.suntheme.widget.passwordField.prototype.getInputClassName = function() {
    if (this.fieldNode.readOnly) {
        return this.widget.getClassName("PASSWORD_FIELD_READONLY", "");
    }

    //invalid style
    var validStyle =  (this.valid == false) 
        ? " " + this.widget.getClassName("PASSWORD_FIELD_INVALID", "")
        : " " + this.widget.getClassName("PASSWORD_FIELD_VALID", "");
    
    // Set default style.    
    return (this.disabled == true)
        ? this.widget.getClassName("PASSWORD_FIELD_DISABLED", "") 
        : this.widget.getClassName("PASSWORD_FIELD", "") + validStyle;
}

/**
 * This function is used to set widget properties using Object literals.
 * <p>
 * Note: This function extends the widget object for later updates. Further, the
 * widget shall be updated only for the given key-value pairs.
 * </p><p>
 * If the notify param is true, the widget's state change event shall be
 * published. This is typically used to keep client-side state in sync with the
 * server.
 * </p>
 *
 * @param {Object} props Key-Value pairs of properties.
 * @config {String} [accesskey] 
 * @config {String} [className] CSS selector.
 * @config {String} [dir] Specifies the directionality of text.
 * @config {boolean} [disabled] Disable element.
 * @config {String} [id] Uniquely identifies an element within a document.
 * @config {String} [label]
 * @config {String} [lang] Specifies the language of attribute values and content.
 * @config {int} [maxLength] 
 * @config {Array} [notify] 
 * @config {String} [onBlur] Element lost focus.
 * @config {String} [onClick] Mouse button is clicked on element.
 * @config {String} [onDblClick] Mouse button is double-clicked on element.
 * @config {String} [onFocus] Element received focus.
 * @config {String} [onKeyDown] Key is pressed down over element.
 * @config {String} [onKeyPress] Key is pressed and released over element.
 * @config {String} [onKeyUp] Key is released over element.
 * @config {String} [onMouseDown] Mouse button is pressed over element.
 * @config {String} [onMouseOut] Mouse is moved away from element.
 * @config {String} [onMouseOver] Mouse is moved onto element.
 * @config {String} [onMouseUp] Mouse button is released over element.
 * @config {String} [onMouseMove] Mouse is moved while over element.
 * @config {boolean} [readOnly] 
 * @config {boolean} [required] 
 * @config {int} [size] 
 * @config {String} [style] Specify style rules inline.
 * @config {boolean} [submitForm]
 * @config {int} [tabIndex] Position in tabbing order.
 * @config {String} [title] Provides a title for element.
 * @config {boolean} [valid]
 * @config {String} [value] Value of input.
 * @config {boolean} [visible] Hide or show element.
 * @return {boolean} true if successful; otherwise, false.
 */
webui.suntheme.widget.passwordField.prototype.setProps = function(props) {
    // Note: This function is overridden for JsDoc.
    return this.inherited("setProps", arguments);
}
