﻿/*
* Xphter.js
* This file defines all common functions provided by Xphter JavaScript Library.
* You must include this file in all pages that want to use functions defined in Xphter JavaScript Library.
*
* Namespace：Xphter
*
* Author: 杜彭(XphteR)
*
* Create Date: 2011-05-16
* Lastest Modify Date: 2012-02-04
*
* Thinks!
*/

(function (global, undefined) {
  /*
  * Namespace Definition
  */
  var Xphter = global.Xphter = {};

  /***************** Append custom method to global object *****************/

  /*
  * Method Description:
  * Gets element by ID.
  *
  * Parameters:
  * id: a string to indicate element ID.
  *
  * Return:
  * Returns the element with the specified ID or null if it is not existing.
  */
  var $ = global.$ = function (id) {
    if (DataTypeIdentity.IsUndefinedOrNull(id)) {
      return null;
    }
    var _id = id + String.Empty;
    return id ? global.document.getElementById(_id) : null;
  };

  /*
  * Method Description:
  * Encodes the specified string which may contains some HTML markups.
  * These characters will be replaced:
  * >     ----------------> &gt;
  * <     ----------------> &lt;
  * &     ----------------> &amp;
  * "     ----------------> &quot;
  * space ----------------> &nbsp;
  *
  * Parameters:
  * html: a string will be encoded.
  *
  * Return:
  * Returns a string after encoding.
  */
  var EncodeHTML = global.EncodeHTML = function (html) {
    if (html === null || html === global.undefined) {
      return html;
    }

    var character = null;
    var characters = [];
    var _html = html + String.Empty;
    for (var i = 0; i < _html.length; i++) {
      switch (character = _html.charAt(i)) {
        case ">":
          characters.push("&gt;");
          break;
        case "<":
          characters.push("&lt;");
          break;
        case "&":
          characters.push("&amp;");
          break;
        case "\"":
          characters.push("&quot;");
          break;
        case String.Space:
          characters.push("&nbsp;");
          break;
        default:
          characters.push(character);
          break;
      }
    }

    return characters.join(String.Empty);
  };

  /*
  * Method Description:
  * Encodes the specified string which may be a static string component of regular expression.
  * These characters will be replaced:
  * \     ----------------> \\
  * /     ----------------> \/
  * [     ----------------> \[
  * ]     ----------------> \]
  * (     ----------------> \(
  * )     ----------------> \)
  * {     ----------------> \{
  * }     ----------------> \}
  * |     ----------------> \|
  * ^     ----------------> \^
  * $     ----------------> \$
  * .     ----------------> \.
  * *     ----------------> \*
  * ?     ----------------> \?
  * +     ----------------> \+
  * =     ----------------> \=
  * !     ----------------> \!
  * :     ----------------> \:
  *
  * Parameters:
  * html: a string will be encoded.
  *
  * Return:
  * Returns a string after encoding.
  */
  var EncodeRegexComponent = global.EncodeRegexComponent = function (s) {
    if (s === null || s === global.undefined) {
      return s;
    }

    var c = null;
    var characters = [];
    var _s = s + String.Empty;
    for (var i = 0; i < _s.length; i++) {
      switch (c = _s.charAt(i)) {
        case "\\":
          characters.push("\\\\");
          break;
        case "/":
          characters.push("\\/");
          break;
        case "[":
          characters.push("\\[");
          break;
        case "]":
          characters.push("\\]");
          break;
        case "(":
          characters.push("\\(");
          break;
        case ")":
          characters.push("\\)");
          break;
        case "{":
          characters.push("\\{");
          break;
        case "}":
          characters.push("\\}");
          break;
        case "|":
          characters.push("\\|");
          break;
        case "^":
          characters.push("\\^");
          break;
        case "$":
          characters.push("\\$");
          break;
        case ".":
          characters.push("\\.");
          break;
        case "*":
          characters.push("\\*");
          break;
        case "?":
          characters.push("\\?");
          break;
        case "+":
          characters.push("\\+");
          break;
        case "=":
          characters.push("\\=");
          break;
        case "!":
          characters.push("\\!");
          break;
        case ":":
          characters.push("\\:");
          break;
        default:
          characters.push(c);
          break;
      }
    }

    return characters.join(String.Empty);
  };

  /***************** Expand String Class *****************/

  /*
  * Static Property Description:
  * Gets a empty string.
  */
  String.Empty = "";

  /*
  * Static Property Description:
  * Gets a space string.
  */
  String.Space = " ";

  /*
  * Method Description:
  * Determines whether this string equals the sepcified string, ignore case.
  *
  * Return:
  * Returns true if this string equals the specified string, otherwise false.
  */
  String.prototype.EqualsIgnoreCase = function (s) {
    return DataTypeIdentity.IsString(s) ? this.toLowerCase() == s.toLowerCase() : false;
  };

  /*
  * Method Description:
  * Trims white characters from the start of this string.
  *
  * Return:
  * Returns a new string trimed white characters from the start of this string.
  */
  String.prototype.TrimStart = function () {
    return this.replace(/^\s+/, String.Empty);
  };

  /*
  * Method Description:
  * Trims white characters from the end of this string.
  *
  * Return:
  * Returns a new string trimed white characters from the end of this string.
  */
  String.prototype.TrimEnd = function () {
    return this.replace(/\s+$/, String.Empty);
  };

  /*
  * Method Description:
  * Trims white characters from this string.
  *
  * Return:
  * Returns a new string trimed white characters from this string.
  */
  String.prototype.Trim = function () {
    return this.replace(/(?:^\s+)|(?:\s+$)/g, String.Empty);
  };

  /*
  * Method Description:
  * Determines whether this string is empty or only contains white characters.
  *
  * Return:
  * Returns true if the specified string is empty or only contains white characters, otherwise return false.
  */
  String.prototype.IsEmptyOrWhiteSpace = function () {
    return (/^\s*$/g).test(this);
  };

  /*
  * Method Description:
  * Determines whether this string starts with the specified string.
  *
  * Return:
  * Returns true if this string starts with the specified string, otherwise return false.
  */
  String.prototype.StartsWith = function (value) {
    return value && new RegExp("^" + global.EncodeRegexComponent(value), "g").test(this);
  };

  /*
  * Method Description:
  * Determines whether this string ends with the specified string.
  *
  * Return:
  * Returns true if this string ends with the specified string, otherwise return false.
  */
  String.prototype.EndsWith = function (value) {
    return value && new RegExp(global.EncodeRegexComponent(value) + "$", "g").test(this);
  };

  /*
  * Method Description:
  * Replace all the specified string in this string with the specified string.
  *
  * Return:
  * Returns a string after replaced.
  */
  String.prototype.ReplaceGlobal = function (value, replacement) {
    return this.replace(new RegExp(global.EncodeRegexComponent(value), "g"), replacement);
  };

  /***************** Expand Date Class *****************/

  /*
  * Method Description:
  * Determine whether this is a leap year.
  *
  * Return:
  * Returns a string after replaced.
  */
  Date.IsLeapYear = function (year) {
    return (year % 100 == 0) ? (year % 400 == 0) : (year % 4 == 0);
  };

  Date._dayNumbers = {
    "0": 31,
    "2": 31,
    "3": 30,
    "4": 31,
    "5": 30,
    "6": 31,
    "7": 31,
    "8": 30,
    "9": 31,
    "10": 30,
    "11": 31
  };

  /*
  * Method Description:
  * Get number of day in the specified month.
  *
  * Return:
  * Returns a string after replaced.
  */
  Date.GetDayCount = function (year, month) {
    if (month < 0 || month > 11) {
      throw new Error("Month is less 0 or greater than 11.");
    }

    return month == 1 ? (Date.IsLeapYear(year) ? 29 : 28) : Date._dayNumbers[month];
  };

  /***************** DataTypeNames Class Definition *****************/

  /*
  * Static Class Description:
  * Represents all data type names used in JavaScript runtime.
  */
  var DataTypeNames = Xphter.DataTypeNames = {
    Undefined: "undefined",
    Number: "number",
    String: "string",
    Boolean: "boolean",
    Function: "function",
    Object: "object"
  };

  /***************** DOMNodeTypes Class Definition *****************/

  /*
  * Static Class Description:
  * Represents all node type in 1 Level DOM.
  */
  var DOMNodeTypes = Xphter.DOMNodeTypes = {
    Element: 1,
    Attribute: 2,
    Text: 3,
    CDDataSection: 4,
    ProcessingInstruction: 7,
    Comment: 8,
    Document: 9,
    DocumentType: 10,
    DocumentFragment: 11
  };

  /***************** DataTypeIdentity Class Definition *****************/

  /*
  * Static Class Description:
  * Provide functions to identity data type.
  */
  var DataTypeIdentity = Xphter.DataTypeIdentity = {
    /*
    * Method Description:
    * Determines whether the specified value is a number.
    *
    * Parameters:
    * value: The value will be check.
    *
    * Return:
    * Returns true if the specified value is a number otherwise false.
    */
    IsNumber: function (value) {
      return typeof value == DataTypeNames.Number;
    },

    /*
    * Method Description:
    * Determines whether the specified value is a integer.
    *
    * Parameters:
    * value: The value will be check.
    *
    * Return:
    * Returns true if the specified value is a integer otherwise false.
    */
    IsInteger: function (value) {
      return (typeof value == DataTypeNames.Number) && (Math.floor(value) == value);
    },

    /*
    * Method Description:
    * Determines whether the specified value is a string.
    *
    * Parameters:
    * value: The value will be check.
    *
    * Return:
    * Returns true if the specified value is a string otherwise false.
    */
    IsString: function (value) {
      return typeof value == DataTypeNames.String;
    },

    /*
    * Method Description:
    * Determines whether the specified value is a boolean.
    *
    * Parameters:
    * value: The value will be check.
    *
    * Return:
    * Returns true if the specified value is a boolean otherwise false.
    */
    IsBoolean: function (value) {
      return typeof value == DataTypeNames.Boolean;
    },

    /*
    * Method Description:
    * Determines whether the specified value is a function.
    *
    * Parameters:
    * value: The value will be check.
    *
    * Return:
    * Returns true if the specified value is a function otherwise false.
    */
    IsFunction: function (value) {
      return typeof value == DataTypeNames.Function;
    },

    /*
    * Method Description:
    * Determines whether the specified value is a object.
    *
    * Parameters:
    * value: The value will be check.
    *
    * Return:
    * Returns true if the specified value is a object otherwise false.
    */
    IsObject: function (value) {
      return typeof value == DataTypeNames.Object;
    },

    /*
    * Method Description:
    * Determines whether the specified value is not null and is a object.
    *
    * Parameters:
    * value: The value will be check.
    *
    * Return:
    * Returns true if the specified value is a non-null object otherwise false.
    */
    IsNotNullObject: function (value) {
      return typeof value == DataTypeNames.Object && value !== null;
    },

    /*
    * Method Description:
    * Determines whether the specified value is undefined.
    *
    * Parameters:
    * value: The value will be check.
    *
    * Return:
    * Returns true if the specified value is undefined otherwise false.
    */
    IsUndefined: function (value) {
      return value === undefined;
    },

    /*
    * Method Description:
    * Determines whether the specified value is undefined or null.
    *
    * Parameters:
    * value: The value will be check.
    *
    * Return:
    * Returns true if the specified value is undefined or null otherwise false.
    */
    IsUndefinedOrNull: function (value) {
      return value === undefined || value === null;
    },

    /*
    * Method Description:
    * Determines whether the specified value is a array.
    *
    * Parameters:
    * value: The value will be check.
    *
    * Return:
    * Returns true if the specified value is a array otherwise false.
    */
    IsArray: function (value) {
      return DataTypeIdentity.IsNotNullObject(value) && value instanceof Array;
    }
  };

  /***************** IInterface Class Definition *****************/

  /*
  * Static Class Description:
  * Represents the base class of all interface.
  *
  * Mark:
  * Define a interface as below:
  * IA = {
  *   IsImplementBy : IInterface.IsImplementBy,
  *
  *   InterfaceMethod : function { throw new Error("Not Implementation."); }
  * }
  *
  * Check whether a object has implemented this interface as below:
  * var hasImplemented = IA.IsImplementBy(obj);
  */
  var IInterface = Xphter.IInterface = {
    /*
    * Method Description:
    * Determines whether the specified value implements this interface.
    *
    * Parameters:
    * value: The value will be check.
    *
    * Return:
    * Returns true if the specified value implements this interface otherwise false.
    */
    IsImplementBy: function (value) {
      if (!DataTypeIdentity.IsNotNullObject(value)) {
        return false;
      }

      var result = true;
      var classMethod = null;
      var interfaceMethod = null;
      for (var name in this) {
        if (!result) {
          break;
        }
        if (!DataTypeIdentity.IsFunction(interfaceMethod = this[name])) {
          continue;
        }
        if (interfaceMethod === arguments.callee) {
          continue;
        }

        classMethod = value[name];
        result = DataTypeIdentity.IsFunction(classMethod) && classMethod.length === interfaceMethod.length;
      }

      return result;
    }
  };

  /***************** Uri Class Definition *****************/

  /*
  * Class Description:
  * Represents a URI.
  *
  * Constructor Parameters:
  * originalString : The string of URI;
  * isRelative: Determine whether the URI string represents a relative URI. If this parameter is missing, then class will identify whether it is a relative URI.
  */
  var Uri = Xphter.Uri = function (originalString, isRelative) {
    var m_originalString = null;
    var m_isRelative = null;
    var m_schema = null;
    var m_hostname = null;
    var m_port = null;
    var m_path = null;
    var m_fragment = null;
    var m_query = null;

    var m_isWellFormat = false;
    var m_absoluteRegex = /^([a-z]+)\:\/\/((?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:[a-z0-9\-]+(?:\.[a-z0-9\-]+)+)|(?:localhost))(?:\:(\d+))?((?:\/[a-z0-9\.\_\-\%\+]*)*)(?:(\#[a-z0-9\.\_\-\%\&\=\*\!\~\(\)]+)|(\?[a-z0-9\.\_\-\%\&\=\*\!\~\(\)]+))?$/i;
    var m_relativeRegex = /^((?:(?:[a-z0-9\.\_\-\%\+]+)|(?:\/[a-z0-9\.\_\-\%\+]+))(?:\/[a-z0-9\.\_\-\%\+]*)*)(?:(\#[a-z0-9\.\_\-\%\&\=\*\!\~\(\)]+)|(\?[a-z0-9\.\_\-\%\&\=\*\!\~\(\)]+))?$/i;

    Constructor();

    /*
    * Constructor.
    */
    function Constructor() {
      if (DataTypeIdentity.IsUndefinedOrNull(originalString)) {
        throw new Error("URI string is undefined.");
      }
      if (!(m_originalString = originalString + String.Empty)) {
        return;
      }
      if (DataTypeIdentity.IsUndefinedOrNull(isRelative)) {
        if (m_isWellFormat = ParseAbsoluteUri()) {
          m_isRelative = false;
        } else if (m_isWellFormat = ParseRelativeUri()) {
          m_isRelative = true;
        }
      } else {
        if (m_isRelative = !!isRelative) {
          m_isWellFormat = ParseRelativeUri();
        } else {
          m_isWellFormat = ParseAbsoluteUri();
        }
      }
    }

    /*
    * Private Method Description:
    * Parses absolute URI.
    */
    function ParseAbsoluteUri() {
      var match = m_absoluteRegex.exec(m_originalString);
      if (match) {
        m_schema = match[1];
        m_hostname = match[2];
        if (!DataTypeIdentity.IsUndefinedOrNull(match[3]) && match[3].length) {
          m_port = global.parseInt(match[3]);
        }
        if (!DataTypeIdentity.IsUndefinedOrNull(match[4]) && match[4].length) {
          m_path = match[4];
        }
        if (!DataTypeIdentity.IsUndefinedOrNull(match[5]) && match[5].length) {
          m_fragment = match[5];
        }
        if (!DataTypeIdentity.IsUndefinedOrNull(match[6]) && match[6].length) {
          m_query = match[6];
        }
      }

      return !!match;
    }

    /*
    * Private Method Description:
    * Parses relative URI.
    */
    function ParseRelativeUri() {
      var match = m_relativeRegex.exec(m_originalString);
      if (match) {
        m_path = match[1];
        if (!DataTypeIdentity.IsUndefinedOrNull(match[2]) && match[2].length) {
          m_fragment = match[2];
        }
        if (!DataTypeIdentity.IsUndefinedOrNull(match[3]) && match[3].length) {
          m_query = match[3];
        }
      }

      return !!match;
    }

    /*
    * Method Description:
    * Gets the originalString parameter.
    */
    this.GetOriginalString = function () {
      return m_originalString;
    };

    /*
    * Method Description:
    * Gets whether the originalString parameter is a a well format URI string.
    */
    this.GetIsWellFormat = function () {
      return m_isWellFormat;
    };

    /*
    * Method Description:
    * Gets whether the originalString parameter represents a relative URI.
    */
    this.GetIsRelative = function () {
      return m_isRelative;
    };

    /*
    * Method Description:
    * Gets protocol schema of this URI.
    */
    this.GetSchema = function () {
      return m_schema;
    };

    /*
    * Method Description:
    * Gets hostname of this URI.
    */
    this.GetHostname = function () {
      return m_hostname;
    };

    /*
    * Method Description:
    * Gets port of this URI.
    */
    this.GetPort = function () {
      return m_port;
    };

    /*
    * Method Description:
    * Gets hostname and port of this URI, format: hostname:port.
    */
    this.GetHost = function () {
      return (m_hostname || String.Empty) + (m_port ? ":" + m_port : String.Empty);
    };

    /*
    * Method Description:
    * Gets hostname of this URI.
    */
    this.GetPath = function () {
      return m_path;
    };

    /*
    * Method Description:
    * Gets fragment of this URI.
    */
    this.GetFragment = function () {
      return m_path;
    };

    /*
    * Method Description:
    * Gets query string of this URI.
    */
    this.GetQuery = function () {
      return m_query;
    };
  };

  /***************** BrowserCapability Class Definition *****************/

  /*
  * Static Class Description:
  * Provide functions to detect browser type and ensure browser capability.
  */
  var BrowserCapability = Xphter.BrowerCapability = {
    /*
    * Property Description:
    * Gets a value to indicate whether the browser is Microsfot Internet Explorer.
    */
    /*@cc_on
    @if (@_jscript)
    IsIE: true,
    @else*/
    IsIE : false,
    /*@end
    @*/

    /*
    * Property Description:
    * Gets the browser capability mode of Microsfot Internet Explorer.
    */
    /*@cc_on
    @if (@_jscript)
    IECompatibilityMode: global.document.documentMode || (global.document.compatMode ? (global.document.compatMode == "CSS1Compat" ? 7 : 5) : 5),
    @end
    @*/

    /*
    * Property Description:
    * Gets a value to indicate whether the browser is Opera.
    */
    IsOpera: !!global.opera,

    /*
    * Property Description:
    * Gets the major version number of browser.
    */
    /*@cc_on
    @if (@_jscript)
    MajorVersion: global.parseInt(/MSIE (\d+)/i.exec(global.navigator.appVersion)[1])
    @else*/
    MajorVersion: global.parseInt(global.opera ? global.opera.version() + String.Empty : global.navigator.appVersion) || 0
    /*@end
    @*/
  };

  /***************** BrowserGeometry Class Definition *****************/

  /*
  * Static Class Description:
  * Provide geometry information of browser.
  */
  var BrowserGeometry = Xphter.BrowserGeometry = {
    /*
    * Method Description:
    * Gets the X position of the browser window on the screen.
    */
    GetWindowX: function () {
      return global.screenX || global.screenLeft;
    },

    /*
    * Method Description:
    * Gets the Y position of the browser window on the screen.
    */
    GetWindowY: function () {
      return global.screenY || global.screenTop;
    },

    /*
    * Method Description:
    * Gets the width of the browser window.
    */
    GetWindowWidth: function () {
      return global.outerWidth || (global.document.documentElement ? global.document.documentElement.scrollWidth : global.document.body ? global.document.body.scrollWidth : 0);
    },

    /*
    * Method Description:
    * Gets the height of the browser window.
    */
    GetWindowHeight: function () {
      return global.outerHeight || (global.document.documentElement ? global.document.documentElement.scrollHeight : global.document.body ? global.document.body.scrollHeight : 0);
    },

    /*
    * Method Description:
    * Gets the width of document viewport.
    */
    GetViewportWidth: function () {
      return global.innerWidth || (global.document.documentElement ? global.document.documentElement.clientWidth : global.document.body ? global.document.body.clientWidth : 0);
    },

    /*
    * Method Description:
    * Gets the height of document viewport.
    */
    GetViewportHeight: function () {
      return global.innerHeight || (global.document.documentElement ? global.document.documentElement.clientHeight : global.document.body ? global.document.body.clientHeight : 0);
    },

    /*
    * Method Description:
    * Gets the horizontal scroll offset of the document.
    */
    GetHorizontalScroll: function () {
      return global.pageXOffset || (global.document.documentElement ? global.document.documentElement.scrollLeft : global.document.body ? global.document.body.scrollLeft : 0);
    },

    /*
    * Method Description:
    * Gets the vertical scroll offset of the document.
    */
    GetVerticalScroll: function () {
      return global.pageYOffset || (global.document.documentElement ? global.document.documentElement.scrollTop : global.document.body ? global.document.body.scrollTop : 0);
    },

    /*
    * Method Description:
    * Gets width of document body.
    *
    * Returns:
    * Return the width of document body.
    */
    GetBodyWidth: function () {
      return global.document.getElementsByTagName("body")[0].offsetWidth;
    },

    /*
    * Method Description:
    * Gets height of document body.
    *
    * Returns:
    * Return the height of document body.
    */
    GetBodyHeight: function () {
      return global.document.getElementsByTagName("body")[0].offsetHeight;
    }
  };

  /***************** DocumentUtility Class Definition *****************/

  /*
  * Static Class Description:
  * Provides a utility for operating HTML document.
  */
  var DocumentUtility = Xphter.DocumentUtility = {
    /*
    * Method Description:
    * Append the specified HTML content to current document then new line.
    *
    * Parameters:
    * html: a string represents HTML content.
    */
    WriteLine: function (html) {
      var _html = html + "<br />";
      global.document.write(_html);
    },

    /*
    * Method Description:
    * Determines whether the specified element is a member of the specified CSS class.
    *
    * Parameters:
    * element: a HTML element.
    * className: a CSS class name.
    *
    * Return:
    * Returns true if the specified element is a meber of the specified CSS class, otherwise return false.
    */
    IsCSSMember: function (element, className) {
      var _className = className + String.Empty;

      return DataTypeIdentity.IsNotNullObject(element) && element.className && /^\S+$/i.test(_className) && new RegExp("(^|\\s+)" + _className + "($|\\s+)", "gi").test(element.className);
    },

    /*
    * Method Description:
    * Gets elements which are member of the specified CSS class in the descendants of the specified element.
    *
    * Parameters:
    * element: a HTML element.
    * className: a CSS class name.
    *
    * Return:
    * Returns a array of these elements or null if these elements are not existing.
    */
    GetElementByClassName: function (element, className) {
      var result = null;
      var node = null;
      var elements = [element];
      var _className = className + String.Empty;

      if (DataTypeIdentity.IsNotNullObject(element) && element.childNodes && _className) {
        do {
          element = elements.shift();

          for (var i = 0; i < element.childNodes.length; i++) {
            if ((node = element.childNodes[i]).nodeType != DOMNodeTypes.Element) {
              continue;
            }

            if (DocumentUtility.IsCSSMember(node, _className)) {
              if (!result) {
                result = [];
              }
              result.push(node);
            }
            elements.push(node);
          }
        } while (elements.length > 0);
      }

      return result;
    },

    /*
    * Method Description:
    * Reverses the children of the specified element.
    *
    * Parameters:
    * element: a HTML element.
    */
    ReverseChildren: function (element) {
      if (!DataTypeIdentity.IsNotNullObject(element)) {
        return;
      }

      var fragment = global.document.createDocumentFragment();
      while (element.lastChild) {
        fragment.appendChild(element.lastChild);
      }
      element.appendChild(fragment);
    },

    /*
    * Method Description:
    * Clears the children of the specified element.
    *
    * Parameters:
    * element: a HTML element.
    */
    ClearChildren: function (element) {
      if (!DataTypeIdentity.IsNotNullObject(element)) {
        return;
      }

      while (element.firstChild) {
        element.removeChild(element.firstChild);
      }
    },

    /*
    * Method Description:
    * Create a HTML element with the specified tag.
    *
    * Parameters:
    * tagName: a HTML tag.
    * attribute: a object which enumerable properties represents the properties of the created element.
    * styles: a object which enumerable properties represents the styles of the created element.
    * children: a node array or a NodeList object which elements are child of the created element.
    *
    * Return:
    * Returns the created element.
    */
    CreateElement: function (tagName, attributes, styles, children) {
      var _tagName = tagName + String.Empty;
      if (!_tagName) {
        return null;
      }

      var element = global.document.createElement(_tagName);

      if (DataTypeIdentity.IsNotNullObject(attributes)) {
        for (var name in attributes) {
          if (name in element) {
            try {
              element[name] = attributes[name];
            } catch (ex) {
              element.setAttribute(name, attributes[name]);
            }
          } else {
            element.setAttribute(name, attributes[name]);
          }
        }
      }

      if (DataTypeIdentity.IsNotNullObject(styles)) {
        for (var name in styles) {
          element.style[name] = styles[name];
        }
      }

      if (DataTypeIdentity.IsArray(children) || (DataTypeIdentity.IsNotNullObject(children) && children.item && children.length)) {
        var child = null;
        for (var i = 0; i < children.length; i++) {
          if (DataTypeIdentity.IsUndefinedOrNull(child = children[i])) {
            continue;
          }

          if (DataTypeIdentity.IsString(child)) {
            element.appendChild(global.document.createTextNode(child));
          } else {
            element.appendChild(child);
          }
        }
      } else if (DataTypeIdentity.IsString(children)) {
        element.appendChild(global.document.createTextNode(children));
      } else if (!DataTypeIdentity.IsUndefinedOrNull(children)) {
        element.appendChild(children);
      }

      return element;
    },

    /*
    * Method Description:
    * Create a div HTML element with style: clear both.
    *
    * Return:
    * Returns the created div HTML element.
    */
    CreateClearBoth: function () {
      return DocumentUtility.CreateElement("div", null, {
        clear: "both",
        fontSize: "0px",
        lineHeight: "0px"
      });
    },

    /*
    * Method Description:
    * Gets computed style of the specified element.
    *
    * Parameters:
    * element: a HTML element.
    *
    * Return:
    * Returns a CSS2Properties object.
    */
    GetComputedStyle: function (element) {
      if (!DataTypeIdentity.IsNotNullObject(element)) {
        return null;
      }

      return element.currentStyle || (global.getComputedStyle ? global.getComputedStyle(element, null) : null);
    },

    /*
    * Method Description:
    * Gets absolute width of the specified element.
    *
    * Parameters:
    * element: a HTML element.
    *
    * Return:
    * Returns absolute width of the specified element.
    */
    GetElementWidth: function (element) {
      if (!DataTypeIdentity.IsNotNullObject(element)) {
        return null;
      }

      css = DocumentUtility.GetComputedStyle(element);
      return (global.parseInt(css.marginLeft) || 0) + element.offsetWidth + (global.parseInt(css.marginRight) || 0);
    },

    /*
    * Method Description:
    * Gets absolute height of the specified element.
    *
    * Parameters:
    * element: a HTML element.
    *
    * Return:
    * Returns absolute height of the specified element.
    */
    GetElementHeight: function (element) {
      if (!DataTypeIdentity.IsNotNullObject(element)) {
        return null;
      }

      var css = DocumentUtility.GetComputedStyle(element);

      var marginTop = (global.parseInt(css.marginTop) || 0);
      var previousSibling = element.previousSibling;
      previousSibling && (marginTop = Math.max(marginTop, global.parseInt(DocumentUtility.GetComputedStyle(previousSibling).marginBottom) || 0));

      var marginBottom = (global.parseInt(css.marginBottom) || 0);
      var nextSibling = element.nextSibling;
      nextSibling && (marginBottom = Math.max(marginBottom, global.parseInt(DocumentUtility.GetComputedStyle(nextSibling).marginTop) || 0));

      return marginTop + element.offsetHeight + marginBottom;
    },

    /*
    * Method Description:
    * Gets absolute X position of the specified element.
    *
    * Parameters:
    * element: a HTML element.
    *
    * Return:
    * Returns absolute X position of the specified element.
    */
    GetElementX: function (element) {
      if (!DataTypeIdentity.IsNotNullObject(element)) {
        return null;
      }

      var x = 0;
      var style = DocumentUtility.GetComputedStyle(element);
      var isFixed = style && style.position && style.position.EqualsIgnoreCase("fixed");
      for (var e = element; e; e = e.offsetParent) {
        x += e.offsetLeft;
      }
      if (!isFixed) {
        for (var e = element.parentNode; e; e = e.parentNode) {
          e.scrollLeft && (x -= e.scrollLeft);
        }
      }

      return x;
    },

    /*
    * Method Description:
    * Gets absolute Y position of the specified element.
    *
    * Parameters:
    * element: a HTML element.
    *
    * Return:
    * Returns absolute Y position of the specified element.
    */
    GetElementY: function (element) {
      if (!DataTypeIdentity.IsNotNullObject(element)) {
        return null;
      }

      var y = 0;
      var style = DocumentUtility.GetComputedStyle(element);
      var isFixed = style && style.position && style.position.EqualsIgnoreCase("fixed");
      for (var e = element; e; e = e.offsetParent) {
        y += e.offsetTop;
      }
      if (!isFixed) {
        for (var e = element.parentNode; e; e = e.parentNode) {
          e.scrollTop && (y -= e.scrollTop);
        }
      }

      return y;
    },

    /*
    * Method Description:
    * Gets the outer HTML of the specified element.
    *
    * Parameters:
    * element: a HTML element.
    *
    * Return:
    * Returns outer HTML of the specified element.
    */
    GetOuterHTML: function (element) {
      if (!element) {
        return null;
      }

      var wrap = global.document.createElement("div");
      wrap.appendChild(element);
      var outerHTML = wrap.innerHTML;

      return outerHTML;
    },

    /*
    * Method Description:
    * Determines whether node1 is a child of node2.
    *
    * Parameters:
    * node1: a HTML node.
    * node2: a HTML node.
    *
    * Return:
    * Returns true if node1 is a child of node2, otherwise false.
    */
    IsChildren: function (node1, node2) {
      if (!node1 || !node2 || node2.parentNode == node1) {
        return false;
      }

      var result = false;
      var parent1 = node1, parent2 = node2.parentNode;

      while (parent1 = parent1.parentNode) {
        if (parent1 == node2) {
          result = true;
          break;
        }
        if (parent1 == parent2) {
          break;
        }
      }

      return result;
    }
  };

  /***************** Cookie Class Definition *****************/

  /*
  * Class Description:
  * Represents a cookie.
  *
  * Constructor Parameters:
  * name : Cookie name;
  * value: Cookie value.
  * maxAge: The persist seconds.
  * path: The associated range path.
  * domain: Cookie domain.
  * isEncrypted: Whether encrypt this cookie.
  */
  var Cookie = Xphter.Cookie = function (name, value, maxAge, path, domain, isEncrypted) {
    var m_name = null;
    var m_value = null;
    var m_maxAge = null;
    var m_path = null;
    var m_domain = null;
    var m_isEncrypted = null;

    Constructor();

    /*
    * Consturctor.
    */
    function Constructor() {
      if (!name) {
        throw new Error("cookie name is empty.");
      }
      if (!Cookie.IsValidName(m_name = name + String.Empty)) {
        throw new Error("cookie name contains invalid character.");
      }
      m_value = global.encodeURIComponent(value + String.Empty);
      !global.isNaN(maxAge) && (m_maxAge = maxAge - 0);
      path && (m_path = path + String.Empty);
      domain && (m_domain = domain + String.Empty);
      !DataTypeIdentity.IsUndefinedOrNull(isEncrypted) && (m_isEncrypted = !!isEncrypted);
    }

    /*
    * Method Description:
    * Gets name of this cookie.
    */
    this.GetName = function () {
      return m_name;
    };

    /*
    * Method Description:
    * Gets value of this cookie.
    */
    this.GetValue = function () {
      return global.decodeURIComponent(m_value);
    };

    /*
    * Method Description:
    * Gets max-age of this cookie.
    */
    this.GetMaxAge = function () {
      return m_maxAge;
    };

    /*
    * Method Description:
    * Gets path of this cookie.
    */
    this.GetPath = function () {
      return m_path;
    };

    /*
    * Method Description:
    * Gets domain of this cookie.
    */
    this.GetDomain = function () {
      return m_domain;
    };

    /*
    * Method Description:
    * Gets whether encrypt this cookie.
    */
    this.GetPath = function () {
      return !!m_isEncrypted;
    };

    /*
    * Method Description:
    * Gets string value of this cookie.
    */
    this.GetCookieString = function () {
      return m_name + "=" + m_value + (m_maxAge === null ? String.Empty : "; max-age=" + m_maxAge) + (m_path === null ? String.Empty : "; path=" + m_path) + (m_domain === null ? String.Empty : "; domain=" + m_domain) + (m_isEncrypted ? "; secure" : String.Empty);
    };
  };

  /*
  * Static Method Description:
  * Determine whether the specified name is a valid cookie name.
  * 
  * Parameters:
  * name: The name which will be check.
  */
  Cookie.IsValidName = function (name) {
    if (!DataTypeIdentity.IsString(name)) {
      return false;
    }

    var isValid = true;
    var character = null;
    for (var i = 0; i < name.length; i++) {
      character = name.charAt(i);
      if (character == "=" || character == ";" || character == "," || character == "\r" || character == "\n" || character == "\t" || character == " ") {
        isValid = false;
        break;
      }
    }

    return isValid;
  };

  /***************** Cookies Class Definition *****************/

  /*
  * Class Description:
  * Provides a utility for operate cookies, such as add, update, delete, query.
  */
  var Cookies = Xphter.Cookies = (function () {
    //singleton pattern: class implementation
    var Implementation = function () {
      var m_cookies = [];

      /*
      * Method Description:
      * Refresh cookie cache.
      */
      (this.Refresh = function () {
        m_cookies = [];

        var result = null;
        var regex = /([^\=\;\,\s]+)\=([^\;\,\s]+)(?:\;|$)/g;
        var cookieValue = global.document.cookie;
        while ((result = regex.exec(cookieValue)) != null) {
          m_cookies.push(new Cookie(result[1], result[2]));
        }
      })();

      /*
      * Method Description:
      * Determine whether the cookie will the specified name is existing.
      * 
      * Parameters:
      * name: Cookie name.
      */
      this.IsExists = function (name) {
        var existing = false;
        var _name = name + String.Empty;
        for (var i = 0; i < m_cookies.length; i++) {
          if (m_cookies[i].GetName() == _name) {
            existing = true;
            break;
          }
        }
        return existing;
      };

      /*
      * Method Description:
      * Gets the cookie with the specified name.
      * 
      * Parameters:
      * name: Cookie name.
      *
      * Returns:
      * Return a Cookie object or null if it is not existing.
      */
      this.GetCookie = function (name) {
        var cookie = null;
        var _name = name + String.Empty;
        for (var i = 0; i < m_cookies.length; i++) {
          if (m_cookies[i].GetName() == _name) {
            cookie = m_cookies[i];
            break;
          }
        }
        return cookie;
      };

      /*
      * Method Description:
      * Gets the all cookies.
      *
      * Returns:
      * Return a array of Cookie object.
      */
      this.GetCookies = function () {
        var cookies = [];
        for (var i = 0; i < m_cookies.length; i++) {
          cookies.push(m_cookies[i]);
        }
        return cookies;
      };

      /*
      * Method Description:
      * Add a new cookie. If it is already existing, then the existing cookie will be update.
      * 
      * Parameters:
      * cookie: A Cookie object.
      */
      this.Add = function (cookie) {
        if (DataTypeIdentity.IsNotNullObject(cookie) && cookie instanceof Cookie) {
          global.document.cookie = cookie.GetCookieString();
          this.Refresh();
        }
      };

      /*
      * Method Description:
      * Delete the specified cookie.
      * 
      * Parameters:
      * cookie: A Cookie object.
      */
      this.Delete = function (cookie) {
        if (DataTypeIdentity.IsNotNullObject(cookie) && cookie instanceof Cookie) {
          this.Add(new Cookie(cookie.GetName(), String.Empty, 0, cookie.GetPath(), cookie.GetDomain(), null));
          this.Refresh();
        }
      };
    };

    //singleton instance
    var m_instance = null;
    return {
      GetInstance: function () {
        if (!m_instance) {
          //create instance
          m_instance = new Implementation();
        }

        return m_instance;
      }
    };
  })();

  /***************** QueryString Class Definition *****************/

  /*
  * Class Description:
  * Analyze the query string:
  * set key as a member of this class;
  * set value as the value of this member;
  */
  var QueryString = Xphter.QueryString = (function () {
    //singleton pattern: class implementation
    var Implementation = function () {
      var search = global.location.search;
      if (search) {
        search.StartsWith("?") && (search = search.slice(1));
        var regex = /([^\=\&\s]+)\=([^\=\&\s]+)(?:\&|$)/g;
        var match = null;
        while (match = regex.exec(search)) {
          try {
            this[match[1]] = global.decodeURIComponent(match[2]);
          } catch (ex) {
            this[match[1]] = match[2];
          }
        }
      }
    };

    //singleton instance
    var m_instance = null;
    return {
      GetInstance: function () {
        if (!m_instance) {
          //create instance
          m_instance = new Implementation();
        }

        return m_instance;
      }
    };
  })();

  /***************** EventUtility Class Definition *****************/

  /*
  * Class Description:
  * Represents a event layer for IE.
  */
  var IEEventLayer = (function () {
    //singleton pattern: class implementation
    var Implementation = function () {
      //all registered handler array.
      this.Handlers = [];

      //Gets the index of existing event handler in element events cache. If it is not existing, return -1.
      this.GetIndex = function (element, event, handler, useCapture) {
        var index = -1;
        var entity = null;

        if (element._handlers) {
          for (var i = 0; i < element._handlers.length; i++) {
            if (!(entity = this.Handlers[element._handlers[i]])) {
              continue;
            }

            if (entity.Element == element && entity.Event == event && entity.Handler == handler && entity.UseCapture == useCapture) {
              index = i;
              break;
            }
          }
        }

        return index;
      }

      //register a event handler for element in IE.
      this.Register = function (element, event, handler, useCapture) {
        //break duplicate register
        if (this.GetIndex(element, event, handler, useCapture) >= 0) {
          return;
        }

        //create the real event handler.
        var realHandler = function () {
          var e = global.event;
          handler.call(element, {
            type: event,
            target: e.srcElement,
            currentTarget: element,
            bubbles: e.srcElement != element,
            cancelable: true,
            detail: null,
            button: e.button,
            altKey: e.altKey,
            ctrlKey: e.ctrlKey,
            metaKey: null,
            shiftKey: e.shiftKey,
            clientX: e.clientX,
            clientY: e.clientY,
            screenX: e.screenX,
            screenY: e.screenY,
            relatedTarget: event == "mouseover" ? e.fromElement : (event == "mouseout" ? e.toElement : null),
            charCode: e.keyCode,
            keyCode: e.keyCode,
            stopPropagation: function () {
              e.cancelBubble = true;
            },
            preventDefault: function () {
              e.returnValue = false;
            }
          });
        };

        //register event handler
        element.attachEvent("on" + event, realHandler);

        //store info of the event handler.
        this.Handlers.push({
          Element: element,
          Event: event,
          Handler: handler,
          UseCapture: useCapture,
          RealHandler: realHandler
        });

        !element._handlers && (element._handlers = []);
        element._handlers.push(this.Handlers.length - 1);
      };

      //unregister a event handler for element in IE.
      this.Unregister = function (element, event, handler, useCapture) {
        var index = this.GetIndex(element, event, handler, useCapture);
        if (index >= 0) {
          element.detachEvent("on" + event, this.Handlers[element._handlers[index]].RealHandler);

          this.Handlers[element._handlers[index]] = null;
          element._handlers.splice(index, 1);
        }
      };

      //unregister all event handlers.
      this.UnregisterAll = function () {
        var entity = null;
        for (var i = 0; i < this.Handlers.length; i++) {
          if (!(entity = this.Handlers[i])) {
            continue;
          }

          entity.Element.detachEvent("on" + entity.Event, entity.RealHandler);
          this.Handlers[i] = null;
        }
      }
    };

    //singleton instance
    var m_instance = null;
    return {
      GetInstance: function () {
        if (!m_instance) {
          //create instance
          m_instance = new Implementation();

          //unregister all event handlers when page unloading.
          global.attachEvent("onunload", function () {
            m_instance.UnregisterAll();
          });
        }

        return m_instance;
      }
    };
  })();

  /*
  * Static Class Description:
  * Provides a utility for register and unregister element event handler.
  */
  var EventUtility = Xphter.EventUtility = {
    /*
    * Static Method Description:
    * Register a event handler for the specified element.
    * 
    * Parameters:
    * element: The element will be registered a event handler.
    * event: The event type without "on" prefix.
    * handler: The event handler.
    * useCapture: Whether register a capture handler.
    */
    Register: function (element, event, handler, useCapture) {
      if (element.addEventListener) {
        element.addEventListener(event, handler, useCapture);
      } else {
        IEEventLayer.GetInstance().Register(element, event, handler, useCapture);
      }
    },

    /*
    * Static Method Description:
    * Unregister a event handler from the specified element.
    * 
    * Parameters:
    * element: The element will be unregistered a event handler.
    * event: The event type without "on" prefix.
    * handler: The event handler.
    * useCapture: Whether unregister a capture handler.
    */
    Unregister: function (element, event, handler, useCapture) {
      if (element.removeEventListener) {
        element.removeEventListener(event, handler, useCapture);
      } else {
        IEEventLayer.GetInstance().Unregister(element, event, handler, useCapture);
      }
    }
  };

  /***************** Event Class Definition *****************/
  /*
  * Class Description:
  * Represents a event object.
  *
  * Constructor Parameters:
  * name : The name of this event;
  */
  var Event = Xphter.Event = function (target, name) {
    var m_target = target;
    var m_name = name + String.Empty;
    var m_handlers = [];

    /*
    * Method Description:
    * Gets the event target.
    */
    this.GetTarget = function () {
      return m_target;
    };

    /*
    * Method Description:
    * Gets the event name.
    */
    this.GetName = function () {
      return m_name;
    };

    /*
    * Method Description:
    * Registers a handler for this event.
    *
    * Parameters:
    * handler: A callback function which will be call by event target when this event occurred.
    */
    this.AddListener = function (handler) {
      if (!DataTypeIdentity.IsFunction(handler)) {
        return;
      }

      m_handlers.push(handler);
    };

    /*
    * Method Description:
    * Unregisters a handler for this event.
    *
    * Parameters:
    * handler: The callback function which has been registered for this event.
    */
    this.RemoveListener = function (handler) {
      if (!DataTypeIdentity.IsFunction(handler)) {
        return;
      }

      for (var i = 0; i < m_handlers.length; i++) {
        if (m_handlers[i] == handler) {
          m_handlers.splice(i, 1);
          break;
        }
      }
    };

    /*
    * Method Description:
    * Raises this event, call all event handlers used event target.
    * All input parameters will pass to event handlers.
    */
    this.Raise = function () {
      if (!DataTypeIdentity.IsNotNullObject(m_target)) {
        return;
      }

      //collect event arguments
      var paramters = [];
      for (var i = 0; i < arguments.length; i++) {
        paramters.push(arguments[i]);
      }

      //call event handlers
      for (var i = 0; i < m_handlers.length; i++) {
        m_handlers[i].apply(m_target, paramters);
      }
    }
  };

  /***************** AjaxUtility Class Definition *****************/

  /*
  * Static Class Description:
  * Provides helper method for ajax.
  */
  var AjaxUtility = Xphter.AjaxUtility = {
    /*
    * Method Description:
    * Create a XMLHttpRequest object.
    *
    * Return:
    * Returns a XMLHttpRequest object or null if browser is not support ajax.
    */
    CreateRequest: function () {
      var request = null;

      if (global.XMLHttpRequest) {
        request = new global.XMLHttpRequest();
      } else if (global.ActiveXObject) {
        try {
          request = new global.ActiveXObject("Microsoft.XMLHTTP");
        } catch (ex) {
        }
      }

      return request;
    },

    /*
    * Method Description:
    * Synchronous invoke a resource, a error will throw if this is a cross-domain invoke.
    *
    * Parameters:
    * parameters: The parameters for this calling which is a object as follow:
    * {
    *   Uri: The resource URI.
    *   Method: The HTTP method.
    *   Data: The post data.
    *   Headers: A object which properties are the HTTP headers will be sent.
    *   OnComplete: The callback function when successfully load resource. A XMLHttpRequest object argument will pass to this method.
    *   OnError: The callback function when unsuccessfully load resource. A XMLHttpRequest object argument will pass to this method.
    * }
    *
    */
    Sync: function (parameters) {
      if (!DataTypeIdentity.IsNotNullObject(parameters)) {
        throw new Error("Parameters is undefined.");
      }

      //check whether the resource URI is legal
      var uri = new Uri((parameters.Uri || String.Empty) + String.Empty);
      if (!uri.GetIsWellFormat()) {
        throw new Error("Invalid URI string.");
      }

      //check whether this is a cross-domain invoke
      if (!uri.GetIsRelative() && (!uri.GetHost().EqualsIgnoreCase(global.location.host))) {
        throw new Error("Cross-domain invoke is not supported, please call CrossDomain method instead.");
      }

      var request = AjaxUtility.CreateRequest();
      if (request) {
        //build request
        request.open(((parameters.Method || "GET") + String.Empty).toUpperCase(), uri.GetOriginalString(), false);
        if (parameters.Headers) {
          for (var name in parameters.Headers) {
            name && request.setRequestHeader(name, parameters.Headers[name] + String.Empty);
          }
        }

        //send request
        request.send(parameters.Data || null);

        //process response
        if (request.status == 200) {
          parameters.OnComplete && parameters.OnComplete(request);
        } else {
          parameters.OnError && parameters.OnError(request);
        }
      }
    },

    /*
    * Method Description:
    * Asynchronous invoke a resource, a error will throw if this is a cross-domain invoke.
    *
    * Parameters:
    * parameters: The parameters for this calling which is a object as follow:
    * {
    *   Uri: The resource URI.
    *   Method: The HTTP method.
    *   Data: The post data.
    *   Headers: A object which properties are the HTTP headers will be sent.
    *   OnComplete: The callback function when successfully load resource. A XMLHttpRequest object argument will pass to this method.
    *   OnError: The callback function when unsuccessfully load resource. A XMLHttpRequest object argument will pass to this method.
    *   OnTimeout: The callback function when load resource timeout.
    *   Timeout: The timeout in milliseconeds. Zero or a negative number indicates infinity timeout.
    * }
    *
    */
    Async: function (parameters) {
      if (!DataTypeIdentity.IsNotNullObject(parameters)) {
        throw new Error("Parameters is undefined.");
      }

      //check whether the resource URI is legal
      var uri = new Uri((parameters.Uri || String.Empty) + String.Empty);
      if (!uri.GetIsWellFormat()) {
        throw new Error("Invalid URI string.");
      }

      //check whether this is a cross-domain invoke
      if (!uri.GetIsRelative() && (!uri.GetHost().EqualsIgnoreCase(global.location.host))) {
        throw new Error("Cross-domain invoke is not supported, please call CrossDomain method instead.");
      }

      var request = AjaxUtility.CreateRequest();
      if (request) {
        var isTimeout = false;
        var isRecevied = false;
        var timeoutTimer = null;
        var timeout = global.isNaN(parameters.Timeout - 0) ? 0 : parameters.Timeout - 0;

        //build request
        request.open(((parameters.Method || "GET") + String.Empty).toUpperCase(), uri.GetOriginalString(), true);
        if (parameters.Headers) {
          for (var name in parameters.Headers) {
            name && request.setRequestHeader(name, parameters.Headers[name] + String.Empty);
          }
        }
        request.onreadystatechange = function () {
          if (request.readyState != 4 || isTimeout) {
            return;
          }

          isRecevied = true;

          //clear timeout timer
          if (timeoutTimer) {
            global.clearTimeout(timeoutTimer);
            timeoutTimer = null;
          }

          //process response
          if (request.status == 200) {
            parameters.OnComplete && parameters.OnComplete(request);
          } else {
            parameters.OnError && parameters.OnError(request);
          }
        };

        //set timeout
        if (timeout > 0) {
          timeoutTimer = global.setTimeout(function () {
            if (isRecevied) {
              return;
            }

            isTimeout = true;

            //abort request            
            request.abort();

            //clear timeout timer
            global.clearTimeout(timeoutTimer);
            timeoutTimer = null;

            //process error
            parameters.OnTimeout && parameters.OnTimeout();
          }, timeout);
        }

        //send request
        request.send(parameters.Data || null);
      }
    },

    /*
    * Method Description:
    * Asynchronous invoke a resource within a different domain.
    *
    * Parameters:
    * parameters: The parameters for this calling which is a object as follow:
    * {
    *   Uri: The resource URI.
    *   Charset: The encoding of response data.
    *   OnComplete: The callback function when completely load resource.
    *   OnTimeout: The callback function when load resource timeout.
    *   Timeout: The timeout in milliseconeds. Zero or a negative number indicates infinity timeout.
    * }
    *
    * Mask:
    * There is not a OnError function in parameters object, so caller should process error in OnComplete or OnTimeout function.
    *
    */
    CrossDomain: function (parameters) {
      if (!DataTypeIdentity.IsNotNullObject(parameters)) {
        throw new Error("Parameters is undefined.");
      }

      //check whether the resource URI is legal
      var uri = new Uri((parameters.Uri || String.Empty) + String.Empty);
      if (!uri.GetIsWellFormat()) {
        return;
      }

      //function used to remove script element.
      function RemoveScript() {
        if (!script) {
          return;
        }

        if (BrowserCapability.IsIE && BrowserCapability.IECompatibilityMode < 9) {
          EventUtility.Unregister(script, "readystatechange", ProcessResponse, false);
        } else {
          EventUtility.Unregister(script, "load", ProcessResponse, false);
          !BrowserCapability.IsOpera && EventUtility.Unregister(script, "error", ProcessResponse, false);
        }
        script.src = null;
        script.parentNode && script.parentNode.removeChild(script);
        script = null;
      }

      //function used to process response.
      function ProcessResponse() {
        if ((BrowserCapability.IsIE && BrowserCapability.IECompatibilityMode < 9 && script.readyState != "loaded" && script.readyState != "complete") || isTimeout || isProcessed) {
          return;
        }

        isProcessed = true;

        //clear timeout timer
        if (timeoutTimer) {
          global.clearTimeout(timeoutTimer);
          timeoutTimer = null;
        }

        //remove script
        RemoveScript();

        //invoke callback
        parameters.OnComplete && parameters.OnComplete();
      }

      var isTimeout = false;
      var isProcessed = false;
      var timeoutTimer = null;
      var timeout = global.isNaN(parameters.Timeout - 0) ? 0 : parameters.Timeout - 0;

      //create script element
      var script = DocumentUtility.CreateElement("script", {
        type: "text/javascript",
        charset: (parameters.Charset || "UTF-8") + String.Empty,
        src: uri.GetOriginalString()
      });

      //register event handler
      if (BrowserCapability.IsIE && BrowserCapability.IECompatibilityMode < 9) {
        EventUtility.Register(script, "readystatechange", ProcessResponse, false);
      } else {
        EventUtility.Register(script, "load", ProcessResponse, false);
        !BrowserCapability.IsOpera && EventUtility.Register(script, "error", ProcessResponse, false);
      }

      //set timeout
      if (timeout > 0) {
        timeoutTimer = global.setTimeout(function () {
          if (isProcessed) {
            return;
          }

          isTimeout = true;

          //clear timeout timer
          global.clearTimeout(timeoutTimer);
          timeoutTimer = null;

          //remove script
          RemoveScript();

          //invoke callback
          parameters.OnTimeout && parameters.OnTimeout();
        }, timeout);
      }

      //send request
      global.document.getElementsByTagName("head")[0].appendChild(script);
    }
  };

  /***************** XmlUtility Class Definition *****************/

  /*
  * Static Class Description:
  * Provides helper method for processing XML document.
  */
  var XmlUtility = Xphter.XmlUtility = {
    /*
    * Method Description:
    * Creates a empty XML document.
    *
    * Return:
    * Returns a Document object.
    */
    CreateDocument: function () {
      var result = null;

      if (global.document.implementation && global.document.implementation.createDocument) {
        result = global.document.implementation.createDocument(null, null, null);
      } else if (global.ActiveXObject) {
        result = new global.ActiveXObject("Microsoft.XMLDOM");
      }

      return result;
    },

    /*
    * Method Description:
    * Load a XML element with the specified namespace.
    *
    * Parameters:
    * xmlDocument: A XML document.
    * name: The element name.
    * namespaceURL: The namespace URL.
    *
    * Return:
    * Returns a Document object.
    */
    CreateElementNS: function (xmlDocument, name, namespaceURL) {
      if (!xmlDocument) {
        throw new Error("XML document is undefined.");
      }
      if (!name) {
        throw new Error("Element name is undefined.");
      }

      var node = null;
      var _name = name + String.Empty;

      if (namespaceURL) {
        if (xmlDocument.createElementNS) {
          node = xmlDocument.createElementNS(namespaceURL + String.Empty, _name);
        } else {
          node = xmlDocument.createElement(_name);
          var match = (/^([^\:]+)\:/i).exec(_name);
          if (match) {
            node.setAttribute("xmlns:" + match[1], namespaceURL + String.Empty);
          } else {
            node.setAttribute("xmlns", namespaceURL + String.Empty);
          }
        }
      } else {
        node = xmlDocument.createElement(_name);
      }

      return node;
    },

    /*
    * Method Description:
    * Load a XML document from the specified string.
    *
    * Parameters:
    * xml: The XML string.
    *
    * Return:
    * Returns a Document object.
    */
    LoadString: function (xml) {
      if (!xml) {
        throw new Error("XML string is undefined.");
      }

      var result = null;
      var _xml = xml + String.Empty;

      if (global.DOMParser) {
        result = new global.DOMParser().parseFromString(_xml, "text/xml");
      } else if (global.ActiveXObject) {
        var result = new global.ActiveXObject("Microsoft.XMLDOM");
        result.async = false;
        result.loadXML(_xml);
      }

      return result;
    },

    /*
    * Method Description:
    * Load a XML document from the specified file in local domain.
    *
    * Parameters:
    * file: The file path.
    *
    * Return:
    * Returns a Document object.
    */
    LoadFile: function (file) {
      if (!file) {
        throw new Error("File URL is undefined.");
      }
      var url = new Uri(file);
      if (!url.GetIsWellFormat()) {
        throw new Error("Invalid URI string.");
      }
      if (!url.GetIsRelative() && (!url.GetHost().EqualsIgnoreCase(global.location.host))) {
        throw new Error("Cross domian is not supported.");
      }

      var result = null;

      AjaxUtility.Sync({
        Uri: url.GetOriginalString(),
        Method: "GET",
        OnComplete: function (request) {
          result = request.responseXML;
        }
      });

      return result;
    },

    /*
    * Method Description:
    * Gets XML string from the specified node.
    *
    * Parameters:
    * xml: A XML node.
    *
    * Return:
    * Returns XML string.
    */
    GetString: function (node) {
      if (!DataTypeIdentity.IsNotNullObject(node)) {
        throw new Error("Node is undefined.");
      }

      var xml = null;

      if (node.xml) {
        xml = node.xml;
      } else if (global.XMLSerializer) {
        xml = new global.XMLSerializer().serializeToString(node);
      }

      return xml;
    },

    /*
    * Method Description:
    * Executes a XPath query and returns the first match node.
    * 
    * Parameters:
    * node: The executing context of XPath query.
    * queryString : A XPath query string;
    * namespaces: A object. The name of a property in this object is a XML namespace prefix and it's value is a XML namespace URL.
    *
    * Returns:
    * Return the first match node.
    */
    SelectSingleNode: function (node, queryString, namespaces) {
      if (!node) {
        throw new Error("Node is undefined.");
      }
      if (!queryString) {
        throw new Error("XPath query string is undefined.");
      }

      var result = null;
      var doc = node.ownerDocument || node;
      var _queryString = queryString + String.Empty;

      if (doc.evaluate) {
        var xPathResult = doc.evaluate(_queryString, node, !namespaces ? null : function (prefix) {
          return (namespaces[prefix] || String.Empty) + String.Empty;
        }, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
        result = xPathResult.singleNodeValue;
      } else {
        var ieNamespaces = String.Empty;
        if (namespaces) {
          for (prefix in namespaces) {
            prefix && namespaces[prefix] && (ieNamespaces += "xmlns:" + prefix + "='" + namespaces[prefix] + "' ");
          }
        }
        ieNamespaces = ieNamespaces.TrimEnd();

        doc.setProperty("SelectionLanguage", "XPath");
        doc.setProperty("SelectionNamespaces", ieNamespaces);
        result = (doc == node ? doc.documentElement : node).selectSingleNode(_queryString);
      }

      return result;
    },

    /*
    * Method Description:
    * Executes a XPath query and returns the match nodes array.
    * 
    * Parameters:
    * node: The executing context of XPath query.
    * queryString : A XPath query string;
    * namespaces: A object. The name of a property in this object is a XML namespace prefix and it's value is a XML namespace URL.
    *
    * Returns:
    * Return the match nodes array.
    */
    SelectNodes: function (node, queryString, namespaces) {
      if (!node) {
        throw new Error("Node is undefined.");
      }
      if (!queryString) {
        throw new Error("XPath query string is undefined.");
      }

      var result = [];
      var doc = node.ownerDocument || node;
      var _queryString = queryString + String.Empty;

      if (doc.evaluate) {
        var xPathResult = doc.evaluate(_queryString, node, !namespaces ? null : function (prefix) {
          return (namespaces[prefix] || String.Empty) + String.Empty;
        }, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        for (var i = 0; i < xPathResult.snapshotLength; i++) {
          result.push(xPathResult.snapshotItem(i));
        }
      } else {
        var ieNamespaces = String.Empty;
        if (namespaces) {
          for (prefix in namespaces) {
            prefix && namespaces[prefix] && (ieNamespaces += "xmlns:" + prefix + "=\"" + namespaces[prefix] + "\" ");
          }
        }
        ieNamespaces = ieNamespaces.TrimEnd();

        doc.setProperty("SelectionLanguage", "XPath");
        doc.setProperty("SelectionNamespaces", ieNamespaces);
        var nodes = (doc == node ? doc.documentElement : node).selectNodes(_queryString);
        for (var i = 0; i < nodes.length; i++) {
          result.push(nodes[i]);
        }
      }

      return result;
    }
  };

  /***************** XmlSerializer Class Definition *****************/
  /*
  * Class Description:
  * Provides a utility for serialize object to XML document.
  *
  * Mask:
  * This class don't serialize function and array property of a object.
  */
  var XmlSerializer = Xphter.XmlSerializer = function () {

    /*
    * Method Description:
    * Serializes the specified object to a XML node.
    *
    * Parameters:
    * xmlDocument: A XML document.
    * obj: A object, can be a number, string, boolean and object.
    * objectName: The tag name, can contains a namespace prefix.
    * namespaceURL: The node namespace.
    *
    * Return:
    * Returns a XML node represents the specified object.
    */
    this.SerializeToXmlNode = function (xmlDocument, obj, objectName, namespaceURL) {
      if (!xmlDocument) {
        throw new Error("XML document is undefined.");
      }
      if (DataTypeIdentity.IsUndefinedOrNull(obj)) {
        throw new Error("Object is undefined.");
      }
      if (!objectName) {
        throw new Error("Object name is undefined.");
      }

      var node = null;
      var _objectName = objectName + String.Empty;
      switch (typeof obj) {
        case "number":
        case "string":
        case "boolean":
          node = XmlUtility.CreateElementNS(xmlDocument, _objectName, namespaceURL);
          node.appendChild(xmlDocument.createTextNode(obj + String.Empty));
          break;
        case "object":
          if (!(obj instanceof Array)) {
            var childNode = null;
            var match = (/^([^\:]+)\:/i).exec(_objectName);
            var prefix = match ? match[1] : null;
            node = XmlUtility.CreateElementNS(xmlDocument, _objectName, namespaceURL);
            for (var name in obj) {
              if (name && (childNode = this.SerializeToXmlNode(xmlDocument, obj[name], (prefix ? prefix + ":" : String.Empty) + name, xmlDocument.createElementNS ? namespaceURL : null))) {
                node.appendChild(childNode);
              }
            }
          }
          break;
      }

      return node;
    };

    /*
    * Method Description:
    * Serializes the specified object.
    *
    * Parameters:
    * obj: A object, can be a number, string, boolean and object.
    * objectName: The tag name of root XML element.
    * namespaceURL: The node namespace.
    *
    * Return:
    * Returns a XML document represents the specified object.
    */
    this.Serialize = function (obj, objectName, namespaceURL) {
      if (DataTypeIdentity.IsUndefinedOrNull(obj)) {
        throw new Error("Object is undefined.");
      }
      if (DataTypeIdentity.IsUndefinedOrNull(objectName)) {
        throw new Error("Object name is undefined.");
      }
      switch (typeof obj) {
        case "function":
          throw new Error("Can not serialize function.");
          break;
        case "object":
          if (obj instanceof Array) {
            throw new Error("Can not serialize array.");
          }
          break;
      }

      var xmlDocument = XmlUtility.CreateDocument();
      xmlDocument.appendChild(this.SerializeToXmlNode(xmlDocument, obj, objectName + String.Empty, namespaceURL));
      return xmlDocument;
    }
  };

  /***************** WebService Class Definition *****************/
  /*
  * Class Description:
  * Provides a utility for calling XML web service.
  *
  * Constructor Parameters:
  * url : The URL of this webservice;
  * encoding: The encoding of page when using SOAP or encoding of response data when cross-domain call.
  */
  var WebService = Xphter.WebService = function (url, encoding) {
    var m_url = null;
    var m_encoding = null;
    var m_namespace = null;
    var m_soap = null;
    var m_isCrossDomain;

    Constructor();

    /*
    * Constructor.
    */
    function Constructor() {
      if (!url) {
        throw new Error("Web service URL string is undefined.");
      }
      var uri = new Uri(m_url = url + String.Empty);
      m_encoding = (encoding || "utf-8") + String.Empty;
      if (!uri.GetIsWellFormat()) {
        throw new Error("Invalid web service URL.");
      }
      m_isCrossDomain = !uri.GetIsRelative() && !uri.GetHost().EqualsIgnoreCase(global.location.host);
      m_soap = "<?xml version=\"1.0\" encoding=\"" + m_encoding + "\"?><soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\"><soap12:Body></soap12:Body></soap12:Envelope>";
    }

    /*
    * Private Method Description:
    * Parse the specified argument object to query string.
    */
    function ArgumentsToQueryString(arguments) {
      if (!arguments) {
        return null;
      }

      var value = null;
      var queryString = String.Empty;

      for (var name in arguments) {
        if (!name || DataTypeIdentity.IsUndefinedOrNull(value = arguments[name])) {
          continue;
        }

        queryString += ((queryString.length > 0 ? "&" : String.Empty) + global.encodeURIComponent(name) + "=" + global.encodeURIComponent(value + String.Empty));
      }

      return queryString;
    }

    /*
    * Private Method Description:
    * Parse the specified argument object to a SOAP XML document.
    */
    function ArgumentsToSoapDocument(arguments, methodName) {
      var doc = XmlUtility.LoadString(m_soap);
      var node = new XmlSerializer().SerializeToXmlNode(doc, arguments || {}, methodName, m_namespace || (m_namespace = GetNamespace()));
      doc.documentElement.firstChild.appendChild(node);
      return doc;
    }

    /*
    * Private Method Description:
    * Gets default namespace of this webservice.
    */
    function GetNamespace() {
      if (m_isCrossDomain) {
        throw new Error("Can not get namespace of a webservice in a cross domain.");
      }

      var wsdl = XmlUtility.LoadFile(m_url + "?WSDL");
      return XmlUtility.SelectSingleNode(wsdl, "//@targetNamespace").value;
    }

    /*
    * Method Description:
    * Gets whether the specified web service is location in a different domain.
    */
    this.GetIsCrossDomain = function () {
      return m_isCrossDomain;
    };

    /*
    * Method Description:
    * Synchronous invoke this webservice, a error will throw if this is a cross-domain invoke.
    *
    * Parameters:
    * parameters: The parameters for this calling which is a object as follow:
    * {
    *   MethodName: The name of a webservice method.
    *   Method: HTTP GET or HTTP POST or SOAP.
    *   Arguments: The arguments.
    *   OnComplete: The callback function when successfully load resource. A XMLHttpRequest object argument will pass to this method.
    *   OnError: The callback function when unsuccessfully load resource. A XMLHttpRequest object argument will pass to this method.
    * }
    *
    */
    this.SyncCall = function (parameters) {
      if (!DataTypeIdentity.IsNotNullObject(parameters)) {
        throw new Error("Parameters is undefined.");
      }
      if (!parameters.MethodName) {
        throw new Error("Method name is empty.");
      }
      if (m_isCrossDomain) {
        throw new Error("This is cross domain invoke, please call CrossDomainCall method instend.");
      }

      switch (((parameters.Method || "soap") + String.Empty).toLowerCase()) {
        case "get":
          AjaxUtility.Sync({
            Uri: m_url + "/" + parameters.MethodName + (parameters.Arguments ? "?" + ArgumentsToQueryString(parameters.Arguments) : String.Empty),
            Method: "get",
            OnComplete: parameters.OnComplete,
            OnError: parameters.OnError
          });
          break;
        case "post":
          AjaxUtility.Sync({
            Uri: m_url + "/" + parameters.MethodName,
            Method: "post",
            Data: ArgumentsToQueryString(parameters.Arguments),
            Headers: {
              "Content-Type": "application/x-www-form-urlencoded"
            },
            OnComplete: parameters.OnComplete,
            OnError: parameters.OnError
          });
          break;
        default:
          AjaxUtility.Sync({
            Uri: m_url,
            Method: "post",
            Data: ArgumentsToSoapDocument(parameters.Arguments, parameters.MethodName),
            Headers: {
              "Content-Type": "application/soap+xml; charset=" + m_encoding
            },
            OnComplete: parameters.OnComplete,
            OnError: parameters.OnError
          });
          break;
      }
    };

    /*
    * Method Description:
    * Asynchronous invoke this webservice, a error will throw if this is a cross-domain invoke.
    *
    * Parameters:
    * parameters: The parameters for this calling which is a object as follow:
    * {
    *   MethodName: The name of a webservice method.
    *   Method: HTTP GET or HTTP POST or SOAP.
    *   Arguments: The arguments.
    *   OnComplete: The callback function when successfully load resource. A XMLHttpRequest object argument will pass to this method.
    *   OnError: The callback function when unsuccessfully load resource. A XMLHttpRequest object argument will pass to this method.
    *   OnTimeout: The callback function when load resource timeout.
    *   Timeout: The timeout in milliseconeds. Zero or a negative number indicates infinity timeout.
    * }
    *
    */
    this.AsyncCall = function (parameters) {
      if (!DataTypeIdentity.IsNotNullObject(parameters)) {
        throw new Error("Parameters is undefined.");
      }
      if (!parameters.MethodName) {
        throw new Error("Method name is empty.");
      }
      if (m_isCrossDomain) {
        throw new Error("This is cross domain invoke, please call CrossDomainCall method instend.");
      }

      switch (((parameters.Method || "soap") + String.Empty).toLowerCase()) {
        case "get":
          AjaxUtility.Async({
            Uri: m_url + "/" + parameters.MethodName + (parameters.Arguments ? "?" + ArgumentsToQueryString(parameters.Arguments) : String.Empty),
            Method: "get",
            OnComplete: parameters.OnComplete,
            OnError: parameters.OnError,
            OnTimeout: parameters.OnTimeout,
            Timeout: parameters.Timeout
          });
          break;
        case "post":
          AjaxUtility.Async({
            Uri: m_url + "/" + parameters.MethodName,
            Method: "post",
            Data: ArgumentsToQueryString(parameters.Arguments),
            Headers: {
              "Content-Type": "application/x-www-form-urlencoded"
            },
            OnComplete: parameters.OnComplete,
            OnError: parameters.OnError,
            OnTimeout: parameters.OnTimeout,
            Timeout: parameters.Timeout
          });
          break;
        default:
          AjaxUtility.Async({
            Uri: m_url,
            Method: "post",
            Data: ArgumentsToSoapDocument(parameters.Arguments, parameters.MethodName),
            Headers: {
              "Content-Type": "application/soap+xml; charset=" + m_encoding
            },
            OnComplete: parameters.OnComplete,
            OnError: parameters.OnError,
            OnTimeout: parameters.OnTimeout,
            Timeout: parameters.Timeout
          });
          break;
      }
    };

    /*
    * Method Description:
    * Cross domain invoke this webservice.
    *
    * Parameters:
    * parameters: The parameters for this calling which is a object as follow:
    * {
    *   MethodName: The name of a webservice method.
    *   Arguments: The arguments.
    *   OnComplete: The callback function when successfully load resource..
    *   OnTimeout: The callback function when load resource timeout.
    *   Timeout: The timeout in milliseconeds. Zero or a negative number indicates infinity timeout.
    * }
    *
    */
    this.CrossDomainCall = function (parameters) {
      if (!DataTypeIdentity.IsNotNullObject(parameters)) {
        throw new Error("Parameters is undefined.");
      }
      if (!parameters.MethodName) {
        throw new Error("Method name is empty.");
      }

      AjaxUtility.CrossDomain({
        Uri: m_url + "/" + parameters.MethodName + (parameters.Arguments ? "?" + ArgumentsToQueryString(parameters.Arguments) : String.Empty),
        Charset: m_encoding,
        OnComplete: parameters.OnComplete,
        OnTimeout: parameters.OnTimeout,
        Timeout: parameters.Timeout
      });
    };
  };

  /***************** Animator Class Definition *****************/

  /*
  * Class Description:
  * Provides base functions of animation.
  *
  * Constructor Parameters:
  * frequency : The frequency of performing routine, it should less than or equal 1000 and greater than or equal 0;
  * duration: The all milliseconds begin start animation to end animation, set zero to indicate never automatic stop animation until invoke Stop method explicitly.
  * initializer: The intialization operation.
  * routine: The animation operation, the elapsed time will pass to this function.
  * cleaner: The clean operation.
  */
  var Animator = Xphter.Animator = function (frequency, duration, initializer, routine, cleaner) {
    var m_frequency = 0;
    var m_duration = 0;
    var m_initializer = null;
    var m_routine = null;
    var m_cleaner = null;

    var m_currentFrameIndex = 0;
    var m_isForever = false;
    var m_isRunning = false;

    var m_timer = null;
    var m_interval = 0;
    var m_baseTick = 0;
    var m_elapsedTime = 0;

    Constructor();

    /*
    * Constructor.
    */
    function Constructor() {
      m_frequency = Math.min(1000, Math.max(1, global.isNaN(frequency) ? 0 : frequency - 0));
      m_duration = Math.max(0, global.isNaN(duration) ? 0 : duration - 0);
      m_initializer = DataTypeIdentity.IsFunction(initializer) ? initializer : null;
      m_routine = DataTypeIdentity.IsFunction(routine) ? routine : null;
      m_cleaner = DataTypeIdentity.IsFunction(cleaner) ? cleaner : null;
      m_isForever = (m_duration == 0);
      m_interval = Math.round(1000 / m_frequency);
    }

    /*
    * Private Method Description:
    * Start animation, do nothing if this animation has already started.
    */
    function StartAnimator() {
      if (m_isRunning) {
        return;
      }

      //initialize
      m_isRunning = true;

      //perform initializer
      if (m_initializer) {
        m_initializer();
      }

      //first frame
      m_currentFrameIndex = 0;
      m_baseTick = new Date().getTime();
      m_elapsedTime = 0;
      if (m_routine) {
        m_routine(m_elapsedTime);
      }

      //start timer
      if (m_isForever) {
        m_timer = global.setInterval(function () {
          //each frame
          ++m_currentFrameIndex;
          m_elapsedTime = new Date().getTime() - m_baseTick;
          if (m_routine) {
            m_routine(m_elapsedTime);
          }
        }, m_interval);
      } else {
        m_timer = global.setInterval(function () {
          //each frame
          ++m_currentFrameIndex;
          m_elapsedTime = new Date().getTime() - m_baseTick;
          if (m_routine) {
            m_routine(m_elapsedTime);
          }

          //check whether the duration is expired.
          if (m_elapsedTime >= m_duration) {
            StopAnimator();
          }
        }, m_interval);
      }
    }

    /*
    * Private Method Description:
    * Stop animation, do nothing if this animation has already stoped.
    */
    function StopAnimator() {
      if (!m_isRunning) {
        return;
      }

      //initialize
      m_isRunning = false;

      //close timer
      global.clearInterval(m_timer);
      m_timer = null;

      //perform cleaner
      if (m_cleaner) {
        m_cleaner();
      }
    }

    /*
    * Method Description:
    * Gets current frame index numbering from zero.
    */
    this.GetCurrentFrameIndex = function () {
      return m_currentFrameIndex;
    };

    /*
    * Method Description:
    * Gets whether the animation is running.
    */
    this.GetIsRunning = function () {
      return m_isRunning;
    };

    /*
    * Method Description:
    * Gets whether the animation will never automatic stop until invoke Stop method explicitly.
    */
    this.GetIsForever = function () {
      return m_isForever;
    };

    /*
    * Method Description:
    * Gets elapsed milliseconds.
    */
    this.GetElapsedTime = function () {
      return m_elapsedTime;
    };

    /*
    * Method Description:
    * Start animation, do nothing if this animation has already started.
    */
    this.Start = StartAnimator;

    /*
    * Method Description:
    * Stop animation, do nothing if this animation has already stoped.
    */
    this.Stop = StopAnimator;
  };

  /***************** Gallery Class Definition *****************/

  /*
  * Class Description:
  * Represents a sliding gallery.
  * In a sliding gallery, it's child elements will slide from bottom to top or slide from left to right.
  * The sliding direction lies on user choice.
  *
  * Constructor Parameters:
  * target : The element will be a sliding gallery.
  * direction: The sliding direction which should be one of SlidingDirection's member.
  * frequency: The count of moving in one second.
  * speed: The count of pixels per seconds when sliding.
  * isPauseOnMouseEnter: Whether pause this gallery when mouse enter the target element.
  */
  var Gallery = Xphter.Gallery = function (target, direction, frequency, speed, isPauseOnMouseEnter) {
    var m_target = null;
    var m_direction = Gallery.SlidingDirection.LeftToRight;
    var m_frequency = 0;
    var m_isPauseOnMouseEnter = false;

    var m_children = [];
    var m_originalStart = 0;
    var m_originalEnd = 0;
    var m_isCircling = false;
    var m_isRunning = false;
    var m_isPaused = false;

    var m_speed = 0;
    var m_offset = 0;
    var m_animator = null;
    var m_previousTime = 0;

    Constructor();

    /*
    * Constructor
    */
    function Constructor() {
      m_target = target;
      m_direction = global.isNaN(direction) ? 0 : direction - 0;
      m_frequency = Math.max(1, global.isNaN(frequency) ? 0 : frequency - 0);
      m_speed = Math.max(1, global.isNaN(speed) ? 0 : speed - 0);
      m_isPauseOnMouseEnter = DataTypeIdentity.IsUndefinedOrNull(isPauseOnMouseEnter) ? true : !!isPauseOnMouseEnter;

      if (!DataTypeIdentity.IsNotNullObject(target)) {
        return;
      }

      //initialize container
      m_target.style.overflow = "hidden";
      m_target.style.position = "relative";
      m_target.style.left = "0px";
      m_target.style.top = "0px";
      switch (m_direction) {
        case Gallery.SlidingDirection.LeftToRight:
        case Gallery.SlidingDirection.RightToLeft:
          m_target.style.whiteSpace = "nowrap";
          break;
      }

      //search element child nodes
      while (m_target.firstChild) {
        if (m_target.firstChild.nodeType == DOMNodeTypes.Element) {
          //set style
          m_target.firstChild.style.position = "relative";
          m_target.firstChild.style.left = "0px";
          m_target.firstChild.style.top = "0px";

          //save element node
          m_children.push(m_target.firstChild);
        }

        m_target.removeChild(m_target.firstChild);
      }

      for (var i = 0; i < m_children.length; i++) {
        m_target.appendChild(m_children[i]);
      }

      //get start and end of elements
      if (m_children.length > 0) {
        var firstChild = m_children[0];
        var lastChild = m_children[m_children.length - 1];
        switch (m_direction) {
          case Gallery.SlidingDirection.BottomToTop:
          case Gallery.SlidingDirection.TopToBottom:
            m_originalStart = firstChild.offsetTop;
            m_originalEnd = (lastChild.offsetTop + lastChild.offsetHeight);
            m_isCircling = (m_originalEnd - m_originalStart) > m_target.offsetHeight && m_children.length > 1;
            break;
          case Gallery.SlidingDirection.LeftToRight:
          case Gallery.SlidingDirection.RightToLeft:
            m_originalStart = firstChild.offsetLeft;
            m_originalEnd = (lastChild.offsetLeft + lastChild.offsetWidth);
            m_isCircling = (m_originalEnd - m_originalStart) > m_target.offsetWidth && m_children.length > 1;
            break;
        }
      }

      //register event handler for mouse moving
      EventUtility.Register(target, "mouseover", function () {
        if (m_isPauseOnMouseEnter && m_isRunning) {
          m_isPaused = true;
        }
      }, false);
      EventUtility.Register(target, "mouseout", function () {
        if (m_isPauseOnMouseEnter && m_isRunning) {
          m_isPaused = false;
        }
      }, false);
    }

    /*
    * Private Method Description:
    * Sliding bottom to top.
    */
    function BottomToTop() {
      if (m_isPaused) {
        m_previousTime = new Date().getTime();
        return;
      }

      var currentTime = new Date().getTime();
      var delta = (currentTime - m_previousTime) * m_speed / 1000;
      if (m_isCircling) {
        var firstChild = m_children[0];
        var secondChild = m_children[1];
        var secondOffsetTop = secondChild.offsetTop;
        if (Math.abs(m_offset - delta) >= DocumentUtility.GetElementHeight(firstChild)) {
          m_target.appendChild(firstChild);
          m_children.push(m_children.shift());

          m_offset -= (secondChild.offsetTop - secondOffsetTop);
        } else {
          m_offset -= delta;
        }
      } else {
        var lastChild = m_children[m_children.length - 1];
        if (lastChild.offsetTop + lastChild.offsetHeight - delta < 0) {
          m_offset = (m_target.offsetHeight - m_originalStart);
        } else {
          m_offset -= delta;
        }
      }

      for (var i = 0; i < m_children.length; i++) {
        m_children[i].style.top = Math.floor(m_offset) + "px";
      }

      m_previousTime = currentTime;
    }

    /*
    * Private Method Description:
    * Sliding top to bottom.
    */
    function TopToBottom() {
      if (m_isPaused) {
        m_previousTime = new Date().getTime();
        return;
      }

      var currentTime = new Date().getTime();
      var delta = (currentTime - m_previousTime) * m_speed / 1000;
      if (m_isCircling) {
        var firstChild = m_children[0];
        var firstOffsetTop = firstChild.offsetTop;
        var lastChild = m_children[m_children.length - 1];
        if (m_offset + delta >= 0) {
          m_target.insertBefore(lastChild, firstChild);
          m_children.unshift(m_children.pop());

          m_offset -= (firstChild.offsetTop - firstOffsetTop);
        } else {
          m_offset += delta;
        }
      } else {
        var firstChild = m_children[0];
        if (firstChild.offsetTop + delta > m_target.offsetHeight) {
          m_offset = (0 - m_originalEnd);
        } else {
          m_offset += delta;
        }
      }

      for (var i = 0; i < m_children.length; i++) {
        m_children[i].style.top = Math.floor(m_offset) + "px";
      }

      m_previousTime = currentTime;
    }

    /*
    * Private Method Description:
    * Sliding left to right.
    */
    function LeftToRight() {
      if (m_isPaused) {
        m_previousTime = new Date().getTime();
        return;
      }

      var currentTime = new Date().getTime();
      var delta = (currentTime - m_previousTime) * m_speed / 1000;
      if (m_isCircling) {
        var firstChild = m_children[0];
        var firstOffsetLeft = firstChild.offsetLeft;
        var lastChild = m_children[m_children.length - 1];
        if (m_offset + delta >= 0) {
          m_target.insertBefore(lastChild, firstChild);
          m_children.unshift(m_children.pop());

          m_offset -= (firstChild.offsetLeft - firstOffsetLeft);
        } else {
          m_offset += delta;
        }
      } else {
        var firstChild = m_children[0];
        if (firstChild.offsetLeft + delta > m_target.offsetWidth) {
          m_offset = (0 - m_originalEnd);
        } else {
          m_offset += delta;
        }
      }

      for (var i = 0; i < m_children.length; i++) {
        m_children[i].style.left = Math.floor(m_offset) + "px";
      }

      m_previousTime = currentTime;
    }

    /*
    * Private Method Description:
    * Sliding right to left.
    */
    function RightToLeft() {
      if (m_isPaused) {
        m_previousTime = new Date().getTime();
        return;
      }

      var currentTime = new Date().getTime();
      var delta = (currentTime - m_previousTime) * m_speed / 1000;
      if (m_isCircling) {
        var firstChild = m_children[0];
        var secondChild = m_children[1];
        var secondOffsetLeft = secondChild.offsetLeft;
        if (Math.abs((m_offset - delta)) >= DocumentUtility.GetElementWidth(firstChild)) {
          m_target.appendChild(firstChild);
          m_children.push(m_children.shift());

          m_offset -= (secondChild.offsetLeft - secondOffsetLeft);
        } else {
          m_offset -= delta;
        }
      } else {
        var lastChild = m_children[m_children.length - 1];
        if (lastChild.offsetLeft + lastChild.offsetWidth - delta < 0) {
          m_offset = m_target.offsetWidth - m_originalStart;
        } else {
          m_offset -= delta;
        }
      }

      for (var i = 0; i < m_children.length; i++) {
        m_children[i].style.left = Math.floor(m_offset) + "px";
      }

      m_previousTime = currentTime;
    }

    /*
    * Method Description:
    * Gets whether the sliding gallery is running.
    */
    this.GetIsRunning = function () {
      return m_isRunning;
    };

    /*
    * Method Description:
    * Gets whether the sliding gallery has paused.
    */
    this.GetIsPaused = function () {
      return m_isPaused;
    };

    /*
    * Method Description:
    * Gets whether pause this sliding gallery when mouse enter the target element.
    *
    * Return:
    * Return whether pause this sliding gallery when mouse enter the target element.
    */
    this.GetIsPauseOnMouseEnter = function () {
      return m_isPauseOnMouseEnter;
    };

    /*
    * Method Description:
    * Sets whether pause this sliding gallery when mouse enter the target element.
    *
    * Parameters:
    * value: whether pause this sliding gallery when mouse enter the target element.
    */
    this.SetIsPauseOnMouseEnter = function (value) {
      m_isPauseOnMouseEnter = !!value;
    };

    /*
    * Method Description:
    * Gets speed of this gallery: pixels per second when moving.
    */
    this.GetSpeed = function () {
      return m_speed;
    };

    /*
    * Method Description:
    * Sets speed of this gallery: pixels per second when moving.
    *
    * Parameters:
    * value: The new value of speed.
    */
    this.SetSpeed = function (value) {
      m_speed = Math.max(1, global.isNaN(value) ? 0 : value - 0);
    };

    /*
    * Method Description:
    * Change the speed.
    *
    * Parameter:
    * offset: The value will changed. If it is a positive number, this gallery will speedup, otherwise it will speeddown.
    */
    this.ChangeSpeed = function (offset) {
      var _offset = global.isNaN(offset) ? 0 : offset - 0;
      m_speed = Math.max(0, m_speed + _offset);
    };

    /*
    * Method Description:
    * Start to run this sliding gallery.
    */
    this.Start = function () {
      if (m_isRunning || m_children.length < 1) {
        return;
      }

      m_isRunning = true;
      m_isPaused = false;
      m_previousTime = new Date().getTime();

      switch (m_direction) {
        case Gallery.SlidingDirection.BottomToTop:
          m_animator = new Animator(m_frequency, 0, null, BottomToTop, null);
          break;
        case Gallery.SlidingDirection.TopToBottom:
          m_animator = new Animator(m_frequency, 0, null, TopToBottom, null);
          break;
        case Gallery.SlidingDirection.LeftToRight:
          m_animator = new Animator(m_frequency, 0, null, LeftToRight, null);
          break;
        case Gallery.SlidingDirection.RightToLeft:
          m_animator = new Animator(m_frequency, 0, null, RightToLeft, null);
          break;
      }
      m_animator.Start();
    };

    /*
    * Method Description:
    * Stop runing this sliding gallery.
    */
    this.Stop = function () {
      if (!m_isRunning) {
        return;
      }

      m_isRunning = false;
      m_animator.Stop();
    };

    /*
    * Method Description:
    * Puase this sliding gallery.
    */
    this.Pause = function () {
      if (!m_isRunning) {
        return;
      }

      m_isPaused = true;
    };

    /*
    * Method Description:
    * Continue to run this sliding gallery if it has paused.
    */
    this.Continue = function () {
      if (!m_isRunning) {
        return;
      }

      m_isPaused = false;
    };
  };

  /*
  * Enumrator Description:
  * Represents the direction of a sliding gallery.
  */
  Gallery.SlidingDirection = {
    BottomToTop: 0,
    TopToBottom: 1,
    LeftToRight: 2,
    RightToLeft: 3
  };

  /***************** Flash Class Definition *****************/

  /*
  * Class Description:
  * Represents a flash control.
  *
  * Constructor Parameters:
  * flash: The URI of a flash file.
  * width: The width of this flash.
  * height: The height of this flash.
  * parameters: A object which's enumerable property will be a parameter of this flash control.
  */
  var Flash = Xphter.Flash = function (flash, width, height, styles, parameters) {
    var m_flash = String.Empty;
    var m_width = 0;
    var m_height = 0;
    var m_flashElement = null;

    Constructor();

    /*
    * Constructor.
    */
    function Constructor() {
      m_flash = flash + String.Empty;
      m_width = global.isNaN(width) ? 0 : width - 0;
      m_height = global.isNaN(height) ? 0 : height - 0;

      //create object markup
      /*var objectParameters = [];
      if (parameters) {
      for (var name in parameters) {
      objectParameters.push(CreateParam(name, parameters[name]));
      }
      }
      objectParameters.push(CreateParam("movie", m_flash));
      objectParameters.push(CreateParam("src", m_flash));
      var obj = DocumentUtility.CreateElement("object", {
      type: "application/x-shockwave-flash",
      classid: "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
      codebase: "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab",
      data: m_flash,
      width: m_width,
      height: m_height
      }, styles, objectParameters);*/

      //create embed markup
      var embedParameters = parameters || {};
      embedParameters["type"] = "application/x-shockwave-flash";
      embedParameters["pluginspage"] = "http://www.macromedia.com/go/getflashplayer";
      embedParameters["src"] = m_flash;
      embedParameters["width"] = m_width;
      embedParameters["height"] = m_height;
      var embed = DocumentUtility.CreateElement("embed", embedParameters, styles);

      m_flashElement = embed;
    }

    /*
    * Private Method Description:
    * Create a param markup for object markup.
    */
    function CreateParam(name, value) {
      var param = global.document.createElement("param");
      param.setAttribute("name", name + "");
      param.setAttribute("value", value + "");
      return param;
    }

    /*
    * Method Description:
    * Gets the width of flash.
    */
    this.GetWidth = function () {
      return m_width;
    };

    /*
    * Method Description:
    * Gets the height of flash.
    */
    this.GetHeight = function () {
      return m_height;
    };

    /*
    * Method Description:
    * Gets the flash HTML element.
    */
    this.GetFlashElement = function () {
      return m_flashElement;
    };

    /*
    * Method Description:
    * Replaces a existing element with this flash control.
    * 
    * Parameters:
    * element: The element will be replaced.
    */
    this.Replace = function (element) {
      if (element && element.parentNode) {
        element.parentNode.replaceChild(m_flashElement, element);
      }
    };

    /*
    * Method Description:
    * Places the flash control in the specified element.
    * 
    * Parameters:
    * element: The element will contain this flash control.
    */
    this.Place = function (element) {
      if (element && element.appendChild) {
        element.appendChild(m_flashElement);
      }
    };
  };

  /***************** FlashAnchor Class Definition *****************/

  /*
  * Class Description:
  * Represents a flash anchor.
  *
  * Constructor Parameters:
  * flash: The URI of a flash file.
  * width: The width of this flash.
  * height: The height of this flash.
  * parameters: A object which's enumerable property will be a parameter of this flash control.
  * clickHandler: The hander for click event.
  */
  var FlashAnchor = Xphter.FlashAnchor = function (flash, width, height, parameters, clickHandler) {
    var m_flash = null;
    var m_width = 0;
    var m_height = 0;
    var m_parameters = null;
    var m_clickHandler = null;
    var m_anchorElement = null;

    Constructor();

    /*
    * Constructor.
    */
    function Constructor() {
      m_flash = flash + String.Empty;
      m_width = global.isNaN(width) ? 0 : width - 0;
      m_height = global.isNaN(height) ? 0 : height - 0;
      m_parameters = parameters || {};
      m_clickHandler = DataTypeIdentity.IsFunction(clickHandler) ? clickHandler : function () { };

      //create flash layer
      var flashLayer = DocumentUtility.CreateElement("div", null, {
        width: m_width + "px",
        height: m_height + "px",
        position: "absolute",
        left: "0px",
        top: "0px",
        margin: "0px",
        padding: "0px"
      });

      //create flash control
      m_parameters["wmode"] = "opaque";
      var flashControl = new Flash(m_flash, m_width, m_height, null, m_parameters);
      flashControl.Place(flashLayer);

      //create anchor layer
      var anchorLayer = DocumentUtility.CreateElement("div", null, {
        width: m_width + "px",
        height: m_height + "px",
        position: "absolute",
        left: "0px",
        top: "0px",
        margin: "0px",
        padding: "0px",
        zIndex: 99,
        cursor: "pointer",
        backgroundImage: "url('about:blank')"
      });
      EventUtility.Register(anchorLayer, "click", m_clickHandler, false);

      //create anchor element
      m_anchorElement = DocumentUtility.CreateElement("div", null, {
        width: m_width + "px",
        height: m_height + "px",
        position: "relative",
        left: "0px",
        top: "0px",
        margin: "0px",
        padding: "0px"
      }, [flashLayer, anchorLayer]);
    }

    /*
    * Method Description:
    * Gets the width of flash.
    */
    this.GetWidth = function () {
      return m_width;
    };

    /*
    * Method Description:
    * Gets the height of flash.
    */
    this.GetHeight = function () {
      return m_height;
    };

    /*
    * Method Description:
    * Gets the anchor HTML element.
    */
    this.GetAnchorElement = function () {
      return m_anchorElement;
    };

    /*
    * Method Description:
    * Replaces a existing element with this flash anchor.
    * 
    * Parameters:
    * element: The element will be replaced.
    */
    this.Replace = function (element) {
      if (element && element.parentNode) {
        element.parentNode.replaceChild(m_anchorElement, element);
      }
    };

    /*
    * Method Description:
    * Places the flash anchor in the specified element.
    * 
    * Parameters:
    * element: The element will contain this flash control.
    */
    this.Place = function (element) {
      if (element && element.appendChild) {
        element.appendChild(m_anchorElement);
      }
    };
  };

  /***************** Couplet Class Definition *****************/

  /*
  * Class Description:
  * Represents a couplet.
  *
  * Constructor Parameters:
  * parameters: The parameters for creating this couplet. It is a object as follow:
  * {
  *   IsFixed: Determine whether fix the position of this couplet.
  *   SlidingDuration: The duration miliseconds when sliding this couplet if position of it is fixed.
  *   Top: The top position of this couplet.
  *   SmallWidth: The width of couplet when it has collapsed.
  *   LargeWidth: The width of couplet when it has expanded.
  *   Height: The height of couplet.
  *   LeftSmallFlashURI: The URI of left flash when couplet has collapsed.
  *   LeftLargeFlashURI: The URI of left flash when couplet has expanded.
  *   RightSmallFlashURI: The URI of right flash when couplet has collapsed.
  *   RightLargeFlashURI: The URI of right flash when couplet has expanded.
  * }
  */
  var Couplet = Xphter.Couplet = function (parameters) {
    var m_isFixed = false;
    var m_slidingDuration = 0;
    var m_top = 0;
    var m_smallWidth = 0;
    var m_largeWidth = 0;
    var m_height = 0;
    var m_leftSmallFlashURI = String.Empty;
    var m_leftLargeFlashURI = String.Empty;
    var m_rightSmallFlashURI = String.Empty;
    var m_rightLargeFlashURI = String.Empty;

    var m_leftVerse = null;
    var m_leftSmallFlash = null;
    var m_leftLargeFlash = null;
    var m_rightVerse = null;
    var m_rightSmallFlash = null;
    var m_rightLargeFlash = null;

    var m_slidingTimer = null;
    var m_slidingDelay = 1000;
    var m_slidingAnimator = null;
    var m_slidingTop = 0;
    var m_slidingSpeed = 0;

    Constructor();

    /*
    * Constructor.
    */
    function Constructor() {
      //get parameters
      m_isFixed = parameters ? !!parameters.IsFixed : false;
      m_slidingDuration = Math.max(10, parameters ? (global.isNaN(parameters.SlidingDuration) ? 0 : parameters.SlidingDuration - 0) : 0);
      m_top = parameters ? (global.isNaN(parameters.Top) ? 0 : parameters.Top - 0) : 0;
      m_smallWidth = parameters ? (global.isNaN(parameters.SmallWidth) ? 0 : parameters.SmallWidth - 0) : 0;
      m_largeWidth = parameters ? (global.isNaN(parameters.LargeWidth) ? 0 : parameters.LargeWidth - 0) : 0;
      m_height = parameters ? (global.isNaN(parameters.Height) ? 0 : parameters.Height - 0) : 0;
      m_leftSmallFlashURI = parameters ? (parameters.LeftSmallFlashURI || String.Empty) + String.Empty : String.Empty;
      m_leftLargeFlashURI = parameters ? (parameters.LeftLargeFlashURI || String.Empty) + String.Empty : String.Empty;
      m_rightSmallFlashURI = parameters ? (parameters.RightSmallFlashURI || String.Empty) + String.Empty : String.Empty;
      m_rightLargeFlashURI = parameters ? (parameters.RightLargeFlashURI || String.Empty) + String.Empty : String.Empty;

      //normalize small width and large width
      m_smallWidth = m_smallWidth || m_largeWidth;
      m_largeWidth = m_largeWidth || m_smallWidth;
      m_smallWidth = Math.min(m_smallWidth, m_largeWidth);
      m_largeWidth = Math.max(m_smallWidth, m_largeWidth);

      //create flashs
      var visible = BrowserGeometry.GetViewportWidth() - BrowserGeometry.GetBodyWidth() - 2 * m_largeWidth > 0;
      var m_leftSmallFlash = (m_smallWidth && m_height && m_leftSmallFlashURI) ? CreateFlash(true, m_leftLargeFlashURI ? !visible : true, m_smallWidth, m_height, m_leftSmallFlashURI) : null;
      var m_leftLargeFlash = (m_largeWidth && m_height && m_leftLargeFlashURI) ? CreateFlash(true, m_leftSmallFlashURI ? visible : true, m_largeWidth, m_height, m_leftLargeFlashURI) : null;
      var m_rightSmallFlash = (m_smallWidth && m_height && m_rightSmallFlashURI) ? CreateFlash(false, m_rightLargeFlashURI ? !visible : true, m_smallWidth, m_height, m_rightSmallFlashURI) : null;
      var m_rightLargeFlash = (m_largeWidth && m_height && m_rightLargeFlashURI) ? CreateFlash(false, m_rightSmallFlashURI ? visible : true, m_largeWidth, m_height, m_rightLargeFlashURI) : null;

      //register event handlers
      RegisterMouseEvent(true, m_leftSmallFlash, m_leftLargeFlash, m_rightSmallFlash, m_rightLargeFlash);
      RegisterMouseEvent(true, m_rightSmallFlash, m_rightLargeFlash, m_leftSmallFlash, m_leftLargeFlash);
      RegisterMouseEvent(false, m_leftLargeFlash, m_leftSmallFlash, m_rightLargeFlash, m_rightSmallFlash);
      RegisterMouseEvent(false, m_rightLargeFlash, m_rightSmallFlash, m_leftLargeFlash, m_leftSmallFlash);

      if (m_largeWidth && m_height) {
        //create left and right verse
        m_leftVerse = CreateVerse(true, m_top, m_largeWidth, m_height, m_leftSmallFlash, m_leftLargeFlash);
        m_rightVerse = CreateVerse(false, m_top, m_largeWidth, m_height, m_rightSmallFlash, m_rightLargeFlash);

        //create sliding animator
        CreateSlidingAnimator();

        //register window scroll event handler
        if (m_isFixed) {
          EventUtility.Register(global, "scroll", function () {
            //stop sliding timer
            if (m_slidingTimer) {
              global.clearTimeout(m_slidingTimer);
              m_slidingTimer = null;
            }
            //stop sliding animator
            if (m_slidingAnimator.GetIsRunning()) {
              m_slidingAnimator.Stop();
            }
            //create a new sliding timer
            m_slidingTimer = global.setTimeout(function () {
              global.clearTimeout(m_slidingTimer);
              m_slidingTimer = null;

              m_slidingAnimator.Start();
            }, m_slidingDelay);
          }, false);
        }

        //show couplet
        global.document.body.appendChild(m_leftVerse);
        global.document.body.appendChild(m_rightVerse);
      }
    }

    /*
    * Private Method Description:
    * Creates a flash in this couplet.
    * 
    * Parameters:
    * isLeft: Whether this is the left flash.
    * visible: Determine whether show this flash.
    * width: The width of this flash.
    * height: The height of this flash.
    * flashURI: The URI of the flash.
    *
    * Returns:
    * Return the HTML element of this flash.
    */
    function CreateFlash(isLeft, visible, width, height, flashURI) {
      var div = DocumentUtility.CreateElement("div", null, {
        width: width + "px",
        height: height + "px",
        margin: "0px",
        padding: "0px",
        position: "absolute",
        top: "0px",
        visibility: visible ? "visible" : "hidden"
      }, new Flash(flashURI, width, height, {
        margin: "0px",
        padding: "0px"
      }, {
        quality: "high",
        wmode: "opaque"
      }).GetFlashElement());
      if (isLeft) {
        div.style.left = "0px";
      } else {
        div.style.right = "0px";
      }

      return div;
    }

    /*
    * Private Method Description:
    * Register mouse event for a flash.
    */
    function RegisterMouseEvent(isMouseOver, flash, siblingFlash, anotherFlash, anotherSiblingFlash) {
      if (flash && siblingFlash) {
        EventUtility.Register(flash, isMouseOver ? "mouseover" : "mouseout", function () {
          flash.style.visibility = "hidden";
          siblingFlash.style.visibility = "visible";

          if (anotherFlash && anotherSiblingFlash) {
            anotherFlash.style.visibility = "hidden";
            anotherSiblingFlash.style.visibility = "visible";
          }
        }, false);
      }
    }

    /*
    * Private Method Description:
    * Creates a verse of this couplet.
    * 
    * Parameters:
    * isLeft: Whether this is the left verse.
    * isFixed: Whether fix position of this verse.
    * top: The top position of this verse.
    * width: The width of this verse.
    * height: The height of this verse.
    * smallFlash: The URI of the flash when this verse has collapsed.
    * largeFlash: The URI of the flash when this verse has expanded.
    *
    * Returns:
    * Return the HTML element of this verse.
    */
    function CreateVerse(isLeft, top, width, height, smallFlash, largeFlash) {
      var div = DocumentUtility.CreateElement("div", null, {
        width: width + "px",
        height: height + "px",
        margin: "0px",
        padding: "0px",
        position: "absolute",
        top: top + "px",
        backgroundColor: "transparent",
        overflow: "hidden"
      }, [smallFlash, largeFlash]);
      if (isLeft) {
        div.style.left = "0px";
      } else {
        div.style.right = "0px";
      }

      return div;
    }

    /*
    * Private Method Description:
    * Creates the sliding animator.
    */
    function CreateSlidingAnimator() {
      m_slidingAnimator = new Animator(10, m_slidingDuration, function () {
        if (m_leftVerse.offsetTop + m_height < BrowserGeometry.GetVerticalScroll()) {
          //verses are on top of viewport
          m_rightVerse.style.top = m_leftVerse.style.top = (m_slidingTop = BrowserGeometry.GetVerticalScroll() - m_height) + "px";
          m_slidingSpeed = (m_height + m_top) / m_slidingDuration;
        } else if (m_leftVerse.offsetTop > BrowserGeometry.GetViewportHeight() + BrowserGeometry.GetVerticalScroll()) {
          //verses are on under of viewport
          m_rightVerse.style.top = m_leftVerse.style.top = (m_slidingTop = BrowserGeometry.GetVerticalScroll() + BrowserGeometry.GetViewportHeight()) + "px";
          m_slidingSpeed = (m_top - BrowserGeometry.GetViewportHeight()) / m_slidingDuration;
        } else {
          //verses are within viewport
          m_slidingTop = m_leftVerse.offsetTop;
          m_slidingSpeed = (BrowserGeometry.GetVerticalScroll() + m_top - m_leftVerse.offsetTop) / m_slidingDuration;
        }
      }, function () {
        m_leftVerse.style.top = m_rightVerse.style.top = (m_slidingTop + Math.floor(m_slidingSpeed * m_slidingAnimator.GetElapsedTime())) + "px";
      }, function () {
        m_rightVerse.style.top = m_leftVerse.style.top = (BrowserGeometry.GetVerticalScroll() + m_top) + "px";
      });
    }
  };

  /***************** SimpleFixedSlidesPlayer Class Definition *****************/

  /*
  * Class Description:
  * Represents a simple slides player which has no animation when switch slides.
  *
  * Constructor Parameters:
  * width: The width of this slides player;
  * height: The height of this slides player.
  * interval：The miliseconds between current picture and next picture.
  * slides: The array of slides which is a object array as follow:
  * {
  *   Title: The title of this slide.
  *   ImageURI: The image URI of this slide.
  *   TargetURI: The target URI of this slide.
  * }
  */
  var SimpleFixedSlidesPlayer = Xphter.SimpleFixedSlidesPlayer = function (width, height, interval, slides) {
    var m_width = 0;
    var m_height = 0;
    var m_interval = 0;
    var m_slides = [];

    var m_selectors = [];
    var m_playerElement = null;
    var m_anchor = null;
    var m_image = null;

    var m_timer = null;
    var m_currentIndex = -1;
    var m_isPlaying = false;

    //Event: CurrentSlideChanged
    var m_currentSlideChanged = new Event(this, "CurrentSlideChanged");

    Constructor();

    /*
    * Consturctor.
    */
    function Constructor() {
      //get parameters
      m_width = global.isNaN(width) ? 0 : width - 0;
      m_height = global.isNaN(height) ? 0 : height - 0;
      m_interval = Math.max(0, global.isNaN(interval) ? 0 : interval - 0);

      if (DataTypeIdentity.IsArray(slides)) {
        for (var i = 0; i < slides.length; i++) {
          if (slides[i]) {
            m_slides.push(slides[i]);
          }
        }
      } else if (DataTypeIdentity.IsNotNullObject(slides)) {
        m_slides.push(slides);
      }

      //create slides selectors
      var selector = null;
      if (m_slides.length > 1) {
        for (var i = 0; i < m_slides.length; i++) {
          selector = DocumentUtility.CreateElement("span", null, {
            display: "inline-block",
            width: "20px",
            height: "20px",
            borderLeft: "2px solid #FFFFFF",
            cursor: "pointer",
            lineHeight: "20px",
            textAlign: "center",
            backgroundColor: "#FFFFFF",
            opacity: "0.5",
            filter: "alpha(opacity=50)"
          }, (i + 1).toString());
          selector._selectorIndex = i;
          EventUtility.Register(selector, "mouseover", function () {
            StopPlay();
            Switch(this._selectorIndex);
          }, false);
          EventUtility.Register(selector, "mouseout", function () {
            PlaySlides();
          }, false);
          m_selectors.push(selector);
        }
      }

      //create selectors container
      var selectorContainer = m_slides.length > 1 ? DocumentUtility.CreateElement("div", null, {
        width: "auto",
        height: "auto",
        margin: "0px",
        padding: "0px",
        position: "absolute",
        right: "0px",
        bottom: "0px"
      }, m_selectors) : null;

      //create player elements
      m_image = m_slides.length ? DocumentUtility.CreateElement("img", null, {
        width: m_width + "px",
        height: m_height + "px",
        margin: "0px",
        padding: "0px",
        borderStyle: "none"
      }) : null;
      m_anchor = m_slides.length ? DocumentUtility.CreateElement("a", {
        target: "_blank"
      }, null, m_image) : null;
      m_playerElement = DocumentUtility.CreateElement("div", null, {
        width: m_width + "px",
        height: m_height + "px",
        margin: "0px",
        padding: "0px",
        position: "relative",
        left: "0px",
        top: "0px"
      }, m_slides.length ? [m_anchor, selectorContainer] : null);
    }

    /*
    * Private Method Description:
    * Switch to the specified slide.
    *
    * Parameters:
    * index: The index of the slide which will be visible.
    */
    function Switch(index) {
      var slide = m_slides[index];
      m_image.src = slide.ImageURI;
      m_image.alt = slide.Title;
      m_image.title = slide.Title;
      m_anchor.href = slide.TargetURI;

      if (m_currentIndex >= 0) {
        var previousSelector = m_selectors[m_currentIndex];
        previousSelector.style.backgroundColor = "#FFFFFF";
        previousSelector.style.opacity = "0.5";
        previousSelector.style.filter = "alpha(opacity=50)";
      }

      if (m_selectors.length) {
        var currentSelector = m_selectors[index];
        currentSelector.style.backgroundColor = "#FF0000";
        currentSelector.style.opacity = "1";
        currentSelector.style.filter = "alpha(opacity=100)";
      }

      m_currentIndex = index;
      m_currentSlideChanged.Raise();
    }

    /*
    * Private Method Description:
    * Start to play these slides.
    */
    function PlaySlides() {
      if (m_isPlaying || !m_slides.length) {
        return;
      }

      m_isPlaying = true;
      if (m_currentIndex < 0) {
        Switch(0);
      }
      if (m_slides.length > 1) {
        m_timer = global.setInterval(function () {
          Switch((m_currentIndex + 1) % m_slides.length);
        }, m_interval);
      }
    }

    /*
    * Private Method Description:
    * Stop to play these slides.
    */
    function StopPlay() {
      if (!m_isPlaying) {
        return;
      }

      m_isPlaying = false;
      if (m_timer) {
        global.clearInterval(m_timer);
        m_timer = null;
      }
    }

    /*
    * Method Description:
    * Gets whether this slides player is playing.
    */
    this.GetIsPlaying = function () {
      return m_isPlaying;
    };

    /*
    * Method Description:
    * Gets current slide.
    */
    this.GetCurrentSlide = function () {
      return m_currentIndex >= 0 ? m_slides[m_currentIndex] : null;
    };

    /*
    * Method Description:
    * Gets the HTML element of this slides player.
    */
    this.GetPlayerElement = function () {
      return m_playerElement;
    };

    /*
    * Property Description:
    * Gets CurrentSlideChanged event.
    */
    this.GetCurrentSlideChangedEvent = function () {
      return m_currentSlideChanged;
    };

    /*
    * Method Description:
    * Start to play these slides.
    */
    this.Play = PlaySlides;

    /*
    * Method Description:
    * Stop to play these slides.
    */
    this.Stop = StopPlay;

    /*
    * Method Description:
    * Replaces a existing element with this flash control.
    * 
    * Parameters:
    * element: The element will be replaced.
    */
    this.Replace = function (element) {
      if (element && element.parentNode) {
        element.parentNode.replaceChild(m_playerElement, element);
      }
    };

    /*
    * Method Description:
    * Places the flash control in the specified control.
    * 
    * Parameters:
    * element: The element will contain this flash control.
    */
    this.Place = function (element) {
      if (element && element.appendChild) {
        element.appendChild(m_playerElement);
      }
    };
  };

  /***************** TabControl Class Definition *****************/

  /*
  * Class Description:
  * Represents a TabControl.
  *
  * Constructor Parameters:
  *
  * tabPages: The array of tab pages which is a object array as follow:
  * {
  *   Tab: The tab element.
  *   Page: The page element.
  *   IsSelected: Determines whether this tab is selected.
  * }
  * isSelectOnMouseOver: Determines whether select a tab when mouse enter it.
  */
  var TabControl = Xphter.TabControl = function (tabPages, isSelectOnMouseOver) {
    var m_tabPages = [];
    var m_isSelectOnMouseOver = false;
    var m_selectedIndex = 0;

    //Event: SelectedTabChanged
    var m_selectedTabChanged = new Event(this, "SelectedTabChanged");

    Constructor();

    /*
    * Consturctor.
    */
    function Constructor() {
      var tabPage = null;

      //initialize members
      if (DataTypeIdentity.IsArray(tabPages)) {
        for (var i = 0; i < tabPages.length; i++) {
          if (tabPage = tabPages[i]) {
            tabPage.Tab._pageIndex = m_tabPages.length;
            m_tabPages.push(tabPage);
          }
        }
      } else if (DataTypeIdentity.IsNotNullObject(tabPages)) {
        tabPages.Tab._pageIndex = 0;
        m_tabPages.push(tabPages);
      }
      m_isSelectOnMouseOver = !!isSelectOnMouseOver;
      m_selectedIndex = -1;

      //routine used to control tabs      
      var eventType = m_isSelectOnMouseOver ? "mouseover" : "mousedown";
      for (var i = 0; i < m_tabPages.length; i++) {
        tabPage = m_tabPages[i];
        EventUtility.Register(tabPage.Tab, eventType, function (event) {
          Select(event.currentTarget._pageIndex);
        }, false);
      }

      //set default selected tab page
      for (var i = 0; i < m_tabPages.length; i++) {
        if (m_tabPages[i].IsSelected) {
          m_selectedIndex = i;
          break;
        }
      }
    }

    /*
    * Private Method Description:
    * Select the specified tab page.
    * 
    * Parameters:
    * index: The index of tab page which will be selected.
    */
    function Select(index) {
      if (index == m_selectedIndex) {
        return;
      }

      var previousIndex = m_selectedIndex;

      for (var i = 0; i < m_tabPages.length; i++) {
        m_tabPages[i].Page.style.display = "none";
      }
      m_tabPages[index].Page.style.display = String.Empty;
      m_selectedIndex = index;

      m_selectedTabChanged.Raise({
        SelectedTab: m_tabPages[index].Tab,
        UnselectedTab: previousIndex >= 0 ? m_tabPages[previousIndex].Tab : null
      });
    }

    /*
    * Property Description:
    * Gets SelectedTabChanged event.
    */
    this.GetSelectedTabChangedEvent = function () {
      return m_selectedTabChanged;
    };
  };

  /***************** Window Class Definition *****************/

  /*
  * Class Description:
  * Represents a window.
  *
  * Constructor Parameters:
  *
  * parameters: The parameters for constructor which is a object as follow:
  * {
  *   Content: A URL string or a HTML element.
  *   Width: The width of window, only take effect when Content is a URL string.
  *   Height: The height of window, only take effect when Content is a URL string.
  *   Title: The title of window.
  *   TitleColor: The color of window title text.
  *   BorderColor: The color of window border.
  *   CloseButtonImage: The image of close button.
  *   CloseButtonOverImage: The image of close button when mouse over.
  *   
  * }
  * isSelectOnMouseOver: Determines whether select a tab when mouse enter it.
  */
  var Window = Xphter.Window = function (parameters) {
    var m_windowWidth = 0;
    var m_windowHeight = 0;
    var m_windowElement = null;

    var m_frameSource = null;
    var m_frameWidth = 0;
    var m_frameHeight = 0;

    var m_contentElement = null;

    var m_title = String.Empty;
    var m_titleColor = "#000000";

    var m_controlBarHeight = 35;
    var m_hasBorder = true;
    var m_borderWidth = 6;
    var m_borderColor = "#E6E8FA";

    var m_closeButtonSize = 24;
    var m_closeButtonImage = null;
    var m_closeButtonOverImage = null;

    var m_isMouseDown = false;
    var m_mouseX = 0;
    var m_mouseY = 0;

    var m_dialogShade = null;

    //Event: Shown
    var m_shown = new Event(this, "Shown");
    //Event Closed
    var m_closed = new Event(this, "Closed");

    var m_returnValue = null;
    var m_onClosed = null;

    Constructor();

    /*
    * Constructor.
    */
    function Constructor() {
      if (!DataTypeIdentity.IsNotNullObject(parameters)) {
        throw new Error("Window parameters is undefined");
      }
      if (!parameters.Content) {
        throw new Error("Window content is undefined.");
      }

      //analyze parameters
      if (!DataTypeIdentity.IsObject(parameters.Content)) {
        if (!new Uri(m_frameSource = (parameters.Content || String.Empty) + String.Empty).GetIsWellFormat()) {
          throw new Error("Invalid window content URL.");
        }
        m_frameWidth = global.isNaN(parameters.Width) ? 0 : parameters.Width + 0;
        m_frameHeight = global.isNaN(parameters.Height) ? 0 : parameters.Height + 0;
      }
      m_title = (parameters.Title || String.Empty) + String.Empty;
      parameters.TitleColor && (m_titleColor = parameters.TitleColor + String.Empty);
      parameters.BorderColor && (m_borderColor = parameters.BorderColor + String.Empty);
      m_closeButtonImage = (parameters.CloseButtonImage || parameters.CloseButtonOverImage || String.Empty) + String.Empty;
      m_closeButtonOverImage = (parameters.CloseButtonOverImage || parameters.CloseButtonImage || String.Empty) + String.Empty;

      //create window element
      m_windowElement = DocumentUtility.CreateElement("div", null, {
        borderColor: "#777777",
        borderWidth: "2px",
        borderStyle: "solid",
        position: "absolute",
        zIndex: "99",
        overflow: "hidden",
        backgroundColor: "#FFFFFF"
      });

      //create content element
      if (m_frameSource) {
        m_contentElement = DocumentUtility.CreateElement("iframe", {
          src: m_frameSource,
          width: m_frameWidth + "px",
          height: m_frameHeight + "px",
          frameBorder: 1,
          marginHeight: "0px",
          marginWidth: "0px"
        });
      } else {
        m_contentElement = parameters.Content;
      }

      //get content element size
      var contentWidth = 0;
      var contentHeight = 0;
      if (!m_contentElement.offsetWidth) {
        global.document.body.appendChild(m_windowElement);
        m_windowElement.appendChild(m_contentElement);
        contentWidth = DocumentUtility.GetElementWidth(m_contentElement);
        contentHeight = DocumentUtility.GetElementHeight(m_contentElement);
        m_windowElement.removeChild(m_contentElement);
        global.document.body.removeChild(m_windowElement);
      } else {
        contentWidth = DocumentUtility.GetElementWidth(m_contentElement);
        contentHeight = DocumentUtility.GetElementHeight(m_contentElement);
      }

      //set window size
      m_windowElement.style.width = (m_windowWidth = contentWidth + 2 * m_borderWidth) + "px";
      m_windowElement.style.height = (m_windowHeight = m_controlBarHeight + contentHeight + m_borderWidth) + "px";

      //create control bar
      var controlBar = DocumentUtility.CreateElement("div", null, {
        width: m_windowWidth + "px",
        height: m_controlBarHeight + "px",
        margin: "0px",
        padding: "0px",
        borderStyle: "none",
        overflow: "hidden",
        backgroundColor: m_borderColor,
        cursor: "move"
      });
      EventUtility.Register(controlBar, "mousedown", onMouseDown);
      m_windowElement.appendChild(controlBar);

      //create window title
      var windowTitle = DocumentUtility.CreateElement("div", null, {
        width: (contentWidth - m_closeButtonSize) + "px",
        margin: m_borderWidth + "px 0px 0px " + Math.floor((BrowserCapability.IsIE && BrowserCapability.MajorVersion == 6 ? 0.5 : 1) * m_borderWidth) + "px",
        padding: "0px",
        cssFloat: "left",
        styleFloat: "left",
        color: m_titleColor,
        overflow: "hidden"
      }, m_title);
      controlBar.appendChild(windowTitle);

      //create close button
      var closeButton = DocumentUtility.CreateElement("div", {
        title: "Close"
      }, {
        width: m_closeButtonSize + "px",
        height: m_closeButtonSize + "px",
        margin: "0px " + Math.floor((BrowserCapability.IsIE && BrowserCapability.MajorVersion == 6 ? 0.5 : 1) * m_borderWidth) + "px 0px 0px",
        padding: "0px",
        cssFloat: "right",
        styleFloat: "right",
        cursor: "pointer",
        backgroundColor: "#000000",
        backgroundImage: "url('" + (m_closeButtonImage || "about:blank") + "')",
        overflow: "hidden"
      });
      EventUtility.Register(closeButton, "click", function () {
        CloseWindow();
      });
      EventUtility.Register(closeButton, "mouseover", function () {
        this.style.backgroundColor = "#FF0000";
        m_closeButtonOverImage && (this.style.backgroundImage = "url('" + m_closeButtonOverImage + "')");
      });
      EventUtility.Register(closeButton, "mouseout", function () {
        this.style.backgroundColor = "#000000";
        m_closeButtonImage && (this.style.backgroundImage = "url('" + m_closeButtonImage + "')");
      });
      controlBar.appendChild(closeButton);
      controlBar.appendChild(DocumentUtility.CreateClearBoth());

      //create left border
      var leftBorder = DocumentUtility.CreateElement("div", null, {
        width: m_borderWidth + "px",
        height: contentHeight + "px",
        margin: "0px",
        padding: "0px",
        borderStyle: "none",
        cssFloat: "left",
        styleFloat: "left",
        backgroundColor: m_borderColor,
        cursor: "move"
      });
      EventUtility.Register(leftBorder, "mousedown", onMouseDown);
      m_windowElement.appendChild(leftBorder);

      //set content element style      
      m_contentElement.style.margin = "0px";
      m_contentElement.style.padding = "0px";
      m_contentElement.style.position = "static";
      m_contentElement.style.left = "0px";
      m_contentElement.style.right = "0px";
      m_contentElement.style.cssFloat = "left";
      m_contentElement.style.styleFloat = "left";
      m_contentElement.style.display = "block";
      m_contentElement.style.visibility = "visible";
      m_windowElement.appendChild(m_contentElement);

      //create right border
      var rightBorder = DocumentUtility.CreateElement("div", null, {
        width: m_borderWidth + "px",
        height: contentHeight + "px",
        margin: "0px",
        padding: "0px",
        borderStyle: "none",
        cssFloat: "right",
        styleFloat: "left",
        backgroundColor: m_borderColor,
        cursor: "move"
      });
      EventUtility.Register(rightBorder, "mousedown", onMouseDown);
      m_windowElement.appendChild(rightBorder);

      //create bottom border      
      var bottomBorder = DocumentUtility.CreateElement("div", null, {
        width: m_windowWidth + "px",
        height: m_borderWidth + "px",
        margin: "0px",
        padding: "0px",
        borderStyle: "none",
        backgroundColor: m_borderColor,
        cursor: "move"
      });
      EventUtility.Register(bottomBorder, "mousedown", onMouseDown);
      m_windowElement.appendChild(DocumentUtility.CreateClearBoth());
      m_windowElement.appendChild(bottomBorder);
    }

    /*
    * Private Method Description:
    * Mouse down event handler.
    */
    function onMouseDown(e) {
      m_isMouseDown = true;
      m_mouseX = e.clientX;
      m_mouseY = e.clientY;
    }

    /*
    * Private Method Description:
    * Mouse move event handler.
    */
    function onMouseMove(e) {
      if (!m_isMouseDown) {
        return;
      }

      var x = e.clientX;
      var y = e.clientY;
      m_windowElement.style.left = (global.parseInt(m_windowElement.style.left) + x - m_mouseX) + "px";
      m_windowElement.style.top = (global.parseInt(m_windowElement.style.top) + y - m_mouseY) + "px";
      m_mouseX = x;
      m_mouseY = y;
    }

    /*
    * Private Method Description:
    * Mouse up event handler.
    */
    function onMouseUp(e) {
      m_isMouseDown = false;
    }

    /*
    * Private Method Description:
    * Close window.
    *
    * Parameters:
    * returnValue: The return value of this window.
    */
    function CloseWindow(returnValue) {
      EventUtility.Unregister(global.document.body, "mousemove", onMouseMove);
      EventUtility.Unregister(global.document.body, "mouseup", onMouseUp);
      if (m_dialogShade) {
        global.document.body.removeChild(m_dialogShade);
        m_dialogShade = null;
      }
      global.document.body.removeChild(m_windowElement);

      m_closed.Raise(m_returnValue = returnValue);

      if (m_onClosed) {
        m_closed.RemoveListener(m_onClosed);
        m_onClosed = null;
      }
    }

    /*
    * Method Description:
    * Show window.
    *
    * Parameters:
    * onClosed: A callback function when window closed, the return value will pass to it.
    * x: The X coordinate of window position.
    * y: The Y coordinate of window position.
    */
    this.Show = function (onClosed, x, y) {
      m_windowElement.style.left = (global.isNaN(x) ? Math.max(0, Math.floor((BrowserGeometry.GetViewportWidth() - m_windowWidth) / 2)) : (x - 0)) + "px";
      m_windowElement.style.top = (global.isNaN(y) ? Math.max(0, Math.floor((BrowserGeometry.GetViewportHeight() - m_windowHeight) / 4)) : (y - 0)) + "px";
      EventUtility.Register(global.document.body, "mousemove", onMouseMove);
      EventUtility.Register(global.document.body, "mouseup", onMouseUp);
      m_dialogShade && global.document.body.appendChild(m_dialogShade);
      global.document.body.appendChild(m_windowElement);
      m_frameSource && (global.frames[global.frames.length - 1]["CloseDialog"] = CloseWindow);

      DataTypeIdentity.IsFunction(onClosed) && m_closed.AddListener(m_onClosed = onClosed);
      m_shown.Raise();
    };

    /*
    * Method Description:
    * Show window as a modal dialog.
    *
    * Parameters:
    * onClosed: A callback function when window closed, the return value will pass to it.
    */
    this.ShowDialog = function (onClosed) {
      m_dialogShade = DocumentUtility.CreateElement("div", null, {
        width: BrowserGeometry.GetViewportWidth() + "px",
        height: BrowserGeometry.GetViewportHeight() + "px",
        position: "absolute",
        left: "0px",
        top: "0px",
        zIndex: "99",
        backgroundColor: "#888888",
        opacity: "0.5",
        filter: "alpha(opacity=50)"
      });
      this.Show(onClosed);
    };

    /*
    * Method Description:
    * Close window.
    */
    this.Close = function () {
      CloseWindow(m_returnValue);
    };

    /*
    * Method Description:
    * Gets Shown Event.
    */
    this.GetShownEvent = function () {
      return m_shown;
    };

    /*
    * Method Description:
    * Gets Closed Event.
    */
    this.GetClosedEvent = function () {
      return m_closed;
    };

    /*
    * Method Description:
    * Gets return value.
    */
    this.GetReturnValue = function () {
      return m_returnValue;
    };

    /*
    * Method Description:
    * Sets return value.
    */
    this.SetReturnValue = function (returnValue) {
      m_returnValue = returnValue;
    };
  };

  /***************** ListView Class Definition *****************/

  /*
  * Class Description:
  * Represents a list.
  *
  * Constructor Parameters:
  *
  * parameters: The parameters for constructor which is a object as follow:
  * {
  *   Container: The container element of this list. All children of it will be removed.
  *   Header: The header of this list.
  *   Footer: The footer of this list.
  *   ItemTemplate: The item template of this list.
  *   PropertyTemplate: The part in item template which match property template will be replaced by property value. 
  *                     Property template should link this: $property$, "property" is required.
  *   PrimaryKey: A property name, which will be used to generate ID of item element. 
  * }
  */
  var ListView = Xphter.ListView = function (parameters) {
    var m_container = null;
    var m_header = null;
    var m_footer = null;
    var m_itemTemplate = null;
    var m_propertyName = "property";
    var m_propertyTemplate = null;
    var m_primaryKey = null;

    var m_itemIDPrefix = "xphterListView_" + new Date().getTime();
    var m_replaceMap = {};

    Constructor();

    /*
    * Constructor.
    */
    function Constructor() {
      if (!DataTypeIdentity.IsNotNullObject(parameters)) {
        throw new Error("ListView parameters is undefined");
      }
      if (!DataTypeIdentity.IsNotNullObject(parameters.Container)) {
        throw new Error("List container is undefined");
      }
      if (!DataTypeIdentity.IsNotNullObject(parameters.ItemTemplate)) {
        throw new Error("Template of list item is undefined.");
      }
      if (!parameters.PropertyTemplate) {
        throw new Error("Property template of list item is undefined");
      }
      if (!parameters.PrimaryKey) {
        throw new Error("Primary key of list item is undefined");
      }

      //analyze parameters
      (m_container = parameters.Container).id && (m_itemIDPrefix = m_container.id + "_");
      m_header = parameters.Header || null;
      m_footer = parameters.Footer || null;
      m_itemTemplate = parameters.ItemTemplate;
      m_propertyTemplate = parameters.PropertyTemplate + String.Empty;
      m_primaryKey = parameters.PrimaryKey + String.Empty;

      //fill list elements
      DocumentUtility.ClearChildren(m_container);
      m_header && m_container.appendChild(m_header);
      m_footer && m_container.appendChild(m_footer);
    }

    function NewNode(item) {
      var value = null;
      var node = m_itemTemplate.cloneNode(true);
      var innerHTML = node.innerHTML;

      for (var name in item) {
        if (!name || DataTypeIdentity.IsUndefinedOrNull(value = item[name])) {
          continue;
        }

        if (!(name in m_replaceMap)) {
          m_replaceMap[name] = m_propertyTemplate.ReplaceGlobal(m_propertyName, name);
        }
        innerHTML = innerHTML.ReplaceGlobal(m_replaceMap[name], value + String.Empty);
      }
      node.innerHTML = innerHTML;
      node.id = m_itemIDPrefix + item[m_primaryKey];

      return node;
    }

    /*
    * Method Description:
    * Refresh this list with the specified data.
    *
    * Parameters:
    * items: A object array.
    */
    this.Refresh = function (items) {
      if (!DataTypeIdentity.IsArray(items)) {
        throw new Error("items is not a array.");
      }

      DocumentUtility.ClearChildren(m_container);
      m_header && m_container.appendChild(m_header);
      m_footer && m_container.appendChild(m_footer);
      for (var i = 0; i < items.length; i++) {
        m_container.insertBefore(NewNode(items[i]), m_footer);
      }
    };

    /*
    * Method Description:
    * Append the specified item to end of this list.
    *
    * Parameters:
    * item: The object will be appended.
    */
    this.Append = function (item) {
      if (!DataTypeIdentity.IsNotNullObject(item)) {
        return;
      }

      var node = null;
      if (node = $(m_itemIDPrefix + item[m_primaryKey])) {
        m_container.replaceChild(NewNode(item), node);
      } else {
        m_container.insertBefore(NewNode(item), m_footer);
      }
    };

    /*
    * Method Description:
    * Insert the specified item to start of this list.
    *
    * Parameters:
    * item: The object will be inserted.
    */
    this.Insert = function (item) {
      if (!DataTypeIdentity.IsNotNullObject(item)) {
        return;
      }

      var node = null;
      if (node = $(m_itemIDPrefix + item[m_primaryKey])) {
        m_container.replaceChild(NewNode(item), node);
      } else {
        m_container.insertBefore(NewNode(item), m_header ? m_header.nextSibling : m_container.firstChild);
      }
    };

    /*
    * Method Description:
    * Update this list by the specified object.
    *
    * Parameters:
    * item: A object.
    */
    this.Update = function (item) {
      if (!DataTypeIdentity.IsNotNullObject(item)) {
        return;
      }

      var node = $(m_itemIDPrefix + item[m_primaryKey]);
      node && m_container.replaceChild(NewNode(item), node);
    };

    /*
    * Method Description:
    * Remove row in this list which associate with the specified object.
    *
    * Parameters:
    * item: A object.
    */
    this.Remove = function (item) {
      if (!DataTypeIdentity.IsNotNullObject(item)) {
        return;
      }

      var node = $(m_itemIDPrefix + item[m_primaryKey]);
      node && m_container.removeChild(node);
    };
  };

  /***************** ProgressBar Class Definition *****************/

  /*
  * Class Description:
  * Represents a progress bar, progress value is between 0 and 100.
  *
  * Constructor Parameters:
  *
  * parameters: The parameters for constructor which is a object as follow:
  * {
  *   Width: The width of progress bar.
  *   Height: The height of progress bar.
  *   BorderColor: The border color of progress bar.
  *   BarColor: The background color of progress bar.
  *   StripColor: The color of the completed part of this progress bar.
  *   StripImage: The image of the completed part of this progress bar.  
  *   TextColor: The color of progress text.
  * }
  */
  var ProgressBar = Xphter.ProgressBar = function (parameters) {
    var m_width = 0;
    var m_height = 0;
    var m_borderColor = "#777777";
    var m_barColor = "transparent";
    var m_stripColor = "#48D1CC";
    var m_stripImage = null;
    var m_textColor = "#FF0000";

    var m_barElement = null;
    var m_stripElement = null;
    var m_textElement = null;

    var m_currentProgress = 0;

    Constructor();

    /*
    * Constructor.
    */
    function Constructor() {
      if (!DataTypeIdentity.IsNotNullObject(parameters)) {
        throw new Error("Parameters of progress bar is undefined.");
      }

      //analyze parameters
      !global.isNaN(parameters.Width) && (m_width = Math.floor(parameters.Width - 0));
      !global.isNaN(parameters.Height) && (m_height = Math.floor(parameters.Height - 0));
      parameters.BorderColor && (m_borderColor = parameters.BorderColor + String.Empty);
      parameters.BarColor && (m_barColor = parameters.BarColor + String.Empty);
      parameters.StripColor && (m_stripColor = parameters.StripColor + String.Empty);
      parameters.StripImage && (m_stripImage = parameters.StripImage + String.Empty);
      parameters.TextColor && (m_textColor = parameters.TextColor + String.Empty);

      //create elements
      m_stripElement = DocumentUtility.CreateElement("div", null, {
        width: "100%",
        height: "100%",
        margin: "0px",
        padding: "0px",
        borderStyle: "none",
        position: "absolute",
        left: (0 - m_width) + "px",
        top: "0px",
        backgroundColor: m_stripColor,
        backgroundImage: "url(\"" + (m_stripImage || "about:blank") + "\")"
      });

      m_textElement = DocumentUtility.CreateElement("div", null, {
        width: "100%",
        height: "100%",
        margin: "0px",
        padding: "0px",
        borderStyle: "none",
        position: "absolute",
        left: "0px",
        top: "0px",
        backgroundColor: "transparent",
        color: m_textColor,
        fontWeight: "bold",
        textAlign: "center",
        lineHeight: m_height + "px"
      }, "0%");

      m_barElement = DocumentUtility.CreateElement("div", null, {
        width: m_width + "px",
        height: m_height + "px",
        margin: "0px",
        padding: "0px",
        position: "relative",
        left: "0px",
        top: "0px",
        overflow: "hidden",
        borderStyle: "solid",
        borderWidth: "1px",
        borderColor: m_borderColor,
        backgroundColor: m_barColor
      }, [m_stripElement, m_textElement]);
    }

    /*
    * Method Description:
    * Sets the progress value which must between 0 and 100.
    */
    this.SetProgressValue = function (value, text) {
      if (global.isNaN(value)) {
        throw new Error("Progress value is not a number.");
      }

      var _text = (text || String.Empty) + String.Empty;
      m_currentProgress = Math.max(0, Math.min(100, value - 0));
      m_stripElement.style.left = Math.floor((m_currentProgress - 100) / 100 * m_width) + "px";
      m_textElement.innerHTML = _text || (m_currentProgress.toFixed(DataTypeIdentity.IsInteger(m_currentProgress) ? 0 : 2) + "%");
    };

    /*
    * Method Description:
    * Gets current progress value.
    */
    this.GetProgressValue = function () {
      return m_currentProgress;
    };

    /*
    * Method Description:
    * Gets the progress bar HTML element.
    */
    this.GetBarElement = function () {
      return m_barElement;
    };

    /*
    * Method Description:
    * Replaces a existing element with this control.
    * 
    * Parameters:
    * element: The element will be replaced.
    */
    this.Replace = function (element) {
      if (element && element.parentNode) {
        element.parentNode.replaceChild(m_barElement, element);
      }
    };

    /*
    * Method Description:
    * Places this control in the specified element.
    * 
    * Parameters:
    * element: The element will contain this flash control.
    */
    this.Place = function (element) {
      if (element && element.appendChild) {
        element.appendChild(m_barElement);
      }
    };
  };

  /***************** RollingImage Class Definition *****************/

  /*
  * Class Description:
  * Represents a rolling image.
  * It show a big image initially, then it start to become short and show a small image in the end.
  *
  * Constructor Parameters:
  *
  * parameters: The parameters for constructor which is a object as follow:
  * {
  *   Title: The title of these images.
  *   BigImageUri: The big image URI.
  *   SmallImageUri: The small image URI.
  *   Uri: The target uri of big image and small image.
  *   DisplayDuration: The duration of displaying big image in milliseconds.
  *   SlidingDuration: The duration of becoming to small image from big image in milliseconds.
  * }
  */
  var RollingImage = Xphter.RollingImage = function (parameters) {
    var m_title = String.Empty;
    var m_bigImageUri = null;
    var m_smallImageUri = null;
    var m_uri = String.Empty;
    var m_displayDuration = 3000;
    var m_slidingDuration = 1000;

    var m_bigImage = null;
    var m_smallImage = null;
    var m_rollingElement = null;

    var m_bigImageHeight = 0;
    var m_smallImageHeight = 0;

    var m_canSwitch = false;

    Constructor();

    /*
    * Constructor.
    */
    function Constructor() {
      if (!DataTypeIdentity.IsNotNullObject(parameters)) {
        throw new Error("Parameters of RollingImage is undefined.");
      }

      //analyze parameters
      parameters.Title && (m_title = parameters.Title + String.Empty);
      if (!(parameters.BigImageUri && (m_bigImageUri = parameters.BigImageUri + String.Empty))) {
        throw new Error("Big image of RollingImage is undefined.");
      }
      if (!(parameters.SmallImageUri && (m_smallImageUri = parameters.SmallImageUri + String.Empty))) {
        throw new Error("Small image of RollingImage is undefined.");
      }
      parameters.Uri && (m_uri = parameters.Uri + String.Empty);
      !global.isNaN(parameters.DisplayDuration) && (m_displayDuration = (parameters.DisplayDuration - 0 || m_displayDuration));
      !global.isNaN(parameters.SlidingDuration) && (m_slidingDuration = (parameters.SlidingDuration - 0 || m_slidingDuration));

      //create elements
      m_smallImage = DocumentUtility.CreateElement("img", {
        src: m_smallImageUri,
        alt: m_title,
        title: m_title
      });
      EventUtility.Register(m_smallImage, "mouseover", function (e) {
        if (!m_canSwitch) {
          return;
        }

        m_rollingElement.removeChild(m_smallImage);
        m_rollingElement.appendChild(m_bigImage);
      });

      m_bigImage = DocumentUtility.CreateElement("img", {
        src: m_bigImageUri,
        alt: m_title,
        title: m_title
      });
      EventUtility.Register(m_bigImage, "mouseout", function (e) {
        if (!m_canSwitch) {
          return;
        }

        m_rollingElement.removeChild(m_bigImage);
        m_rollingElement.appendChild(m_smallImage);
      });

      m_rollingElement = DocumentUtility.CreateElement("div", null, {
        margin: "0px",
        padding: "0px",
        cursor: "pointer",
        overflow: "hidden"
      }, m_bigImage);
      EventUtility.Register(m_rollingElement, "click", function (e) {
        global.open(m_uri, "_blank");
      });
    }

    /*
    * Roll these images.
    */
    function Roll() {
      if (m_bigImage.height && m_smallImage.height) {
        var speed = 0;
        /**************************************
        * Get image height, in IE9 image height will be zero when remove it from it's parent element.
        ***************************************/
        m_bigImageHeight = m_bigImage.height;
        m_smallImageHeight = m_smallImage.height;
        new Animator(60, m_slidingDuration, function () {
          speed = (m_smallImageHeight - m_bigImageHeight) / m_slidingDuration;
        }, function (elapsedTime) {
          m_rollingElement.style.height = (m_bigImageHeight + Math.floor(speed * elapsedTime)) + "px";
        }, function () {
          m_rollingElement.removeChild(m_bigImage);
          m_rollingElement.appendChild(m_smallImage);
          m_rollingElement.style.height = "auto";
          m_canSwitch = true;
        }).Start();
      } else {
        global.setTimeout(Roll, 1);
      }
    }

    /*
    * Method Description:
    * Start to roll image.
    */
    this.StartRoll = function () {
      global.setTimeout(Roll, m_displayDuration);
    };

    /*
    * Method Description:
    * Gets HTML element of this control.
    */
    this.GetRollingElement = function () {
      return m_rollingElement;
    };

    /*
    * Method Description:
    * Replaces a existing element with this control.
    * 
    * Parameters:
    * element: The element will be replaced.
    */
    this.Replace = function (element) {
      if (element && element.parentNode) {
        element.parentNode.replaceChild(m_rollingElement, element);
      }
    };

    /*
    * Method Description:
    * Places this control in the specified element.
    * 
    * Parameters:
    * element: The element will contain this flash control.
    */
    this.Place = function (element) {
      if (element && element.appendChild) {
        element.appendChild(m_rollingElement);
      }
    };
  };

  /***************** RestrictedTextBox Class Definition *****************/

  /*
  * Class Description:
  * Use regular expression to restrict input of a TextBox.
  * This class can not check none-ascii characters.
  *
  * Constructor Parameters:
  *
  * pattern: The regular expression pattern or a RegExp object.
  * target: The target TextBox element.
  */
  var RestrictedTextBox = Xphter.RestrictedTextBox = function (pattern, target) {
    var m_regex = null;
    var m_target = null;

    Constructor();

    /*
    * Constructor.
    */
    function Constructor() {
      if (!target) {
        throw new Error("Target input text is null.");
      }

      //initialize
      m_regex = (pattern instanceof RegExp) ? pattern : ((pattern + String.Empty) ? new RegExp(pattern + String.Empty, "gi") : null);
      m_target = target;

      //register event handler
      if (m_regex) {
        EventUtility.Register(m_target, "keypress", function (e) {
          var charCode = DataTypeIdentity.IsUndefined(e.charCode) ? e.keyCode : e.charCode;
          if (!charCode || charCode < 32) {
            return;
          }

          m_regex.lastIndex = 0;
          !m_regex.test(m_target.value + String.fromCharCode(charCode)) && e.preventDefault();
        }, false);
      }
    }
  };

  /***************** Calendar Class Definition *****************/

  /*
  * Class Description:
  * Represents a calendar which attach a input text box.
  *
  * Constructor Parameters:
  *
  * target: The target input text box.
  * time: The original time.
  * options: The options for this calendar which is a object as follow:
  * {
  *   IsShowTime: Whether show hour, minute and second info.
  *   IsCloseAfterChoise: Whether close this picker after choising a datetime.
  * }
  */
  var Calendar = Xphter.Calendar = function (target, time, options) {
    //size arguments
    var m_headerHeight = 20;
    var m_arrowWidth = 20;
    var m_tableWidth = 250;
    var m_margin = 10;

    //gloabl color
    var m_backgroundColor = "#F7F7F7";
    var m_borderColor = "#AAAAAA";

    //header options
    var m_headerBackgroundColor = "#E9F1FA";
    var m_headerFontSize = 12;
    var m_headerButtonBorderColor = "#738899";
    var m_headerButtonBackgroundColor = "#FFFFFF";
    var m_headerButtonTextColor = "#738899";

    //week header options
    var m_weekBackgroundColor = "#E9F1FA";
    var m_weekTextColor = "#999999";
    var m_weekFontSize = 12;

    //time table options
    var m_timeBackgroundColor = "#E9F1FA";
    var m_timeTextColor = "#000000";
    var m_timeFontSize = 14;

    //day table options
    var m_dayRowHeight = 25;
    var m_dayTextColor = "#333333";
    var m_dayOtherTextColor = "#777777";
    var m_dayHoverTextColor = "#FF6600";
    var m_daySelectedBackgroundColor = "#FF9900";
    var m_dayFontSize = 14;

    //month table options
    var m_monthRowHeight = 40;
    var m_monthTextColor = "#000000";
    var m_monthHoverTextColor = "#FF6600";
    var m_monthSelectedBackgroundColor = "#FF9900";
    var m_monthFontSize = 14;

    //year table options
    var m_yearRowHeight = 40;
    var m_yearTextColor = "#000000";
    var m_yearHoverTextColor = "#FF6600";
    var m_yearSelectedBackgroundColor = "#FF9900";
    var m_yearFontSize = 14;

    //hour table options
    var m_hourRowHeight = 32;
    var m_hourTextColor = "#000000";
    var m_hourHoverTextColor = "#FF6600";
    var m_hourSelectedBackgroundColor = "#FF9900";
    var m_hourFontSize = 14;

    //minute table options
    var m_minuteRowHeight = 25;
    var m_minuteTextColor = "#000000";
    var m_minuteHoverTextColor = "#FF6600";
    var m_minuteSelectedBackgroundColor = "#FF9900";
    var m_minuteFontSize = 14;

    //second table options
    var m_secondRowHeight = 25;
    var m_secondTextColor = "#000000";
    var m_secondHoverTextColor = "#FF6600";
    var m_secondSelectedBackgroundColor = "#FF9900";
    var m_secondFontSize = 14;

    //HTML elements
    var m_pickerElement = null;
    var m_target = null;
    var m_iframeElement = null;

    var m_dayPanel = null;
    var m_dayHeader = null;
    var m_dayTable = null;
    var m_dayTimeTable = null;

    var m_monthPanel = null;
    var m_monthHeader = null;
    var m_monthTable = null;

    var m_yearPanel = null;
    var m_yearHeader = null;
    var m_yearTable = null;

    var m_hourPanel = null;
    var m_hourHeader = null;
    var m_hourTable = null;

    var m_minutePanel = null;
    var m_minuteHeader = null;
    var m_minuteTable = null;

    var m_secondPanel = null;
    var m_secondHeader = null;
    var m_secondTable = null;

    var m_panels = null;

    //current display time
    var m_time = new Date();

    //selected time
    var m_selectedTime = new Date();

    //datetime picker options
    var m_isShown = false;
    var m_isShowTime = false;
    var m_isCloseAfterChoise = false;

    var m_chineseNumbers = {
      "1": "一",
      "2": "二",
      "3": "三",
      "4": "四",
      "5": "五",
      "6": "六",
      "7": "七",
      "8": "八",
      "9": "九",
      "10": "十",
      "11": "十一",
      "12": "十二"
    };

    Constructor();

    /*
    * Constructor.
    */
    function Constructor() {
      if (!target) {
        throw new Error("Target input text is undefined.");
      }

      //analyze parameters
      m_target = target;
      m_target.readOnly = true;
      if (options) {
        !DataTypeIdentity.IsUndefined(options.IsShowTime) && (m_isShowTime = !!options.IsShowTime);
        !DataTypeIdentity.IsUndefined(options.IsCloseAfterChoise) && (m_isCloseAfterChoise = !!options.IsCloseAfterChoise);
      }

      //create elements of picker
      m_pickerElement = DocumentUtility.CreateElement("div", null, {
        width: m_tableWidth + m_margin * 2 + "px",
        height: "auto",
        margin: "0px",
        padding: "0px",
        borderStyle: "solid",
        borderWidth: "1px",
        borderColor: m_borderColor,
        position: "absolute",
        left: "0px",
        top: "0px",
        zIndex: "99",
        display: "none",
        backgroundColor: m_backgroundColor
      });
      global.document.body.appendChild(m_pickerElement);

      //avoid a bug in IE6: SELECT element cover all DIV elements.
      if (BrowserCapability.IsIE && BrowserCapability.MajorVersion == 6) {
        m_iframeElement = DocumentUtility.CreateElement("iframe", {
          width: (m_tableWidth + m_margin * 2) + String.Empty,
          height: "1",
          frameborder: "0",
          marginwidth: "0",
          marginheight: "0"
        }, {
          margin: "0px",
          padding: "0px",
          position: "absolute",
          left: "0px",
          top: "0px",
          zIndex: "98",
          display: "none",
          backgroundColor: "#FF00FF"
        });
        global.document.body.appendChild(m_iframeElement);
      }

      CreateDayPanel();
      CreateMonthPanel();
      CreateYearPanel();
      CreateHourPanel();
      CreateMinutePanel();
      CreateSecondPanel();
      m_panels = [m_dayPanel, m_monthPanel, m_yearPanel, m_hourPanel, m_minutePanel, m_secondPanel];
      for (var i = 0; i < m_panels.length; i++) {
        m_pickerElement.appendChild(m_panels[i]);
      }

      var buttonNow = null;
      var buttonOK = null;
      var buttonCancel = null;
      m_pickerElement.appendChild(DocumentUtility.CreateElement("div", null, {
        width: m_tableWidth + "px",
        height: "auto",
        margin: "0px " + m_margin + "px",
        padding: "0px"
      }, [buttonNow = DocumentUtility.CreateElement("button", null, {
        margin: "0px",
        marginBottom: m_margin + "px",
        cssFloat: "left",
        styleFloat: "left"
      }, "现在"), buttonCancel = DocumentUtility.CreateElement("button", null, {
        margin: "0px",
        marginBottom: m_margin + "px",
        cssFloat: "right",
        styleFloat: "right"
      }, "取消"), buttonOK = DocumentUtility.CreateElement("button", null, {
        margin: "0px",
        marginRight: m_margin + "px",
        marginBottom: m_margin + "px",
        cssFloat: "right",
        styleFloat: "right"
      }, "确定"), DocumentUtility.CreateClearBoth()]));

      //register event handlers
      EventUtility.Register(buttonNow, "click", function () {
        m_time = new Date();
        if (m_isCloseAfterChoise) {
          Ensure();
        } else {
          RefereshDayPanel();
          ShowPanel(m_dayPanel);
        }
      });
      EventUtility.Register(buttonOK, "click", function () {
        Ensure();
      });
      EventUtility.Register(buttonCancel, "click", function () {
        Close();
      });
      EventUtility.Register(m_target, "click", function () {
        Show();
      }, false);
      EventUtility.Register(global.document.body, "click", function (e) {
        var target = e.target;
        if (target == m_target || target == m_pickerElement || DocumentUtility.IsChildren(target, m_pickerElement)) {
          return;
        }

        Close();
      }, false);

      SetTime(time);
    }

    /*
    * Show the specified panel.
    */
    function ShowPanel(panel) {
      for (var i = 0; i < m_panels.length; i++) {
        m_panels[i].style.display = "none";
      }
      panel.style.display = String.Empty;
    }

    /*
    * Set display time to the specified time.
    */
    function SetTime(time) {
      time = (time instanceof Date ? time : new Date());

      var year = time.getFullYear();
      var month = time.getMonth();
      var date = time.getDate();
      var hour = time.getHours();
      var minute = time.getMinutes();
      var second = time.getSeconds();

      //update time
      m_time = new Date(year, month, date, hour, minute, second);
      m_selectedTime = new Date(year, month, date, hour, minute, second);

      //update target
      var value = m_time.getFullYear() + "-" + (month < 9 ? "0" : String.Empty) + (month + 1) + "-" + (date < 10 ? "0" : String.Empty) + date;
      if (m_isShowTime) {
        value += " " + (hour < 10 ? "0" : String.Empty) + hour + ":" + (minute < 10 ? "0" : String.Empty) + minute + ":" + (second < 10 ? "0" : String.Empty) + second;
      }
      m_target.value = value;

      //update picker element
      if (m_isShown) {
        RefereshDayPanel();
        ShowPanel(m_dayPanel);
      }
    }

    /*
    * Show picker.
    */
    function Show() {
      var x = DocumentUtility.GetElementX(m_target) + BrowserGeometry.GetHorizontalScroll();
      var y = DocumentUtility.GetElementY(m_target) + m_target.offsetHeight + BrowserGeometry.GetVerticalScroll();

      if (m_isShown) {
        if (m_iframeElement) {
          m_iframeElement.style.left = x + "px";
          m_iframeElement.style.top = y + "px";
        }

        m_pickerElement.style.left = x + "px";
        m_pickerElement.style.top = y + "px";
        return;
      }

      m_isShown = true;
      m_time = new Date(m_selectedTime.getFullYear(), m_selectedTime.getMonth(), m_selectedTime.getDate(), m_selectedTime.getHours(), m_selectedTime.getMinutes(), m_selectedTime.getSeconds());
      RefereshDayPanel();
      ShowPanel(m_dayPanel);

      if (m_iframeElement) {
        m_iframeElement.style.left = x + "px";
        m_iframeElement.style.top = y + "px";
        m_iframeElement.style.display = String.Empty;
        global.setTimeout(function () {
          m_iframeElement.height = m_pickerElement.offsetHeight + String.Empty;
        }, 0);
      }

      m_pickerElement.style.left = x + "px";
      m_pickerElement.style.top = y + "px";
      m_pickerElement.style.display = "block";
    }

    /*
    * Close picker.
    */
    function Close() {
      m_isShown = false;
      m_iframeElement && (m_iframeElement.style.display = "none");
      m_pickerElement.style.display = "none";
    }

    /*
    * Close picker and accept current display time.
    */
    function Ensure() {
      Close();
      SetTime(m_time);
    }

    /*
    * Create a table element.
    */
    function CreateTable(rowCount, columnCount, width, rowHeight) {
      var table = DocumentUtility.CreateElement("table", {
        border: "0",
        cellpadding: "0",
        cellspacing: "0"
      }, {
        width: width + "px",
        height: "auto",
        margin: "0px",
        padding: "0px"
      });

      var row = null;
      var cell = null;
      for (var i = 0; i < rowCount; i++) {
        row = table.insertRow(i);
        row.style.height = rowHeight + "px";

        for (var j = 0; j < columnCount; j++) {
          cell = row.insertCell(j);
          cell.align = "center";
          cell.vAlign = "middle";
        }
      }

      return table;
    }

    /*
    * Fill the specified table.
    */
    function FillTable(table, style, items) {
      var index = 0;
      var row = null;
      var cell = null;
      var item = null;

      for (var member in style) {
        table.style[member] = style[member] || String.Empty;
      }
      for (var i = 0; i < table.rows.length; i++) {
        if (index >= items.length) {
          break;
        }

        row = table.rows[i];

        for (var j = 0; j < row.cells.length; j++) {
          if (index >= items.length) {
            break;
          }

          cell = row.cells[j];
          item = items[index++];

          cell.innerHTML = item.text || String.Empty;
          cell.tag = item.tag;
          if (item.style) {
            for (var member in item.style) {
              cell.style[member] = item.style[member] || String.Empty;
            }
          }
          if (item.attributes) {
            for (var member in item.attributes) {
              cell[member] = item.attributes[member] || null;
            }
          }
        }
      }

      return table;
    }

    /*
    * Create the panel used to show days.
    */
    function CreateDayPanel() {
      m_dayPanel = DocumentUtility.CreateElement("div", null, {
        width: m_tableWidth + "px",
        height: "auto",
        margin: m_margin + "px",
        padding: "0px",
        overflow: "hidden"
      });
      m_dayPanel.appendChild(m_dayHeader = CreateTable(1, 4, m_tableWidth, m_headerHeight));
      m_dayPanel.appendChild(FillTable(CreateTable(1, 7, m_tableWidth, m_headerHeight), {
        backgroundColor: m_weekBackgroundColor,
        fontSize: m_weekFontSize + "px",
        color: m_weekTextColor
      }, [{
        text: "一"
      }, {
        text: "二"
      }, {
        text: "三"
      }, {
        text: "四"
      }, {
        text: "五"
      }, {
        text: "六"
      }, {
        text: "日"
      }]));
      m_dayPanel.appendChild(m_dayTable = CreateTable(6, 7, m_tableWidth, m_dayRowHeight));
      m_dayPanel.appendChild(m_dayTimeTable = CreateTable(1, 3, m_tableWidth, m_headerHeight));
      !m_isShowTime && (m_dayTimeTable.style.display = "none");
    }

    /*
    * Referesh day panel.
    */
    function RefereshDayPanel() {
      var days = [];
      var year = m_time.getFullYear();
      var month = m_time.getMonth();
      var date = m_time.getDate();
      var hour = m_time.getHours();
      var minute = m_time.getMinutes();
      var second = m_time.getSeconds();
      var firstDay = new Date(year, month, 1, hour, minute, second);

      //create previous month days
      var previousYear = year - (month == 0 ? 1 : 0);
      var previousMonth = month == 0 ? 11 : (month - 1);
      var previousCount = firstDay.getDay() == 0 ? 6 : firstDay.getDay() - 1;
      var previousMonthDayCount = Date.GetDayCount(previousYear, previousMonth);
      for (var i = previousCount - 1; i >= 0; i--) {
        days.push(new Date(previousYear, previousMonth, previousMonthDayCount - i, hour, minute, second));
      }

      //create current month days
      var currentCount = Date.GetDayCount(year, month);
      for (var i = 0; i < currentCount; i++) {
        days.push(new Date(year, month, i + 1, hour, minute, second));
      }

      //create next month days
      var nextYear = year + (month == 11 ? 1 : 0);
      var nextMonth = month == 11 ? 0 : (month + 1);
      var nextCount = 42 - days.length;
      for (var i = 0; i < nextCount; i++) {
        days.push(new Date(nextYear, nextMonth, i + 1));
      }

      //fill day header
      FillTable(m_dayHeader, {
        backgroundColor: m_headerBackgroundColor,
        fontSize: m_headerFontSize + "px"
      }, [{
        text: "＜",
        style: {
          width: m_arrowWidth + "px",
          borderStyle: "solid",
          borderWidth: "1px",
          borderColor: m_headerButtonBorderColor,
          backgroundColor: m_headerButtonBackgroundColor,
          color: m_headerButtonTextColor,
          fontWeight: "bold",
          cursor: "pointer"
        },
        attributes: {
          title: "上个月",
          onclick: function () {
            var year = m_time.getFullYear();
            var month = m_time.getMonth();
            var date = m_time.getDate();
            m_time = new Date(month == 0 ? year - 1 : year, month == 0 ? 11 : month - 1, 1, m_time.getHours(), m_time.getMinutes(), m_time.getSeconds());
            m_time.setDate(Math.min(Date.GetDayCount(m_time.getFullYear(), m_time.getMonth()), date));

            RefereshDayPanel();
          }
        }
      }, {
        text: (month + 1) + "月",
        style: {
          cursor: "pointer"
        },
        attributes: {
          title: "点击选择月份",
          onclick: function () {
            RefereshMonthPanel();
            ShowPanel(m_monthPanel);
          }
        }
      }, {
        text: year + "年",
        style: {
          cursor: "pointer"
        },
        attributes: {
          title: "点击选择年份",
          onclick: function () {
            var year = m_time.getFullYear();
            RefereshYearPanel(year - 6, year + 5);
            ShowPanel(m_yearPanel);
          }
        }
      }, {
        text: "＞",
        style: {
          width: m_arrowWidth + "px",
          borderStyle: "solid",
          borderWidth: "1px",
          borderColor: m_headerButtonBorderColor,
          backgroundColor: m_headerButtonBackgroundColor,
          color: m_headerButtonTextColor,
          fontWeight: "bold",
          cursor: "pointer"
        },
        attributes: {
          title: "下个月",
          onclick: function () {
            var year = m_time.getFullYear();
            var month = m_time.getMonth();
            var date = m_time.getDate();
            m_time = new Date(month == 11 ? year + 1 : year, month == 11 ? 0 : month + 1, 1, m_time.getHours(), m_time.getMinutes(), m_time.getSeconds());
            m_time.setDate(Math.min(Date.GetDayCount(m_time.getFullYear(), m_time.getMonth()), date));

            RefereshDayPanel();
          }
        }
      }]);

      //fill day table
      var items = [];
      var day = null;
      for (var i = 0; i < days.length; i++) {
        day = days[i];

        items.push({
          text: day.getDate() + String.Empty,
          tag: day,
          style: {
            backgroundColor: i >= previousCount && i < previousCount + currentCount && day.getDate() == date ? m_daySelectedBackgroundColor : String.Empty,
            color: i < previousCount || i >= previousCount + currentCount ? m_dayOtherTextColor : m_dayTextColor,
            fontWeight: i >= previousCount && i < previousCount + currentCount ? "bold" : String.Empty,
            cursor: "default"
          },
          attributes: {
            onmouseover: function () {
              this._outColor = this.style.color;
              this.style.color = m_dayHoverTextColor;
            },
            onmouseout: function () {
              this._outColor && (this.style.color = this._outColor);
            },
            onclick: function () {
              m_time = this.tag;
              if (m_isCloseAfterChoise) {
                Ensure();
              } else {
                var row = null;
                for (var i = 0; i < m_dayTable.rows.length; i++) {
                  row = m_dayTable.rows[i];
                  for (var j = 0; j < row.cells.length; j++) {
                    row.cells[j].style.backgroundColor = String.Empty;
                  }
                }
                this.style.backgroundColor = m_daySelectedBackgroundColor;
              }
            }
          }
        });
      }
      FillTable(m_dayTable, {
        fontSize: m_dayFontSize + "px"
      }, items);

      //fill time header
      FillTable(m_dayTimeTable, {
        backgroundColor: m_timeBackgroundColor,
        color: m_timeTextColor,
        fontSize: m_timeFontSize
      }, [{
        text: hour + "时",
        tag: hour,
        style: {
          cursor: "pointer"
        },
        attributes: {
          title: "点击选择小时",
          onclick: function () {
            RefereshHourPanel();
            ShowPanel(m_hourPanel);
          }
        }
      }, {
        text: minute + "分",
        tag: minute,
        style: {
          cursor: "pointer"
        },
        attributes: {
          title: "点击选择分钟",
          onclick: function () {
            RefereshMinutePanel();
            ShowPanel(m_minutePanel);
          }
        }
      }, {
        text: second + "秒",
        tag: second,
        style: {
          cursor: "pointer"
        },
        attributes: {
          title: "点击选择秒",
          onclick: function () {
            RefereshSecondPanel();
            ShowPanel(m_secondPanel);
          }
        }
      }]);
    }

    /*
    * Create the panel used to show months.
    */
    function CreateMonthPanel() {
      m_monthPanel = DocumentUtility.CreateElement("div", null, {
        width: m_tableWidth + "px",
        height: "auto",
        margin: m_margin + "px",
        padding: "0px",
        overflow: "hidden"
      });
      m_monthPanel.appendChild(m_monthHeader = CreateTable(1, 3, m_tableWidth, m_headerHeight));
      m_monthPanel.appendChild(m_monthTable = CreateTable(3, 4, m_tableWidth, m_monthRowHeight));
    }

    /*
    * Referesh month panel.
    */
    function RefereshMonthPanel() {
      var month = m_time.getMonth();

      //fill month header
      FillTable(m_monthHeader, {
        backgroundColor: m_headerBackgroundColor,
        fontSize: m_headerFontSize + "px"
      }, [{
        text: "＜",
        style: {
          width: m_arrowWidth + "px",
          borderStyle: "solid",
          borderWidth: "1px",
          borderColor: m_headerButtonBorderColor,
          backgroundColor: m_headerButtonBackgroundColor,
          color: m_headerButtonTextColor,
          fontWeight: "bold",
          cursor: "pointer"
        },
        attributes: {
          title: "上个月",
          onclick: function () {
            var year = m_time.getFullYear();
            var month = m_time.getMonth();
            var date = m_time.getDate();
            if (month > 0) {
              m_time = new Date(year, month - 1, 1, m_time.getHours(), m_time.getMinutes(), m_time.getSeconds());
              m_time.setDate(Math.min(Date.GetDayCount(year, month - 1), date));

              RefereshMonthPanel();
            }
          }
        }
      }, {
        text: m_chineseNumbers[month + 1] + "月",
        tag: month,
        style: {
          cursor: "pointer"
        },
        attributes: {
          title: "点击选择日期",
          onclick: function () {
            RefereshDayPanel();
            ShowPanel(m_dayPanel);
          }
        }
      }, {
        text: "＞",
        style: {
          width: m_arrowWidth + "px",
          borderStyle: "solid",
          borderWidth: "1px",
          borderColor: m_headerButtonBorderColor,
          backgroundColor: m_headerButtonBackgroundColor,
          color: m_headerButtonTextColor,
          fontWeight: "bold",
          cursor: "pointer"
        },
        attributes: {
          title: "下个月",
          onclick: function () {
            var year = m_time.getFullYear();
            var month = m_time.getMonth();
            var date = m_time.getDate();
            if (month < 11) {
              m_time = new Date(year, month + 1, 1, m_time.getHours(), m_time.getMinutes(), m_time.getSeconds());
              m_time.setDate(Math.min(Date.GetDayCount(year, month + 1), date));

              RefereshMonthPanel();
            }
          }
        }
      }]);

      //fill month table
      var items = [];
      for (var i = 0; i < 12; i++) {
        items.push({
          text: m_chineseNumbers[i + 1] + "月",
          tag: i,
          style: {
            backgroundColor: i == month ? m_monthSelectedBackgroundColor : String.Empty,
            color: m_monthTextColor,
            cursor: "default"
          },
          attributes: {
            onmouseover: function () {
              this._outColor = this.style.color;
              this.style.color = m_monthHoverTextColor;
            },
            onmouseout: function () {
              this._outColor && (this.style.color = this._outColor);
            },
            onclick: function () {
              m_time = new Date(m_time.getFullYear(), this.tag, Math.min(m_time.getDate(), Date.GetDayCount(m_time.getFullYear(), this.tag)), m_time.getHours(), m_time.getMinutes(), m_time.getSeconds());
              RefereshDayPanel();
              ShowPanel(m_dayPanel);
            }
          }
        });
      }
      FillTable(m_monthTable, {
        fontSize: m_monthFontSize + "px"
      }, items);
    }

    /*
    * Create the panel used to show years.
    */
    function CreateYearPanel() {
      m_yearPanel = DocumentUtility.CreateElement("div", null, {
        width: m_tableWidth + "px",
        height: "auto",
        margin: m_margin + "px",
        padding: "0px",
        overflow: "hidden"
      });
      m_yearPanel.appendChild(m_yearHeader = CreateTable(1, 3, m_tableWidth, m_headerHeight));
      m_yearPanel.appendChild(m_yearTable = CreateTable(3, 4, m_tableWidth, m_yearRowHeight));
    }

    /*
    * Referesh year panel.
    */
    function RefereshYearPanel(minYear, maxYear) {
      var year = m_time.getFullYear();

      //fill year header
      FillTable(m_yearHeader, {
        backgroundColor: m_headerBackgroundColor,
        fontSize: m_headerFontSize + "px"
      }, [{
        text: "＜",
        style: {
          width: m_arrowWidth + "px",
          borderStyle: "solid",
          borderWidth: "1px",
          borderColor: m_headerButtonBorderColor,
          backgroundColor: m_headerButtonBackgroundColor,
          color: m_headerButtonTextColor,
          fontWeight: "bold",
          cursor: "pointer"
        },
        attributes: {
          title: "向前",
          onclick: function () {
            RefereshYearPanel(minYear - 12, maxYear - 12);
          }
        }
      }, {
        text: minYear + " - " + maxYear,
        style: {
          cursor: "pointer"
        },
        attributes: {
          title: "点击选择日期",
          onclick: function () {
            ShowPanel(m_dayPanel);
          }
        }
      }, {
        text: "＞",
        style: {
          width: m_arrowWidth + "px",
          borderStyle: "solid",
          borderWidth: "1px",
          borderColor: m_headerButtonBorderColor,
          backgroundColor: m_headerButtonBackgroundColor,
          color: m_headerButtonTextColor,
          fontWeight: "bold",
          cursor: "pointer"
        },
        attributes: {
          title: "向后",
          onclick: function () {
            RefereshYearPanel(minYear + 12, maxYear + 12);
          }
        }
      }]);

      //fill year table
      var items = [];
      for (var i = minYear; i <= maxYear; i++) {
        items.push({
          text: i + String.Empty,
          tag: i,
          style: {
            backgroundColor: i == year ? m_yearSelectedBackgroundColor : String.Empty,
            color: m_yearTextColor,
            cursor: "default"
          },
          attributes: {
            onmouseover: function () {
              this._outColor = this.style.color;
              this.style.color = m_monthHoverTextColor;
            },
            onmouseout: function () {
              this._outColor && (this.style.color = this._outColor);
            },
            onclick: function () {
              m_time = new Date(this.tag, m_time.getMonth(), Math.min(Date.GetDayCount(this.tag, m_time.getMonth()), m_time.getDate()), m_time.getHours(), m_time.getMinutes(), m_time.getSeconds());
              RefereshDayPanel();
              ShowPanel(m_dayPanel);
            }
          }
        });
      }
      FillTable(m_yearTable, {
        fontSize: m_yearFontSize + "px"
      }, items);
    }

    /*
    * Create the panel used to show hours.
    */
    function CreateHourPanel() {
      m_hourPanel = DocumentUtility.CreateElement("div", null, {
        width: m_tableWidth + "px",
        height: "auto",
        margin: m_margin + "px",
        padding: "0px",
        overflow: "hidden"
      });
      m_hourPanel.appendChild(m_hourHeader = CreateTable(1, 3, m_tableWidth, m_headerHeight));
      m_hourPanel.appendChild(m_hourTable = CreateTable(4, 6, m_tableWidth, m_hourRowHeight));
    }

    /*
    * Referesh hour panel.
    */
    function RefereshHourPanel() {
      var hour = m_time.getHours();

      //fill hour header
      FillTable(m_hourHeader, {
        backgroundColor: m_headerBackgroundColor,
        fontSize: m_headerFontSize + "px"
      }, [{
        text: "＜",
        style: {
          width: m_arrowWidth + "px",
          borderStyle: "solid",
          borderWidth: "1px",
          borderColor: m_headerButtonBorderColor,
          backgroundColor: m_headerButtonBackgroundColor,
          color: m_headerButtonTextColor,
          fontWeight: "bold",
          cursor: "pointer"
        },
        attributes: {
          title: "上一小时",
          onclick: function () {
            var hour = m_time.getHours();
            if (hour > 0) {
              m_time = new Date(m_time.getFullYear(), m_time.getMonth(), m_time.getDate(), hour - 1, m_time.getMinutes(), m_time.getSeconds());
              RefereshHourPanel();
            }
          }
        }
      }, {
        text: hour + "时",
        tag: hour,
        style: {
          cursor: "pointer"
        },
        attributes: {
          title: "点击选择日期",
          onclick: function () {
            RefereshDayPanel();
            ShowPanel(m_dayPanel);
          }
        }
      }, {
        text: "＞",
        style: {
          width: m_arrowWidth + "px",
          borderStyle: "solid",
          borderWidth: "1px",
          borderColor: m_headerButtonBorderColor,
          backgroundColor: m_headerButtonBackgroundColor,
          color: m_headerButtonTextColor,
          fontWeight: "bold",
          cursor: "pointer"
        },
        attributes: {
          title: "下一小时",
          onclick: function () {
            var hour = m_time.getHours();
            if (hour < 23) {
              m_time = new Date(m_time.getFullYear(), m_time.getMonth(), m_time.getDate(), hour + 1, m_time.getMinutes(), m_time.getSeconds());
              RefereshHourPanel();
            }
          }
        }
      }]);

      //fill hour table
      var items = [];
      for (var i = 0; i < 24; i++) {
        items.push({
          text: i + String.Empty,
          tag: i,
          style: {
            backgroundColor: i == hour ? m_hourSelectedBackgroundColor : String.Empty,
            color: m_hourTextColor,
            cursor: "default"
          },
          attributes: {
            onmouseover: function () {
              this._outColor = this.style.color;
              this.style.color = m_monthHoverTextColor;
            },
            onmouseout: function () {
              this._outColor && (this.style.color = this._outColor);
            },
            onclick: function () {
              m_time = new Date(m_time.getFullYear(), m_time.getMonth(), m_time.getDate(), this.tag, m_time.getMinutes(), m_time.getSeconds());
              RefereshDayPanel();
              ShowPanel(m_dayPanel);
            }
          }
        });
      }
      FillTable(m_hourTable, {
        fontSize: m_hourFontSize + "px"
      }, items);
    }

    /*
    * Create the panel used to show minutes.
    */
    function CreateMinutePanel() {
      m_minutePanel = DocumentUtility.CreateElement("div", null, {
        width: m_tableWidth + "px",
        height: "auto",
        margin: m_margin + "px",
        padding: "0px",
        overflow: "hidden"
      });
      m_minutePanel.appendChild(m_minuteHeader = CreateTable(1, 3, m_tableWidth, m_headerHeight));
      m_minutePanel.appendChild(m_minuteTable = CreateTable(6, 10, m_tableWidth, m_minuteRowHeight));
    }

    /*
    * Referesh minute panel.
    */
    function RefereshMinutePanel() {
      var minute = m_time.getMinutes();

      //fill minute header
      FillTable(m_minuteHeader, {
        backgroundColor: m_headerBackgroundColor,
        fontSize: m_headerFontSize + "px"
      }, [{
        text: "＜",
        style: {
          width: m_arrowWidth + "px",
          borderStyle: "solid",
          borderWidth: "1px",
          borderColor: m_headerButtonBorderColor,
          backgroundColor: m_headerButtonBackgroundColor,
          color: m_headerButtonTextColor,
          fontWeight: "bold",
          cursor: "pointer"
        },
        attributes: {
          title: "上一分钟",
          onclick: function () {
            var minute = m_time.getMinutes();
            if (minute > 0) {
              m_time = new Date(m_time.getFullYear(), m_time.getMonth(), m_time.getDate(), m_time.getHours(), minute - 1, m_time.getSeconds());
              RefereshMinutePanel();
            }
          }
        }
      }, {
        text: minute + "分",
        tag: minute,
        style: {
          cursor: "pointer"
        },
        attributes: {
          title: "点击选择日期",
          onclick: function () {
            RefereshDayPanel();
            ShowPanel(m_dayPanel);
          }
        }
      }, {
        text: "＞",
        style: {
          width: m_arrowWidth + "px",
          borderStyle: "solid",
          borderWidth: "1px",
          borderColor: m_headerButtonBorderColor,
          backgroundColor: m_headerButtonBackgroundColor,
          color: m_headerButtonTextColor,
          fontWeight: "bold",
          cursor: "pointer"
        },
        attributes: {
          title: "下一分钟",
          onclick: function () {
            var minute = m_time.getMinutes();
            if (minute < 59) {
              m_time = new Date(m_time.getFullYear(), m_time.getMonth(), m_time.getDate(), m_time.getHours(), minute + 1, m_time.getSeconds());
              RefereshMinutePanel();
            }
          }
        }
      }]);

      //fill minute table
      var items = [];
      for (var i = 0; i < 60; i++) {
        items.push({
          text: i + String.Empty,
          tag: i,
          style: {
            backgroundColor: i == minute ? m_minuteSelectedBackgroundColor : String.Empty,
            color: m_minuteTextColor,
            cursor: "default"
          },
          attributes: {
            onmouseover: function () {
              this._outColor = this.style.color;
              this.style.color = m_monthHoverTextColor;
            },
            onmouseout: function () {
              this._outColor && (this.style.color = this._outColor);
            },
            onclick: function () {
              m_time = new Date(m_time.getFullYear(), m_time.getMonth(), m_time.getDate(), m_time.getHours(), this.tag, m_time.getSeconds());
              RefereshDayPanel();
              ShowPanel(m_dayPanel);
            }
          }
        });
      }
      FillTable(m_minuteTable, {
        fontSize: m_minuteFontSize + "px"
      }, items);
    }

    /*
    * Create the panel used to show seconds.
    */
    function CreateSecondPanel() {
      m_secondPanel = DocumentUtility.CreateElement("div", null, {
        width: m_tableWidth + "px",
        height: "auto",
        margin: m_margin + "px",
        padding: "0px",
        overflow: "hidden"
      });
      m_secondPanel.appendChild(m_secondHeader = CreateTable(1, 3, m_tableWidth, m_headerHeight));
      m_secondPanel.appendChild(m_secondTable = CreateTable(6, 10, m_tableWidth, m_secondRowHeight));
    }

    /*
    * Referesh second panel.
    */
    function RefereshSecondPanel() {
      var second = m_time.getSeconds();

      //fill second header
      FillTable(m_secondHeader, {
        backgroundColor: m_headerBackgroundColor,
        fontSize: m_headerFontSize + "px"
      }, [{
        text: "＜",
        style: {
          width: m_arrowWidth + "px",
          borderStyle: "solid",
          borderWidth: "1px",
          borderColor: m_headerButtonBorderColor,
          backgroundColor: m_headerButtonBackgroundColor,
          color: m_headerButtonTextColor,
          fontWeight: "bold",
          cursor: "pointer"
        },
        attributes: {
          title: "上一秒",
          onclick: function () {
            var second = m_time.getSeconds();
            if (second > 0) {
              m_time = new Date(m_time.getFullYear(), m_time.getMonth(), m_time.getDate(), m_time.getHours(), m_time.getMinutes(), second - 1);
              RefereshSecondPanel();
            }
          }
        }
      }, {
        text: second + "秒",
        tag: second,
        style: {
          cursor: "pointer"
        },
        attributes: {
          title: "点击选择日期",
          onclick: function () {
            RefereshDayPanel();
            ShowPanel(m_dayPanel);
          }
        }
      }, {
        text: "＞",
        style: {
          width: m_arrowWidth + "px",
          borderStyle: "solid",
          borderWidth: "1px",
          borderColor: m_headerButtonBorderColor,
          backgroundColor: m_headerButtonBackgroundColor,
          color: m_headerButtonTextColor,
          fontWeight: "bold",
          cursor: "pointer"
        },
        attributes: {
          title: "下一秒",
          onclick: function () {
            var second = m_time.getSeconds();
            if (second < 59) {
              m_time = new Date(m_time.getFullYear(), m_time.getMonth(), m_time.getDate(), m_time.getHours(), m_time.getMinutes(), second + 1);
              RefereshSecondPanel();
            }
          }
        }
      }]);

      //fill second table
      var items = [];
      for (var i = 0; i < 60; i++) {
        items.push({
          text: i + String.Empty,
          tag: i,
          style: {
            backgroundColor: i == second ? m_secondSelectedBackgroundColor : String.Empty,
            color: m_secondTextColor,
            cursor: "default"
          },
          attributes: {
            onmouseover: function () {
              this._outColor = this.style.color;
              this.style.color = m_monthHoverTextColor;
            },
            onmouseout: function () {
              this._outColor && (this.style.color = this._outColor);
            },
            onclick: function () {
              m_time = new Date(m_time.getFullYear(), m_time.getMonth(), m_time.getDate(), m_time.getHours(), m_time.getMinutes(), this.tag);
              RefereshDayPanel();
              ShowPanel(m_dayPanel);
            }
          }
        });
      }
      FillTable(m_secondTable, {
        fontSize: m_secondFontSize + "px"
      }, items);
    }

    /*
    * Show the datetime picker.
    */
    this.Show = Show;

    /*
    * Hide the datetime picker.
    */
    this.Close = Close;

    /*
    * Hide the datetime picker and accept the display time.
    */
    this.Ensure = Ensure;

    /*
    * Set current selected time.
    */
    this.SetTime = SetTime;
  };
})(window);
