<!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>
вторник, 2 июня 2015 г.
четверг, 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!
// Функция '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;
}());
// Используется для разложения на составляющие внутреннего `[[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 = {};
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>
среда, 25 февраля 2015 г.
Node JS Client Request and Server Response
Маленькая библиотека для получения данных сайта в стиле jQuery AJAX.
Файл init.js
var request = require('./request');
var cheerio = require('cheerio');
var data = JSON.stringify({
'key1': 'value1'
, 'key2': 'value2'
});
request.httpRequest({
host: 'www.google.com'
, port: 80
, url: '/'
, type: 'POST'
, headers: {
'Content-Type': 'application/x-www-form-urlencoded'
, 'Content-Length': data.length
}
, data: data
, 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) {
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('data')) {options.data = {};}
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];
}
var requestOptions = {
hostname: options.host
, port: options.port
, path: options.url
, method: options.type
, headers: options.headers
};
var request = http.request(requestOptions, function(response) {
console.log('Response status code: ' + response.statusCode);
console.log('Response header: ' + JSON.stringify(response.headers));
response.setEncoding('utf8');
var data = '';
response.on('data', function(chunk){data += chunk;});
response.on('end', function(){options.success(data);});
});
request.setTimeout(options.timeout, function(){console.log('Request timeout');});
request.on('error', options.error);
if (options.data.length > 0) {request.write(options.data);}
request.end();
}
exports.httpRequest = httpRequest;
Пример запуска кода в консоли командной строки
node init.js
Пример полного взаимодействия клиента и сервера на Node JS.
Файл client.js
var http = require('http');
var postData = JSON.stringify({
'msg' : 'Hello World!'
});
var options = {
hostname: '127.0.0.1',
port: 80,
path: '/',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': postData.length
}
};
var request = http.request(options, function(response) {
console.log('STATUS: ' + response.statusCode);
console.log('HEADERS: ' + JSON.stringify(response.headers));
response.setEncoding('utf8');
response.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
});
request.on('error', function(error) {
console.log('problem with request: ' + error.message);
});
// write data to request body
request.write(postData);
request.end(); // Always need to write request.end();
Файл server.js
var server = require("./export");
var router = require("./router");
var requestHandlers = require("./requestHandlers");
var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;
handle["/show"] = requestHandlers.show;
server.start(router.route, handle);
Файл export.js
var http = require("http");
var url = require("url");
function start(route, handle) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
/*
var postData = "";
request.setEncoding("utf8");
request.addListener("data", function(postDataChunk) {
postData += postDataChunk;
console.log("Received POST data chunk '"+
postDataChunk + "'.");
});
request.addListener("end", function() {
route(handle, pathname, response, postData);
});
*/
route(handle, pathname, response, request);
}
http.createServer(onRequest).listen(80);
console.log("Server has started.");
}
exports.start = start;
Файл router.js
function route(handle, pathname, response, request/*postData*/) {
console.log("About to route a request for " + pathname);
if (typeof handle[pathname] === 'function') {
handle[pathname](response, request/*postData*/);
} else {
console.log("No request handler found for " + pathname);
response.writeHead(404, {"Content-Type": "text/plain"});
response.write("404 Not found");
response.end();
}
}
exports.route = route;
Файл requestHandler.js
var querystring = require("querystring"),
fs = require("fs"),
formidable = require("formidable");
function start(response) {
console.log("Request handler 'start' was called.");
var body = '<html>'+
'<head>'+
'<meta http-equiv="Content-Type" '+
'content="text/html; charset=UTF-8" />'+
'</head>'+
'<body>'+
'<form action="/upload" enctype="multipart/form-data" '+
'method="post">'+
'<input type="file" name="upload" multiple="multiple">'+
'<input type="submit" value="Upload file" />'+
'</form>'+
'</body>'+
'</html>';
response.writeHead(200, {"Content-Type": "text/html"});
response.write(body);
response.end();
}
function upload(response, request) {
console.log("Request handler 'upload' was called.");
var form = new formidable.IncomingForm();
console.log("about to parse");
form.parse(request, function(error, fields, files) {
console.log("parsing done");
/* Возможна ошибка в Windows: попытка переименования уже существующего файла */
fs.rename(files.upload.path, "/tmp/test.png", function(err) {
if (err) {
fs.unlink("/tmp/test.png");
fs.rename(files.upload.path, "/tmp/test.png");
}
});
response.writeHead(200, {"Content-Type": "text/html"});
response.write("received image:<br/>");
response.write("<img src='/show' />");
response.end();
});
}
function show(response) {
console.log("Request handler 'show' was called.");
fs.readFile("/tmp/test.png", "binary", function(error, file) {
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(file, "binary");
response.end();
}
});
}
exports.start = start;
exports.upload = upload;
exports.show = show;
Пример запуска кода сервера в консоли командной строки
node server.js
Пример запуска кода клиента в консоли командной строки
node client.js
Файл init.js
var request = require('./request');
var cheerio = require('cheerio');
var data = JSON.stringify({
'key1': 'value1'
, 'key2': 'value2'
});
request.httpRequest({
host: 'www.google.com'
, port: 80
, url: '/'
, type: 'POST'
, headers: {
'Content-Type': 'application/x-www-form-urlencoded'
, 'Content-Length': data.length
}
, data: data
, 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) {
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('data')) {options.data = {};}
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];
}
var requestOptions = {
hostname: options.host
, port: options.port
, path: options.url
, method: options.type
, headers: options.headers
};
var request = http.request(requestOptions, function(response) {
console.log('Response status code: ' + response.statusCode);
console.log('Response header: ' + JSON.stringify(response.headers));
response.setEncoding('utf8');
var data = '';
response.on('data', function(chunk){data += chunk;});
response.on('end', function(){options.success(data);});
});
request.setTimeout(options.timeout, function(){console.log('Request timeout');});
request.on('error', options.error);
if (options.data.length > 0) {request.write(options.data);}
request.end();
}
exports.httpRequest = httpRequest;
Пример запуска кода в консоли командной строки
node init.js
Пример полного взаимодействия клиента и сервера на Node JS.
Файл client.js
var http = require('http');
var postData = JSON.stringify({
'msg' : 'Hello World!'
});
var options = {
hostname: '127.0.0.1',
port: 80,
path: '/',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': postData.length
}
};
var request = http.request(options, function(response) {
console.log('STATUS: ' + response.statusCode);
console.log('HEADERS: ' + JSON.stringify(response.headers));
response.setEncoding('utf8');
response.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
});
request.on('error', function(error) {
console.log('problem with request: ' + error.message);
});
// write data to request body
request.write(postData);
request.end(); // Always need to write request.end();
Файл server.js
var server = require("./export");
var router = require("./router");
var requestHandlers = require("./requestHandlers");
var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;
handle["/show"] = requestHandlers.show;
server.start(router.route, handle);
Файл export.js
var http = require("http");
var url = require("url");
function start(route, handle) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
/*
var postData = "";
request.setEncoding("utf8");
request.addListener("data", function(postDataChunk) {
postData += postDataChunk;
console.log("Received POST data chunk '"+
postDataChunk + "'.");
});
request.addListener("end", function() {
route(handle, pathname, response, postData);
});
*/
route(handle, pathname, response, request);
}
http.createServer(onRequest).listen(80);
console.log("Server has started.");
}
exports.start = start;
Файл router.js
function route(handle, pathname, response, request/*postData*/) {
console.log("About to route a request for " + pathname);
if (typeof handle[pathname] === 'function') {
handle[pathname](response, request/*postData*/);
} else {
console.log("No request handler found for " + pathname);
response.writeHead(404, {"Content-Type": "text/plain"});
response.write("404 Not found");
response.end();
}
}
exports.route = route;
Файл requestHandler.js
var querystring = require("querystring"),
fs = require("fs"),
formidable = require("formidable");
function start(response) {
console.log("Request handler 'start' was called.");
var body = '<html>'+
'<head>'+
'<meta http-equiv="Content-Type" '+
'content="text/html; charset=UTF-8" />'+
'</head>'+
'<body>'+
'<form action="/upload" enctype="multipart/form-data" '+
'method="post">'+
'<input type="file" name="upload" multiple="multiple">'+
'<input type="submit" value="Upload file" />'+
'</form>'+
'</body>'+
'</html>';
response.writeHead(200, {"Content-Type": "text/html"});
response.write(body);
response.end();
}
function upload(response, request) {
console.log("Request handler 'upload' was called.");
var form = new formidable.IncomingForm();
console.log("about to parse");
form.parse(request, function(error, fields, files) {
console.log("parsing done");
/* Возможна ошибка в Windows: попытка переименования уже существующего файла */
fs.rename(files.upload.path, "/tmp/test.png", function(err) {
if (err) {
fs.unlink("/tmp/test.png");
fs.rename(files.upload.path, "/tmp/test.png");
}
});
response.writeHead(200, {"Content-Type": "text/html"});
response.write("received image:<br/>");
response.write("<img src='/show' />");
response.end();
});
}
function show(response) {
console.log("Request handler 'show' was called.");
fs.readFile("/tmp/test.png", "binary", function(error, file) {
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(file, "binary");
response.end();
}
});
}
exports.start = start;
exports.upload = upload;
exports.show = show;
Пример запуска кода сервера в консоли командной строки
node server.js
Пример запуска кода клиента в консоли командной строки
node client.js
Подписаться на:
Сообщения (Atom)
