Source: url.js

/**
 * @file URL解析和生成的小工具
 * @author Leon(leon@outlook.com)
 * @module url
 * @requires underscore
 */

var u = require('underscore');

var parts = [{
    delimiter: '#',
    name: 'hash'
}, {
    delimiter: '?',
    name: 'search'
}, {
    // 这里用了path,而没有用pathname的原因是与express的req保持一致
    name: 'path',
    delimiter: '/'
}, {
    delimiter: ':',
    name: 'port'
}];

function parseQuery(search) {

    var query = {};

    if (!search) {
        return query;
    }

    return u.reduce(
        search.slice(1).split('&'),
        function (result, term) {

            var index = term.indexOf('=');

            var name;
            var value;

            // 每项里可能会N个=号哟,我们只取头一个
            if (~index) {
                name = term.slice(0, index);
                value = term.slice(index + 1);
            }
            // 没有等号,那么就全是name,value是空字符
            else {
                name = term;
                value = '';
            }

            // 如果有当前值
            var currentValue = result[name];

            switch (typeof currentValue) {
                case 'string':
                    result[name] = [currentValue, value];
                    break;
                case 'object':
                    result[name].push(value);
                    break;
                default:
                    result[name] = value;
            }

            return result;
        },
        query
    );

}

/**
 * 解析URL
 *
 * @notice 只能处理正确的url
 *
 * @param {string} url url
 * @return {Object}
 */
exports.parse = function (url) {

    var result = {};

    var protocalIndex = url.indexOf('://');

    if (~protocalIndex) {
        result.protocal = url.slice(0, protocalIndex);
        url = url.slice(protocalIndex + 3);
    }

    for (var i = 0, len = parts.length; i < len; ++i) {
        var part = parts[i];

        var index = url.indexOf(part.delimiter);

        if (~index) {
            result[part.name] = url.slice(index);
            url = url.slice(0, index);
        }

    }

    if (result.port) {
        result.port = +result.port.slice(1);
    }

    if (url) {
        result.host = url;
    }

    result.query = parseQuery(result.search);

    return result;

};

/**
 * 根据path和query生成URL片段
 *
 * @param {!string} path url path
 * @param {?Object} query search
 * @return {string}
 */
exports.makeUrl = function (path, query) {

    return ''
        + path
        + '?'
        + u.map(
            query,
            function (key, name) {
                return encodeURIComponent(name) + '=' + encodeURIComponent(key);
            }
        ).join('&');

};