The source code

    /**
     * @ignore
     * Uri class for KISSY.
     * @author yiminghe@gmail.com
     */
    (function (S, undefined) {

        var reDisallowedInSchemeOrUserInfo = /[#\/\?@]/g,
            reDisallowedInPathName = /[#\?]/g,
        // ?? combo of taobao
            reDisallowedInQuery = /[#@]/g,
            reDisallowedInFragment = /#/g,

            URI_SPLIT_REG = new RegExp(
                '^' +
                    /*
                     Scheme names consist of a sequence of characters beginning with a
                     letter and followed by any combination of letters, digits, plus
                     ('+'), period ('.'), or hyphen ('-').
                     */
                    '(?:([\\w\\d+.-]+):)?' + // scheme

                    '(?://' +
                    /*
                     The authority component is preceded by a double slash ('//') and is
                     terminated by the next slash ('/'), question mark ('?'), or number
                     sign ('#') character, or by the end of the URI.
                     */
                    '(?:([^/?#@]*)@)?' + // userInfo

                    '(' +
                    '[\\w\\d\\-\\u0100-\\uffff.+%]*' +
                    '|' +
                    // ipv6
                    '\\[[^\\]]+\\]' +
                    ')' + // hostname - restrict to letters,
                    // digits, dashes, dots, percent
                    // escapes, and unicode characters.
                    '(?::([0-9]+))?' + // port
                    ')?' +
                    /*
                     The path is terminated
                     by the first question mark ('?') or number sign ('#') character, or
                     by the end of the URI.
                     */
                    '([^?#]+)?' + // path. hierarchical part
                    /*
                     The query component is indicated by the first question
                     mark ('?') character and terminated by a number sign ('#') character
                     or by the end of the URI.
                     */
                    '(?:\\?([^#]*))?' + // query. non-hierarchical data
                    /*
                     The fragment identifier component of a URI allows indirect
                     identification of a secondary resource by reference to a primary
                     resource and additional identifying information.

                     A
                     fragment identifier component is indicated by the presence of a
                     number sign ('#') character and terminated by the end of the URI.
                     */
                    '(?:#(.*))?' + // fragment
                    '$'),

            Path = S.Path,

            REG_INFO = {
                scheme: 1,
                userInfo: 2,
                hostname: 3,
                port: 4,
                path: 5,
                query: 6,
                fragment: 7
            };

        function parseQuery(self) {
            if (!self._queryMap) {
                self._queryMap = S.unparam(self._query);
            }
        }

        /**
         * @class KISSY.Uri.Query
         * Query data structure.
         * @param {String} [query] encoded query string(without question mask).
         */
        function Query(query) {
            this._query = query || '';
        }

        Query.prototype =
        {
            constructor: Query,

            /**
             * Cloned new instance.
             * @return {KISSY.Uri.Query}
             */
            clone: function () {
                return new Query(this.toString());
            },

            /**
             * reset to a new query string
             * @param {String} query
             * @chainable
             */
            reset: function (query) {
                var self = this;
                self._query = query || '';
                self._queryMap = null;
                return self;
            },

            /**
             * Parameter count.
             * @return {Number}
             */
            count: function () {
                var self = this, count = 0,
                    _queryMap = self._queryMap,
                    k;
                parseQuery(self);
                for (k in _queryMap) {

                    if (S.isArray(_queryMap[k])) {
                        count += _queryMap[k].length;
                    } else {
                        count++;
                    }

                }
                return count;
            },

            /**
             * judge whether has query parameter
             * @param {String} [key]
             */
            has: function (key) {
                var self = this, _queryMap;
                parseQuery(self);
                _queryMap = self._queryMap;
                if (key) {
                    return key in _queryMap;
                } else {
                    return !S.isEmptyObject(_queryMap);
                }
            },

            /**
             * Return parameter value corresponding to current key
             * @param {String} [key]
             */
            get: function (key) {
                var self = this, _queryMap;
                parseQuery(self);
                _queryMap = self._queryMap;
                if (key) {
                    return _queryMap[key];
                } else {
                    return _queryMap;
                }
            },

            /**
             * Parameter names.
             * @return {String[]}
             */
            keys: function () {
                var self = this;
                parseQuery(self);
                return S.keys(self._queryMap);
            },

            /**
             * Set parameter value corresponding to current key
             * @param {String} key
             * @param value
             * @chainable
             */
            set: function (key, value) {
                var self = this, _queryMap;
                parseQuery(self);
                _queryMap = self._queryMap;
                if (typeof key == 'string') {
                    self._queryMap[key] = value;
                } else {
                    if (key instanceof Query) {
                        key = key.get();
                    }
                    S.each(key, function (v, k) {
                        _queryMap[k] = v;
                    });
                }
                return self;
            },

            /**
             * Remove parameter with specified name.
             * @param {String} key
             * @chainable
             */
            remove: function (key) {
                var self = this;
                parseQuery(self);
                if (key) {
                    delete self._queryMap[key];
                } else {
                    self._queryMap = {};
                }
                return self;

            },

            /**
             * Add parameter value corresponding to current key
             * @param {String} key
             * @param value
             * @chainable
             */
            add: function (key, value) {
                var self = this,
                    _queryMap,
                    currentValue;
                if (S.isObject(key)) {
                    if (key instanceof Query) {
                        key = key.get();
                    }
                    S.each(key, function (v, k) {
                        self.add(k, v);
                    });
                } else {
                    parseQuery(self);
                    _queryMap = self._queryMap;
                    currentValue = _queryMap[key];
                    if (currentValue === undefined) {
                        currentValue = value;
                    } else {
                        currentValue = [].concat(currentValue).concat(value);
                    }
                    _queryMap[key] = currentValue;
                }
                return self;
            },

            /**
             * Serialize query to string.
             * @param {Boolean} [serializeArray=true]
             * whether append [] to key name when value 's type is array
             */
            toString: function (serializeArray) {
                var self = this;
                parseQuery(self);
                return S.param(self._queryMap, undefined, undefined, serializeArray);
            }
        };

        function padding2(str) {
            return str.length == 1 ? '0' + str : str;
        }

        function equalsIgnoreCase(str1, str2) {
            return str1.toLowerCase() == str2.toLowerCase();
        }

        // www.ta#bao.com // => www.ta.com/#bao.com
        // www.ta%23bao.com
        // Percent-Encoding
        function encodeSpecialChars(str, specialCharsReg) {
            // encodeURI( ) is intended to encode complete URIs,
            // the following ASCII punctuation characters,
            // which have special meaning in URIs, are not escaped either:
            // ; / ? : @ & = + $ , #
            return encodeURI(str).replace(specialCharsReg, function (m) {
                return '%' + padding2(m.charCodeAt(0).toString(16));
            });
        }

        /**
         * @class KISSY.Uri
         * Uri class for KISSY.
         * Most of its interfaces are same with window.location.
         * @param {String|KISSY.Uri} [uriStr] Encoded uri string.
         */
        function Uri(uriStr) {

            if (uriStr instanceof  Uri) {
                return uriStr['clone']();
            }

            var components, self = this;

            S.mix(self,
                {
                    /**
                     * scheme such as 'http:'. aka protocol without colon
                     * @type {String}
                     */
                    scheme: '',
                    /**
                     * User credentials such as 'yiminghe:gmail'
                     * @type {String}
                     */
                    userInfo: '',
                    /**
                     * hostname such as 'docs.kissyui.com'. aka domain
                     * @type {String}
                     */
                    hostname: '',
                    /**
                     * Port such as '8080'
                     * @type {String}
                     */
                    port: '',
                    /**
                     * path such as '/index.htm'. aka pathname
                     * @type {String}
                     */
                    path: '',
                    /**
                     * Query object for search string. aka search
                     * @type {KISSY.Uri.Query}
                     */
                    query: '',
                    /**
                     * fragment such as '#!/test/2'. aka hash
                     */
                    fragment: ''
                });

            components = Uri.getComponents(uriStr);

            S.each(components, function (v, key) {
                v = v || '';
                if (key == 'query') {
                    // need encoded content
                    self.query = new Query(v);
                } else {
                    // need to decode to get data structure in memory
                    self[key] = S.urlDecode(v);
                }
            });

            return self;
        }

        Uri.prototype =
        {

            constructor: Uri,

            /**
             * Return a cloned new instance.
             * @return {KISSY.Uri}
             */
            clone: function () {
                var uri = new Uri(), self = this;
                S.each(REG_INFO, function (index, key) {
                    uri[key] = self[key];
                });
                uri.query = uri.query.clone();
                return uri;
            },

            /**
             * The reference resolution algorithm.rfc 5.2
             * return a resolved uri corresponding to current uri
             * @param {KISSY.Uri|String} relativeUri
             *
             * for example:
             *      @example
             *      this: 'http://y/yy/z.com?t=1#v=2'
             *      'https:/y/' => 'https:/y/'
             *      '//foo' => 'http://foo'
             *      'foo' => 'http://y/yy/foo'
             *      '/foo' => 'http://y/foo'
             *      '?foo' => 'http://y/yy/z.com?foo'
             *      '#foo' => http://y/yy/z.com?t=1#foo'
             *
             * @return {KISSY.Uri}
             */
            resolve: function (relativeUri) {

                if (typeof relativeUri == 'string') {
                    relativeUri = new Uri(relativeUri);
                }

                var self = this,
                    override = 0,
                    lastSlashIndex,
                    order = ['scheme', 'userInfo', 'hostname', 'port', 'path', 'query', 'fragment'],
                    target = self.clone();

                S.each(order, function (o) {
                    if (o == 'path') {
                        // relativeUri does not set for scheme/userInfo/hostname/port
                        if (override) {
                            target[o] = relativeUri[o];
                        } else {
                            var path = relativeUri['path'];
                            if (path) {
                                // force to override target 's query with relative
                                override = 1;
                                if (!S.startsWith(path, '/')) {
                                    if (target.hostname && !target.path) {
                                        // RFC 3986, section 5.2.3, case 1
                                        path = '/' + path;
                                    } else if (target.path) {
                                        // RFC 3986, section 5.2.3, case 2
                                        lastSlashIndex = target.path.lastIndexOf('/');
                                        if (lastSlashIndex != -1) {
                                            path = target.path.slice(0, lastSlashIndex + 1) + path;
                                        }
                                    }
                                }
                                // remove .. / .  as part of the resolution process
                                target.path = Path.normalize(path);
                            }
                        }
                    } else if (o == 'query') {
                        if (override || relativeUri['query'].toString()) {
                            target.query = relativeUri['query'].clone();
                            override = 1;
                        }
                    } else if (override || relativeUri[o]) {
                        target[o] = relativeUri[o];
                        override = 1;
                    }
                });

                return target;

            },

            /**
             * Get scheme part
             */
            getScheme: function () {
                return this.scheme;
            },

            /**
             * Set scheme part
             * @param {String} scheme
             * @chainable
             */
            setScheme: function (scheme) {
                this.scheme = scheme;
                return this;
            },

            /**
             * Return hostname
             * @return {String}
             */
            getHostname: function () {
                return this.hostname;
            },

            /**
             * Set hostname
             * @param {String} hostname
             * @chainable
             */
            setHostname: function (hostname) {
                this.hostname = hostname;
                return this;
            },

            /**
             * Set user info
             * @param {String} userInfo
             * @chainable
             */
            'setUserInfo': function (userInfo) {
                this.userInfo = userInfo;
                return this;
            },

            /**
             * Get user info
             * @return {String}
             */
            getUserInfo: function () {
                return this.userInfo;
            },

            /**
             * Set port
             * @param {String} port
             * @chainable
             */
            'setPort': function (port) {
                this.port = port;
                return this;
            },

            /**
             * Get port
             * @return {String}
             */
            'getPort': function () {
                return this.port;
            },

            /**
             * Set path
             * @param {string} path
             * @chainable
             */
            setPath: function (path) {
                this.path = path;
                return this;
            },

            /**
             * Get path
             * @return {String}
             */
            getPath: function () {
                return this.path;
            },

            /**
             * Set query
             * @param {String|KISSY.Uri.Query} query
             * @chainable
             */
            'setQuery': function (query) {
                if (typeof query == 'string') {
                    if (S.startsWith(query, '?')) {
                        query = query.slice(1);
                    }
                    query = new Query(encodeSpecialChars(query, reDisallowedInQuery));
                }
                this.query = query;
                return this;
            },

            /**
             * Get query
             * @return {KISSY.Uri.Query}
             */
            getQuery: function () {
                return this.query;
            },

            /**
             * Get fragment
             * @return {String}
             */
            getFragment: function () {
                return this.fragment;
            },

            /**
             * Set fragment
             * @param {String} fragment
             * @chainable
             */
            'setFragment': function (fragment) {
                var self = this;
                if (S.startsWith(fragment, '#')) {
                    fragment = fragment.slice(1);
                }
                self.fragment = fragment;
                return self;
            },

            /**
             * Judge whether two uri has same domain.
             * @param {KISSY.Uri} other
             * @return {Boolean}
             */
            isSameOriginAs: function (other) {
                var self = this;
                // port and hostname has to be same
                return equalsIgnoreCase(self.hostname, other['hostname']) &&
                    equalsIgnoreCase(self.scheme, other['scheme']) &&
                    equalsIgnoreCase(self.port, other['port']);
            },

            /**
             * Serialize to string.
             * See rfc 5.3 Component Recomposition.
             * But kissy does not differentiate between undefined and empty.
             * @param {boolean} [serializeArray=true]
             * whether append [] to key name when value 's type is array
             * @return {String}
             */
            toString: function (serializeArray) {

                var out = [],
                    self = this,
                    scheme,
                    hostname,
                    path,
                    port,
                    fragment,
                    query,
                    userInfo;

                if (scheme = self.scheme) {
                    out.push(encodeSpecialChars(scheme, reDisallowedInSchemeOrUserInfo));
                    out.push(':');
                }

                if (hostname = self.hostname) {
                    out.push('//');
                    if (userInfo = self.userInfo) {
                        out.push(encodeSpecialChars(userInfo, reDisallowedInSchemeOrUserInfo));
                        out.push('@');
                    }

                    out.push(encodeURIComponent(hostname));

                    if (port = self.port) {
                        out.push(':');
                        out.push(port);
                    }
                }

                if (path = self.path) {
                    if (hostname && !S.startsWith(path, '/')) {
                        path = '/' + path;
                    }
                    path = Path.normalize(path);
                    out.push(encodeSpecialChars(path, reDisallowedInPathName));
                }

                if (query = ( self.query.toString.call(self.query, serializeArray))) {
                    out.push('?');
                    out.push(query);
                }

                if (fragment = self.fragment) {
                    out.push('#');
                    out.push(encodeSpecialChars(fragment, reDisallowedInFragment))
                }

                return out.join('');
            }
        };

        Uri.Query = Query;

        Uri.getComponents = function (url) {
            url = url || "";
            var m = url.match(URI_SPLIT_REG) || [],
                ret = {};
            S.each(REG_INFO, function (index, key) {
                ret[key] = m[index];
            });
            return ret;
        };

        S.Uri = Uri;

    })(KISSY);
    /*
     Refer
     - application/x-www-form-urlencoded
     - http://www.ietf.org/rfc/rfc3986.txt
     - http://en.wikipedia.org/wiki/URI_scheme
     - http://unixpapa.com/js/querystring.html
     - http://code.stephenmorley.org/javascript/parsing-query-strings-for-get-data/
     - same origin: http://tools.ietf.org/html/rfc6454
     */