/*
    http://www.JSON.org/json2.js
    2008-03-24

    Public Domain.

    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.

    See http://www.JSON.org/js.html

    This file creates a global JSON object containing three methods: stringify,
    parse, and quote.


        JSON.stringify(value, replacer, space)
            value       any JavaScript value, usually an object or array.

            replacer    an optional parameter that determines how object
                        values are stringified for objects without a toJSON
                        method. It can be a function or an array.

            space       an optional parameter that specifies the indentation
                        of nested structures. If it is omitted, the text will
                        be packed without extra whitespace. If it is a number,
                        it will specify the number of spaces to indent at each
                        level. If it is a string (such as '\t'), it contains the
                        characters used to indent at each level.

            This method produces a JSON text from a JavaScript value.

            When an object value is found, if the object contains a toJSON
            method, its toJSON method will be called and the result will be
            stringified. A toJSON method does not serialize: it returns the
            value represented by the name/value pair that should be serialized,
            or undefined if nothing should be serialized. The toJSON method will
            be passed the key associated with the value, and this will be bound
            to the object holding the key.

            This is the toJSON method added to Dates:

                function toJSON(key) {
                    return this.getUTCFullYear()   + '-' +
                         f(this.getUTCMonth() + 1) + '-' +
                         f(this.getUTCDate())      + 'T' +
                         f(this.getUTCHours())     + ':' +
                         f(this.getUTCMinutes())   + ':' +
                         f(this.getUTCSeconds())   + 'Z';
                }

            You can provide an optional replacer method. It will be passed the
            key and value of each member, with this bound to the containing
            object. The value that is returned from your method will be
            serialized. If your method returns undefined, then the member will
            be excluded from the serialization.

            If no replacer parameter is provided, then a default replacer
            will be used:

                function replacer(key, value) {
                    return Object.hasOwnProperty.call(this, key) ?
                        value : undefined;
                }

            The default replacer is passed the key and value for each item in
            the structure. It excludes inherited members.

            If the replacer parameter is an array, then it will be used to
            select the members to be serialized. It filters the results such
            that only members with keys listed in the replacer array are
            stringified.

            Values that do not have JSON representaions, such as undefined or
            functions, will not be serialized. Such values in objects will be
            dropped; in arrays they will be replaced with null. You can use
            a replacer function to replace those with JSON values.
            JSON.stringify(undefined) returns undefined.

            The optional space parameter produces a stringification of the value
            that is filled with line breaks and indentation to make it easier to
            read.

            If the space parameter is a non-empty string, then that string will
            be used for indentation. If the space parameter is a number, then
            then indentation will be that many spaces.

            Example:

            text = JSON.stringify(['e', {pluribus: 'unum'}]);
            // text is '["e",{"pluribus":"unum"}]'


            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'


        JSON.parse(text, reviver)
            This method parses a JSON text to produce an object or array.
            It can throw a SyntaxError exception.

            The optional reviver parameter is a function that can filter and
            transform the results. It receives each of the keys and values,
            and its return value is used instead of the original value.
            If it returns what it received, then the structure is not modified.
            If it returns undefined then the member is deleted.

            Example:

            // Parse the text. Values that look like ISO date strings will
            // be converted to Date objects.

            myData = JSON.parse(text, function (key, value) {
                var a;
                if (typeof value === 'string') {
                    a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
                    if (a) {
                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
                            +a[5], +a[6]));
                    }
                }
                return value;
            });


        JSON.quote(text)
            This method wraps a string in quotes, escaping some characters
            as needed.


    This is a reference implementation. You are free to copy, modify, or
    redistribute.

    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD THIRD PARTY
    CODE INTO YOUR PAGES.
*/

/*jslint regexp: true, forin: true, evil: true */

/*global JSON */

/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
    call, charCodeAt, floor, getUTCDate, getUTCFullYear, getUTCHours,
    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, length,
    parse, propertyIsEnumerable, prototype, push, quote, replace, stringify,
    test, toJSON, toString
*/

if (!this.JSON) {

// Create a JSON object only if one does not already exist. We create the
// object in a closure to avoid global variables.

    JSON = function () {

        function f(n) {    // Format integers to have at least two digits.
            return n < 10 ? '0' + n : n;
        }

        Date.prototype.toJSON = function () {

// Eventually, this method will be based on the date.toISOString method.

            return this.getUTCFullYear()   + '-' +
                 f(this.getUTCMonth() + 1) + '-' +
                 f(this.getUTCDate())      + 'T' +
                 f(this.getUTCHours())     + ':' +
                 f(this.getUTCMinutes())   + ':' +
                 f(this.getUTCSeconds())   + 'Z';
        };


        var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
            gap,
            indent,
            meta = {    // table of character substitutions
                '\b': '\\b',
                '\t': '\\t',
                '\n': '\\n',
                '\f': '\\f',
                '\r': '\\r',
                '"' : '\\"',
                '\\': '\\\\'
            },
            rep;


        function quote(string) {

// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.

            return escapeable.test(string) ?
                '"' + string.replace(escapeable, function (a) {
                    var c = meta[a];
                    if (typeof c === 'string') {
                        return c;
                    }
                    c = a.charCodeAt();
                    return '\\u00' + Math.floor(c / 16).toString(16) +
                                               (c % 16).toString(16);
                }) + '"' :
                '"' + string + '"';
        }


        function str(key, holder) {

// Produce a string from holder[key].

            var i,          // The loop counter.
                k,          // The member key.
                v,          // The member value.
                length,
                mind = gap,
                partial,
                value = holder[key];

// If the value has a toJSON method, call it to obtain a replacement value.

            if (value && typeof value === 'object' &&
                    typeof value.toJSON === 'function') {
                value = value.toJSON(key);
            }

// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.

            if (typeof rep === 'function') {
                value = rep.call(holder, key, value);
            }

// What happens next depends on the value's type.

            switch (typeof value) {
            case 'string':
                return quote(value);

            case 'number':

// JSON numbers must be finite. Encode non-finite numbers as null.

                return isFinite(value) ? String(value) : 'null';

            case 'boolean':
            case 'null':

// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.

                return String(value);

// If the type is 'object', we might be dealing with an object or an array or
// null.

            case 'object':

// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.

                if (!value) {
                    return 'null';
                }

// Make an array to hold the partial results of stringifying this object value.

                gap += indent;
                partial = [];

// If the object has a dontEnum length property, we'll treat it as an array.

                if (typeof value.length === 'number' &&
                        !(value.propertyIsEnumerable('length'))) {

// The object is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.

                    length = value.length;
                    for (i = 0; i < length; i += 1) {
                        partial[i] = str(i, value) || 'null';
                    }

// Join all of the elements together, separated with commas, and wrap them in
// brackets.

                    v = partial.length === 0 ? '[]' :
                        gap ? '[\n' + gap + partial.join(',\n' + gap) +
                                  '\n' + mind + ']' :
                              '[' + partial.join(',') + ']';
                    gap = mind;
                    return v;
                }

// If the replacer is an array, use it to select the members to be stringified.

                if (typeof rep === 'object') {
                    length = rep.length;
                    for (i = 0; i < length; i += 1) {
                        k = rep[i];
                        if (typeof k === 'string') {
                            v = str(k, value, rep);
                            if (v) {
                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
                            }
                        }
                    }
                } else {

// Otherwise, iterate through all of the keys in the object.

                    for (k in value) {
                        v = str(k, value, rep);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }

// Join all of the member texts together, separated with commas,
// and wrap them in braces.

                v = partial.length === 0 ? '{}' :
                    gap ? '{\n' + gap + partial.join(',\n' + gap) +
                              '\n' + mind + '}' :
                          '{' + partial.join(',') + '}';
                gap = mind;
                return v;
            }
        }


// Return the JSON object containing the stringify, parse, and quote methods.

        return {
            stringify: function (value, replacer, space) {

// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.

                var i;
                gap = '';
                indent = '';
                if (space) {

// If the space parameter is a number, make an indent string containing that
// many spaces.

                    if (typeof space === 'number') {
                        for (i = 0; i < space; i += 1) {
                            indent += ' ';
                        }

// If the space parameter is a string, it will be used as the indent string.

                    } else if (typeof space === 'string') {
                        indent = space;
                    }
                }

// If there is no replacer parameter, use the default replacer.

                if (!replacer) {
                    rep = function (key, value) {
                        if (!Object.hasOwnProperty.call(this, key)) {
                            return undefined;
                        }
                        return value;
                    };

// The replacer can be a function or an array. Otherwise, throw an error.

                } else if (typeof replacer === 'function' ||
                        (typeof replacer === 'object' &&
                         typeof replacer.length === 'number')) {
                    rep = replacer;
                } else {
                    throw new Error('JSON.stringify');
                }

// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.

                return str('', {'': value});
            },


            parse: function (text, reviver) {

// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.

                var j;

                function walk(holder, key) {

// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.

                    var k, v, value = holder[key];
                    if (value && typeof value === 'object') {
                        for (k in value) {
                            if (Object.hasOwnProperty.call(value, k)) {
                                v = walk(value, k);
                                if (v !== undefined) {
                                    value[k] = v;
                                } else {
                                    delete value[k];
                                }
                            }
                        }
                    }
                    return reviver.call(holder, key, value);
                }


// Parsing happens in three stages. In the first stage, we run the text against
// regular expressions that look for non-JSON patterns. We are especially
// concerned with '()' and 'new' because they can cause invocation, and '='
// because it can cause mutation. But just to be safe, we want to reject all
// unexpected forms.

// We split the first stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace all backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

                if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

// In the second stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                    j = eval('(' + text + ')');

// In the optional third stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.

                    return typeof reviver === 'function' ?
                        walk({'': j}, '') : j;
                }

// If the text is not JSON parseable, then a SyntaxError is thrown.

                throw new SyntaxError('JSON.parse');
            },

            quote: quote
        };
    }();
}


// We only need one instance of JS
if(!this.JS) {



/**
 * Copyright (c) 2007-2008 James Coglan
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * Parts of this software are derived from the following open-source projects:
 *
 * - The Prototype framework, (c) 2005-2007 Sam Stephenson
 * - Alex Arnell's Inheritance library, (c) 2006, Alex Arnell
 * - Base, (c) 2006-7, Dean Edwards
 */

JS = {
  extend: function(object, methods) {
    for (var prop in methods) object[prop] = methods[prop];
  },
  
  method: function(name) {
    var self = this, cache = self._methods = self._methods || {};
    if ((cache[name] || {}).fn == self[name]) return cache[name].bd;
    return (cache[name] = {fn: self[name], bd: self[name].bind(self)}).bd;
  },
  
  util: {}
};

Array.from = function(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) return iterable.toArray();
  var length = iterable.length, results = [];
  while (length--) results[length] = iterable[length];
  return results;
};

JS.extend(Function.prototype, {
  bind: function() {
    var __method = this, args = Array.from(arguments), object = args.shift() || null;
    return function() {
      return __method.apply(object, args.concat(Array.from(arguments)));
    };
  },
  callsSuper: function() {
    return /\bcallSuper\b/.test(this.toString());
  },
  is: function(object) {
    return typeof object == 'function';
  }
});

JS.Class = function() {
  var args = Array.from(arguments), arg,
      parent = Function.is(args[0]) ? args.shift() : null,
      klass = JS.Class.create(parent);
  
  while (arg = args.shift())
    klass.include(arg);
  
  parent && Function.is(parent.inherited) &&
    parent.inherited(klass);
  
  return klass;
};

JS.extend(JS.Class, {
  create: function(parent) {
    var klass = function() {
      this.initialize.apply(this, arguments);
    };
    this.ify(klass);
    parent && this.subclass(parent, klass);
    var p = klass.prototype;
    p.klass = p.constructor = klass;
    klass.include(this.INSTANCE_METHODS, false);
    klass.instanceMethod('extend', this.INSTANCE_METHODS.extend, false);
    return klass;
  },
  
  ify: function(klass, noExtend) {
    klass.superclass = klass.superclass || Object;
    klass.subclasses = klass.subclasses || [];
    if (noExtend === false) return klass;
    for (var method in this.CLASS_METHODS)
      this.CLASS_METHODS.hasOwnProperty(method) &&
        (klass[method] = this.CLASS_METHODS[method]);
    return klass;
  },
  
  subclass: function(superklass, klass) {
    this.ify(superklass, false);
    klass.superclass = superklass;
    superklass.subclasses.push(klass);
    var bridge = function() {};
    bridge.prototype = superklass.prototype;
    klass.prototype = new bridge();
    klass.extend(superklass);
    return klass;
  },
  
  properties: function(klass) {
    var properties = {}, prop, K = this.ify(function(){});
    loop: for (var method in klass) {
      for (prop in K) { if (method == prop) continue loop; }
      properties[method] = klass[method];
    }
    return properties;
  },
  
  addMethod: function(object, superObject, name, func) {
    if (!Function.is(func)) return (object[name] = func);
    if (!func.callsSuper()) return (object[name] = func);
    
    var method = function() {
      var _super = superObject[name], args = Array.from(arguments), currentSuper = this.callSuper, result;
      Function.is(_super) && (this.callSuper = function() {
        var i = arguments.length;
        while (i--) args[i] = arguments[i];
        return _super.apply(this, args);
      });
      result = func.apply(this, arguments);
      currentSuper ? this.callSuper = currentSuper : delete this.callSuper;
      return result;
    };
    method.valueOf = function() { return func; };
    method.toString = function() { return func.toString(); };
    object[name] = method;
  },
  
  INSTANCE_METHODS: {
    initialize: function() {},
    
    method: JS.method,
    
    extend: function(source) {
      for (var method in source)
        source.hasOwnProperty(method) &&
          JS.Class.addMethod(this, this.klass.prototype, method, source[method]);
      return this;
    },
    
    isA: function(klass) {
      var _class = this.klass;
      while (_class) {
        if (_class === klass) return true;
        _class = _class.superclass;
      }
      return false;
    }
  },
  
  CLASS_METHODS: {
    include: function(source, overwrite) {
      var modules, i, n, inc = source.include, ext = source.extend;
      if (inc) {
        modules = [].concat(inc);
        for (i = 0, n = modules.length; i < n; i++)
          this.include(modules[i], overwrite);
      }
      if (ext) {
        modules = [].concat(ext);
        for (i = 0, n = modules.length; i < n; i++)
          this.extend(modules[i], overwrite);
      }
      for (var method in source) {
        !/^(included?|extend(ed)?)$/.test(method) &&
          this.instanceMethod(method, source[method], overwrite);
      }
      Function.is(source.included) && source.included(this);
      return this;
    },
    
    instanceMethod: function(name, func, overwrite) {
      if (!this.prototype[name] || overwrite !== false)
        JS.Class.addMethod(this.prototype, this.superclass.prototype, name, func);
      return this;
    },
    
    extend: function(source, overwrite) {
      Function.is(source) && (source = JS.Class.properties(source));
      for (var method in source) {
        source.hasOwnProperty(method) && !/^(included?|extend(ed)?)$/.test(method) &&
          this.classMethod(method, source[method], overwrite);
      }
      Function.is(source.extended) && source.extended(this);
      return this;
    },
    
    classMethod: function(name, func, overwrite) {
      for (var i = 0, n = this.subclasses.length; i < n; i++)
        this.subclasses[i].classMethod(name, func, false);
      (!this[name] || overwrite !== false) &&
        JS.Class.addMethod(this, this.superclass, name, func);
      return this;
    },
    
    method: JS.method
  }
});

JS.extend(JS, {
  Interface: JS.Class({
    initialize: function(methods) {
      this.test = function(object, returnName) {
        var n = methods.length;
        while (n--) {
          if (!Function.is(object[methods[n]]))
            return returnName ? methods[n] : false;
        }
        return true;
      };
    },
    
    extend: {
      ensure: function() {
        var args = Array.from(arguments), object = args.shift(), face, result;
        while (face = args.shift()) {
          result = face.test(object, true);
          if (result !== true) throw new Error('object does not implement ' + result + '()');
        }
      }
    }
  }),
  
  Singleton: function() {
    return new (JS.Class.apply(JS, arguments));
  },
  
  Module: function(source) {
    return {
      included: function(klass) { klass.include(source); },
      extended: function(klass) { klass.extend(source); }
    };
  }
});



}


// We only need one instance of DLSupport
if(!this.DLSupport) {



/**
 * Contains various functions.
 *
 * @author      Duc Tri Le
 * @copyright   Copyright (c) 2008, Duc Tri Le
 * @version     2.0RC2 - August 9, 2008: Fix DLSupport.Form.addParameter to
 *              handle select boxes with multiple values.
 *              2.0RC1 - July 25, 2008: Added DLSupport.Tab and made some minor
 *              bug fixes.
 *              1.3 - May 30, 2008: The following member data was added to
 *              DLSupport: ErrorLog. The following methods were added to
 *              DLSupport: addErrorLog, humanReadable, newEvent. The following
 *              methods has been updated: checkboxSelectAllToggle, curry,
 *              defaultValues, delayFunction, inArray, removeElement,
 *              and windowOnload. The following methods were updated to
 *              DLSupport.AJAX: monitor and request. The following method was
 *              updated in DLSupport.Form: submitValue.
 *              1.1 - May 9, 2008: The following member data was added to
 *              DLSupport: ModuleUrl. The following methods was added to
 *              DLSupport: checkboxSelectAllToggle, cloneArray, combineArray,
 *              defaultValues, delayFunction, hideLoadingImage, inArray,
 *              isArray, isNumeric, propertyNames, reverseArray,
 *              showLoadingImage, toggleDisplay, toQueryString, and uniqueArray.
 *              The following methods was updated in DLSupport: curry and
 *              windowOnload. A new object, DLSupport.AJAX was added and it
 *              includes the following methods: monitor and request. Another
 *              new object was added, DLSupport.Form and it contains the
 *              following methods: addParameter, disableSubmitFields,
 *              enableSubmitFields, fieldCount, submitValue, and toQueryString.
 *              Closure has been added so that only one instance of DLSupport
 *              exist.
 *              1.0 - April 4, 2008: First stable version. This is the same as
 *              RC1 so an update to this is not really needed but encouraged.
 *              1.0RC1 - March 28, 2008: Updated the method windowOnload.
 *              0.1 - March 15, 2008: Initial release.
 *              0.0 - March 10, 2008: Started.
 */
var DLSupport = {
    /**
     * Contains a log of all the errors during the script execution.
     *
     * @var array
     */
    ErrorLog: [],

    /**
     * Counter used for generating unique IDs.
     *
     * @var int
     */
    IdCounter: 1,

    /**
     * The URL to the directory containing this module. This will be set
     * dynamically after the script loads.
     *
     * @var string
     */
    ModuleUrl: '',

    /**
     * Add the given class name to the given DOM element.
     *
     * @param object    obj         The DOM element that the class name will be
     *      added to if it does not already have it.
     * @param string    class_name  The class name to be added.
     */
    addClassName: function(obj, class_name) {
        try {
            if(!DLSupport.hasClassName(obj, class_name)) {
                obj.className += (obj.className ? ' ' : '') + class_name;
            }
        } catch(e) {
            // Add error message to error log
            DLSupport.addErrorLog('DLSupport.addClassName', [obj, class_name], e.message);
        }
    },

    /**
     * Add an message to the error log.
     *
     * @param string    method      The name of the method that generated the
     *      error.
     * @param array     parameters  The parameters that was given to the method
     *      that generated the error.
     * @param string    message     The error message.
     */
    addErrorLog: function(method, parameters, message) {
        var item = {
            method: method,
            parameters: parameters,
            message: message
        };

        DLSupport.ErrorLog.push(item);
    },

    /**
     * Check or uncheck all input checkboxes with the same class name as the
     * given target based on the current status of the given element.
     *
     * @param object    element     The DOM element in which if it is checked,
     *      all input boxes with the given class name, will be checked and vice
     *      versa.
     * @param string    target      The class name of all input checkboxes that
     *      will be checked or unchecked.
     * @return void
     */
    checkboxSelectAllToggle: function(element, target) {
        // Get all input check boxes
        var check_boxes = document.getElementsByTagName('input');

        // Now go through list of input boxes
        var class_name;
        for(var i = 0; i < check_boxes.length; ++i) {
            class_name = check_boxes[i].className;

            // Set element's status if input has given class name
            if(class_name.indexOf(target) >= 0) {
                check_boxes[i].checked = element.checked;
            }
        }
    },

    /**
     * Retrieves an array of all direct child DOM elements of the given element.
     *
     * @param object    parent      The DOM element that will serve as the
     *      parent.
     * @return array
     */
    childElements: function(parent) {
        try {
            var result = [];

            for(var i = 0; i < parent.childNodes.length; ++i) {
                // Make sure it is a DOM element
                if(parent.childNodes[i].nodeType == 1) {
                    result.push(parent.childNodes[i]);
                }
            }

            return result;
        } catch(e) {
            DLSupport.addErrorLog('DLSupport.childElements', [parent], e.message);

            return [];
        }
    },

    /**
     * Create a copy of the given array.
     *
     * @param array     data    The array to be cloned.
     * @return array    An array containing all of the data of the given array.
     */
    cloneArray: function(data) {
        var result = [].concat(data);

        return result;
    },

    /**
     * Create an associative array (object) by using the first given array as
     * the keys and the second given array as the value. It is required that
     * both arrays be of the same length.
     *
     * @param array     keys    The array to use as the keys.
     * @param array     values  The array to use as the values.
     * @return object   An object with the given keys as the property names and
     *      the given values as its corresponding value.
     */
    combineArray: function(keys, values) {
        // Create an empty object
        var result = {};

        // Combine the arrays
        for(var i = 0; (i < keys.length) && (i < values.length); ++i) {
            result[keys[i]] = values[i];
        }

        return result;
    },

    /**
     * Currify the given function.
     *
     * @param function  func    The function to currify.
     * @param object    scope   The scope of the function.
     * @return function     A function that wraps the given function with some
     *      of its parameters preset.
     */
    curry: function(func, scope) {
        // Make sure the scope is given, otherwise, default to window
        scope = scope || window;

        // Get the arguments
        var args = [].slice.call(arguments,2);

        // Create the function
        return function() {
            return func.apply(scope, args.concat([].slice.call(arguments,0)));
        };
    },

    /**
     * Using the given base values object, copy values from the source object
     * if they have the same property name.
     *
     * @param object    source      The object being used to copy values over
     *      from.
     * @param object    base        The object that contains the default values.
     * @return object   The object containing values from both the source and
     *      base object with the source object's value replacing the base
     *      object's value if they have the same property name.
     */
    defaultValues: function(source, base) {
        // Create an empty object
        var result = {};

        // Get all the property names of base
        var property_names = DLSupport.propertyNames(base);

        // Traverse the property names
        var property;
        for(var i = 0; i < property_names.length; ++i) {
            property = property_names[i];

            // If source has a value, use it, otherwise use base's
            if(source[property]) {
                result[property] = source[property];
            } else {
                result[property] = base[property];
            }
        }

        return result;
    },

    /**
     * Delay the execution of the given function by the given number of seconds.
     *
     * @param function  func    The function whose execution will be delayed.
     * @param numeric   delay   The number of seconds to wait before the given
     *      function is executed.
     *
     * @return void
     */
    delayFunction: function(func, delay) {
        // No need to delay the function if it is 0 seconds
        if(delay == 0) {
            func.apply(window);
        } else {
            // Convert the given delay to milliseconds
            delay = delay * 1000;

            window.setTimeout(func, delay);
        }
    },

    /**
     * Get a value from the cookie.
     *
     * @param string    key     The key under which the cookie was stored.
     * @return string|null      Returns the value of the cookie of the given
     *      key or null if it does not exist.
     */
    getCookie: function(key) {
        var nameEQ = key+'=';
        var ca = document.cookie.split(';');

        for(var i = 0; i < ca.length; ++i) {
            var c = ca[i];

            while(c.charAt(0) == ' ') {
                c = c.substring(1, c.length);
            }

            if(c.indexOf(nameEQ) === 0) {
                return c.substring(nameEQ.length, c.length);
            }
        }

        return null;
    },

    /**
     * If given is a string, this will try to get the object with the same ID as
     * the string. Otherwise, this just returns back the given data if it is
     * anything else.
     *
     * @param mixed     data    A string representing the ID of a DOM element or
     *      any thing else.
     * @return object
     */
    getElement: function(data) {
        if(typeof(data) == 'string') {
            data = document.getElementById(data);
        }

        return data;
    },

    /**
     * Determine whether or not the given DOM element has the given class name.
     *
     * @param object    obj         The DOM element that will be tested.
     * @param string    class_name  The class name to look for.
     * @return boolean  Whether or not the given class name exist in the given
     *      DOM element.
     */
    hasClassName: function(obj, class_name) {
        try {
            // First, make sure the element actually has any class names
            var result = obj.className.length > 0;

            // If it does but doesn't match the given class
            if(result && (obj.className != class_name)) {
                // Try regular expression since it can have multiple class
                var regex = new RegExp('(^|\\s)'+class_name+'(\\s|$)');

                result = regex.test(obj.className);
            }

            return result;
        } catch(e) {
            // Add error message to error log
            DLSupport.addErrorLog('DLSupport.hasClassName', [obj, class_name], e.message);

            return false;
        }
    },

    /**
     * Hide the loading image.
     *
     * @return void
     */
    hideLoadingImage: function() {
        var loading_overlay = document.getElementById('DLSupport_loading_overlay');
        var loading_div = document.getElementById('DLSupport_loading_div');

        // If the elements exist, hide it
        if(loading_overlay) {
            loading_overlay.style.display = 'none';
        }
        if(loading_div) {
            loading_div.style.display = 'none';
        }
    },

    /**
     * Retrieves the given data into human readable format. Credits goes to
     * Michael White & Ben Bryan.
     *
     * @param mixed     data    The data to be parsed and returned.
     * @return string   The string representation of the given data.
     */
    humanReadable: function(data) {
        var result = '';
        var pad_char = ' ';
        var pad_val = 4;

        var formatArray = function(obj, cur_depth, pad_val, pad_char) {
            if(cur_depth > 0) {
                ++cur_depth;
            }

            var base_pad = repeat_char(pad_val*cur_depth, pad_char);
            var thick_pad = repeat_char(pad_val*(cur_depth+1), pad_char);
            var str = '';

            if((obj instanceof Array) || (obj instanceof Object)) {
                str += "Array\n" + base_pad + "(\n";
                var property_names = DLSupport.propertyNames(obj);
                var key;
                for(var i = 0; i < property_names.length; ++i) {
                    key = property_names[i];

                    if(DLSupport.isArray(obj[key])) {
                        str += thick_pad + "["+key+"] => "+formatArray(obj[key], cur_depth+1, pad_val, pad_char);
                    } else {
                        str += thick_pad + "["+key+"] => " + obj[key] + "\n";
                    }
                }
                str += base_pad + ")\n";
            } else {
                str = obj.toString();
            }

            return str;
        };

        var repeat_char = function(len, pad_char) {
            var str = '';
            for(var i=0; i < len; ++i) {
                str += pad_char;
            };

            return str;
        };

        result = formatArray(data, 0, pad_val, pad_char);

        return result;
    },

    /**
     * Retrieve the ID of the given object, creating one if it does not exist.
     *
     * @param object    obj     The object to retrieve the ID from.
     * @return string   The ID of the given object. This will return an empty
     *      string if the object does not exist.
     */
    identify: function(obj) {
        try {
            // If object doesn't have an ID, create one
            if(obj.id.length < 1) {
                while(document.getElementById('dlsupport_id_'+DLSupport.IdCounter)) {
                    ++DLSupport.IdCounter;
                }

                obj.id = 'dlsupport_id_'+IdCounter;
            }

            return obj.id;
        } catch(e) {
            // Add error message to error log
            DLSupport.addErrorLog('DLSupport.identify', [obj], e.message);

            return '';
        }
    },

    /**
     * Find out whether or not the given item is in the given array.
     *
     * @param mixed     item        The item to look for.
     * @param array     haystack    The array to used for the search.
     * @param boolean   strict      This is optional and defaults to true. If
     *      this is true, then the search will use === when comparing the data,
     *      otherwise, it will just use ==.
     * @return boolean  Returns true if the given item is in the given array,
     *      false otherwise.
     */
    inArray: function(item, haystack) {
        // Get the strict parameter
        var strict = arguments[2] || true;

        // Traverse the array and look for the item
        var result = false;
        for(var i = 0; (i < haystack.length) && !result; ++i) {
            if(strict) {
                result = item === haystack[i];
            } else {
                result = item == haystack[i];
            }
        }

        return result;
    },

    /**
     * Determine whether or not the given item is an array.
     *
     * @param mixed     item    The item to check.
     * @return boolean  Returns true if the given item is an array, false
     *      otherwise.
     */
    isArray: function(item) {
        var result = (item instanceof Array);

        return result;
    },

    /**
     * Determine whether or not the given item is numeric.
     *
     * @param mixed     item    The item to check.
     * @return boolean  Returns true if the given item is a number, false
     *      otherwise.
     */
    isNumeric: function(item) {
        var result = !isNaN(item);

        return result;
    },

    /**
     * Attach a new event to the given element.
     *
     * @param object    element     The element in which the event should be
     *      attached to.
     * @param string    event       The event by which the given function should
     *      be executed. This should not include the text "on".
     * @param function  func        The function to append to the list.
     * @param int       delay       The time, in seconds, to delay the execution
     *      after the page has loaded. This is optional & defaults to 0.
     * @param boolean   Returns true if successful, otherwise, false.
     */
    newEvent: function(element, event, func, delay) {
        // See if we have a delay param
        var delay = arguments[3] || 0;

        // Delay the function execution
        var delayed_func = DLSupport.curry(DLSupport.delayFunction, window, func, delay);

        if(element.addEventListener) {       // W3C compliant browsers
            element.addEventListener(event, delayed_func, false);
        } else if(element.attachEvent) {     // IE
            element.attachEvent('on'+event, delayed_func);
        } else {                            // Give up
            // Add error message to error log
            DLSupport.addErrorLog('DLSupport.newEvent', [element, event, func, delay], e.message);

            return false;
        }

        return true;
    },

    /**
     * Get the property names of the given object.
     *
     * @param object    item    The item to retrieve the property names.
     * @return array    An array containing all the properties belonging to
     *      the given object. Note that this will only contain properties that
     *      the given object actually own.
     */
    propertyNames: function(item) {
        // Create an empty array
        var result = new Array();

        for(var key in item) {
            if(item.hasOwnProperty(key)) {
                result.push(key);
            }
        }

        return result;
    },

    /**
     * Remove the given class name from the given DOM element.
     *
     * @param object    obj         The DOM element that will have the given
     *      class name removed.
     * @param string    class_name  The class name to remove.
     */
    removeClassName: function(obj, class_name) {
        try {
            var regex = new RegExp('(^|\\s+)'+class_name+'(\\s+|$)');

            obj.className = obj.className.replace(regex, ' ');
        } catch(e) {
            // Add error message to error log
            DLSupport.addErrorLog('DLSupport.removeClassName', [obj, class_name], e.message);
        }
    },

    /**
     * Wrapper method around the method "setCookie". This method will set the
     * expire time to the current time.
     *
     * @param string    key     The key under which the cookie was stored.
     */
    removeCookie: function(key) {
        DLSupport.setCookie(key, '', 0);
    },

    /**
     * This will remove the given element from the DOM structure of the web
     * site. Be careful when using this method because it is a mutation of the
     * DOM structure. Removing the given element will also remove all of its
     * children.
     *
     * @param object    element     The DOM element to remove.
     * @return boolean  Returns true if successful, false, otherwise.
     */
    removeElement: function(element) {
        try {
            element.parentNode.removeChild(element);

            return true;
        } catch(e) {
            // Add error message to error log
            DLSupport.addErrorLog('DLSupport.removeElement', [element], e.message);

            return false;
        }
    },

    /**
     * Get the array that has the reverse order of the given array. This differs
     * from the pre-defined reverse array function in that this can be set
     * to be done recursively.
     *
     * @param array     data        The array to have it values reversed.
     * @param boolean   recursive   Optional & defaults to false. If true,
     *      recur into an inner arrays.
     * @return array    The given array but its order in reverse.
     */
    reverseArray: function(data) {
        // Get the recursive parameter
        var recursive = arguments[1] || false;

        // Traverse the array in descending order and add back the items
        var result = new Array();
        for(var i = data.length-1; i >= 0; --i) {
            if(recursive && DLSupport.isArray(data[i])) {
                result.push(DLSupport.reverseArray(data[i]));
            } else {
                result.push(data[i]);
            }
        }

        return result;
    },

    /**
     * Store the given value to cookie under the given key for the given number
     * of days.
     *
     * @param string    key     The key under which the cookie's value is
     *      stored.
     * @param string    value   The value to be stored in the cookie.
     * @param int       days    The number of days until the cookie expires from
     *      the current date.
     */
    setCookie: function(key, value, days) {
        // Get the current date
        var date = new Date();

        // Incremenet the date by the given number of days
        date.setTime(date.getTime() + (days*24*60*60*1000));

        // Set the cookie
        document.cookie = key+'='+value+'; expires='+date.toGMTString()+
                          '; domain='+window.location.hostname+'; path=/';
    },

    /**
     * Fade out the entire page and show the loading image. Note that this
     * function should only be called after the page has finished loading.
     *
     * @param string    color       The color of the loading image. The possible
     *      values are: "black", "blue", "brown", "gold", "gray", "green",
     *      "navy", "orange", "purple", "red", "white", and "yellow".
     * @return void
     */
    showLoadingImage: function(color) {
        var loading_overlay = document.getElementById('DLSupport_loading_overlay');
        var loading_div = document.getElementById('DLSupport_loading_div');

        // Calculate the screen information
        var page_offset_top, page_offset_left;
        var page_height = document.documentElement.scrollHeight;
        var page_width = document.documentElement.scrollWidth;
        if(DLSupport.Browser.IE) {
            page_offset_top = document.documentElement.scrollTop;
            page_offset_left = document.documentElement.scrollLeft;
        } else {
            page_offset_top = window.pageYOffset;
            page_offset_left = window.pageXOffset;
        }

        // Create them if they don't exist
        if(!loading_overlay) {
            loading_overlay = document.createElement('div');
            loading_overlay.id = 'DLSupport_loading_overlay';
            loading_overlay.style.display = 'none';
            document.body.appendChild(loading_overlay);
        }
        if(!loading_div) {
            loading_div = document.createElement('div');
            loading_div.id = 'DLSupport_loading_div';
            loading_div.style.display = 'none';

            var loading_image = document.createElement('img');
            loading_image.src = DLSupport.ModuleUrl+'/images/loading_images/'+color+'.gif';
            loading_image.alt = 'Loading...';

            document.body.appendChild(loading_div);
            loading_div.appendChild(loading_image);
        }

        // Dynamically set following data
        loading_overlay.style.height = page_height+'px';
        loading_overlay.style.width = page_width+'px';
        loading_div.style.top = Math.floor(((page_height-page_offset_top)/2) + page_offset_top - 50)+'px';
        loading_div.style.left = Math.floor(((page_width-page_offset_left)/2) + page_offset_left - 50)+'px';

        loading_overlay.style.display = '';
        loading_div.style.display = '';
    },

    /**
     * Toggle the display of the given DOM element. If it is currently is
     * hidden, then show it. If it is currently visibile, then hdie it.
     *
     * @param object    element     The DOM element whose display will be
     *      toggled.
     * @return void
     */
    toggleDisplay: function(element) {
        if(element.style.display == 'none') {
            // Show the given element
            element.style.display = '';
        } else {
            // Hide the given element
            element.style.display = 'none';
        }
    },

    /**
     * Convert the given object to a query string.
     *
     * @param object    data        The object to be converted.
     * @return string   The given data as a query string.
     */
    toQueryString: function(data) {
        // Get all the property names for the given data
        var property_names = DLSupport.propertyNames(data);

        // Traverse all the property names and create query string
        var result = new Array(), property, extra;
        for(var i = 0, j; i < property_names.length; ++i) {
            property = property_names[i];

            // If the property value is an array, combine them together first
            if(DLSupport.isArray(data[property])) {
                extra = new Array();
                for(j = 0; j < data[property].length; ++j) {
                    extra[j] = encodeURIComponent(property)+'='+encodeURIComponent(data[property][j]);
                }

                result[i] = extra.join('&');
            } else {
                result[i] = encodeURIComponent(property)+'='+encodeURIComponent(data[property]);
            }
        }

        return result.join('&');
    },

    /**
     * Get the array that has only the unique values of the given array.
     *
     * @param array     data        The array to use to create a unique array.
     * @param boolean   strict      This is optional and defaults to true. If
     *      this is true, then the search will use === when comparing the data,
     *      otherwise, it will just use ==.
     * @return array    Returns the given array with repeated items removed.
     */
    uniqueArray: function(data) {
        // Get the strict parameter
        var strict = arguments[1] || true;

        // Traverse the array and add only unique items
        var result = new Array();
        for(var i = 0; i < data.length; ++i) {
            if(!DLSupport.inArray(data[i], result, strict)) {
                result.push(data[i]);
            }
        }

        return result;
    },

    /**
     * This will add a new event to the event listener that will run the given
     * function after the window has finish loading (basically when all of the
     * DOM structure has finish loading). Since this is adding to the list, you
     * do not have to worry about overwriting someone else's events or theirs
     * overwriting yours. The second parameter can futher delay the execution
     * so you can pretty much sort the order of the execution of the function.
     * It also allows you to wait for other javascript functions to finish
     * running before yours can go. At worse case, it uses window.setTimeout.
     *
     * @param function  func    The function to append to the list.
     * @param int       delay   The time, in seconds, to delay the execution
     *      after the page has loaded. This is optional & defaults to 0.
     * @return boolean  Returns true if it was successful in adding the onload
     *      to the list. Returns false if it has to default to using
     *      window.setTimeout.
     */
    windowOnload: function(func) {
        // See if we have a delay param
        var delay = arguments[1] || 0;

        // Try to add the event
        if(DLSupport.newEvent(window, 'load', func, delay)) {
            return true;
        } else {
            // Add error message to error log
            DLSupport.addErrorLog('DLSupport.windowOnload', [func], 'Failed to add event, using window.setTimeout');

            // Delay the function execution
            var delayed_func = DLSupport.curry(DLSupport.delayFunction, window, func, delay);

            // Delay it even further by 5 seconds so hopefully the page has
            // loaded
            window.setTimeout(delayed_func, 5000);

            return false;
        }
    }
};



}


// We only need one instance of DLSupport.AJAX
if(!this.DLSupport.AJAX) {



/**
 * A wrapper for AJAX functionalities.
 *
 * @var object
 */
DLSupport.AJAX = {
    /**
     * Monitor the states of the AJAX object and call the appropriate callback
     * functions.
     *
     * @param object    ajax        The AJAX transport.
     * @param function  error       The function to call if there was an error
     *      while the request is being made.
     * @param function  failure     The function to call if the request was a
     *      failure.
     * @param function  success     The function to call if the request was a
     *      success.
     * @return void
     */
    monitor: function(ajax, error, failure, success) {
        // Only continue if the request is loaded (readyState is 4)
        if(ajax.readyState == 4) {
            // Make sure it was successful
            try {
                if(ajax.status == 200) {
                    // It was a success, call the success callback function
                    success.apply(this, [ajax]);
                } else {
                    // It was not a success but is not an error, call the
                    // failure callback function
                    failure.apply(this, [ajax]);
                }
            } catch(e) {
                // Add error message to error log
                DLSupport.addErrorLog('DLSupport.AJAX.monitor', [ajax, error, failure, success], e.message);

                // Call the error callback function
                error.apply(this, [ajax, e.message]);
            }
        }
    },

    /**
     * Make a request to the given url using the given method and parameters.
     *
     * @param string    url         The URL of the page to request.
     * @param object    options     Optional. An object containing various
     *      options for the request. The following are the available options:
     *      1) method - (string) The method to use to make the request. This can
     *      either be "get" or "post". 2) onError - (function) The callback
     *      function to call if at any time an error occurred in the request.
     *      3) onFailure - (function) The callback function for a request that
     *      has failed. If not given, this will default in no function being
     *      called. 4) onSuccess - (function) The callback function for a
     *      successful request. If not given, this will default in no function
     *      being called. 5) parameters - (object) An object containing any
     *      information to be sent to the request url. 6) query_string -
     *      (string) If this is given, this will be used instead of the
     *      parameters option. This should be a properly formed query string to
     *      be sent to the server.
     * @return XMLHttpRequest|ActiveXObject The AJAX transport.
     */
    request: function(url) {
        var options = DLSupport.defaultValues(arguments[1] || {}, {
            method: 'POST',
            parameters: {},
            query_string: '',
            onError: function(ajax, message) { window.alert(message); },
            onFailure: function() {},
            onSuccess: function() {}
        });

        // Convert the paramters to a query string
        var parameters = (options.query_string.length > 0) ?
                         options.query_string :
                         DLSupport.toQueryString(options.parameters);

        // Create the AJAX object
        var ajax;
        if(window.XMLHttpRequest) { // Mozilla, Safari, ...
            ajax = new XMLHttpRequest();
        } else if(window.ActiveXObject) { // IE
            try {
                ajax = new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e) {
                try {
                    ajax = new ActiveXObject("Microsoft.XMLHTTP");
                } catch (e) { // If we made it here, we have to just give up
                    // Add error message to error log
                    DLSupport.addErrorLog('DLSupport.AJAX.request', [url], e.message);

                    // Call the error callback function
                    options.onError.apply(this, [ajax, 'Failed to create AJAX object: '+e.message]);
                    return;
                }
            }
        }

        // Monitor the changes in the AJAX state
        ajax.onreadystatechange = DLSupport.curry(
            DLSupport.AJAX.monitor, this, ajax,
            options.onError, options.onFailure, options.onSuccess
        );

        // If method is "GET", append params to the url
        if(options.method.toUpperCase() == 'GET') {
            url = ((url.indexOf('?') == -1) ? '?' : '&')+parameters;
            parameters = '';
        }

        // Open the connection
        ajax.open(options.method.toUpperCase(), url, true);

        // If method is "POST", set the appropriate request header
        if(options.method.toUpperCase() == 'POST') {
            ajax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        }

        // Send the request
        ajax.send(parameters);

        return ajax;
    }
};



}


// We only need one instance of DLSupport.Browser
if(!this.DLSupport.Browser) {



/**
 * Contains browser information.
 *
 * @var object
 */
DLSupport.Browser = {
    /**
     * A boolean specifying whether or not the current browser is Gecko based
     * (Firefox).
     *
     * @var boolean
     */
    Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,

    /**
     * A boolean specifying whether or not the current browser is Internet
     * Explorer.
     *
     * @var boolean
     */
    IE: !!(window.attachEvent && !window.opera),

    /**
     * A boolean specifying whether or not the current browser is Mobile Safari.
     *
     * @var boolean
     */
    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/),

    /**
     * A boolean specifying whether or not the current browser is Opera.
     *
     * @var boolean
     */
    Opera: !!window.opera,

    /**
     * A boolean specifying whether or not the current browser is Safari.
     *
     * @var boolean
     */
    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1
};



}


// We only need one instance of DLSupport.Form
if(!this.DLSupport.Form) {



/**
 * Provides support for a form.
 *
 * @var object
 */
DLSupport.Form = {
    /**
     * Add the given element into the given parameters object based on some pre-
     * set rules.
     *
     * @param object    parameters      The parameter object.
     * @param object    element         The element to try to add to the
     *      parameters object.
     * @param boolean   is_array        Whether or not the value should be
     *      considered as an array.
     * @return void
     */
    addParameter: function(parameters, element, is_array) {
        // If the element is a checkbox or radio and it is checked, no need to
        // do anything
        if((element.type == 'checkbox') || (element.type == 'radio')) {
            if(!element.checked) {
                return;
            }
        }

        // If the element is a select with multiple value, or the value is
        // expected to be an array, we do things differently
        if(element.type == 'select-multiple') {
            parameters[element.name] = new Array();

            for(var i = 0; i < element.options.length; ++i) {
                if(element.options[i].selected) {
                    parameters[element.name].push(element.options[i].value);
                }
            }
        } else if(is_array) {
            parameters[element.name].push(element.value);
        } else {
            parameters[element.name] = element.value;
        }
    },

    /**
     * Disable all of the submit fields of the given form.
     *
     * @param object    form    The form for which all of its submit fields will
     *      be disabled.
     * @return void
     */
    disableSubmitFields: function(form) {
        // Traverse all of the form elements and disable the submit fields
        var element;
        for(var i = 0; i < form.elements.length; ++i) {
            element = form.elements[i];

            if(element.type == 'submit') {
                element.disabled = true;
            }
        }
    },

    /**
     * Enable all of the submit fields for the given form.
     *
     * @param object    form    The form for which all of its submit fields will
     *      be enabled.
     * @return void
     */
    enableSubmitFields: function(form) {
        // Traverse all of the form elements and enable the submit fields
        var element;
        for(var i = 0; i < form.elements.length; ++i) {
            element = form.elements[i];

            if(element.type == 'submit') {
                element.disabled = false;
            }
        }
    },

    /**
     * Retrieve an object with its property names being all the named fields
     * of the given form and its value being how many fields have that name.
     *
     * @param object    form    The form for which the data is to be retrieved
     *      from.
     * @return object   An associative array with the field name as the property
     *      and the number of fields with the same name as the value.
     */
    fieldCount: function(form) {
        // For each element with a name, add their count to result
        var result = {}, element;
        for(var i = 0; i < form.elements.length; ++i) {
            element = form.elements[i];

            // Make sure element has a name
            if(element.name.length > 0) {
                // If element has not been added before, initialize it
                if(!result[element.name]) {
                    result[element.name] = 0;
                }

                ++result[element.name];
            }
        }

        return result;
    },

    /**
     * Set the value of the given element as the submit value for the element's
     * form. This is useful for forms to be sent via AJAX that has multiple
     * submit buttons.
     *
     * @param object    element     The element that contains the submit value.
     *      Note that the element must be a DOM element with the property "form"
     *      which points to the form for which the element belongs to.
     * @return boolean  Returns true if successful, false, otherwise.
     */
    submitValue: function(element) {
        try {
            element.form.DLSupport_SubmitValue = element.value;

            return true;
        } catch(e) {
            // Add error message to error log
            DLSupport.addErrorLog('DLSupport.Form.submitValue', [element], e.message);

            return false;
        }
    },

    /**
     * Convert the given form data to a query string. Although this will handle
     * fields that can have multiple values like checkboxes, make sure you
     * name it appropriately however.
     *
     * @param object    form    The form for which the data is to be retrieved
     *      from.
     * @return string   The given form data as a query string.
     */
    toQueryString: function(form) {
        // Get the field's count for the given form
        var field_count = DLSupport.Form.fieldCount(form);

        // Prepare the object with the form data
        var parameters = {}, gotSubmit = false, element;
        for(var i = 0; i < form.elements.length; ++i) {
            element = form.elements[i];

            // Only continue if the element has a name
            if(element.name.length < 1) {
                continue;
            }

            // If an element has more than 1 field count, its value should be
            // store as an array unless it is a submit type
            if((field_count[element.name] > 1)) {
                // There can only be one submit button, so handle this case
                if((element.type == 'submit') || (element.type == 'image')) {
                    // If the submit value stored in the form equals the given
                    // element's value, then it is the value we want to send
                    if(form.DLSupport_SubmitValue == element.value) {
                        gotSubmit = true;
                        DLSupport.Form.addParameter(parameters, element, false);
                    } else if(!gotSubmit) {
                        // Otherwise, just add current one as later ones will
                        // overwrite this value
                        DLSupport.Form.addParameter(parameters, element, false);
                    }

                    continue;
                }

                // Create the array if needed
                if(!DLSupport.isArray(parameters[element.name])) {
                    parameters[element.name] = new Array();
                }

                DLSupport.Form.addParameter(parameters, element, true);
            } else {
                DLSupport.Form.addParameter(parameters, element, false);
            }
        }

        // Convert the parameters object to a query string
        var result = DLSupport.toQueryString(parameters);

        return result;
    }
};



}


// We only need one instance of DLSupport.Tab
if(!this.DLSupport.Tab) {



/**
 * Provides support for tabbing content.
 *
 * @var object
 */
DLSupport.Tab = JS.Class({
    /**
     * An array of contents.
     *
     * @var array
     */
    Contents: [],

    /**
     * The wrapper element for the contents.
     *
     * @var object
     */
    ContentWrapper: null,

    /**
     * The content of the currently opened tab.
     *
     * @var object
     */
    CurrentlyOpenTab: null,

    /**
     * An array of tabs.
     *
     * @var array
     */
    Tabs: [],

    /**
     * The wrapper element for the tabs.
     *
     * @var object
     */
    TabWrapper: null,

    /**
     * Initialize the script.
     *
     * @param string    tab         The ID or the object that should be the
     *      currently opened tab. Note that this should not include the wrapper
     *      ID.
     * @param mixed     tabs        The ID or the object that is the wrapper for
     *      the tabs.
     * @param mixed     contents    The ID or the object that is the wrapper for
     *      the contents.
     */
    initialize: function(tab, tabs, contents) {
        tabs = DLSupport.getElement(tabs);
        contents = DLSupport.getElement(contents);

        // Make sure given objects exist
        if(!(tabs && contents)) { return; }

        // Set the tabs wrapper data
        DLSupport.identify(tabs);
        this.TabWrapper = tabs;
        this.Tabs = DLSupport.childElements(tabs);

        // Set the contents wrapper data
        DLSupport.identify(contents);
        this.ContentWrapper = contents;
        this.Contents = DLSupport.childElements(contents);

        // Add events to the current tabs
        for(var i = 0; i < this.Tabs.length; ++i) {
            this.addEvents(this.Tabs[i]);
        }

        // Show the selected table
        tab = DLSupport.getElement(this.getId(tab));
        this.showTab(tab);
    },

    /**
     * Add events to the given tab.
     *
     * @param object    tab     The tab that will observe various events.
     */
    addEvents: function(tab) {
        DLSupport.newEvent(tab, 'click', DLSupport.curry(function(tab) {
            this.showTab(tab);
        }, this, tab));

        DLSupport.newEvent(tab, 'mouseover', DLSupport.curry(function(tab) {
            DLSupport.addClassName(tab, 'hover');
        }, this, tab));

        DLSupport.newEvent(tab, 'mouseout', DLSupport.curry(function(tab) {
            DLSupport.removeClassName(tab, 'hover');
        }, this, tab));
    },

    /**
     * Add in a new tab with the given ID. If the ID already exist, this will
     * just show that tab.
     *
     * @param string    id          The ID of the tab.
     * @param string    name        The title of the tab.
     * @param string    data        The data for the content of the tab.
     * @param object    options     Optional. The available options are:
     *      Key         Default Value   Description
     *      - link      false           (boolean) If this is true, then the
     *                                  given "data" should be the URL for which
     *                                  the content should be retrieved.
     *      - once      false           (boolean) If this is true and if 'link'
     *                                  is true, then the content will only be
     *                                  fetched one time.
     */
    addTab: function(id, name, data) {
        // Get the options
        var options = DLSupport.defaultValues(arguments[3] || {}, {
            link: false,
            once: false
        });

        // Get the full ID
        var tabID = this.getId(id);
        var contentID = this.getId(id, 'content');

        // Make sure both the tab ID and content ID doesn't exist
        var tab = DLSupport.getElement(tabID);
        var content = DLSupport.getElement(contentID);
        if(tab && content) {
            // Since it already exist, just show the tab
            this.showTab(tab); return;
        } else if(tab || content) {
            // Since only one exist, we cannot add
            return;
        }

        // Create the tab
        var tabLi = document.createElement('li');
        tabLi.id = tabID;
        tabLi.innerHTML = name;

        // Set link information
        if(options.link) {
            tabLi.setAttribute('TabURL', data);
            tabLi.setAttribute('TabLoaded', 'false');
            tabLi.setAttribute('TabOnce', options.once ? 'true' : 'false');
            data = '&nbsp;';
        }

        // Create the content
        var contentDiv = document.createElement('div');
        contentDiv.id = contentID;
        contentDiv.innerHTML = data;

        // Append the new tab and its content
        this.TabWrapper.appendChild(tabLi);
        this.ContentWrapper.appendChild(contentDiv);

        // Update stored data
        this.Tabs = DLSupport.childElements(this.TabWrapper);
        this.Contents = DLSupport.childElements(this.ContentWrapper);

        // Add events for the tab
        this.addEvents(tabLi);

        // Show the tab
        this.showTab(tabLi);
    },

    /**
     * Get the true ID of the given data.
     *
     * @param mixed     data    The current ID or the object with the ID.
     * @param string    type    The type of ID to get. Possible values are "tab"
     *      and "content". This is optional and defaults to "tab".
     * @return string
     */
    getId: function(data) {
        var type = arguments[1] || 'tab';

        try {
            // If it is an object with the id property, use other, otherwise,
            // just use the given data
            var result = data.id || data;

            // First, remove the wrapper portion
            result = result.replace(this.TabWrapper.id+'_', '');
            result = result.replace(this.ContentWrapper.id+'_', '');

            // Add back in the appropriate type
            if(type == 'tab') {
                result = this.TabWrapper.id+'_'+result;
            } else {
                result = this.ContentWrapper.id+'_'+result;
            }

            return result;
        } catch(e) {
            // Add error message to error log
            DLSupport.addErrorLog('DLSupport.Tab.getId', [data], e.message);

            return '';
        }
    },

    /**
     * Load the page of the given object.
     *
     * @param object    obj     The object for which the page is to be loaded.
     */
    loadPage: function(obj) {
        DLSupport.AJAX.request(obj.getAttribute('TabURL'), {
            onFailuire: function() {
                window.alert('Sorry, but the content of this tab could not be loaded');
            },
            onSuccess: DLSupport.curry(function(obj, transport) {
                var content = DLSupport.getElement(this.getId(obj, 'content'));

                content.innerHTML = transport.responseText;
                obj.setAttribute('TabLoaded', 'true');
            }, this, obj)
        });
    },

    /**
     * Remove the given tab and its contents. Note that if the tab to be removed
     * is the currently opened tab, the new currently opened tab will be the
     * first tab in the tabs list.
     *
     * @param string    tab     The ID of the tab to remove. Note that this
     *      should not include the wrapper ID.
     */
    removeTab: function(tab) {
        tab = DLSupport.getElement(this.getId(tab));
        var content = DLSupport.getElement(this.getId(tab, 'content'));

        // Make sure they exist
        if(!(tab && content)) {
            return;
        }

        // See if the tab to be removed is the currently opened tab
        if(content == this.CurrentlyOpenTab) {
            // Show the first tab that is not the tab to be removed
            var tabToShow = null;
            for(var i = 0; i < this.Contents.length; ++i) {
                tabToShow = this.Contents[i];
                if(tabToShow != this.CurrentlyOpenTab) { break; }
            }
            this.showTab(tabToShow);
        }

        // Remove the tab
        DLSupport.removeElement(tab);

        // Remove the content
        DLSupport.removeElement(content);

        // Update stored data
        this.Tabs = DLSupport.childElements(this.TabWrapper);
        this.Contents = DLSupport.childElements(this.ContentWrapper);
    },

    /**
     * Show the given tab while hiding all the other tabs.
     *
     * @param object    tab     The tab to show.
     */
    showTab: function(tab) {
        // Get the ID of the tab
        tab = this.getId(tab);

        // Hide all the other tabs while showing the given tab
        var currentTabId;
        for(var i = 0 ; i < this.Tabs.length; ++i) {
            currentTabId = this.getId(this.Tabs[i]);

            if(tab == currentTabId) {
                DLSupport.addClassName(this.Tabs[i], 'selected');

                this.Contents[i].style.display = '';
                this.CurrentlyOpenTab = this.Contents[i];

                // If it is a link, retrieve its content
                if(this.Tabs[i].getAttribute('TabURL')) {
                    if(!(this.Tabs[i].getAttribute('TabOnce') == 'true') ||
                       !(this.Tabs[i].getAttribute('TabLoaded') == 'true')) {
                        this.loadPage(this.Tabs[i]);
                        continue;
                    }
                }
            } else {
                DLSupport.removeClassName(this.Tabs[i], 'selected');
                this.Contents[i].style.display = 'none';
            }
        }
    }
});



}


