
/**
 *
 * Script:
 *   jui.js
 *   JUI(JavaScript User Interface) JavaScript Library v1.0.0
 *
 * Version: 
 *   1.0.0
 *
 * License:
 *	MIT-style license.
 *
 * Author:
 *   xushengs@gmail.com
 *   http://fdream.net/
 *
 * Thanks to:
 *   Yahoo! YUI Team & contributors,
 *   Valerio Proietti & MooTools contributors, 
 *   John Resig & jQuery contributors,
 *
 * */

(function() {

    var 
    window = this,
    // Map over JUI in case of overwrite
	_JUI = window.JUI,
    // Map over the $ in case of overwrite
	_$ = window.$,
    // global uid
	_uid = 1,
    // loaded modules
    _modules = {},
    // modules and scripts url
	_modList = {
	    'browser': 'browser.js',
	    'element': 'element.js',
	    'selector': 'selector.js',
	    'string': 'string.js',
	    'array': 'array.js'
	},

    $ = JUI = window.JUI = window.$ = function(selector, context) {
        ///<summary>
        /// 根据选择器获取元素
        ///</summary>
        ///<param name="selector" type="String">选择器</param>
        ///<param name="context" type="$.Element">要查找的上下文</param>
        ///<returns type="$.Element" />
        if (_modules['element']) {
            return new $.Element(selector);
        }

        return document.getElementById(selector);
    };

    var Native = {
        initialize: function(options) {
            options = options || {};
            var initialize = options.initialize;
            var legacy = options.legacy;
            var name = options.name || JUI.name;
            var object = initialize || legacy;
            var protect = options.protect;
            var afterImplement = options.afterImplement || function() { };

            object.constructor = this.initialize;
            object.$family = name.toLowerCase();
            if (legacy && initialize) object.prototype = legacy.prototype;
            object.prototype.constructor = object;
            object.prototype.$family = object.$family;

            var add = function(obj, name, method, force) {
                if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
                afterImplement.call(obj, name, method);
                return obj;
            };

            object.alias = function(a1, a2, a3) {
                if (typeof a1 == 'string') {
                    if ((a1 = this.prototype[a1])) return add(this, a2, a1, a3);
                }
                for (var a in a1) this.alias(a, a1[a], a2);
                return this;
            };

            object.genericize = function(a1, a2) {
                if (typeof a1 == 'string') {
                    if ((!a2 || !this[a1]) && typeof this.prototype[a1] == 'function') this[a1] = function() {
                        var args = Array.prototype.slice.call(arguments);
                        return this.prototype[a1].apply(args.shift(), args);
                    };
                    return;
                }
                for (var i = 0; i < a1.length; i++) this.genericize(a1[i], a2);
                return this;
            };

            object.implement = function(a1, a2, a3) {
                if (typeof a1 == 'string') return add(this, a1, a2, a3);
                for (var p in a1) add(this, p, a1[p], a2);
                return this;
            };
        },

        genericize: function(object, properties) {
            object && object.genericize(properties);
        },

        implement: function(objects, properties) {
            for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
        }
    };

    $.Native = Native;

    (function() {
        var natives = { 'Array': Array, 'Boolean': Boolean, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String, 'JUI': $ };
        for (var n in natives) Native.initialize({ name: n, initialize: natives[n], protect: true });

        var generics = {
            'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"],
            'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"]
        };
        for (var g in generics) {
            for (var i = generics[g].length; i--; ) Native.genericize(window[g], generics[g]);
        }
    })();

    $.name = 'jui';         // name of framework
    $.version = '1.0.0.0';  // current version of framework
    $.expando = '_JUI_' + new Date, // name of uid property

    /**
    * return type of an object
    * 
    * @obj
    *    the object you want to do type test
    * */
    $.type = function(obj) {
        if (obj == undefined) return false;
        if (obj.$family) return (obj.$family == 'number' && !isFinite(obj)) ? false : obj.$family;
        if (obj.nodeName) {
            switch (obj.nodeType) {
                case 1: return 'element';
                case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
            }
        } else if (typeof obj.length == 'number') {
            if (obj.callee) return 'arguments';
            else if (obj.item) return 'collection';
        }
        return typeof obj;

        //return obj.$family ? obj.$family : typeof obj;
    };

    /**
    * just a empty function
    *
    * */
    $.empty = function() { };

    /**
    * to resolve confilict problems
    *
    *  @deep:
    *    true to resolve JUI confilict
    * */
    $.noConfilict = function() {
        window.$ = _$;

        return this;
    };

    /*
    * 类继承的实现
    * */
    $.extend = function(child, parent) {
        if (!parent) {
            throw 'Failed! Inherit from a null object';
        }

        var pp = parent.prototype,
            F = function() { };
        F.prototype = pp;
        var ext = new F();
        child.prototype = ext;
        ext.constructor = child;
        child.superclass = pp;

        // 如果没有构造函数，则指定一个
        if (parent != Object && pp.constructor == Object.prototype.constructor) {
            pp.constructor = parent;
        }

        return child;
    };

    /**
    * register loaded modules
    *
    *  @module:
    *     module name
    *  @version:
    *     module version
    * */
    $.register = function(module, version) {
        _modules[module] = version;
    }

    /**
    * load external script
    *
    *  @url:
    *     script url
    * */
    function loadScripts(url) {
        var script = document.createElement("script");
        script.type = "text/javascript";
        script.src = url;
        document.getElementsByTagName("head")[0].appendChild(script);
    }

    /**
    * load script of specific module 
    *
    * @modules:
    *   module name or an array list contains names of modules
    * */
    $.requires = function() {
        var i = 0, mod;
        while (mod = arguments[i++]) {
            (!_modules[mod]) && loadScripts(_modList[mod]);
        }
    };

    /**
    * judge if the module is loaded
    *
    * @module:
    *   module name
    * */
    $.loaded = function(module) {
        return _modules[module] !== null;
    };

    /**
    * return current timestamp
    *
    * */
    $.now = function() {
        return +new Date;
    };

    /**
    * return a global unique id of an element
    *
    * */
    $.getUid = (window.ActiveXObject) ? function(node) {
        return (node[$.expando] || (node[$.expando] = [_uid++]))[0];
    } : function(node) {
        return node[$.expando] || (node[$.expando] = _uid++);
    };

})();
/*
* Author:
*   xushengs@gmail.com
*   http://fdream.net/
* */
(function($) {
    // add to loaded module-list
    $.register('selector', '1.0.0.0');

    /**
    *
    * whiz.js - A fast CSS selector engine and matcher
    *
    * Version:
    *    1.0.0 Preview
    *
    * Licence:
    *    MIT License
    *
    * Author: 
    *    xushengs@gmail.com
    *    http://fdream.net
    *
    * Thanks to:
    *   Yahoo! YUI Team & contributors,
    *   Valerio Proietti & MooTools contributors, 
    *   John Resig & jQuery/Sizzle contributors,
    *   Harald Kirschner & Sly contributors,
    *   Thomas Aylott & Slick contributors
    *
    * */

    var Whizz = (function() {
        var //uid = 1,            // global uid of nodes
        current = {},       // current found
        support = {},       // features detection
        parsedCache = {},   // cache parsed selectors
        attributeAlias = {  // attribute names
            'class': 'className'
        },

        // these regular expressions are from YUI
        // tag: /^((?:-?[_a-z][\w-]*)|\*)/i, // tag must be the first, or it will be *
        // id: /^#([\w-]+)/,    // id starts with #
        // class: /^\.([\w-]+)/
        // attribute: /^\[([a-z]+\w*)+([~\|\^\$\*!]?=)?['"]?([^\]]*?)['"]?\]/i,
        // pseudo: /^:([\-\w]+)(?:\(['"]?(.+?)["']?\))*/ //,
        // combinator: /^\s*((?:[>+~\s,])|$)\s*/    // comma for multi-selectors
        // nth: /^(?:(?:([-]?\d*)(n{1}))?([-+]?\d*)|(odd|even))$/, // supports an+b, b, an, odd, even

        /**
        * large regular expression, match all types
        * match list:
        *  ----------------
        *  tag:        m[1]
        *  ----------------
        *  id:         m[2]
        *  ----------------
        *  class:      m[3]
        *  ----------------
        *  attribute:  m[4]
        *  operator:   m[5]
        *  value:      m[6]
        *  ----------------
        *  pseudo:     m[7]
        *  expression: m[8]
        *  ----------------
        *  combinator: m[9]
        *  ----------------
        *  
        * */
        nthRE = /^(?:(?:([-]?\d*)(n{1}))?([-+]?\d*)|(odd|even))$/, // supports an+b, b, an, odd, even
        re = /((?:[_a-zA-Z][\w-]*)|\*)|(?:#([\w-]+))|(?:\.([\w-]+))|(?:\[([a-z]+\w*)+([~\|\^\$\*!]?=)?['"]?([^\]]*?)["']?\])|(?::([\-\w]+)(?:\(['"]?(.+?)["']?\))*)|(?:\s*((?:[>+~\s,])|$)\s*)/g;


        // check features
        (function() {
            // Our guinea pig
            var testee = document.createElement('div'), id = (new Date()).getTime();
            testee.innerHTML = '<a name="' + id + '" class="€ b"></a>';

            // Safari can't handle uppercase or unicode characters when in quirks mode.
            support.qsa = !!(testee.querySelectorAll && testee.querySelectorAll('.€').length);
        })();

        // get unique id
//        var getUid = (window.ActiveXObject) ? function(node) {
//            return (node[$.expando] || (node[$.expando] = [$.getUid()]))[0];
//        } : function(node) {
//            return node[$.expando] || (node[$.expando] = $.getUid());
//        };

        // locate current found
        function locateCurrent(node) {
            var uid = $.getUid(node);
            return (current[uid]) ? null : (current[uid] = true);
        };

        // locate fast
        function locateFast(node) {
            return true;
        }

        // escape regular expressions
        function escapeRegExp(text) {
            return text.replace(/[-.*+?^${}()|[\]\/\\]/g, '\\$&');
        }

        // create a parsed selector
        function create(combinator) {
            return {
                combinator: combinator || ' ',
                tag: '*',
                id: null,
                classes: [],
                attributes: [],
                pseudos: []
            }
        }

        // parse a selector
        function parse(s) {
            if (parsedCache[s]) {
                return parsedCache[s];
            }

            var selectors = [], sentence = [], parsed, match, combinator,
            sni = sli = ci = ai = pi = 0;

            parsed = create();
            re.lastIndex = 0;
            while (match = re.exec(s)) {
                // tag
                if (match[1]) {
                    parsed.tag = match[1].toUpperCase();
                }
                // id
                else if (match[2]) {
                    parsed.id = match[2];
                }
                // classes
                else if (match[3]) {
                    parsed.classes[ci++] = match[3];
                }
                // attributes
                else if (match[4]) {
                    parsed.attributes[ai++] = { key: match[4], op: match[5], value: match[6] };
                }
                // pseudos
                else if (match[7]) {
                    parsed.pseudos[pi++] = { key: match[7], value: match[8] };
                }
                // combinators
                else if (match[9]) {
                    sentence[sni++] = parsed;

                    if (match[9] == ',') {
                        selectors[sli++] = sentence;
                        sentence = [];
                        sni = 0;
                        combinator = null;
                    }
                    else {
                        combinator = match[9];
                    }

                    parsed = create(combinator);
                    ci = ai = pi = 0;
                }
                else {
                    break;
                }
            }

            sentence[sni++] = parsed;
            selectors[sli++] = sentence;

            return parsedCache[s] = selectors;
        }

        // combine by tag
        var combineByTag = {
            ' ': function(tag, ctx, ret, locate) {
                var nodes, n, i = 0, len = ret.length;
                nodes = ctx.getElementsByTagName(tag);
                if (locate) {
                    while (n = nodes[i++]) {
                        n.nodeType == 1 && locate(n) && (ret[len++] = n);
                    }
                }
                else {
                    while (n = nodes[i++]) {
                        n.nodeType == 1 && (ret[len++] = n);
                    }
                }

                return ret;
            },
            '>': function(tag, ctx, ret) {
                var nodes, n, i = 0, len = ret.length;
                nodes = ctx.getElementsByTagName(tag);

                while (n = nodes[i++]) {
                    n.parentNode == ctx && (ret[len++] = n);
                }

                return ret;
            },
            '+': function(tag, ctx, ret, locate) {
                var len = ret.length;
                while (ctx = ctx.nextSibling) {
                    if (ctx.nodeType == 1) {
                        ctx.tagName == tag && locate(ctx) && (ret[len++] = ctx);
                        break;
                    }
                }

                return ret;
            },
            '~': function(tag, ctx, ret, locate) {
                var len = ret.length;
                while (ctx = ctx.nextSibling) {
                    if (ctx.nodeType == 1) {
                        if (!locate(ctx)) {
                            break;
                        }
                        ctx.tagName == tag && (ret[len++] = ctx);
                    }
                }

                return ret;
            }
        };

        // combine by id
        var combineById = {
            ' ': function(node, cxt) {
                while (node = node.parentNode) {
                    if (node == cxt) {
                        return true;
                    }
                }

                return false;
            },

            '>': function(node, cxt) {
                return node.parentNode == cxt;
            },

            '+': function(node, cxt) {
                while (node = node.previousSibling) {
                    if (node.nodeType != 1) {
                        continue;
                    }
                    if (node == cxt) {
                        return true;
                    }
                    else if (node.tagName == node.tagName) {
                        return false;
                    }
                }
                return false;
            },

            '~': function(node, cxt) {
                while (n = n.previousSibling) {
                    if (n == cxt) {
                        return true;
                    }
                }

                return false;
            }
        };

        var attributeRE = {
            '=': function(val) {
                return val;
            },
            '~=': function(val) {
                return new RegExp('(?:^|\\s+)' + escapeRegExp(val) + '(?:\\s+|$)');
            },
            '!=': function(val) {
                return val;
            },
            '^=': function(val) {
                return new RegExp('^' + escapeRegExp(val));
            },
            '$=': function(val) {
                return new RegExp(escapeRegExp(val) + '$');
            },
            '*=': function(val) {
                return new RegExp(escapeRegExp(val));
            },
            '|=': function(val) {
                return new RegExp('^' + escapeRegExp(val) + '-?');
            }
        };

        // attribute filters
        var attribute = {
            '=': function(attr, val) {
                // value is equal to val
                return attr == val;
            },
            '~=': function(attr, val) {
                // value is seperated by space, one of them is equal to val
                return val.test(attr);
            },
            '!=': function(attr, val) {
                // value is not equal to val
                return attr != val;
            },
            '^=': function(attr, val) {
                // value is started with val
                return val.test(attr);
            },
            '$=': function(attr, val) {
                // value is ended with val
                return val.test(attr);
            },
            '*=': function(attr, val) {
                // value contains val(string)
                return val.test(attr);
            },
            '|=': function(attr, val) {
                // value is seperated by hyphen, one of them is started with val
                // optional hyphen-delimited
                return val.test(attr);
            }
        };

        // cache parsed nth expression and nth nodes
        var nthCache = {}, nthNodesCache = {};

        // parse nth expression
        function parseNth(expr) {
            if (nthCache[expr]) {
                return nthCache[expr];
            }

            var m, a, b;

            m = expr.match(nthRE);
            switch (m[4]) {
                case 'even':
                    a = 2;
                    b = 0;
                    break;
                case 'odd':
                    a = 2;
                    b = 1;
                    break;
                default:
                    a = parseInt(m[1], 10);
                    a = isNaN(a) ? (m[2] ? 1 : 0) : a;
                    b = parseInt(m[3], 10);
                    isNaN(b) && (b = 0);
                    break;
            }

            return (nthCache[expr] = { a: a, b: b });
        }

        // whether is nth-child or nth-type
        function isNth(node, parsed, sibling, tag) {
            var uid, puid, pos, cache, count = 1;

            uid = $.getUid(node);
            puid = $.getUid(node.parentNode);

            cache = nthNodesCache[puid] || (nthNodesCache[puid] = {});

            if (!cache[uid]) {
                while ((node = node[sibling])) {
                    if (node.nodeType != 1 || (tag && node.tagName != tag)) continue;

                    pos = cache[$.getUid(node)];

                    if (pos) {
                        count = pos + count;
                        break;
                    }
                    count++;
                }
                cache[uid] = count;
            }

            return parsed.a ? cache[uid] % parsed.a == parsed.b : parsed.b == cache[uid];
        }

        // whether is only child or type
        function isOnly(node, tag) {
            var prev = node;
            while ((prev = prev.previousSibling)) {
                if (prev.nodeType === 1 && (!tag || prev.tagName == tag)) return false;
            }
            var next = node;
            while ((next = next.nextSibling)) {
                if (next.nodeType === 1 && (!tag || next.tagName == tag)) return false;
            }
            return true;
        }

        var pseudo = {
            'root': function(node) {
                return node === node.ownerDocument.documentElement;
            },
            'nth-child': function(node, parsed) {
                return (parsed.a == 1 && !parsed.b) ? true : isNth(node, parsed, 'previousSibling', false);
            },
            'nth-last-child': function(node, parsed) {
                return (parsed.a == 1 && !parsed.b) ? true : isNth(node, parsed, 'previousSibling', false);
            },
            'nth-of-type': function(node, parsed) {
                return isNth(node, parsed, 'previousSibling', node.tagName);
            },
            'nth-last-of-type': function(node, parsed) {
                return isNth(node, parsed, 'nextSibling', node.tagName);
            },
            'first-child': function(node) {
                var sibling = node.parentNode.firstChild;
                while (sibling.nodeType != 1) {
                    sibling = sibling.nextSibling;
                }
                return node === sibling;
            },
            'last-child': function(node) {
                while ((node = node.nextSibling)) {
                    if (node.nodeType === 1) return false;
                }
                return true;
            },
            'first-of-type': function(node) {
                var sibling = node.parentNode.firstChild, tagName = node.tagName;
                while (sibling.nodeType != 1 || sibling.tagName != tagName) {
                    sibling = sibling.nextSibling;
                }
                return node === sibling;
            },
            'last-of-type': function(node) {
                var tagName = node.tagName;
                while ((node = node.nextSibling)) {
                    if (node.nodeType == 1 && node.tagName == tagName) return false;
                }
                return true;
            },
            'only-child': function(node) {
                return isOnly(node);
            },
            'only-of-type': function(node) {
                return isOnly(node, node.tagName);
            },
            'empty': function(node) {
                return !node.firstChild;
            },
            'parent': function(node) {
                return !!node.firstChild;
            },
            //'link': function() { return; },
            //'visited': function() { return; },
            //'active': function() { return; },
            //'hover': function() { return; },
            //'focus': function() { return; },
            //'target': function() { return; },
            //'lang': function() { return; },
            'enabled': function() {
                return node.disabled === false && node.type !== "hidden";
            },
            'disabled': function() {
                return node.disabled === true;
            },
            'checked': function(node) {
                return node.checked === true;
            },
            'selected': function(node) {
                // Accessing this property makes selected-by-default
                // options in Safari work properly
                node.parentNode.selectedIndex;
                return node.selected === true;
            },
            'visible': function(node) {
                return node.offsetWidth > 0 || node.offsetHeight > 0;
            },
            'hidden': function(node) {
                return node.offsetWidth === 0 || node.offsetHeight === 0;
            },
            //'first-line': function() { return; },
            //'first-letter': function() { return; },
            //'before': function() { return; },
            //'after': function() { return; },
            'not': function(node, value) {
                return !testNode(node, value);
            },
            'contains': function(node, re) {
                return re.test(node.innerText || node.textContent || '');
            },
            'odd': function(node) {
                return;
            },
            'even': function(node) {
                return;
            }
        };
        pseudo.nth = pseudo['nth-child'];
        pseudo.index = pseudo['nth-child'];

        var pseudoRE = {
            't': function(value) {      // not pseudo class
                return parse(value);
            },
            'n': function(value) {      // contains and lang pseudo class
                return new RegExp(escapeRegExp(value));
            },
            'h': function(value) {      // nth pseduo class
                return parseNth(value);
            }
        };

        // filters
        var filter = {
            klass: function(nodes, name) {
                var n, i = 0, results = [], r = 0, pattern;

                pattern = new RegExp('(?:^|\\s+)' + escapeRegExp(name) + '(?:\\s+|$)');

                while (n = nodes[i++]) {
                    pattern.test(n.className) && (results[r++] = n);
                }
                return results;
            },

            attribute: function(nodes, attr) {
                var n, i = 0, results = [], r = 0, pattern,
                key = attributeAlias[attr.key] || attr.key,
                flag = /^(?:src|href|action)$/.test(key) ? 2 : 0;

                if (attr.op) {
                    pattern = attributeRE[attr.op](attr.value);
                    while (n = nodes[i++]) {
                        attribute[attr.op](n[key] || n.getAttribute(key, flag), pattern) && (results[r++] = n);
                    }
                }
                else {
                    while (n = nodes[i++]) {
                        ((n[key] || n.getAttribute(key, flag)) != null) && (results[r++] = n);
                    }
                }

                return results;
            },

            pseudo: function(nodes, pdo) {
                var parsed = pdo.value, key = pdo.key, n, i = 0, results = [], r = 0;

                parsed && (parsed = pseudoRE[key.charAt(2)](parsed));

                while (n = nodes[i++]) {
                    pseudo[key](n, parsed) && (results[r++] = n);
                }

                return results;
            }
        };

        // query sub selector
        function combine(selector, contexts) {
            var ret = [], klass, i = 0, item,
            locate = locateCurrent,
            // selector related
            combinator = selector.combinator,
            id = selector.id,
            tag = selector.tag,
            classes = selector.classes,
            attributes = selector.attributes,
            pseudos = selector.pseudos;

            // if id is supplied
            if (id) {
                // match id
                var node = document.getElementById(id);

                // match tag and match combinator
                if (tag == '*' || node.tagName == tag) {
                    while (cxt = contexts[i++]) {
                        if (combineById[combinator](node, cxt)) {
                            ret = [node];
                            break;
                        }
                    }
                }
            }
            else if (tag) {
                i = 0;
                current = {};
                if (contexts.length == 1) {
                    locate = false;
                }
                while (cxt = contexts[i++]) {
                    ret = combineByTag[combinator](tag, cxt, ret, locate);
                }
            }

            if (classes.length > (i = 0)) {
                // filter nodes by class
                while (item = classes[i++]) {
                    ret = filter.klass(ret, item);
                }
            }

            if (attributes.length > (i = 0)) {
                // filter nodes by attributes
                while (item = attributes[i++]) {
                    ret = filter.attribute(ret, item);
                }
            }

            if (pseudos.length > (i = 0)) {
                // filter nodes by pseudos
                while (item = pseudos[i++]) {
                    ret = filter.pseudo(ret, item);
                }
            }

            return ret;
        }

        // query a sentence
        function search(sentence, contexts) {
            var i = 0, selector;
            current = {};
            nthNodesCache = {};
            while (selector = sentence[i++]) {
                contexts = combine(selector, contexts);
            }

            return contexts;
        }

        // query a selector
        function query(selector, contexts) {
            var results = [], i = 0, sentence,
            selectors = parse(selector);

            while (sentence = selectors[i++]) {
                if (results.length > 0) {
                    results = search(sentence, contexts).concat(results);
                }
                else {
                    results = search(sentence, contexts);
                }
            }

            return results;
        }

        // test a node whether match a selector
        function testNode(node, parsed) {
            var i = 0, item, key, pattern, flag;
            parsed = parsed[0][0];

            if (parsed.id && parsed.id != node.id) {
                return false;
            }

            if (parsed.classes.length > (i = 0)) {
                // filter node by class
                while (item = parsed.classes[i++]) {
                    if (!(new RegExp('(?:^|\\s+)' + escapeRegExp(item) + '(?:\\s+|$)')).test(node.className)) {
                        return false;
                    }
                }
            }

            if (parsed.attributes.length > (i = 0)) {
                // filter node by attributes
                while (item = parsed.attributes[i++]) {
                    key = attributeAlias[item.key];
                    flag = /^(?:src|href|action)$/.test(key) ? 2 : 0;
                    key = node[key] || node.getAttribute(key, flag);
                    if (item.op) {
                        if (!attribute[item.op](key, attributeRE[item.op](item.value))) {
                            return false;
                        }
                    }
                    else if (key == null) {
                        return false;
                    }
                }
            }

            if (parsed.pseudos.length > (i = 0)) {
                // filter node by pseudos
                while (item = parsed.pseudos[i++]) {
                    (pattern = item.value) && (pattern = pseudoRE[item.key.charAt(2)](pattern));
                    if (!pseudo[item.key](node, pattern)) {
                        return false;
                    }
                }
            }

            return true;
        }

        // return the selector function
        return function(selector, context) {
            // TODO: handle empty string
            if (!selector || typeof selector !== "string") {
                return [];
            }

            context = context || document;

            if (context.nodeType !== 1 && context.nodeType !== 9) {
                return [];
            }

            if (support.qsa) {
                try {
                    return context.querySelectorAll(selector);
                }
                catch (e) {
                    return query(selector, [context]);
                }
            }
            else {
                return query(selector, [context]);
            }
        }
    })();

    $.Whizz = Whizz;
})(JUI);
/*
* Author:
*   xushengs@gmail.com
*   http://fdream.net/
* */
(function($) {
    // add to loaded module-list
    $.register('element', '1.0.0.0');

    // detect features
    var support = {}, cache = {}, collected = {};

    (function() {
        var /*de = document.documentElement,*/testee = document.createElement('div'), id = '_jui_' + (new Date()).getTime(), testee_a;
        testee.innerHTML = '   <link/><table></table><a name="' + id + '" class="€ b" href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select>';
        //support.opacity = (typeof testee.style.opacity) !== 'undefined' ? 1 : ((typeof testee.filters === 'object') || (typeof testee.filter === 'string')) ? 2 : 0;
        // do not support any other old browsers
        support = {
            // IE don't support opacity
            // but use filter instead
            opacity: (typeof testee.style.opacity) !== 'undefined' ? true : false,

            // FF use textContent instead of innerText
            innerText: (typeof testee.innerText) !== undefined ? true : false,

            // IE strips leading whitespace when .innerHTML is used
            leadingWhitespace: testee.firstChild && testee.firstChild.nodeType == 3,

            // Verify style float existence
            // (IE uses styleFloat instead of cssFloat)
            cssFloat: !(testee.style.cssFloat === undefined),

            // these will be specified later
            cloneEvent: false,

            // Make sure that tbody elements aren't automatically inserted
            // IE will insert them into empty tables
            tbody: false,

            // Make sure that link elements get serialized correctly by innerHTML
            // This requires a wrapper element in IE
            htmlSerialize: false
        };

        if (testee.getElementsByTagName) {
            support.tbody = !!testee.getElementsByTagName("tbody").length;
            support.htmlSerialize = !!testee.getElementsByTagName("link").length;
        }

        // clone event test
        if (testee.attachEvent && testee.fireEvent) {
            testee.attachEvent("onclick", function click() {
                // Cloning a node shouldn't copy over any
                // bound event handlers (IE does this)
                support.cloneEvent = true;
                testee.detachEvent("onclick", click);
            });
            testee.cloneNode(true).fireEvent("onclick");
        }
    })();


    function toCamelCase(str) {
        return str.replace(/-\D/g, function(match) {
            return match.charAt(1).toUpperCase();
        });
    }

    function toHyphenCase(str) {
        return str.replace(/[A-Z]/g, function(match) {
            return ('-' + match.charAt(0).toLowerCase());
        });
    }

    var alias = {
        'class': 'className',
        'for': 'htmlFor',
        'float': support.cssFloat ? 'cssFloat' : 'styleFloat'
    },
        unit = {
            left: '@px', top: '@px', bottom: '@px', right: '@px',
            width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
            backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
            fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
            margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
            borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
            zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
        },
        shorts = { margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {} };

    (function() {
        var direction = ['Top', 'Right', 'Bottom', 'Left'],
            m = 'margin', p = 'padding', b = 'border',
            i = direction.length, d;
        while (d = direction[--i]) {
            var md = m + d, pd = p + d, bd = b + d;
            shorts[m][md] = unit[md] = '@px';
            shorts[p][pd] = unit[pd] = '@px';
            shorts[b][bd] = unit[bd] = '@px @ rgb(@, @, @)';
            var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
            shorts[bd] = {};
            shorts.borderWidth[bdw] = shorts[bd][bdw] = unit[bdw] = '@px';
            shorts.borderStyle[bds] = shorts[bd][bds] = unit[bds] = '@';
            shorts.borderColor[bdc] = shorts[bd][bdc] = unit[bdc] = 'rgb(@, @, @)';
        }
    })();

    var bools = {
        'compact': true,
        'nowrap': true,
        'ismap': true,
        'declare': true,
        'noshade': true,
        'checked': true,
        'disabled': true,
        'readonly': true,
        'multiple': true,
        'selected': true,
        'noresize': true,
        'defer': true
    };

    var Element = function(selector) {
        if ($.type(selector) !== 'string') {
            return repack(selector);
        }
        var el, els, re = /^#([\w-]+)$/;
        if (re.test(selector) || !$.loaded('selector')) {
            return repack(document.getElementById(selector.replace('#', '')));
        }
        else {
            els = $.Whizz(selector);
            return new Elements(els);
        }
    };

    var Elements = function(els) {
        if (els && els.$family !== 'elements') {
            var i = 0, array = [];
            while ((array[i] = repack(els[i++]))) { }
            array.length--;
            var proto = Elements.prototype;
            for (var p in proto) {
                array[p] = proto[p];
            }
            els = array;
        }
        return els;
    };

    function repack(el) {
        if (el && !el.$family && !(/^object|embed$/i).test(el.tagName)) {
            var proto = Element.prototype;
            for (var p in proto) {
                el[p] = proto[p];
            }
        }
        return el;
    }

    $.Native.initialize({
        name: 'Element',
        initialize: Element,
        protect: true,
        afterImplement: function(key, value) {
            if (Array[key]) return;
            Elements.implement(key, function() {
                var items = [], elements = true;
                for (var i = 0, j = this.length; i < j; i++) {
                    var returns = this[i][key].apply(this[i], arguments);
                    items.push(returns);
                    if (elements) elements = ($.type(returns) == 'element');
                }
                return (elements) ? new Elements(items) : items;
            });
        }
    });

    $.Native.initialize({
        name: 'Elements',
        initialize: Elements,
        protect: true
    });

    function clean(html) {
        // If a single string is passed in and it's a single tag
        // just do a createElement and skip the rest
        var match = /^<(\w+)\s*\/?>$/.exec(html);
        if (match) {
            return document.createElement(match[1]);
        }

        var ret = [], scripts = [], div = document.createElement("div");

        // Fix "XHTML"-style tags in all browsers
        html = html.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag) {
            return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
						all :
						front + "></" + tag + ">";
        });

        // Trim whitespace, otherwise indexOf won't work as expected
        var tags = html.replace(/^\s+/, "").substring(0, 10).toLowerCase();

        var wrap =
        // option or optgroup
					!tags.indexOf("<opt") &&
					[1, "<select multiple='multiple'>", "</select>"] ||

					!tags.indexOf("<leg") &&
					[1, "<fieldset>", "</fieldset>"] ||

					tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
					[1, "<table>", "</table>"] ||

					!tags.indexOf("<tr") &&
					[2, "<table><tbody>", "</tbody></table>"] ||

        // <thead> matched above
					(!tags.indexOf("<td") || !tags.indexOf("<th")) &&
					[3, "<table><tbody><tr>", "</tr></tbody></table>"] ||

					!tags.indexOf("<col") &&
					[2, "<table><tbody></tbody><colgroup>", "</colgroup></table>"] ||

        // IE can't serialize <link> and <script> tags normally
					!support.htmlSerialize &&
					[1, "div<div>", "</div>"] ||

					[0, "", ""];

        // Go to html and back, then peel off extra wrappers
        div.innerHTML = wrap[1] + html + wrap[2];

        // Move to the right depth
        while (wrap[0]--) {
            div = div.lastChild;
        }

        // Remove IE's autoinserted <tbody> from table fragments
        if (support.tbody) {
            // String was a <table>, *may* have spurious <tbody>
            var hasBody = /<tbody/i.test(html),
						tbody = !tags.indexOf("<table") && !hasBody ?
							div.firstChild && div.firstChild.childNodes :
            // String was a bare <thead> or <tfoot>
						    wrap[1] == "<table>" && !hasBody ?
							div.childNodes :
							[];

            for (var j = tbody.length - 1; j >= 0; --j) {
                if ((tbody[j].tagName == "TBODY") && !tbody[j].childNodes.length) {
                    tbody[j].parentNode.removeChild(tbody[j]);
                }
            }
        }

        // IE completely kills leading whitespace when innerHTML is used
        if (!support.leadingWhitespace && /^\s/.test(html)) {
            div.insertBefore(document.createTextNode(html.match(/^\s*/)[0]), div.firstChild);
        }

        return div.firstChild;
    }

    function contains(arr, item) {
        var i = 0, l = arr.length;
        for (var i = 0; i < l; i++) {
            if (arr[i] == item) {
                return true;
            }
        }
        return false;
    }

    /**
    * Element.Style.js
    *
    * */
    Element.implement({
        setStyle: function(style, value) {
            if (style == 'opacity') {
                value = parseFloat(value);
                if (support.opacity) {
                    this.style.opacity = value;
                }
                else {
                    // Set the alpha filter to set the opacity
                    this.style.filter = (this.style.filter || '').replace(/alpha\([^)]*\)/, '') + (value + '' == 'NaN' ? '' : 'alpha(opacity=' + value * 100 + ')');
                    // IE has trouble with opacity if it does not have layout
                    // Force it by setting the zoom level
                    this.zoom = 1;
                }
                return;
            }

            style = alias[style] || toCamelCase(style);
            var type = $.type(value);
            if (type != 'string') {
                value = (type != 'array' && type != 'arguments') ? [value] : value;
                var fmt = (unit[style] || '@').split(' '), i = fmt.length, v;
                while (i--) {
                    v = value[i];
                    if (!(v === 0 || v)) {
                        fmt[i] = '';
                    }
                    else {
                        fmt[i] = $.type(v) == 'number' ? fmt[i].replace('@', Math.round(v)) : v;
                    }
                }
                value = fmt.join(' ');
            }
            else if (value == '' + Number(value)) {
                value = Math.round(value);
            }
            try {
                this.style[style] = value;
            }
            catch (e) { }
            return this;
        },

        getStyle: function(style) {
            if (style == 'opacity') {
                if (support.opacity) {
                    return this.style.opacity;
                }
                else {
                    return this.style.filter && this.style.filter.indexOf('opacity=') >= 0 ? (parseFloat(this.style.filter.match(/opacity=([^)]*)/)[1]) / 100) + '' : '';
                }
            }

            style = alias[style] || toCamelCase(style);
            var result = this.style[style];
            if (!(result === 0 || result)) {
                result = [];
                // if is a short, return joined value
                for (var ss in shorts) {

                    if (style != ss) {
                        continue;
                    }
                    console.log(ss);
                    for (var s in shorts[ss]) {
                        console.log(s);
                        result.push(this.getStyle(s));
                    }
                    return result.join(' ');
                }
                // or get computed style
                if (this.currentStyle) {
                    return this.currentStyle[style];
                }
                var computed = this.getDocument().defaultView.getComputedStyle(this, null);
                return (computed) ? computed.getPropertyValue([toHyphenCase(style)]) : null;
            }
            /*
            * convert to hex
            *
            if (result) {
            result = ''+ result;
            var color = result.match(/rgba?\([\d\s,]+\)/);
            if (color) result = result.replace(color[0], color[0].rgbToHex());
            }
            //*/
            /*
            * minus border and padding in IE & Opera
            *
            if (Browser.Engine.presto || (Browser.Engine.trident && !$chk(parseInt(result, 10)))) {
            if (style.test(/^(height|width)$/)) {
            var values = (style == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
            values.each(function(value) {
            size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
            }, this);
            return this['offset' + property.capitalize()] - size + 'px';
            }
            if ((Browser.Engine.presto) && String(result).test('px')) return result;
            if (property.test(/(border(.+)Width|margin|padding)/)) return '0px';
            }
            //*/
            return result;
        },

        css: function(style, value) {
            if ($.type(style) == 'object') {
                for (var p in style) {
                    this.setStyle(p, style[p]);
                }
                return this;
            }

            if (value === undefined) {
                return this.getStyle(style);
            }
            else {
                this.setStyle(style, value);
                return this;
            }
        },

        getProperty: function(attr) {
            var key = alias[attr];
            var value = (key) ? this[key] : this.getAttribute(attr, 2);
            return (bools[attr]) ? !!value : (key) ? value : value || null;
        },

        setProperty: function(attr, value) {
            var key = alias[attr];
            if (key && bools[attr]) value = !!value;
            key ? this[key] = value : this.setAttribute(attr, '' + value);
        },

        attr: function(attr, value) {
            if ($.type(attr) == 'object') {
                for (var a in attr) {
                    this.setProperty(a, attr[a]);
                }
                return this;
            }

            if (value === undefined) {
                return this.getProperty(attr);
            }
            else {
                this.setProperty(attr, value);
                return this;
            }
        },

        dimension: function(sz) {
            if (!(sz === 0 || sz)) {
                return { width: this.offsetWidth, height: this.offsetHeight };
            }

            if (sz.width !== undefined) {
                this.css('width', sz.width);
            }
            if (sz.height !== undefined) {
                this.css('height', sz.height);
            }
            return this;
        },

        position: function(pos) {
            if (pos === undefined) {
                if (this.parentNode === null || this.style.display == 'none') {
                    return false;
                }
                if (this.getBoundingClientRect)	// IE
                {
                    box = this.getBoundingClientRect();
                    var scrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
                    var scrollLeft = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft);

                    return { x: box.left + scrollLeft, y: box.top + scrollTop };
                }
                else if (document.getBoxObjectFor)	// gecko
                {
                    box = document.getBoxObjectFor(this);

                    var borderLeft = (this.style.borderLeftWidth) ? parseInt(this.style.borderLeftWidth) : 0;
                    var borderTop = (this.style.borderTopWidth) ? parseInt(this.style.borderTopWidth) : 0;

                    pos = [box.x - borderLeft, box.y - borderTop];
                }
                else	// safari & opera
                {
                    pos = [this.offsetLeft, this.offsetTop];
                    parent = this.offsetParent;
                    if (parent != this) {
                        while (parent) {
                            pos[0] += parent.offsetLeft;
                            pos[1] += parent.offsetTop;
                            parent = parent.offsetParent;
                        }
                    }
                    if (this.style.position == 'absolute') {
                        pos[0] -= document.body.offsetLeft;
                        pos[1] -= document.body.offsetTop;
                    }
                }

                if (this.parentNode) { parent = this.parentNode; }
                else { parent = null; }

                while (parent && parent.tagName != 'BODY' && parent.tagName != 'HTML') {
                    // account for any scrolled ancestors
                    pos[0] -= parent.scrollLeft;
                    pos[1] -= parent.scrollTop;

                    if (parent.parentNode) { parent = parent.parentNode; }
                    else { parent = null; }
                }

                return { x: pos[0], y: pos[1] };
            }

            if (pos.x !== undefined) {
                this.css('left', pos.x);
            }
            if (pos.y !== undefined) {
                this.css('top', pos.y);
            }

            return this;
        }
    });

    /**
    * Element.Data.js
    *
    * */
    Element.implement({
        data: function(name, value) {
            ///<summary>
            /// 在该元素上存储或者读取数据
            ///</summary>
            ///<param name="name" type="String">
            ///   要存储的数据的名称
            ///</param>
            ///<param name="value" type="Object">
            ///   [可选]要存储的数据的值，
            ///   若不提供，则读取该元素上已存储的对应的数据。
            ///</param>
            ///<returns type="Object" />

            var uid = $.getUid(this);

            // Only generate the data cache if we're
            // trying to access or manipulate it
            if (name && !cache[uid]) {
                cache[uid] = {};
            }

            // Prevent overriding the named cache with undefined values
            if (value !== undefined) {
                cache[uid][name] = value;
                return value;
            }

            // Return the named cache data, or the ID for the element
            return name ? cache[uid][name] : uid;
        },

        erase: function(name) {
            ///<summary>
            /// 清除在该元素上存储的数据
            ///</summary>
            ///<param name="name" type="String">
            ///   [可选]要删除的数据的名称，
            ///   若不提供，则删除该节点上存储的所有数据。
            ///</param>
            ///<returns type="$.Element" />

            var uid = $.getUid(this);

            // If we want to remove a specific section of the element's data
            if (name) {
                if (cache[uid]) {
                    // Remove the section of cache data
                    delete cache[uid][name];

                    // If we've removed all the data, remove the element's cache
                    name = "";

                    for (name in cache[uid])
                        break;

                    (!name) && this.erase();
                }

                // Otherwise, we want to remove all of the element's data
            } else {
                // Completely remove the data cache
                delete cache[uid];
            }

            return this;
        }
    });

    /**
    * Element.Dom.js
    *
    * */
    Element.implement({
        getDocument: function() {
            return this.ownerDocument;
        },
        getElement: function(selector) {
            ///<summary>
            /// 获取当前元素下符合选择器的第一个元素
            ///</summary>
            ///<param name="selector" type="String">
            ///   CSS选择器
            ///</param>
            ///<returns type="$.Element" />

            var els = [];
            if ($.loaded('selector')) {
                els = $.Whizz(selector, this);
            }
            else {
                els = this.getElementsByTagName(selector);
            }
            return els[0] ? new Element(els[0]) : null;
        },

        getElements: function(selector) {
            ///<summary>
            /// 获取当前元素下符合选择器的所有元素
            ///</summary>
            ///<param name="selector" type="String">
            ///   CSS选择器
            ///</param>
            ///<returns type="$.Elements" />

            if ($.loaded('selector')) {
                return new Elements($.Whizz(selector, this));
            }
            else {
                return new Elements(this.getElementsByTagName(selector));
            }
        }
    });

    /**
    * Element.Move.js
    *
    * */
    Element.implement({
        txt: function(text) {
            ///<summary>
            /// 读取或者设置元素内的文本内容
            ///</summary>
            ///<param name="text" type="String">
            ///   [可选]要设置的文本值，如不提供此参数，则读取该元素的文本内容
            ///</param>
            ///<returns type="STRING" />

            if (text === undefined) {
                return this[support.innerText ? 'innerText' : 'textContent'];
            }
            else {
                this.html(text.escapeHTML());
                return text;
            }
        },

        html: function(html) {
            ///<summary>
            /// 读取或者设置元素内的HTML文本
            ///</summary>
            ///<param name="text" type="String">
            ///   [可选]要设置的HTML文本，如不提供此参数，则读取该元素的HTML文本
            ///</param>
            ///<returns type="STRING" />

            if (html !== undefined) {
                this.innerHTML = html;
            }
            return this.innerHTML;
        },

        clone: function(content) {
            ///<summary>
            /// 复制当前元素，返回复制后的元素
            ///</summary>
            ///<param name="content" type="Boolean">
            ///   [可选]是否复制元素的子元素，默认为true
            ///</param>
            ///<returns type="$.Element" />

            // default is clone context of the element
            content = content !== false;
            // Do the clone
            if (support.cloneEvent) {
                // IE copies events bound via attachEvent when
                // using cloneNode. Calling detachEvent on the
                // clone will also remove the events from the orignal
                // In order to get around this, we use innerHTML.
                // Unfortunately, this means some modifications to
                // attributes in IE that are actually only stored
                // as properties will not be copied (such as the
                // the name attribute on an input).
                var html = this.outerHTML;
                if (!html) {
                    var div = this.ownerDocument.createElement("div");
                    div.appendChild(this.cloneNode(content));
                    html = div.innerHTML;
                }

                return new Element(clean(html.replace(new RegExp($.expando + '="(?:\d+|null)"', 'g'), "").replace(/^\s*/, "")));
            }
            else {
                return new Element(this.cloneNode(content));
            }
        },

        prependTo: function(el) {
            ///<summary>
            /// 把当前元素插入到指定元素内头部
            ///</summary>
            ///<param name="el" type="$.Element">
            ///   要插入的目标容器
            ///</param>
            ///<returns type="$.Element" />

            el.insertBefore(this, el.firstChild);
            return this;
        },

        appendTo: function(el) {
            ///<summary>
            /// 把当前元素插入到指定元素内尾部
            ///</summary>
            ///<param name="el" type="$.Element">
            ///   要插入的目标容器
            ///</param>
            ///<returns type="$.Element" />

            el.appendChild(this);
            return this;
        },

        inject: function(el, pos) {
            ///<summary>
            /// 把当前元素插入到指定元素内头部或者尾部
            ///</summary>
            ///<param name="el" type="$.Element">
            ///   要插入的目标容器
            ///</param>
            ///<param name="pos" type="String">
            ///   [可选]要插入的位置，可为top或者bottom，默认为bottom
            ///</param>
            ///<returns type="$.Element" />

            if (pos == 'top') {
                this.prependTo(el);
            }
            else {
                this.appendTo(el);
            }

            return this;
        },

        insert: function(el, pos) {
            ///<summary>
            /// 把当前元素插入到指定元素的前面或者后面
            ///</summary>
            ///<param name="el" type="$.Element">
            ///   要插入位置的参考元素
            ///</param>
            ///<param name="pos" type="String">
            ///   [可选]要插入的位置，可为before或者after，默认为before
            ///</param>
            ///<returns type="$.Element" />

            if (pos == 'after') {
                this.after(el);
            }
            else {
                this.before(el);
            }

            return this;
        },

        before: function(el) {
            ///<summary>
            /// 把当前元素插入到指定元素前面
            ///</summary>
            ///<param name="el" type="$.Element">
            ///   要插入位置的参考元素
            ///</param>
            ///<returns type="$.Element" />

            el.parentNode.insertBefore(this, el);
            return this;
        },

        after: function(el) {
            ///<summary>
            /// 把当前元素插入到指定元素后面
            ///</summary>
            ///<param name="el" type="$.Element">
            ///   要插入位置的参考元素
            ///</param>
            ///<returns type="$.Element" />

            var p = el.parentNode;
            if (el.nextSibling) {
                p.insertBefore(this, el.nextSibling);
            }
            else {
                p.appendChild(this);
            }
            return this;
        },

        dispose: function() {
            ///<summary>
            /// 把当前元素从父元素中移除
            ///</summary>
            ///<returns type="$.Element" />

            return (this.parentNode) ? this.parentNode.removeChild(this) : this;
        },

        empty: function() {
            ///<summary>
            /// 清除当前元素中的所有内容
            ///</summary>
            ///<returns type="$.Element" />

            var child, childNodes = this.childNodes, i = 0;
            while (child = childNodes[i++]) {
                child.destroy();
            }
            return this;
        },

        destroy: function() {
            ///<summary>
            /// 销毁当前元素并释放内存
            ///</summary>
            ///<returns type="null" />

            this.empty();
            this.dispose();
            this.removeEvents();
            return null;
        }
    });
    Element.alias({ dispose: 'remove' });

    /**
    * Element.Event.js
    *
    * */
    Element.implement({
        cloneEvents: function(from, type) {
            ///<summary>
            /// 从目标元素复制事件
            ///</summary>
            ///<param name="from" type="$.Element">要复制事件的目标元素</param>
            ///<param name="type" type="String">要复制的事件类型</param>
            ///<returns type="$.Element" />

            from = new Element(from);
            var fevents = this.data('events');
            if (!fevents) {
                return;
            }

            if (!type) {
                for (var evType in fevents) this.cloneEvents(from, evType);
            }
            else if (fevents[type]) {
                var i = 0, fn, fns = fevents[type].keys;
                while (fn = fns[i++]) {
                    this.addEvent(type, fn);
                }
            }
            return this;
        },

        addEvent: function(type, fn, bind, same) {
            ///<summary>
            /// 给元素添加事件
            ///</summary>
            ///<param name="type" type="String">事件类型，不带前面的on</param>
            ///<param name="fn" type="Fucntion">事件处理函数</param>
            ///<param name="bind" type="Object">事件处理函数中this指向的对象</param>
            ///<param name="same" type="Boolean">是否允许重复添加完全相同的事件</param>
            ///<returns type="$.Element" />

            var events = this.data('events') || this.data('events', {});
            bind = bind ? bind : this;

            events[type] = events[type] || { keys: [], values: [] };
            if (!same && contains(events[type].keys, fn)) {
                return this;
            }

            var defn = function(e) {
                if ($.loaded('event')) {
                    e = new $.Event(e);
                }
                fn.call(bind, e);
            };

            if (type == 'unload') {
                var old = defn;
                defn = function() {
                    self.removeListener('unload', defn);
                    old();
                };
            }
            //else {
            //    collected[this.uid] = this;
            //}

            if (this.addEventListener) {
                this.addEventListener(type, defn, false);
            }
            else {
                this.attachEvent('on' + type, defn);
            }

            events[type].keys.push(fn);
            events[type].values.push(defn);

            return this;
        },

        removeEvent: function(type, fn) {
            ///<summary>
            /// 给元素添加事件
            ///</summary>
            ///<param name="type" type="String">事件类型，不带前面的on</param>
            ///<param name="fn" type="Fucntion">
            ///   [可选]事件处理函数，如果不提供则删除所有该类型的事件
            ///</param>
            ///<returns type="$.Element" />

            var events = this.data('events');
            if (!events || !events[type]) {
                return this;
            }

            if (!fn) {
                // remove all events of this type
                var i = 0, fns = events[type].keys;
                while (fn = fns[i++]) {
                    this.removeEvent(type, fn);
                }
                delete events[type];

                type = "";
                for (type in events) {
                    break;
                }

                if (!type) {
                    this.erase();
                }
                else {
                    this.data('events', events);
                }

                return this;
            }

            var pos = -1, i = 0, f;
            while (f = events[type].keys[i]) {
                if (f == fn) {
                    pos = i;
                    break;
                }
                i++;
            }
            if (pos == -1) {
                return this;
            }

            events[type].keys.splice(pos, 1)
            fn = events[type].values.splice(pos, 1)[0];
            if (this.removeEventListener) {
                this.removeEventListener(type, fn, false);
            }
            else {
                this.detachEvent('on' + type, fn);
            }

            return this;
        },

        addEvents: function(events) {
            ///<summary>
            /// 一次性给元素添加多个事件
            ///</summary>
            ///<param name="events" type="Object">
            ///   一个以事件类型为键（key），以事件处理函数为值（value）的hash对象
            ///</param>
            ///<returns type="$.Element" />

            for (var type in events) {
                this.addEvent(type, events[type]);
            }

            return this;
        },

        removeEvents: function(events) {
            ///<summary>
            /// 一次性删除多个事件
            ///</summary>
            ///<param name="events" type="Object">
            ///   [可选]一个以事件类型为键（key），以事件处理函数为值（value）的hash对象；
            ///   如果此参数为一个事件类型，这删除该类型的所有事件；
            ///   如不提供此参数，则删除所有事件。
            ///</param>
            ///<returns type="$.Element" />

            if ($.type(events) == 'object') {
                for (var type in events) {
                    this.removeEvent(type, events[type]);
                }
                return this;
            }

            var attached = this.data('events');
            if (!attached) {
                return this;
            }

            if (!events) {
                for (var type in attached) {
                    this.removeEvent(type);
                }
                this.erase('events');
            }
            else {
                this.removeEvent(events);
            }
            return this;
        },

        fireEvent: function(type, args, delay) {
            ///<summary>
            /// 给元素添加事件
            ///</summary>
            ///<param name="type" type="String">事件类型，不带前面的on</param>
            ///<param name="args" type="Array">要传递给事件处理函数的参数</param>
            ///<param name="delay" type="Number">延迟触发事件的时间</param>
            ///<returns type="$.Element" />

            var events = this.data('events');
            if (!events || !events[type]) {
                return this;
            }

            var i = 0, fns = events[type], fn, ret, self = this;
            while (fn = fns[i++]) {
                ret = function(f) {
                    return function() {
                        f.apply(self, args);
                    }
                };
                setTimeout(ret(fn), delay);
            }

            return this;
        }
    });

    $.Element = Element;
    $.Elements = Elements;
})(JUI);
/*
* Author:
*   xushengs@gmail.com
*   http://fdream.net/
* */
(function($) {
    // add to loaded module-list
    $.register('event', '1.0.0.0');

    var keys = {
        '8': 'backspace',
        '9': 'tab',
        '13': 'enter',
        '27': 'esc',
        '32': 'space',
        '38': 'up',
        '40': 'down',
        '37': 'left',
        '39': 'right',
        '46': 'delete'
    };

    var Event = function(event) {
        if (event.$family === 'event') {
            return event;
        }

        var doc = document, win = window, type = event.type;
        var target = event.target || event.srcElement;
        while (target && target.nodeType == 3) {
            target = target.parentNode;
        }

        if (/key/.test(type)) {
            var code = event.which || event.keyCode;
            var key = keys[code];
            if (type == 'keydown') {
                var fKey = code - 111;
                if (fKey > 0 && fKey < 13) key = 'f' + fKey;
            }
            key = key || String.fromCharCode(code).toLowerCase();
        }
        else if (type.match(/(click|mouse|menu)/i)) {
            doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.documentElement : doc.body;
            var page = {
                x: event.pageX || event.clientX + doc.scrollLeft,
                y: event.pageY || event.clientY + doc.scrollTop
            };
            var client = {
                x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX,
                y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY
            };
            if (type.match(/DOMMouseScroll|mousewheel/)) {
                var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
            }
            var rightClick = (event.which == 3) || (event.button == 2);
        }

        return (function(a, b) {
            for (var p in b) {
                a[p] = b[p];
            }
            return a;
        })(this, {
            event: event,
            type: type,

            page: page,
            client: client,
            rightClick: rightClick,

            wheel: wheel,

            target: target,

            code: code,
            key: key,

            shift: event.shiftKey,
            control: event.ctrlKey,
            alt: event.altKey,
            meta: event.metaKey
        });
    };

    $.Native.initialize({
        name: 'Event',
        initialize: Event,
        protect: true
    });

    Event.implement({
        stop: function() {
            return this.stopPropagation().preventDefault();
        },

        stopPropagation: function() {
            if (this.event.stopPropagation) {
                this.event.stopPropagation();
            }
            else {
                this.event.cancelBubble = true;
            }

            return this;
        },

        preventDefault: function() {
            if (this.event.preventDefault) {
                this.event.preventDefault();
            }
            else {
                this.event.returnValue = false;
            }

            return this;
        }
    });

    $.Event = Event;
})(JUI);
/*
* Author:
*   xushengs@gmail.com
*   http://fdream.net/
* */
(function ($) {
    // add to loaded module-list
    $.register('customevent', '1.0.0.0');

    var CustomEvent = function (name) {
        this.events = [];
        this.name = name;
    };

    $.Native.initialize({
        name: 'CustomEvent',
        initialize: CustomEvent,
        protect: true
    });

    CustomEvent.implement({
        fire: function () {
            var args = [];
            for (var i = 0; i < arguments.length; i++) {
                args.push(arguments[i]);
            }
            for (var i = 0, len = this.events.length; i < len; i++) {
                var et = this.events[i];
                et[0].call(et[1], this.name, args);
            }
        },

        subscribe: function (fn, scope) {
            this.events.push([fn, scope]);
        },

        clear: function () {
            this.events = [];
        }
    });

    $.CustomEvent = CustomEvent;
})(JUI);    
/*
* Author:
*   xushengs@gmail.com
*   http://fdream.net/
* */
(function ($) {
    // add to loaded module-list
    $.register('cookie', '1.0.0.0');

    var _options = {
        encode: false,
        decode: false,
        path: false,
        domain: false,
        duration: false,
        secure: false,
        document: document
    };

    function mergeOptions(options) {
        for (var p in options) {
            _options[p] = options[p];
        }
    }

    var Cookie = {
        write: function (key, value, options) {
            mergeOptions(options);
            if (_options.encode) value = encodeURIComponent(value);
            if (_options.domain) value += '; domain=' + _options.domain;
            if (_options.path) value += '; path=' + _options.path;
            if (_options.duration) {
                var date = new Date();
                date.setTime(date.getTime() + _options.duration * 24 * 3600000);
                value += '; expires=' + date.toGMTString();
            }
            if (_options.secure) value += '; secure';
            _options.document.cookie = key + '=' + value;
            return this;
        },

        read: function (key, options) {
            var value = _options.document.cookie.match('(?:^|;)\\s*' + key.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1') + '=([^;]*)');
            // 默认decode，否则不decode
            if (_options.decode) {
                return (value) ? decodeURIComponent(value[1]) : null;
            }
            else {
                return (value) ? value[1] : null;
            }
        },

        remove: function (key, options) {
            mergeOptions(options);
            _options.duration = -1;
            Cookie.write(key, '');
            return this;
        }
    };

    $.Cookie = Cookie;

})(JUI);
/*
* Author:
*   xushengs@gmail.com
*   http://fdream.net/
* */
(function($) {
    // add to loaded module-list
    $.register('loader', '1.0.0.0');

    var loading = {}, _source, cbIndex = 1, cbPrefix = 'jui_cb_';

    var Loader = function(source) {
        _source = source;
        return this;
    };

    $.Native.initialize({
        name: 'Loader',
        initialize: Loader,
        protect: true
    });

    function load(options) {
        options = options || _source || {};

        var src, charset, type = 'js', callback = $.empty, useParam, bind, cache;
        src = options.url;
        type = options.type;
        charset = options.charset;
        callback = options.callback;
        bind = options.bind;
        useParam = options.param;
        cache = options.cache;

        if (!src || src == '') {
            return;
        }

        try {
            // generate callback functions and add parameters to url
            if (useParam && callback) {
                $.Loader[cbPrefix + cbIndex] = function() {
                    callback.apply(bind, arguments);
                }
                if (src.indexOf('?') > -1) {
                    src = src + '&cb=JUI.Loader.' + cbPrefix + cbIndex;
                }
                else {
                    src = src + '?cb=JUI.Loader.' + cbPrefix + cbIndex;
                }
                if (!cache) {
                    src = src + '&r=' + Math.random();
                }
                cbIndex++;
            }

            var dom;
            if (type == 'css') {
                dom = document.createElement('link');
                dom.rel = 'stylesheet';
                dom.type = 'text/css';
                dom.href = src;
            }
            else {
                dom = document.createElement('script');
                dom.src = src;
                dom.type = 'text/javascript';
            }
            charset && (dom.charset = charset);

            if (!useParam && callback) {
                dom.onload = function() {
                    callback.apply(bind, [src, true]);
                    //callback(src, true);
                };

                dom.onerror = function() {
                    callback.apply(bind, [src, false]);
                    //callback(src, false);
                };

                dom.onreadystatechange = function() {
                    if (dom.readyState == 'loaded') {
                        callback.apply(bind, [src, true]);
                        //callback(src, true);
                    }
                }
            }

            loading[src] = dom;
            setTimeout(function() { document.getElementsByTagName('head')[0].appendChild(dom); }, 100);
        } catch (e) {
            callback(src, false);
        }
    }

    Loader.implement({
        load: function(list) {
            if (!list) {
                list = [_source];
            }
            else if ($.type(list) != 'array') {
                list = Array.prototype.slice.call(arguments, 0);
            }
            var i = 0, source;
            while (source = list[i++]) {
                load(source);
            }

            return this;
        },

        chain: function(list) {
            if ($.type(list) != 'array') {
                list = Array.prototype.slice.call(arguments, 0);
            }
            if (!list || list.length == 0) {
                return;
            }

            var source = list.shift(), self = this;
            cb = function(l, s) {
                s.callback(s.url);
                self.chain(l);
            };
            this.load({ url: source.url, type: source.type, callback: cb(list, source) });
        },

        cancel: function(src) {
            if (!loading[src]) {
                return;
            }

            document.removeChild(loading[src]);
            delete loading[src];
        }
    });

    $.Loader = Loader;
})(JUI);    
/*
 * Author:
 *   xushengs@gmail.com
 *   http://fdream.net/
 * */
(function($) {
    // add to loaded module-list
    $.register('fx', '1.0.0.0');

    function mergeOptions(o, n) {
        for (var p in n) {
            o[p] = n[p];
        }

        return o;
    }

    function capitalize(str) {
        return str.replace(/\b[a-z]/g, function(match) {
            return match.toUpperCase();
        });
    }

    var Fx = function(options) {
        var _options = {
            // events
            onStart: $.empty,
            onComplete: $.empty,
            onCancel: $.empty,
            onEnterFrame: $.empty,   // just like actionscript, it fired when it changed
            // settings
            fps: 50,        // frames per second
            duration: 500,  // duration
            unit: false,    // it canbe px, em, % and etc. TODO: implement it
            link: 'ignore', // TODO: implement it
            effect: false   // no effect in default 
        },
            _period = 20,
            _timer = null,
            _time = 0,      // current time
            _from = 0,
            _change = 0,
            _transition = function(p) {
                return p;
            },
            _self = this;

        _options = mergeOptions(_options, options);

        function move() {
            var pos = ($.now() - _time) / _options.duration;
            if (pos >= 1) {
                _self.stopTimer();
                pos = 1;
            }
            pos = _transition(pos);
            _self.change(pos);
            _options.onEnterFrame(pos);
        }

        this.change = function(value) {
            return value;
        };

        this.startTimer = function(options) {
            _options = mergeOptions(_options, options);
            _period = Math.round(1000 / _options.fps);

            if (_options.effect && $.loaded('fx.transitions')) {
                var data = _options.effect.split(':');
                _transition = $.Fx.Transitions;
                _transition = _transition[capitalize(data[0])];
                if (data[1]) {
                    _transition = _transition['ease' + capitalize(data[1]) + (data[2] ? capitalize(data[2]) : '')];
                }
            }

            _time = $.now();
            try {
                clearInterval(_timer);
            }
            catch (e) { }
            _timer = setInterval(move, _period);
            _options.onStart();
        };

        this.stopTimer = function() {
            clearInterval(_timer);
            _options.onComplete();
        };

        this.cancelTimer = function() {
            clearInterval(_timer);
            _options.onCancel();
        };

        return this;
    };

    $.Native.initialize({
        name: 'Fx',
        initialize: Fx,
        protect: false
    });

    $.Fx = Fx;
})(JUI);
/*
* Author:
*   xushengs@gmail.com
*   http://fdream.net/
* */
/*
* requires:
*   fx.js
*
* */

(function($) {
    // add to loaded module-list
    $.register('fx.transitions', '1.0.0.0');

    /*
    * Easing Equations by Robert Penner
    * http://www.robertpenner.com/easing/
    * Modified from mootools
    * http://mootools.net
    * 
    **/
    var transitions = {
        Linear: function(p) {
            return p;
        },

        Pow: function(p, x) {
            return Math.pow(p, x[0] || 6);
        },

        Expo: function(p) {
            return Math.pow(2, 8 * (p - 1));
        },

        Circ: function(p) {
            return 1 - Math.sin(Math.acos(p));
        },

        Sine: function(p) {
            return 1 - Math.sin((1 - p) * Math.PI / 2);
        },

        Back: function(p, x) {
            x = x[0] || 1.618;
            return Math.pow(p, 2) * ((x + 1) * p - x);
        },

        Bounce: function(p) {
            var value;
            for (var a = 0, b = 1; 1; a += b, b /= 2) {
                if (p >= (7 - 4 * a) / 11) {
                    value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
                    break;
                }
            }
            return value;
        },

        Elastic: function(p, x) {
            return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
        }
    };

    var ts = ['Quad', 'Cubic', 'Quart', 'Quint'], i = 0, t;
    while (t = ts[i]) {
        transitions[t] = function(p) {
            return Math.pow(p, [i + 2]);
        };
        i++;
    }

    $.Fx.Transitions = {};

    for (t in transitions) {
        $.Fx.Transitions[t] = {
            easeIn: function(pos, seg) {
                return transitions[t](pos, seg);
            },
            easeOut: function(pos, seg) {
                return 1 - transitions[t](1 - pos, seg);
            },
            easeInOut: function(pos, seg) {
                return (pos <= 0.5) ? transitions[t](2 * pos, seg) / 2 : (2 - transitions[t](2 * (1 - pos), seg)) / 2;
            }
        };
    }
})(JUI);
/*
* Author:
*   xushengs@gmail.com
*   http://fdream.net/
* */
/*
* requires:
*   element.js
*   fx.js
*
* */
(function($) {
    // add to loaded module-list
    $.register('fx.morph', '1.0.0.0');

    var Morph = function(dom, options) {
        var _from = {}, _change = {}, _dom = $(dom);

        // 继承父类的方法
        this.constructor.superclass.constructor.apply(this, [options]);

        this.change = function(pos) {
            for (var p in _from) {
                _dom.setStyle(p, Math.round(_from[p] + pos * _change[p]));
            }
        };

        this.start = function(styles, options) {
            if (!styles) {
                return;
            }

            for (var p in styles) {
                if ($.type(styles[p]) !== 'array' || styles[p].length === 1 || styles[p][0] === undefined) {
                    _from[p] = parseInt(_dom.getStyle(p), 10);
                    _change[p] = (styles[p][0] === undefined ? styles[p] : styles[p][1]) - _from[p];
                }
                else {
                    _from[p] = styles[p][0];
                    _change[p] = styles[p][1] - _from[p];
                }
            }
            this.startTimer();
        };
    };

    Morph = $.extend(Morph, $.Fx);


    $.Native.initialize({
        name: 'Fx.Morph',
        initialize: Morph,
        protect: false
    });

    $.Fx.Morph = Morph;
})(JUI);
/*
* Author:
*   xushengs@gmail.com
*   http://fdream.net/
* */
(function($) {
    $.register('ikan.user2', '1.0.0.0');

    var domain = 'pplive.com';
    if (document.domain.toLowerCase().indexOf('pptv.com') != -1) {
        domain = 'pptv.com';
    }

    var urls = {
        login: 'http://passport.pptv.com/weblogin.do?',
        addFavor: 'http://bk.pptv.com/xihttp/ikan2/favor/json/post/?',
        favorCheck: 'http://bk.pptv.com/xihttp/ikan2/favor/json/check/?',
        like: 'http://bk.pptv.com/xihttp/ikan2/score/json/post/?',
        likeCheck: 'http://bk.pptv.com/xihttp/ikan2/score/json/check/?'
    };

    var isLogined = false, isOneLoad = true;

    var User2 = {
        onLogined: new $.CustomEvent('onLogined'),
        onlogouted: new $.CustomEvent('onlogouted'),

        userInfo: {
            Gender: '',
            PpNum: '',
            ExpNum: '',
            LevelName: '',
            NextLevelName: '',
            NextLevelExpNum: '',
            Area: '',
            Subscribe: '',
            UnreadNotes: '',
            HeadPic: '',
            Email: '',
            OnlineTime: '',
            UserName: ''
        },

        tryReadUserInfo: function() {
            var UDI = $.Cookie.read('UDI');
            var PPName = $.Cookie.read('PPName');

            if (UDI == null || PPName == null) {
                this.onlogouted.fire();
                return;
            }
            var ss = PPName.split('$');
            this.userInfo['UserName'] = decodeURIComponent(ss[0]);

            var uinfo = UDI.split('$');
            var ix = 0;
            for (var p in this.userInfo) {
                if (p == 'UserName') {
                    break;
                }
                this.userInfo[p] = decodeURIComponent(uinfo[ix]);
                ix++;
            }
            isLogined = true;
            this.onLogined.fire(this.userInfo);
        },

        wirteUserInfo: function(d) {
            var ops = { domain: domain, path: '/', duration: 7 };
            for (var p in d) {
                $.Cookie.write(p, d[p], ops);
            }
        },

        logout: function() {
            var ops = { domain: domain, path: '/' };
            $.Cookie.remove('PPKey', ops);
            $.Cookie.remove('UDI', ops);
            $.Cookie.remove('PPName', ops);
            isLogined = false;
            this.onlogouted.fire();
        },

        login: function(name, pwd) {
            var self = this;
            var url = urls.login + 'username=' + name + '&password=' + pwd;
            new $.Loader({ url: url, type: 'js', callback: function(status, d) { self.loginFnc(status, d); }, param: true }).load();
        },

        loginFnc: function(status, d) {
            if (status == 0) {
                alert(d);
                return;
            }
            if (status == 1) {
                this.usernameel.value = '';
                this.userpwdel.value = '';
                this.hideLoginBox();
                this.wirteUserInfo(d);
            }
            this.tryReadUserInfo();
        },

        checkLogined: function() {
            return isLogined;
        },

        showLoginBox: function(position) {
            if (isOneLoad) {
                isOneLoad = false;
                this.boxel = $('#loginBox');
                this.btnpostel = this.boxel.getElement('.btnPost');
                this.usernameel = this.boxel.getElement('.userName');
                this.userpwdel = this.boxel.getElement('.userPwd');

                this.usernameel.value = '';
                this.userpwdel.value = '';

                $(document).addEvent('click', function(ev) { if (ev.target.getAttribute('stopdocumentclick') == null) { this.hideLoginBox(); } }, this);
                this.boxel.addEvent('click', function(ev) { if (ev.target.tagName != 'A') { ev.stop(); } });

                this.btnpostel.addEvent('click', function(ev) { this.checkLoginForm(); }, this);
                this.usernameel.addEvent('keydown', function(ev) { if (ev.key == 'enter') { this.checkLoginForm(); } }, this);
                this.userpwdel.addEvent('keydown', function(ev) { if (ev.key == 'enter') { this.checkLoginForm(); } }, this);
            }

            this.boxel.css({ 'display': 'block' });

            var x = 0;
            var y = 0;
            x = position.x;
            y = position.y;
            this.boxel.css({ 'top': y, 'left': x });
            this.usernameel.focus();
        },

        hideLoginBox: function() {
            this.boxel.css('display', 'none');
        },

        checkLoginForm: function() {
            if (this.usernameel.value.length == 0) {
                this.usernameel.focus();
                return;
            }
            if (this.userpwdel.value.length == 0) {
                this.userpwdel.focus();
                return;
            }

            this.login(this.usernameel.value, this.userpwdel.value);
        },

        favor: function(mode, type, id, cb) {
            var url = (mode == 'add' ? urls.addFavor : urls.favorCheck) + 'type=' + type + '&id=' + id;
            new $.Loader({ url: url, type: 'js', callback: function(d) {
                cb(d);
            }, param: true
            }).load();
        },

        like: function(mode, type, id, like2nolike, cb) {
            var url = (mode == 'add' ? urls.like : urls.likeCheck) + 'type=' + type + '&id=' + id + '&attitude=' + like2nolike;
            new $.Loader({ url: url, type: 'js', callback: function(d) {
                cb(d);
            }, param: true
            }).load();
        }
    };

    if (typeof $.IKan == 'undefined') {
        $.IKan = {};
    }

    $.IKan.User2 = User2;

})(JUI);
/*
* Author:
*   xushengs@gmail.com
*   http://fdream.net/
* */
(function($) {
    $.register('ikan.searchtips', '1.0.0.0');

    var domain = 'pplive.com';
    if (document.domain != '') {
        domain = document.domain.indexOf('pptv.com') != -1 ? 'pptv.com' : domain;
    }

    var SearchTips = function(inputEl, btnEl, ops) {
        this.inputel = inputEl;
        this.btnel = btnEl;

        this.ops = {
            defaultText: '-- 请输入关键字 --',
            defaultTextColor: '#999999',
            textColor: '#000000',
            tipBoxWidth: '182px',
            strLength: 26,
            searchSuggestUrl: 'http://ikan.' + domain + '/search/suggest/?kw=',
            searchUrl: 'http://ikan.' + domain + '/search/?kw='
        };
        this.mergeOptions(ops);

        this.init();
    };

    $.Native.initialize({
        name: 'SearchTips',
        initialize: SearchTips,
        protect: true
    });

    SearchTips.implement({
        mergeOptions: function(ops) {
            for (var p in ops) {
                this.ops[p] = ops[p];
            }
        },

        truncate: function(txt, len, ae) {
            var tl = 0, ts = [], tt = txt.length;
            for (var i = 0; i < tt; i++) {
                if (txt.charCodeAt(i) > 255) {
                    tl += 2;
                }
                else {
                    tl++;
                }
                if (tl > len) {
                    break;
                }
            }
            return (ae && i < tt) ? txt.substring(0, i) + '...' : txt.substring(0, i);
        },

        getText: function() {
            return this.inputel.value.replace(/(^\s+)|(\s+$)/gm, '');
        },

        setDefaultState: function() {
            this.inputel.value = this.ops.defaultText;
            this.inputel.css('color', this.ops.defaultTextColor);
        },

        setNormalState: function(txt) {
            this.inputel.value = txt ? txt : '';
            this.inputel.css('color', this.ops.textColor);
        },

        init: function() {
            this.tipsLis = [];
            this.selectedIx = -1;
            this.prevText = '';
            this.gotourl = '';


            this.setDefaultState();

            this.tipboxel = document.createElement('ul');
            this.tipboxel.className = 'sm_search_tips';
            this.tipboxel.style.overflow = 'hidden';
            this.tipboxel.style.position = 'absolute';
            this.tipboxel.style.zIndex = 1000;
            this.tipboxel.style.width = this.ops.tipBoxWidth;
            this.tipboxel.style.display = 'none';
            document.documentElement.getElementsByTagName('body')[0].appendChild(this.tipboxel);

            this.inputel.addEvent('focus', this.input_focus, this);
            this.inputel.addEvent('blur', this.input_blur, this);
            this.inputel.addEvent('keydown', this.input_key_down, this);
            this.inputel.addEvent('keyup', this.input_key_up, this);
            this.btnel.addEvent('click', this.btn_click, this);
            $(document).addEvent('click', function() {
                this.hideTipBox();
            }, this);
        },

        fillList: function(d) {
            this.tipsLis = [];
            this.selectedIx = -1;
            this.prevText = this.getText();
            if (d.length == 0) {
                this.hideTipBox();
                return;
            }
            var s = [];
            for (var i = 0, len = d.length; i < len; i++) {
                s.push(['<li><a href="', d[i].link, '">', this.truncate(d[i].name, this.ops.strLength), '</a></li>'].join(''));
            }
            this.tipboxel.innerHTML = s.join('');

            this.tipsLis = this.tipboxel.getElementsByTagName('li');

            this.showTipBox();
        },

        setSelectedItem: function(dp) {
            if (this.tipsLis.length == 0) {
                return;
            }

            if (this.selectedIx == -1) {
                this.selectedIx = 0;
            } else {
                this.tipsLis[this.selectedIx].className = '';
                this.selectedIx += dp;
            }

            if (this.selectedIx < 0) {
                this.selectedIx = this.tipsLis.length - 1;
            }

            if (this.selectedIx >= this.tipsLis.length) {
                this.selectedIx = 0;
            }

            this.tipsLis[this.selectedIx].className = 'current';
            this.gotourl = this.tipsLis[this.selectedIx].getElementsByTagName('a')[0].href;
        },

        showTipBox: function() {
            this.gotourl = '';
            var pos = this.inputel.position();
            this.tipboxel.style.top = pos.y + 24 + 'px';
            this.tipboxel.style.left = pos.x + 'px';
            this.tipboxel.style.display = 'block';
        },

        hideTipBox: function() {
            this.gotourl = '';
            this.tipboxel.style.display = 'none';
        },

        getSuggestList: function() {
            var self = this;

            var ld = new $.Loader({ url: this.ops.searchSuggestUrl + encodeURIComponent(this.getText()),
                type: 'js',
                callback: function(d) {
                    self.fillList.call(self, d);
                },
                param: true
            });
            ld.load();
        },

        input_key_up: function() {
            if (this.getText() == '') {
                this.hideTipBox();
            }
            if (this.getText() != '' && this.getText() != this.ops.defaultTex && this.prevText != this.getText()) {
                var self = this;
                clearTimeout(this.tid);
                this.tid = setTimeout(function() {
                    self.getSuggestList();
                }, 400);
            }

        },

        input_focus: function() {
            if (this.getText() == this.ops.defaultText) {
                this.setNormalState();
            }
        },

        input_blur: function() {
            if (this.getText() == '') {
                this.setDefaultState();
            }
        },

        btn_click: function() {
            if (this.getText() != '' && this.getText() != this.ops.defaultText) {
                window.location.href = this.ops.searchUrl + encodeURIComponent(this.getText());
            }
        },

        input_key_down: function(ev) {
            switch (ev.key) {
                case 'enter':
                    if (this.gotourl != '') {
                        window.location.href = this.gotourl;
                    } else {
                        this.btn_click();
                    }
                    break;
                case 'up':
                    this.setSelectedItem(-1);
                    break;
                case 'down':
                    this.setSelectedItem(1);
                    break;
            }
        }
    });

    if (typeof $.IKan == 'undefined') {
        $.IKan = {};
    }

    $.IKan.SearchTips = SearchTips;

})(JUI);