четверг, 4 июня 2015 г.

JavaScript Build Folder Tree Visualization

var fs = require('fs') // file system module
    , path = require('path'); // file path module

// Function dirTree() returns json tree of directory structure
function dirTree (rootDirectoryPath) {
    rootDirectoryPath = rootDirectoryPath.replace(/\/+$/ , ''); // clean trailing '/'(s)
   
    var directoryElement
        , treeObject;
       
    if (fs.existsSync(rootDirectoryPath)) {directoryElement = fs.lstatSync(rootDirectoryPath); // extract tree element if root exists
    } else {return 'Error: root does not exist.';
    }
   
    // tree treeObjectect info
    var treeObject = {
          path: rootDirectoryPath
        , name: path.basename(rootDirectoryPath)
        , type: 'unknown'
        , children: []
    };
   
    if (directoryElement.isDirectory()) {
        // execute for each child and call tree recursively
        treeObject.children = fs.readdirSync(rootDirectoryPath).map(function(child){return dirTree(rootDirectoryPath + '/' + child);});
                                                                      treeObject.type = 'folder';
    } else if (directoryElement.isFile()) {             treeObject.type = 'file';
    } else if (directoryElement.isSymbolicLink()) {treeObject.type = 'link';
    } else {                                                       treeObject.type = 'unknown';
    }
   
    return treeObject; // return tree
}

/*

// Пример использования dirTree()

console.log(JSON.stringify(dirTree('./node_modules')));
console.log(dirTree('./node_modules'));

*/

// hierarchy(obj, prefix='', opts={})
// Функция 'hierarchy' возвращает строку, представляющую из себя иерархию элементов объекта 'obj', соединенных с помощью символов труб в формате Unicode.
// 'obj' должен представлять из себя дерево, состоящее из вложенных друг в друга объектов, имеющих метки 'name' и массивы узлов 'children'.
// 'name' - это строка с текстом, который выводится на соотвествующем уровне узла, а 'children' - это массив зависимостей текущего узла.
// Если узел является строкой, то эта строка будет использована в качестве метки 'name', а вместо узла 'children' для нее будет использован пустой массив.
// 'prefix' - это строка, которая вставляется перед основным содержимым на каждом шаге сформированного графа. Используется внутри алгоритма  рекурсивного обхода дерева.
// Если метка 'name' имеет внутри себя символы перехода на новую строку (\n), то в этом случае они будут использованы в качестве перехода на новую строку в месте вывода текста метки, учитывая текущий отступ и 'prefix' на данному уровне.
// Для отключения вывода результата выполнения функции в формате Unicode при предпочтении вывода результата в формате ANSI установите значение opts.unicode в false.

function hierarchy (obj, prefix, opts) {

    if (prefix === undefined) {prefix = '';}
    if (!opts) {opts = {};}

    function chr (s) {
        var chars = {
            '│' : '|',
            '└' : '`',
            '├' : '+',
            '─' : '-',
            '┬' : '-'
        };
        return opts.unicode === false ? chars[s] : s;
    };
 
    if (typeof obj === 'string') {
        obj = {name: obj};
    }
 
    var children = obj.children || []
        , lines = (obj.name || '').split('\n')
        , splitter = '\n' + prefix + (children.length ? chr('│') : ' ') + ' ';
 
    return prefix
           + lines.join(splitter) + '\n'
           + children.map(function (child, ix) {
                                    var last = ix === children.length - 1
                                       , more = child.children && child.children.length
                                       , prefix_ = prefix + (last ? ' ' : chr('│')) + ' ';

                                    return prefix
                                           + (last ? chr('└') : chr('├')) + chr('─')
                                           + (more ? chr('┬') : chr('─')) + ' '
                                           + hierarchy(child, prefix_, opts).slice(prefix.length + 2);
                               }).join('');

}

/*

// Пример использования hierarchy() без префикса 'prefix' и опций 'opts'

var result = hierarchy(
    {
          name: 'beep'
        , children: [
              'ity'
            , {
                  name: 'boop'
                , children: [
                      {
                            name: 'o_O'
                          , children: [
                                {
                                      name: 'oh'
                                    , children: [
                                          'hello'
                                        , 'puny'
                                      ]
                                }
                              , 'human'
                            ]
                      }
                    , 'party\ntime!'
                  ]
              }
          ]
    }
);

console.log(result);

// beep
// ├── ity
// └─┬ boop
//   ├─┬ o_O
//   │ ├─┬ oh
//   │ │ ├── hello
//   │ │ └── puny
//   │ └── human
//   └── party
//          time!

// Пример использования hierarchy() с префиксом 'prefix' и опциями 'opts'

var result = hierarchy(
      {
          name: 'beep'
        , children: [
              'ity'
            , {
                  name: 'boop'
                , children: [
                      {
                            name: 'o_O'
                          , children: [
                                {
                                      name: 'oh'
                                    , children: [
                                          'hello'
                                        , 'puny'
                                      ]
                                }
                              , 'human'
                            ]
                      }
                    , 'party\ntime!'
                  ]
              }
          ]
      }
    , '...'
    , {unicode: false}
);

console.log(result);

// ...beep
// ...+-- ity
// ...`-- boop
// ...  +-- o_O
// ...  | +-- oh
// ...  | | +-- hello
// ...  | | `-- puny
// ...  | `-- human
// ...  `-- party
// ...      time!

*/

console.log(hierarchy(dirTree('./node_modules')));

/*
if (module.parent === undefined) {
    // child dirTree.js ~/foo/bar
    var util = require('util');
    console.log(util.inspect(dirTree(process.argv[2]), false, null));
}
*/

вторник, 2 июня 2015 г.

JavaScript File Download

<!DOCTYPE html>
<html>
<head>
    <title>JavaScript File Download</title>
</head>
<body>
    <script type="text/javascript">
        function saveAs (fileContents, fileName) {
            fileContents = 'data:text/plain;charset=utf-8;headers=Content-Disposition%3A%20attachment%3B%20filename%3D%22with%20spaces.txt%22%0D%0A,' + escape(fileContents);
            var downloadWindow
                , link = document.createElement('a');
            // For non-IE
            if (! window.ActiveXObject && typeof link.download === 'string') {
                document.body.appendChild(link); // Firefox requires the link to be in the body
                link.download = fileName;
                link.href = fileContents;
                link.target = '_blank';
                link.click(); // simulate click
                document.body.removeChild(link); // remove the link when done
            } else if (!! window.ActiveXObject && document.execCommand) {
                // For IE
                window.open(fileContents, '_blank');
                document.execCommand('SaveAs', true, fileName || fileContents);
                document.close();
            }
        }
        saveAs('Text', 'File.txt');
    </script>
</body>
</html>

четверг, 28 мая 2015 г.

JavaScript Object Hierarchy Visualization Tree Function

// hierarchy(obj, prefix='', opts={})
// Функция 'hierarchy' возвращает строку, представляющую из себя иерархию элементов объекта 'obj', соединенных с помощью символов труб в формате Unicode.
// 'obj' должен представлять из себя дерево, состоящее из вложенных друг в друга объектов, имеющих метки 'label' и массивы узлов 'nodes'.
// 'label' это строка с текстом, который выводится на соотвествующем уровне узла, а 'nodes' - это массив зависимостей текущего узла.
// Если узел является строкой, то эта строка будет использована в качестве метки 'label', а вместо узла 'nodes' для нее будет использован пустой массив.
// 'prefix' - это строка, которая вставляется перед основным содержимым на каждом шаге сформированного графа. Используется внутри алгоритма  рекурсивного обхода дерева.
// Если метка 'label' имеет внутри себя символы перехода на новую строку (\n), то в этом случае они будут использованы в качестве перехода на новую строку в месте вывода текста метки, учитывая текущий отступ и 'prefix' на данному уровне.
// Для отключения вывода результата выполнения функции в формате Unicode при предпочтении вывода результата в формате ANSI установите значение opts.unicode в false.

function hierarchy (obj, prefix, opts) {

    if (prefix === undefined) {prefix = '';}
    if (!opts) {opts = {};}

    function chr (s) {
        var chars = {
            '│' : '|',
            '└' : '`',
            '├' : '+',
            '─' : '-',
            '┬' : '-'
        };
        return opts.unicode === false ? chars[s] : s;
    };
 
    if (typeof obj === 'string') {
        obj = {label: obj};
    }
 
    var nodes = obj.nodes || []
        , lines = (obj.label || '').split('\n')
        , splitter = '\n' + prefix + (nodes.length ? chr('│') : ' ') + ' ';
 
    return prefix
           + lines.join(splitter) + '\n'
           + nodes.map(function (node, ix) {
                                    var last = ix === nodes.length - 1
                                       , more = node.nodes && node.nodes.length
                                       , prefix_ = prefix + (last ? ' ' : chr('│')) + ' ';

                                    return prefix
                                           + (last ? chr('└') : chr('├')) + chr('─')
                                           + (more ? chr('┬') : chr('─')) + ' '
                                           + hierarchy(node, prefix_, opts).slice(prefix.length + 2);
                               }).join('');

}

// Пример использования без префикса 'prefix' и опций 'opts'

var result = hierarchy(
    {
          label: 'beep'
        , nodes: [
              'ity'
            , {
                  label: 'boop'
                , nodes: [
                      {
                            label: 'o_O'
                          , nodes: [
                                {
                                      label: 'oh'
                                    , nodes: [
                                          'hello'
                                        , 'puny'
                                      ]
                                }
                              , 'human'
                            ]
                      }
                    , 'party\ntime!'
                  ]
              }
          ]
    }
);

console.log(result);

// beep
// ├── ity
// └─┬ boop
//   ├─┬ o_O
//   │ ├─┬ oh
//   │ │ ├── hello
//   │ │ └── puny
//   │ └── human
//   └── party
//          time!

// Пример использования с префиксом 'prefix' и опциями 'opts'

var result = hierarchy(
      {
          label: 'beep'
        , nodes: [
              'ity'
            , {
                  label: 'boop'
                , nodes: [
                      {
                            label: 'o_O'
                          , nodes: [
                                {
                                      label: 'oh'
                                    , nodes: [
                                          'hello'
                                        , 'puny'
                                      ]
                                }
                              , 'human'
                            ]
                      }
                    , 'party\ntime!'
                  ]
              }
          ]
      }
    , '...'
    , {unicode: false}
);

console.log(result);

// ...beep
// ...+-- ity
// ...`-- boop
// ...  +-- o_O
// ...  | +-- oh
// ...  | | +-- hello
// ...  | | `-- puny
// ...  | `-- human
// ...  `-- party
// ...      time!

четверг, 21 мая 2015 г.

Как определить, является ли функция нативной

;(function() {

  // Используется для разложения на составляющие внутреннего `[[Class]]` значений
  var toString = Object.prototype.toString;

  // Используется для разложения на составляющие декомпилированного
  // исходного кода функции
  var fnToString = Function.prototype.toString;

  // Используется для определения конструкторов среды (Safari > 4;
  // по сути, предназначено специально для типизированных массивов)
  var reHostCtor = /^\[object .+?Constructor\]$/;

  // Составление регулярного выражения на основе часто употребляемого
  // нативного метода в качестве шаблона.
  // Выбираем `Object#toString`, так как вполне вероятно, что он ещё не задействован.
  var reNative = RegExp('^' +
    // Применяем `Object#toString` к строке
    String(toString)
    // Избавляемся от любых специальных символов регулярных выражений
    .replace(/[.*+?^${}()|[\]\/\\]/g, '\\$&')
    // Заменяем упоминания `toString` на `.*?`, чтобы сохранить обобщённый вид шаблона.
    // Заменяем `for ...` и тому подобное для поддержки окружений вроде Rhino,
    // которые добавляют дополнительную информацию, такую как арность метода.
    .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
  );

  function isNative(value) {
    var type = typeof value;
    return type == 'function'
      // Используем `Function#toString`, чтобы обойти собственный метод
      // `toString` самого значения и избежать ложного результата.
      ? reNative.test(fnToString.call(value))
      // На всякий случай выполняем проверку на наличие объектов среды, так
      // как некоторые окружения могут представлять компоненты вроде
      // типизированных массивов как методы DOM, что может не соответствовать
      // нормальному нативному паттерну.
      : (value && type == 'object' && reHostCtor.test(toString.call(value))) || false;
  }

  // экспортируем в удобном для вас виде
  module.exports = isNative;
}());

понедельник, 20 апреля 2015 г.

Node JS - Полные Client и Server

Клиент

Файл initRequest.js

var request = require('./request');
var cheerio = require('cheerio');

var data = JSON.stringify({
      'key1': 'value1'
    , 'key2': 'value2'
});

request.httpRequest({
      host: '127.0.0.1'
    , port: 80
    , url: '/'
    , username: 'boris'
    , password: '12345'
    , type: 'get'
    , cache: false
    , headers: {
                          'Content-Type': 'application/x-www-form-urlencoded'
                        , 'Content-Length': data.length
                    }
    , data: data
    , dataType: 'html'
    , timeout: 120000
    , success: function (data) {
        var $ = cheerio.load(data);
        console.log('Request body text inside <p></p>: ' + $('p').first().text());
      }
    , error: function (error) {console.log('New request error');}
});

Файл request.js

var http = require('http');

function randomNumber (min, max) {
    min = parseInt(min, 10);
    max = parseInt(max, 10);
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

var commonHeaders = {
      'Accept': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.' + randomNumber(2, 5)
    , 'Accept-Language': 'en-us,en;q=0.' + randomNumber(5, 9)
    , 'Accept-Charset': 'utf-8,windows-1251;q=0.7,*;q=0.' + randomNumber(5, 7)
    , 'Keep-Alive': '300'
    , 'Expect': ''
};

function httpRequest (options) {
    options = options || {};

    if (!options.hasOwnProperty('host')) {options.host = 'localhost';}
    if (!options.hasOwnProperty('port')) {options.port = 80;}
    if (!options.hasOwnProperty('url')) {options.url = '/';}
    if (!options.hasOwnProperty('type')) {options.type = 'GET';}
    if (!options.hasOwnProperty('headers')) {options.headers = {};}
    if (!options.hasOwnProperty('username')) {options.username = '';}
    if (!options.hasOwnProperty('password')) {options.password = '';}
    if (!options.hasOwnProperty('data')) {options.data = {};}
    if (!options.hasOwnProperty('dataType')) {options.dataType = 'text';}
    if (!options.hasOwnProperty('cache')) {options.cache = true;}
    if (!options.hasOwnProperty('timeout')) {options.timeout = 120000;}
    if (!options.hasOwnProperty('success')) {options.success = function(){};}
    if (!options.hasOwnProperty('error')) {options.error = function(error){console.log('Request error: ' + error);};}

    for (var key in options.headers) {
        commonHeaders[key] = options.headers[key];
    }
    
    options.type = options.type.toUpperCase();
    
    if (options.type === 'GET' && options.data.length > 0) {
        options.url += '?' + options.data;
    }
    
    if (options.cache === false) {
        options.url += ( (/\?/).test(options.url) ? '&' : '?' ) + "_=" + randomNumber(1000000000, 9999999999);
    }
    
    if (options.data.length > 0 && options.headers['Content-Type'] === undefined) {
        options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
    }

    var requestOptions = {
          hostname: options.host
        , port: options.port
        , path: options.url
        , method: options.type
        , headers: options.headers
    };
    
    if (options.username !== '') {
        requestOptions.auth = options.username + ':' + options.password;
    }

    var request = http.request(requestOptions, function(response) {
        console.log('Response status code: ' + response.statusCode);
        console.log('Response headers: ' + JSON.stringify(response.headers));
        response.setEncoding('utf8');
        var data = '';
        response.on('data', function(chunk){
            data += chunk;
        });
        response.on('end', function(){
            if (options.dataType === 'json') {
                data = JSON.parse(data);
            }
            options.success(data);
        });
    });

    request.setTimeout(options.timeout, function(){
        request.abort();
        console.log('Request timeout');
    });
    
    request.on('error', options.error);

    // write data to request body
    if (options.type === 'POST' && options.data.length > 0) {
        request.write(options.data);
    }
    request.end(); // Always need to write request.end();
}

exports.httpRequest = httpRequest;

Сервер

Файл startServer.js

var http = require('http')
    , url = require('url')
    , router = require('./router');

function onRequest (request, response) {
    console.log('Request received.');
    
    var pathname = url.parse(request.url).pathname
        , postData = '';
        
    request.setEncoding('utf8');
    
    request.addListener('data', function(postDataChunk){
        postData += postDataChunk;
        console.log('Received POST data chunk ' + postDataChunk);
    });
    
    request.addListener('end', function(){
        router(pathname, request, response, postData);
    });
}

http.createServer(onRequest).listen(80);

console.log('Server has started at 127.0.0.1:80');

Файл router.js

var urls = require('./urls');

function router (pathname, request, response, postData) {
    console.log('pathname: ' + pathname);
    if (pathname.length > 1 && pathname.slice(-1) === '/') {
        pathname = pathname.slice(0, -1);
    }
    console.log('pathname after slice: ' + pathname);
    if (typeof urls[pathname] === 'function') {
        return urls[pathname](request, response, postData);
    } else {
        console.log('No request urls for ' + pathname);
        urls['404'](request, response, postData);
    }
}

exports.router = router;

Файл urls.js

var requestHandlers = require('./requestHandlers')
    , urls = {};
    
urls['/'] = requestHandlers.start;
urls['/start'] = requestHandlers.start;
urls['/upload'] = requestHandlers.upload;
urls['/form'] = requestHandlers.form;
urls['/send'] = requestHandlers.send;
urls['/upload_file_form'] = requestHandlers.uploadFileForm;
urls['/receive_file'] = requestHandlers.receiveFile;
urls['/show'] = requestHandlers.show;
urls['/send_html'] = requestHandlers.sendHTML;
urls['404'] = requestHandlers.notFound;

exports.urls = urls;

Файл requestHandlers.js

var querystring = require('querystring')
    , fs = require('fs')
    , exec = require('child_process').exec;
    
function sleep (milliseconds) {
    var startTime = (new Date()).getTime()
        , endTime = startTime + milliseconds;
    while ((new Date()).getTime() < endTime) {}
}
    
var requestHandlers = {
      start: function (request, response, postData) {
        console.log('Request "start" was called.');
        sleep(5000);
        response.writeHead(200, {'Content-Type': 'text/plain', 'Cache-Control': 'max-age=3600'});
        response.setHeader('name', 'value');
        response.removeHeader('name', 'value');
        response.write('Hello!');
        response.write(' | pathname: ' + pathname);
        if (postData) {response.write(postData);}
        response.end(' | The end.');
      }
      
    , upload: function (request, response, postData) {
        console.log('Request "upload" was called.');
        var content = 'empty';
        exec('dir', function(error, stdout, stderr){
            content = stdout;
            console.log(1);
        });
        console.log(2);
        return content;
      }
      
    , form: function (request, response, postData) {
        var html = '<!doctype html><html><head><meta charset="utf-8" /><title>FORM</title></head><body><form method="post" action="/send"><textarea name="text" rows="20" cols="60"></textarea><input type="submit" value="Send text" /></form></body></html>';
        response.writeHead(200, {'Content-Type': 'text/html'});
        response.write(html);
        response.end();
      }
      
    , send: function (request, response, postData) {
        console.log('Request urlsr "send" was called.');
        var query = querystring.parse(postData).text;
        response.writeHead(200, {'Content-Type': 'text/plain'});
        response.write('You sent: ' + query);
        response.end();
      }
      
    , uploadFileForm: function (request, response, postData) {
        var html = '<!doctype html><html><head><meta charset="utf-8" /><title>FORM</title></head><body><form method="post" enctype="multipart/form-data" action="/receive_file"><input type="text" name="title" /><br /><input type="file" name="upload" /><br /><input type="submit" value="Upload" /></form></body></html>';
        response.writeHead(200, {'Content-Type': 'text/html'});
        response.write(html);
        response.end();
      }
      
    , receiveFile: function (request, response, postData) {
        if (request.method.toLowerCase() === 'post') {
            response.writeHead(200, {'Content-Type': 'text/plain'});
            response.write('File uploaded');
           
            response.end();
        }
      }
      
    , show: function(request, response, postData) {
        console.log('Request urlsr "show" was called.');
        fs.readFile('./tmp/test.png', 'binary', function(error, fileData){
            if (error) {
                response.writeHead(500, {'Content-Type': 'text/plain'});
                response.write(error + '\n');
                response.end();
            } else {
                response.writeHead(200, {'Content-Type': 'image/png'});
                response.write(fileData, 'binary');
                response.end();
            }
        });
      }
      
    , sendHTML: function (request, response, postData) {
        console.log('Request urlsr "sendHTML" was called.');
        fs.readFile('./tmp/index.html', function(error, fileData){
            if (error) {
                response.writeHead(500, {'Content-Type': 'text/plain'});
                response.write(error + '\n');
                response.end();
            } else {
                response.writeHead(200, {'Content-Type': 'text/html'});
                response.write(fileData);
                response.end();
            }
        });
      }
      
    , notFound: function (request, response, postData) {
        console.log('Request "404 - Not Found" was called.');
        response.writeHead(404, {'Content-Type': 'text/plain'});
        response.write('404 Not found');
        response.end();
      }
      
};

exports.requestHandlers = requestHandlers;

Файл filesystem.js

// НЕ ЗАБЫТЬ ЧТО ВЕСЬ КОД РАБОТАЕТ АСИНХРОННО

var fs = require('fs');

// Создать
var fileNameAndPath = './tmp/test.txt'
    , fileData = 'Hello!';
    
fs.writeFile(fileNameAndPath, fileData, function (error) {
    if (error) {throw error;}
    console.log('Done');
});

// Прочитать
fs.exists(fileNameAndPath, function(exists){
    if (exists) {
        fs.stat(fileNameAndPath, function(error, stats){
            fs.open(fileNameAndPath, 'r', function(error, fileData){
                if (error) {throw error;}
                var readBuffer = new Buffer(stats.size)
                    , bufferOffset = 0
                    , bufferLength = readBuffer.length
                    , filePosition = 0;
                fs.read(fileData, readBuffer, bufferOffset, bufferLength, filePosition, function(error, readBytes){
                    if (error) {throw error;}
                    var fileDataChunk = readBuffer.toString('utf8', 0, bufferLength);
                    console.log(fileDataChunk);
                    console.log(readBuffer.slice(0, readBytes));
                    fs.close(fileData);
                });
            });
        });
    }
});

// Записать
fs.exists(fileNameAndPath, function(exists){
    if (exists) {
        fs.open(fileNameAndPath, 'a', function(error, fileData){
            if (error) {throw error;}
            var writeBufferData = new Buffer(' World!')
                , bufferPosition = 0
                , bufferLength = writeBufferData.length
                , filePosition = 0;
            fs.write(fileData, writeBufferData, bufferPosition, bufferLength, filePosition, function(error, written){
                if (error) {
                    throw error;
                } else {
                    fs.close(fileData, function(){
                        console.log('Wrote ' + written + ' bytes.');
                    });
                }
            });
        });
    }
});

// Переименовать
var oldPath = './tmp/test.txt'
    , newPath = './tmp/newtest.txt';

fs.rename(oldPath, newPath, function (error) {
    // В Windows возможна ошибка при попытке переименования уже существующего файла
    if (error) {
        // throw error;
        fs.unlink(newPath);
        fs.rename(oldPath, newPath);
    }
    console.log('Done');
});

// Удалить
fs.unlink(newPath, function (error) {
    if (error) {throw error;}
    console.log('Done');
});

Файл index.html

<html><head><title>Zagolovok</title></head></body>Telo</body></html>

понедельник, 23 марта 2015 г.

Архитектура модулей MVC R на JavaScript

Models - данные при своем изменении сообщают об этом наблюдателям.
Views - следят за изменением в моделях и отображают данные на экране.
Controllers - отслеживают что происходит когда пользователь взаимодейтсвует с интерфейсом view.
Router - отслеживает переходы пользователя по URL и вызывает соответсвующие им функции.
Observer - отслеживает изменения в данных.

Пример модуля:
/calendar
- calendar.Model.js
- calendar.View.html
- calendar.Controller.js
- calendar.Router.js
- calendar.Css.css
/calendar/img/calendar.icon.png
/calendar/img/calendar.dark.background.png

var calendar = {};
calendar.Model = {};
calendar.View = {};
calendar.Controller = {};
calendar.Router = {};
calendar.Observer = {};

вторник, 10 марта 2015 г.

URL Parsing with JavaScript


Server-side URL Parsing

// Server-side JavaScript
var urlapi = require('url'),
      url = urlapi.parse('http://site.com:81/path/page?a=1&b=2#hash');

console.log(
    url.href + '\n' +              // the full URL
    url.protocol + '\n' +       // http:
    url.hostname + '\n' +     // site.com
    url.port + '\n' +             // 81
    url.pathname + '\n' +    // /path/page
    url.search + '\n' +         // ?a=1&b=2
    url.hash                        // #hash
);

Client-side URL Parsing

// Client-side JavaScript
// find the first link in the DOM
var url = document.getElementsByTagName('a')[0];

console.log(
    url.href + '\n' +           // the full URL
    url.protocol + '\n' +    // http:
    url.hostname + '\n' +  // site.com
    url.port + '\n' +           // 81
    url.pathname + '\n' +  // /path/page
    url.search + '\n' +       // ?a=1&b=2
    url.hash                      // #hash
);

If we have a URL string, we can use it on an in-memory anchor element (a) so it can be parsed without regular expressions, e.g.:

// Client-side JavaScript
// create dummy link
var url = document.createElement('a');
url.href = 'http://site.com:81/path/page?a=1&b=2#hash';

console.log(url.hostname); // site.com

Isomorphic URL Parsing

// lib.js library functions

// running on Node.js?
var isNode = (typeof module === 'object' && module.exports);

// alternative check of running on Node.js?
// var isNode = typeof window === 'undefined';

(function(lib) {

    "use strict";

    // require Node URL API
    var url = (isNode ? require('url') : null);

    // parse URL
    lib.URLparse = function(str) {

        if (isNode) {
            return url.parse(str);
        }
        else {
            url = document.createElement('a');
            url.href = str;
            return url;
        }

    }

})(isNode ? module.exports : this.lib = {});

Server-side, URLparse is exported as a Common.JS module. To use it:

// include lib.js module
var lib = require('./lib.js');

var url = lib.URLparse('http://site.com:81/path/page?a=1&b=2#hash');
console.log(
    url.href + '\n' +           // the full URL
    url.protocol + '\n' +       // http:
    url.hostname + '\n' +       // site.com
    url.port + '\n' +           // 81
    url.pathname + '\n' +       // /path/page
    url.search + '\n' +         // ?a=1&b=2
    url.hash                    // #hash
);

Client-side, URLparse is added as a method to the global lib object:

<script src="./lib.js"></script>
<script>
var url = lib.URLparse('http://site.com:81/path/page?a=1&b=2#hash');
console.log(
    url.href + '\n' +           // the full URL
    url.protocol + '\n' +       // http:
    url.hostname + '\n' +       // site.com
    url.port + '\n' +           // 81
    url.pathname + '\n' +       // /path/page
    url.search + '\n' +         // ?a=1&b=2
    url.hash                    // #hash
);
</script>