пятница, 29 июля 2016 г.

Краткий перечень API Node.js

=======================================
| Cheat sheet small - краткая подсказка
=======================================

npm init
npm install --save gulp
npm uninstall --save gulp
npm update
npm list

global

require
export
module.exports

assert()

console.log()

process.argv
process.stdout.write()
porcess.stderr.write()
porcess.stdin
process.exit()
process.stdout.clearLine()
process.stdout.cursorTo()

child_porcess.spawn()
child_process.exec()

var emeitter = new events.EventEmitter();
emeitter.on()
emeitter.once()
emeitter.addListener()
emeitter.removeListener()
emeitter.emit()

setTimeout()
setInterval()
setImmediate()

Buffer
__filename
__dirname
path
fs.writeFile()
fs.appendFile()
fs.readFile()
fs.rename()
fs.unlink()
fs.stat.isFile()
fs.stat.isDirectory()

fs.readdir()
fs.mkdir()
fs.rename()
fd.rmdir

fs.createReadStream()
fs.createWriteStream()
writeStream.write()
readStream.pipe()

url.parse()
url.format()
querystring.parse(
querystring.stringify()
querystring.escape()
querystring.unescape()
http.request()
http.createServer()
bodyParser()

os

debugger

=======================================
| Cheat sheet big - большая подсказка
=======================================

// NPM

npm init
npm install --save gulp
npm uninstall --save gulp
npm update
npm list

// Global, __dirname, __filename

console.log(global); // аналог window

console.log(__dirname); // абсолютный путь до папки из которой запущен данный файл
console.log(__filename); // абсолютный путь до данного файла

console.log(process.argv); // массив аргументов

// Process, stdout, stderr, stdin

process.stdout.write(`\n\n\n${__filename}\n\n\n`); // аналог console.log()

process.stderr.write(`\n\n\n${__dirname}\n\n\n`); // аналог console.error()

process.stdin.on("data", function (data) {
    process.stdout.write(data.toString());
    process.exit();
});

process.on("exit", function () {
    process.stdout.write("Goodbye");
});

process.stdout.clearLine(); // очистить текущую строку выводы в окне терминала

process.stdout.cursorTo(0); // переместить курсор в окне терминала на позицию 0

// Дочерний процесс - выполнить команду для терминала - exec

var exec = require("child_process").exec; // выполнить команду для терминала

var command = "open http://www.goggle.com"; // команда для терминала

exec(command, function (error, stdout) {
    if (error) {throw error;}
    console.log("Done");
    console.log(stdout);
});

// Дочерний процесс - запустить дочерний процесс - spawn

var spawnProcess = require("child_process").spawn;

var terminalCommand = "node";

var options = ["script-file.js", "say", "hi!"];

var childProcess = spawn(terminalCommand, options);

childProcess.stdout.on("data", function (data) {
    console.log(data.toString());
});

childProcess.on("close", function () {
    console.log("Done");
    process.exit();
});

setTimeout(function(){
    childProcess.stdin.write("stop"); // выполнить команду в консоли дочернего процесса
}, 5000);

// Модули - require, export, module.exports

var moduleFile = require("moduleFile"); // пути: ./ - текущая папка, ../ - внешняя папка, / - внутренняя папка

export var a = 1;
export function b () {}

module.exports = {a: a, b: b}; // аналог return из RequireJS

// События - on, once, emit, addListener, removeListener

var events = require("events");

var emitter = new events.EventEmitter();

emitter.on("say", function say (message) {});

emitter.once("say", function say (message) {});

emitter.addListener("say", function say (message) {});

emitter.removeListener("say", say);

emitter.emit("say", "Hello World");

// Работа с файлами - получение списка файлов, находящихся в папке - readdir

var fs = require("fs");

var files = readdirSync("./some/folder");
console.log(files);

readdir("./some/folder", function (error, files) {
    if (error) {throw error;}
    console.log(files);
});

// Работа с файлами - чтение содержимого файла - readFile

var fs = require("fs");

var fileContent = fs.readFileSync("./some/file.txt", "UTF-8"); // кодировку нужно указывать !!!
console.log(fileContent);

readFile("./some/file.txt", "UTF-8", function (error, fileContent) {
    if (error) {throw error;}
    console.log(fileContent);
});

// Работа с файлами - получение описания файла stat

var fs = require("fs");
var path = require("path");

fs.readdir("./libs", function (error, files) {
    if (error) {throw error;}
    files.forEach(function (fileName) {
        var file = path.join(__dirname, "libs", fileName); // абсолютный путь до файла
        var stats = fs.statSync(file);
        if (stats.isFile()) {
            fs.readFile(file, "UTF-8", function (error, fileContent) {
                if (error) {throw error;}
                console.log(fileContent);
            });
        } else if (stats.isDirectory()) {
            console.log("It is directory");
        }
    });
});

// Работа с файлами - создание файла и запись в файл - writeFile

var fs = require("fs");

var fileContent = "Hello World";

fs.writeFileSync("sample.txt", fileContent);
console.log("Done");

fs.writeFile("sample.txt", fileContent, function (error) {
    if (error) {throw error;}
    console.log("Done");
});

fs.appendFile("sample.txt", "\nGoodbye");

// Работа с файлами - переименование и перемещение файлов в другую папку - rename

var fs = require("fs");

fs.renameSync("./lib/file-1.js", "./lib/file-2.js"); // переименование файла
console.log("Файл переименован");

fs.rename("./lib/file-1.js", "./file-1.js", function (error) { // перемещение файла в другую папку
    if (error) {throw error;}
    console.log("Файл пемемещен в другую папку");
});

// Работа с файлами - удаление файлов - unlink

var fs = require("fs");

try {
    fs.unlinkSync("./lib/file-1.js"); // удаление файла
    console.log("Done");
} catch (error) {
    console.log(error);
}

fs.unlink("./lib/file-1.js", function (error) { // удаление файла
    if (error) {throw error;}
    console.log("Done");
});

// Работа с файлами - создание директорий - mkdir, exists

var fs = require("fs");

if (fs.existsSync("lib")) {
    console.log("Directory lib already exists");
} else {
    fs.mkdir("libs", function (error) {
        if (error) {throw error;}
        console.log("Directory created");
    });  
}

// Работа с файлами - переименование и перемещение папок в другую папку - rename

var fs = require("fs");

fs.renameSync("./assets/logs", "./logs"); // перемещение папки в другую папку
console.log("Done");

// Работа с файлами - удаление папки - rmdir - НЕЛЬЗЯ УДАЛИТЬ ПАПКУ, ЕСЛИ В НЕЙ ЕЩЕ ЕСТЬ ФАЙЛЫ! СНАЧАЛА НАДО УДАЛИТЬ ВСЕ ФАЙЛЫ!

var fs = require("fs");

var files = fs.readdirSync("./libs");

if (files.length > 0) {
    files.forEach(function (fileName) {
        fs.unlinkSync("./libs" + fileName);
    });
}

fs.rmdir("./libs", function (error) {
    if (error) {throw error;}
    console.log("Done");
});

// Потоки - readable, writable, duplex = readable + writable

// Потоки - создание потока для чтения данных - createReadStream

var fs = require("fs");

var readStream = fs.createReadStream("./chat.log", "UTF-8"); // прочитать содержимое файла в потоке

var fileContent = "";

readStream.on("data", function (chunk) {
    fileContent += chunk;
    process.stdout.write(chunk);
});

readStream.on("end", function () {
    console.log("Done");
});

// Потоки - создание потока для записи данных - createWriteStream

var fs = require("fs");

var writeSteam = fs.createWriteStream("./chat.log");

for (var i = 0; i < 1000; i++) {
    writeStream.write(Math.random());
}

// HTTP - клиентский запрос - request

var http = require("http");
var fs = require("fs");

var options = {
    hostname: "google.com",
    post: 80
    path: "/search/data",
    method: "GET"
};

var request = http.request(options, function (response) {
    response.setEncoding("UTF-8");
    console.log(response.statusCode);
    console.log(response.headers);
    var responseBody = "";
    response.on("data", function (chunk) {
        responseBody += chunk;
    });
    response.on("end", function () {
        fs.writeFile("some.html", responseBody, function (error) {
            if (error) {throw error;}
            console.log("File downloaded");
        });
    });
});

request.on("error", function () {
    console.log("Error in request");
});

request.end(); // Послать запрос на сервер

// HTTP - создание сервера - createServer

var http = require("http");

var server = http.createServer(function (request, response) {
    response.writeHead(200, {"Content-Type": "text/html"});
    response.write("<!DOCTYPE html>");
    response.end(); // Завершить отправку ответа
});

server.listen("127.0.0.1:80"); // Создать сервер

// HTTP - обслуживание файлов - writeHead, write, end, url, method

var http = require("http");
var fs = require("fs");
var path = require("path");

var server = http.createServer(function (request, responce) {
    console.log(respose.method);
    if (requres.url === "/") {
        fs.readFile("./public/index.html", "UTF-8", function (error, fileContent) {
            if (error) {throw error;}
            response.writeHead(200, {"Content-Type": "text/html"});
            response.end(fileContent);
        });
    } else if (request.url.match(/.css$/)) {
        var cssPath = path.join(__dirname, "public", request.url);
        var readFileStream = fs.createReadStream(cssPath, "UTF-8"); // текстовые данные
        response.writeHead(200, {"Content-Type": "text/css"});
        readFileStream.on("data", function (chunk) {
            response.write(chunk);
        });
        readFileStream.on("end", function () {
            response.end();
        });
        // или можно так
        // readFileStream.pipe(response); // автоматически ставит в конце response.end()
    } else if (request.url.match(/.jpg$/)) {
        var imagePath = path.join(__dirname, "public", request.url);
        var readFileStream = fs.createReadStream(imagePath); // бинарные данные
        response.writeHead(200, {"Content-Type": "image/jpeg"});
        readFileStream.pipe(response);
    } else {
        response.writeHead(404, {"Content-Type": "text/plain"});
        response.end("404 File Not Found");
    }
    console.log(request);
   
});

server.listen("127.0.0.1:80");

// HTTP - обслуживание JSON - writeHead, write, end, url, method

var http = require("http");
var data = require("./json-file");

var server = http.createServer(function (request, response) {
    if (request.url === "/") {
        response.writeHead(200, {"Content-Type": "text/json"});
        response.end(JSON.stringify(data));
    } else if (request.url === "/instock") {
        listInStock(response);
    } else if (requrest.url === "/onorder") {
        listOnBackOrder(response);
    } else {
        response.writeHead(404, {"Content-Type": "text/plain");
        response.end("404 File Not Found");
    }
});

server.listen("127.0.0.1:80");

function listInStock (response) {
    var inStock = data.filter(function (item) {
        return item.available === "In stock";
    });
    response.writeHead(200, {"Content-Type": "text/json"});
    reponse.end(JSON.stringify(inStock));
}

function listOnBackOrder (response) {
    var onOrder = data.filter(function (item) {
        return item.available === "On back order";
    });
    response.writeHead(200, {"Content-Type": "text/json"});
    reponse.end(JSON.stringify(onOrder));
}

// HTTP - обслуживание GET, POST - writeHead, write, end, url, method

var http = require("http");
var fs = require("fs");

var server = http.createServer(function (request, response) {
    if (request.method === "GET") {
        response.writeHead(200, {"Content-Type": "text/html"});
        var fileReadStream = fs.createReadStream("./public/form.html", "UTF-8");
        fileReadStream.pipe(response);
    } else if (request.method === "POST") {
        var body = "";
        request.on("data", function (chunk) {
            body += chunk;
        });
        request.on("end", function () {
            response.writeHead(200, {"Content-Type": "text/html"});
            reponse.end(body);
        });
    }

});

server.listen("127.0.0.1:80");

// Express - сервер и прослойки middleware - app, use, get, post, delete, static, bodyParser

var express = require("express");

var app = express();

app.use(express.static("./public"));

app.use(epress.bodyParser({uploadDir: "./upload"}));

app.use(function (request, response, next) {
    console.log(request.method);
    console.log(request.url);
    next();
});

app.get("/", function (request, response) {
    response.json({one: 1, two: 2});
});

app.post("/form", function (request, response) {
    response.end("<!DOCTYPE html>" + request.body);
});

app.delete("/delete/:id", function (request, response) {
    response.end(request.params.id);
});

app.listen("127.0.0.1:80");

module.exports = app;

// WebSockets - стронняя библиотека WS для работы с WebSockets - WebSocketServer, send

var WebSocketServer = require("ws");

var wss = new WebSocketServer({port: 80});

wss.on("connection", function (webSocket) {
    webSocket.on("message", function (message) {
        if (message === "exit") {
            webSocket.close();
        } else {
            wss.clients.forEach(function (client) {
                client.send(message);
            });
        }
    });
    webSocket.send("Welcome to chat");
});

// Socket.IO - стронняя библиотека Socket.IO для работы с WebSockets - emit

var express = require("express");
var http = require("http");
var io = require("socket.io");

var app = express();

var server = http.createServer(app).listen("127.0.0.1:80");

io(server);

app.use(express.static("./public"));

io.on("connection", function (socket) {
    socket.on("chat", function (message) {
        socket.broadcast.emit("message", message);
    });
    socket.emit("message", "Welcome to chat");
});

=======================================
| Assertion Testing - ассерты для тестирования
=======================================

const assert = require('assert');

assert(false, 'it is false'); // throws: AssertionError: it is false

assert.ok(false, 'it is false'); // throws: AssertionError: it is false

assert.equal({a: {b: 1}}, {a: {b: 1}}); // throws: AssertionError: { a: { b: 1 } } == { a: { b: 1 } }

assert.strictEqual(1, '1'); // throws: AssertionError: 1 === '1'

assert.deepEqual({a: {b: 1}}, {a: {b: 2}}; // throws: AssertionError: { a: { b: 1 } } deepEqual { a: { b: 2 } }

assert.deepStrictEqual({a: 1}, {a: '1'}); // throws: AssertionError: { a: 1 } deepStrictEqual { a: '1' }

assert.fail(1, 2, undefined, '>'); // throws: AssertionError: 1 > 2

assert.fail(1, 2, 'whoops', '>'); // throws: AssertionError: whoops

assert.notEqual(1, 1); // throws: AssertionError: 1 != 1

assert.notStrictEqual(1, 1); // throws: AssertionError: 1 != 1

assert.notDeepEqual({a: {b: 1}}, {a: {b: 1}}); // throws: AssertionError: { a: { b: 1 } } notDeepEqual { a: { b: 1 } }

assert.notDeepStrictEqual({a: '1'}, {a: '1'}); // throws: AssertionError: { a: '1' } notDeepStrictEqual { a: '1' }

=======================================
| Buffer - буферы - массивы для двоичных данных
=======================================

const buf = Buffer.alloc(10); // <Buffer 00 00 00 00 00 00 00 00 00 00>

const buf = Buffer.alloc(10, 1); // <Buffer 01 01 01 01 01 01 01 01 01 01>

const buf = Buffer.from([1, 2, 3]); // <Buffer 01 02 03>

const buf = Buffer.from('test', 'utf8'); // <Buffer 74 65 73 74>

const buf = Buffer.from('hello world', 'ascii');

Варианты метода from:
- Buffer.from(array);
- Buffer.from(buffer);
- Buffer.from(arrayBuffer[, byteOffset [, length]]);
- Buffer.from(str[, encoding]);

const buf = Buffer.from([1, 2, 3]);
for (var b of buf) {
  console.log(b);
}
// Prints:
//   1
//   2
//   3

console.log(buf.toString('hex')); // prints: 68656c6c6f20776f726c64
console.log(buf.toString('base64')); // prints: aGVsbG8gd29ybGQ=

Варианты кодировок:
- 'ascii'
- 'utf8'
- 'utf16le'
- 'ucs2'
- 'base64'
- 'binary'
- 'hex'

const arr = new Uint16Array(20);
const buf = Buffer.from(arr.buffer, 0, 16);
console.log(buf.length); // Prints: 16

const buf = Buffer.from([1, 2, 3]); // <Buffer 01 02 03>
buf[1] = 5; // <Buffer 01 05 03>

console.log(buf.compare(buf)); // Prints: 0

console.log(buf1.equals(buf2)); // Prints: true

buf1.copy(buf2, 8, 16, 20);

const buf = Buffer.allocUnsafe(10);
buf.fill('h');
console.log(buf.toString()); // Prints: hhhhhhhhhh

const buf = Buffer.from('this is a buffer');

buf.indexOf('this'); // returns 0
buf.indexOf('is'); // returns 2

buf.lastIndexOf('this'); // returns 0
buf.lastIndexOf('buffer'); // returns 17

buf.includes('this'); // returns true

const buf = Buffer.from('buffer');
for (var key of buf.keys()) {
  console.log(key);
}
// prints:
//   0
//   1
//   2
//   3
//   4
//   5

Buffer.isBuffer(obj) // true или false

const buf = Buffer.alloc(1234);
console.log(buf.length); // Prints: 1234

const buf2 = buf1.slice(0, 3);

const buf = Buffer.alloc(3, 1); // <Buffer 01 01 01>
buf.toJSON(); // {type: 'Buffer', data: [1, 1, 1]}

const buf = Buffer.from('buffer');
for (var value of buf.values()) {
  console.log(value);
}
// prints:
//   98
//   117
//   102
//   102
//   101
//   114

const buf = Buffer.allocUnsafe(256);
const len = buf.write('\u00bd + \u00bc = \u00be', 0);
console.log(`${len} bytes: ${buf.toString('utf8', 0, len)}`); // Prints: 12 bytes: 1/2 + 1/4 = 3/4


const buf = Buffer.alloc(3, 1); // <Buffer 01 01 01>
buf.inspect(); // <Buffer 01 01 01>

buffer.INSPECT_MAX_BYTES; // Default: 50

=======================================
| Child Processes - работа с окном командной строки и запуск дополнительных дочерних процессов Node.js
=======================================

child_process.exec() - асинхронно запускает окно командной строки и выполняет внутри него команду, передавая stdout и stderr в callback

function когда выполнение команды завершится.

child_process.execFile() - такой же как child_process.exec() за исключением того, что команда выполняется напрямую без запуска окна

командной строки.

child_process.execSync() - синхронная версия child_process.exec(), блокирующая Node.js event loop.

child_process.execFileSync() - синхронная версия child_process.execFile(), блокирующая Node.js event loop.

child_process.fork() - запускает новый отдельный процесс Node.js и открывает канал для передачи сообщений между родительским процессом

Node.js и дочерним процессом Node.js.

child_process.spawn() - асинхронно запускает дочерний процесс Node.js, передавая stdout и stderr в callback function когда выполнение

команды завершится.

child_process.spawnSync() - синхронная версия child_process.spawn(), блокирующая Node.js event loop до завершения выполнения дочернего

процесса.

Достыпные Events:
- on('data, function () {})
- on('close', function () {})
- on('disconnect', function () {})
- on('error', function () {})
- on('exit', function () {})
- on('message', function () {})

Методы:
- child.send(message[, sendHandle[, options]][, callback])

- child.connected
- child.disconnect()

- child.kill([signal])
- child.pid

- child.stderr
- child.stdin
- child.stdio
- child.stdout

Примеры:

const spawn = require('child_process').spawn;
const listProcess = spawn('ls', ['-lh', '/usr']);

listProcess.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

listProcess.stderr.on('data', (data) => {
  console.log(`stderr: ${data}`);
});

listProcess.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});

// Excute gulp task
var task = process.argv.slice(2).join(' ')
    , command = 'node ./node_modules/gulp/bin/gulp.js ' + task
    , childProcess = require('child_process').exec(command);
childProcess.stdout.on('data', function (data) {console.log(data);});
childProcess.stderr.on('data', function (data) {console.log(data);});
childProcess.on('data', function (code) {if (code === 0) {console.log('Done');} else {console.log('Exit code: ' + code);}});

// или

const exec = require('child_process').exec;
exec('cat *.js bad_file | wc -l', function (error, stdout, stderr) {
  if (error) {
    console.error(`exec error: ${error}`);
    return;
  }
  console.log(`stdout: ${stdout}`);
  console.log(`stderr: ${stderr}`);
});

// execFile

const execFile = require('child_process').execFile;
const child = execFile('node', ['--version'], function (error, stdout, stderr) {
  if (error) {
    throw error;
  }
  console.log(stdout);
});

=======================================
| Console - console.log() - вывод сообщений на консоль
=======================================

console.log('hello world'); // Prints: hello world, to stdout

console.log('hello %s', 'world'); // Prints: hello world, to stdout

console.info([data][, ...]) - тоже самое, что и console.log([data][, ...])

console.dir(obj[, options]) // печатает свойства объекта

console.trace('Show me'); // Prints: (stack trace will vary based on where trace is called)

const code = 5;

console.error('error #%d', code); // Prints: error #5, to stderr

console.error('error', code); // Prints: error 5, to stderr

console.warn([data][, ...]) - тоже самое, что и console.error([data][, ...])

console.assert(true, 'does nothing'); // OK

console.assert(false, 'Whoops %s', 'didn\'t work'); // AssertionError: Whoops didn't work

console.time('100-elements');

for (var i = 0; i < 100; i++) {}

console.timeEnd('100-elements'); // prints 100-elements: 225.438ms

=======================================
| Debugger - запуск отладчика debugger; в браузере для отладки скрипта Node.js
=======================================

Описание команд:

Команды перехода по коду:

cont, c - продолжить выполнения кода
next, n - перейти к следующему шагу
step, s - войти внутрь функции
out, o - выйти из функции
pause - поставить выполнение кода на паузу, как при нажатии на кнопку паузы в Chrome Developer Tools

Команды для расстановки точек остановок в коде:

setBreakpoint(), sb() - установить breakpoint на текущую линию кода
setBreakpoint(line), sb(line) - установить breakpoint конкретную линию кода
setBreakpoint('fn()'), sb(...) - установить breakpoint первую строчку внутри тела функции
setBreakpoint('script.js', 1), sb(...) - установить breakpoint первую строчку файлы script.js
clearBreakpoint('script.js', 1), cb(...) - удалить breakpoint в файле script.js с линии 1

// myscript.js

x = 5;
setTimeout(() => {
  debugger;
  console.log('world');
}, 1000);
console.log('hello');

// запуск отладки скрипта

$ node debug myscript.js

< debugger listening on port 5858
connecting... ok

break in /home/indutny/Code/git/indutny/myscript.js:1
  1 x = 5;
  2 setTimeout(() => {
  3   debugger;

debug> cont

< hello
break in /home/indutny/Code/git/indutny/myscript.js:3
  1 x = 5;
  2 setTimeout(() => {
  3   debugger;
  4   console.log('world');
  5 }, 1000);

debug> next

break in /home/indutny/Code/git/indutny/myscript.js:4
  2 setTimeout(() => {
  3   debugger;
  4   console.log('world');
  5 }, 1000);
  6 console.log('hello');

debug> repl

Press Ctrl + C to leave debug repl

> x
5
> 2+2
4

debug> next

< world
break in /home/indutny/Code/git/indutny/myscript.js:5
  3   debugger;
  4   console.log('world');
  5 }, 1000);
  6 console.log('hello');
  7

debug> quit

=======================================
| Events - события addListener(), removeListener(), emit()
=======================================

emitter.addListener(eventName, listener) - тоже самое, что и emitter.on(eventName, listener) - добавить событие

emitter.prependListener() - добавить событие в самое начало списка имеющихся событий

emitter.once(eventName, listener) - добавить событие, которое будет удалено сразу после его вызова

emitter.prependOnceListener() -  добавить событие в самое начало списка имеющихся событий, которое будет удалено сразу после его вызова

emitter.emit(eventName[, arg1][, arg2][, ...]) - синхронно вызвать функции привязанные к события в порядке их привязки с передачей в них

аргументов

emitter.removeListener(eventName) - удалить событие

emitter.removeAllListeners([eventName]) - удалить все события

emitter.eventNames() - вернуть массив существующих имен событий

emitter.listenerCount(eventName) - подсчитать число существующих событий

emitter.getMaxListeners() - получить предельно допустимое число возможных событий (по умолчанию 10)

emitter.setMaxListeners(n) - установить предельно допустимое число возможных событий

emitter.listeners(eventName) - получить массив с копией функций привязанных к событию

// Пример добавления событий

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();

myEmitter.on('event', function callback (a, b) {
  console.log(a, b, this);
    // Prints:
    //   a b MyEmitter {
    //     domain: null,
    //     _events: { event: [Function] },
    //     _eventsCount: 1,
    //     _maxListeners: undefined }
});

myEmitter.emit('event', 'a', 'b');

myEmitter.removeEvent('event', callback);

// метод on

const myEmitter = new MyEmitter();

var m = 0;

myEmitter.on('event', () => {
  console.log(++m);
});

myEmitter.emit('event'); // Prints: 1
myEmitter.emit('event'); // Prints: 2

// метод once

const myEmitter = new MyEmitter();

var m = 0;

myEmitter.once('event', () => {
  console.log(++m);
});

myEmitter.emit('event'); // Prints: 1
myEmitter.emit('event'); // Ignored

// Error event

const myEmitter = new MyEmitter();

myEmitter.emit('error', new Error('whoops!')); // Throws and crashes Node.js

=======================================
| Stream - end, read, write, pause, resume, pipe, unpipe
=======================================

const stream = require('stream');

Types of Streams:
- Readable - fs.createReadStream()
- Writable - fs.createWriteStream()
- Duplex - net.Socket
- Transform

Readable

readable.read([size])

readable.pause()
readable.resume()
readable.isPaused()

readable.pipe(destination[, options])
readable.unpipe([destination])

readable.setEncoding(encoding)

readable.wrap(stream)

readable.unshift(chunk)

Readable  Events
- 'close'
- 'data'
- 'end'
- 'error'
- 'readable'

Writable

writable.write(chunk[, encoding][, callback])
writable.end([chunk][, encoding][, callback])

writable.cork()
writable.uncork()

writable.setDefaultEncoding(encoding)

Writable Events
- 'close'
- 'drain'
- 'error'
- 'finish'
- 'pipe'
- 'unpipe'

=======================================
| File System - open, close, readFile, writeFile, appendFile unlink, stats, access, watch
=======================================

// Создать поток для чтения из файла
fs.createReadStream(path[, options])

// Создать поток для записи в файл
fs.createWriteStream(path[, options])

// Открыть файл
fs.open(path, flags[, mode], callback)

// Закрыть файл
fs.close(fd, callback)

// Прочитать файл
fs.read(fd, buffer, offset, length, position, callback)

// Асинхронно прочитать весь файл целиком
fs.readFile('/etc/passwd', (err, data) => {
  if (err) throw err;
  console.log(data);
});

// Прочитать содержимое директории
fs.readdir(path[, options], callback)

// Добавление текста в конец файла
fs.appendFile('message.txt', 'data to append', (err) => {
  if (err) throw err;
  console.log('The "data to append" was appended to file!');
});

// Усечь файл
fs.ftruncate(fd, len, callback)

// Записать данные в файл или создать новый файл
fs.write(fd, buffer, offset, length[, position], callback)
fs.write(fd, data[, position[, encoding]], callback)

// Записать данные в файл и заменить его на новый, если он уже существует
fs.writeFile(file, data[, options], callback)

// Переименовать файл
fs.rename(oldPath, newPath, callback)

// Проверить существует ли файл - deprecated
fs.exists('/etc/passwd', (exists) => {
  console.log(exists ? 'it\'s there' : 'no passwd!');
});

// Сведения о файлах
fs.stat()
fs.lstat()
fs.fstat()

stats.isFile()
stats.isDirectory()

stats.isBlockDevice()
stats.isCharacterDevice()
stats.isSymbolicLink() (only valid with fs.lstat())
stats.isFIFO()
stats.isSocket()

// Изменение атрибутов файла
fs.chmod(path, mode, callback)

// Изменение владельца файла
fs.chown(path, uid, gid, callback)

// Изменения атрибутов файла
fs.constants.F_OK - файл видим для текущего процесса (значение по умолчанию)
fs.constants.R_OK - файл можешт быть прочитан текущим процессом
fs.constants.W_OK - файл можешт быть записан текущим процессом
fs.constants.X_OK - файл можешт быть выполнен текущим процессом

fs.access('/etc/passwd', fs.constants.R_OK | fs.constants.W_OK, (err) => {
  console.log(err ? 'no access!' : 'can read/write');
});

// Отслеживание изменений файлов
fs.watch('./tmp', {encoding: 'buffer'}, (eventType, filename) => {
  if (filename)
    console.log(filename);
    // Prints: <Buffer ...>
});

// Отслеживать изменения конкретного файла
fs.watchFile('message.text', (curr, prev) => {
  console.log(`the current mtime is: ${curr.mtime}`);
  console.log(`the previous mtime was: ${prev.mtime}`);
});

// Скопировать файл
fs.link(srcpath, dstpath, callback)

// Асинхронная версия удаления файла
const fs = require('fs');

fs.unlink('/tmp/hello', (err) => {
  if (err) throw err;
  console.log('successfully deleted /tmp/hello');
});

// Синхронная версия удаления файла
const fs = require('fs');

fs.unlinkSync('/tmp/hello');
console.log('successfully deleted /tmp/hello');

// Создать директорию
fs.mkdir(path[, mode], callback)

// Удалить директорию
fs.rmdir(path, callback)

=======================================
| Path
=======================================

const path = require('path');

path.basename('/foo/bar/baz/asdf/quux.html') // returns 'quux.html'
path.basename('/foo/bar/baz/asdf/quux.html', '.html') // returns 'quux'

path.win32..
path.posix..

path.delimiter
; for Windows
: for POSIX

path.sep
\ on Windows
/ on POSIX

// on POSIX:
'foo/bar/baz'.split(path.sep) // returns ['foo', 'bar', 'baz']

// on Windows:
'foo\\bar\\baz'.split(path.sep) // returns ['foo', 'bar', 'baz']

// on POSIX:
console.log(process.env.PATH) // '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin'
process.env.PATH.split(path.delimiter) // returns ['/usr/bin', '/bin', '/usr/sbin', '/sbin', '/usr/local/bin']

// on Windows:
console.log(process.env.PATH) // 'C:\Windows\system32;C:\Windows;C:\Program Files\node\'
process.env.PATH.split(path.delimiter) // returns ['C:\\Windows\\system32', 'C:\\Windows', 'C:\\Program Files\\node\\']

path.dirname('/foo/bar/baz/asdf/quux') // returns '/foo/bar/baz/asdf'

path.extname('index.html') // returns '.html'
path.extname('index.coffee.md') // returns '.md'
path.extname('index.') // returns '.'
path.extname('index') // returns ''
path.extname('.index') // returns ''

path.format({dir: '/home/user/dir', base: 'file.txt'}); // returns '/home/user/dir/file.txt'
path.format({root: '/', base: 'file.txt'}); // returns '/file.txt'
path.format({root: '/', name: 'file', ext: '.txt'}); // returns '/file.txt'
path.format({base: 'file.txt'}); // returns 'file.txt'
path.format({root : "C:\\", dir : "C:\\path\\dir", base : "file.txt", ext : ".txt", name : "file"}); // returns 'C:\\path\\dir\\file.txt'

path.isAbsolute('/foo/bar') // true
path.isAbsolute('/baz/..')  // true
path.isAbsolute('qux/')     // false
path.isAbsolute('.')        // false
path.isAbsolute('//server')  // true
path.isAbsolute('C:/foo/..') // true
path.isAbsolute('bar\\baz')  // false
path.isAbsolute('.')         // false

ath.join('/foo', 'bar', 'baz/asdf', 'quux', '..') // returns '/foo/bar/baz/asdf'
path.join('foo', {}, 'bar') // throws TypeError: Arguments to path.join must be strings

path.normalize('/foo/bar//baz/asdf/quux/..') // returns '/foo/bar/baz/asdf'
path.normalize('C:\\temp\\\\foo\\bar\\..\\'); // returns 'C:\\temp\\foo\\'

// on POSIX:
path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb') // returns '../../impl/bbb'

// on Windows:
path.relative('C:\\orandea\\test\\aaa', 'C:\\orandea\\impl\\bbb') // returns '..\\..\\impl\\bbb'

path.resolve('/foo/bar', './baz') // returns '/foo/bar/baz'

path.resolve('/foo/bar', '/tmp/file/') // returns '/tmp/file'

path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif') // if the current working directory is /home/myself/node, this returns

'/home/myself/node/wwwroot/static_files/gif/image.gif'

// on POSIX:
┌───────────────────┬────────────┐
│                 dir              │         base       │
├──────┬                    ├─────┬──────┤
│   root   │                     │  name │   ext   │
│     /        home/user/dir  /   file       .txt   │
└──────┴───────────┴──────┴─────┘

path.parse('/home/user/dir/file.txt')
// returns
// {
//    root : "/",
//    dir : "/home/user/dir",
//    base : "file.txt",
//    ext : ".txt",
//    name : "file"
// }

on Windows:

┌───────────── ──┬────────────┐
│                 dir        │         base        │
├──────┬              ├──────┬─────┤
│   root   │              │  name   │   ext  │
│    C:\      path\dir   \   file         .txt  │
└──────┴──── ───┴──────┴─────┘

path.parse('C:\\path\\dir\\file.txt')
// returns
// {
//    root : "C:\\",
//    dir : "C:\\path\\dir",
//    base : "file.txt",
//    ext : ".txt",
//    name : "file"
// }

=======================================
| Globals - __driname, __filename, global, Buffer, process, setTimeout, clearTimweout, console, module, exports, require
=======================================

console.log(__dirname); // /Users/mjr
console.log(__filename); // /Users/mjr/example.js

=======================================
| Modules - require, module, export, exports, name, id, parent, loaded
=======================================

require(X) from module at path Y
1. If X is a core module,
   a. return the core module
   b. STOP
2. If X begins with './' or '/' or '../'
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)
3. LOAD_NODE_MODULES(X, dirname(Y))
4. THROW "not found"

LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text.  STOP
2. If X.js is a file, load X.js as JavaScript text.  STOP
3. If X.json is a file, parse X.json to a JavaScript Object.  STOP
4. If X.node is a file, load X.node as binary addon.  STOP

LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
   a. Parse X/package.json, and look for "main" field.
   b. let M = X + (json main field)
   c. LOAD_AS_FILE(M)
2. If X/index.js is a file, load X/index.js as JavaScript text.  STOP
3. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
4. If X/index.node is a file, load X/index.node as binary addon.  STOP

LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
   a. LOAD_AS_FILE(DIR/X)
   b. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
   a. if PARTS[I] = "node_modules" CONTINUE
   c. DIR = path join(PARTS[0 .. I] + "node_modules")
   b. DIRS = DIRS + DIR
   c. let I = I - 1
5. return DIRS

The module wrapper#
Before a module's code is executed, Node.js will wrap it with a function wrapper that looks like the following:
(function (exports, require, module, __filename, __dirname) {
    // Your module code actually lives in here
});

module.filename
module.id
module.loaded
module.parent
module.require(id)

require.main === module

const circle = require('./circle.js');
console.log( `The area of a circle of radius 4 is ${circle.area(4)}`);

// assigning to exports will not modify module, must use module.exports
module.exports = (width) => {
  return {
    area: () => width * width
  };
}

=======================================
| OS - EOL, platform, type, cpus, homedir, tmpdir
=======================================

const os = require('os');

os.EOL
\n on POSIX
\r\n on Windows

os.endianness()
'BE' for big endian
'LE' for little endian.

os.arch() - тоже самое, что и process.arch
// 'arm', 'arm64', 'ia32', 'mips', 'mipsel', 'ppc', 'ppc64', 's390', 's390x', 'x32', 'x64', 'x86'

os.platform() - тоже самое, что и process.platform
// 'aix', 'darwin', 'freebsd', 'linux', 'openbsd', 'sunos', 'win32'

os.type() // 'Linux' on Linux, 'Darwin' on OS X, 'Windows_NT' on Windows

os.release()

os.constants

os.cpus()

os.freemem() - число доступных байт системной памяти

os.totalmem() - общее число байт системной памяти

os.homedir() - домашняя директория в операционной системе

os.tmpdir() - директория для временных файлов в операционной системе

os.loadavg()

os.uptime()

os.networkInterfaces()
{
    address: '127.0.0.1',
    netmask: '255.0.0.0',
    family: 'IPv4',
    mac: '00:00:00:00:00:00',
    internal: true
}

OS Constants:
- Signal Constants
- Error Constants
- POSIX Error Constants
- Windows Specific Error Constants
- libuv Constants

=======================================
| Process - argv, nextTick, exit, abort, stdin, stdout, stderr, chdir, cwd, env, hrtime
=======================================

process - это глобальный объект, экземляр Event Emitter, предоставляющий информацию и контролирующий текущий процесс работы Node.js

process.stdin

process.stdin.setEncoding('utf8');

process.stdin.on('readable', () => {
  var chunk = process.stdin.read();
  if (chunk !== null) {
    process.stdout.write(`data: ${chunk}`);
  }
});

process.stdin.on('end', () => {
  process.stdout.write('end');
});

process.stdout

console.log = (msg) => {
  process.stdout.write(`${msg}\n`);
};

process.stderr

process.nextTick(callback[, arg][, ...])

console.log('start');
process.nextTick(() => {
  console.log('nextTick callback');
});
console.log('scheduled');
// Output:
// start
// scheduled
// nextTick callback

process.exit(1)

if (someConditionNotMet()) {
  printUsageToStdout();
  process.exit(1);
}

process.exitCode

process.kill(pid[, signal])

process.kill(process.pid, 'SIGHUP');

process.getegid()

if (process.getegid) {
  console.log(`Current gid: ${process.getegid()}`);
}

process.geteuid()

process.getgid()

process.pid

process.title

process.abort()

process.argv

$ node process-2.js one two=three four

process.argv.forEach((val, index) => {
  console.log(`${index}: ${val}`);
});

$ node --harmony script.js --version
['/usr/local/bin/node', 'script.js', '--version']

process.execArgv

$ node --harmony script.js --version
['--harmony']

process.execPath // /usr/local/bin/node

process.stdin.resume();

process.chdir(directory)

process.cwd()
console.log(`Current directory: ${process.cwd()}`);

process.chdir('/tmp');
console.log(`Starting directory: ${process.cwd()}`);
try {
  process.chdir('/tmp');
  console.log(`New directory: ${process.cwd()}`);
}
catch (err) {
  console.log(`chdir: ${err}`);
}

process.hrtime([time])

var time = process.hrtime(); // [ 1800216, 25 ]

process.uptime()

process.versions
{
  http_parser: '2.3.0',
  node: '1.1.1',
  v8: '4.1.0.14',
  uv: '1.3.0',
  zlib: '1.2.8',
  ares: '1.10.0-DEV',
  modules: '43',
  icu: '55.1',
  openssl: '1.0.1k'
}

process.config

process.connected

process.disconnect()

process.arch

process.platform

process.cpuUsage([previousValue])

const startUsage = process.cpuUsage(); // { user: 38579, system: 6986 }

process.release

process.env
{
  TERM: 'xterm-256color',
  SHELL: '/usr/local/bin/bash',
  USER: 'maciej',
  PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
  PWD: '/Users/maciej',
  EDITOR: 'vim',
  SHLVL: '1',
  HOME: '/Users/maciej',
  LOGNAME: 'maciej',
  _: '/usr/local/bin/node'
}
process.env.foo = 'bar';
console.log(process.env.foo);

process.env.test = null;
console.log(process.env.test); // => 'null'

process.env.test = undefined;
console.log(process.env.test); // => 'undefined'

process.env.TEST = 1;
delete process.env.TEST;
console.log(process.env.TEST); // => undefined

process.mainModule

process.memoryUsage()

process.send(message[, sendHandle[, options]][, callback])

process.on('exit', (code) => {
  console.log(`About to exit with code: ${code}`);
});

process.on('beforeExit', function () {});

process.on('disconnect', function () {});

process.on('message', function () {});

process.on('rejectionHandled', function () {});

const unhandledRejections = new Map();
process.on('unhandledRejection', (reason, p) => {
  unhandledRejections.set(p, reason);
});

process.on('rejectionHandled', (p) => {
  unhandledRejections.delete(p);
});

process.on('uncaughtException', function () {});

process.on('uncaughtException', (err) => {
  fs.writeSync(1, `Caught exception: ${err}`);
});

process.on('warning', function () {});

process.on('warning', (warning) => {
  console.warn(warning.name);    // Print the warning name
  console.warn(warning.message); // Print the warning message
  console.warn(warning.stack);   // Print the stack trace
});


process.emitWarning('Something happened!'); // Prints: (node 12345) Warning: Something happened!
process.emitWarning('This API is deprecated', 'DeprecationWarning');

Signal Events

process.on('SIGINT', () => {
  console.log('Received SIGINT.  Press Control-D to exit.');
});

=======================================
| HTTP - request, createServer, request, response, writeHead, write, end, setHeader, getHeader, removeHeader
=======================================

http.request(options[, callback])

http.createClient([port][, host])

http.createServer([requestListener])

http.globalAgent

http.get(options[, callback])

http.METHODS

http.STATUS_CODES

http.Agent
new Agent([options])

agent.createConnection(options[, callback])

agent.destroy()

agent.freeSockets

agent.getName(options)

agent.maxFreeSockets

agent.maxSockets

agent.requests

agent.sockets

http.ClientRequest

on('abort', function(){})
on('aborted', function(){})
on('checkExpectation', function(){})
on('connect', function(){})
on('continue', function(){})
on('response', function(){})
on('socket', function(){})
on('upgrade', function(){})

request.write(chunk[, encoding][, callback])

request.end([data][, encoding][, callback])

request.abort()

request.flushHeaders()

request.setNoDelay([noDelay])

request.setSocketKeepAlive([enable][, initialDelay])

request.setTimeout(timeout[, callback])

http.Server

on('checkContinue', function(){})
on('clientError', function(){})
on('close', function(){})
on('connect', function(){})
on('connection', function(){})
on('request', function(){})
on('upgrade', function(){})

server.listen(handle[, callback])
server.listen(path[, callback])
server.listen(port[, hostname][, backlog][, callback])

server.listening

server.close([callback])

server.maxHeadersCount

server.setTimeout(msecs, callback)

server.timeout

http.ServerResponse

on('close', function(){})
on('finish', function(){})

response.writeHead(statusCode[, statusMessage][, headers])
response.writeHead(200, {
  'Content-Length': Buffer.byteLength(body),
  'Content-Type': 'text/plain' });

response.write(chunk[, encoding][, callback])

response.end([data][, encoding][, callback])

response.setHeader(name, value)
response.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);

response.getHeader(name)
var contentType = response.getHeader('content-type');

response.removeHeader(name)
response.removeHeader('Content-Encoding');

response.headersSent

response.setTimeout(msecs, callback)

response.statusCode
response.statusCode = 404;

response.statusMessage
response.statusMessage = 'Not found';

response.addTrailers(headers)

response.finished

response.sendDate

response.writeContinue()

http.IncomingMessage

on('aborted', function(){})
on('close', function(){})

message.headers

message.rawHeaders

message.setTimeout(msecs, callback)

message.statusCode

message.httpVersion

message.destroy([error])

message.method

message.rawTrailers

message.statusMessage

message.socket

message.trailers

message.url

const http = require('http');
const net = require('net');
const url = require('url');

// Create an HTTP tunneling proxy
var proxy = http.createServer( (req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('okay');
});
proxy.on('connect', (req, cltSocket, head) => {
  // connect to an origin server
  var srvUrl = url.parse(`http://${req.url}`);
  var srvSocket = net.connect(srvUrl.port, srvUrl.hostname, () => {
    cltSocket.write('HTTP/1.1 200 Connection Established\r\n' +
                    'Proxy-agent: Node.js-Proxy\r\n' +
                    '\r\n');
    srvSocket.write(head);
    srvSocket.pipe(cltSocket);
    cltSocket.pipe(srvSocket);
  });
});

// now that proxy is running
proxy.listen(1337, '127.0.0.1', () => {

  // make a request to a tunneling proxy
  var options = {
    port: 1337,
    hostname: '127.0.0.1',
    method: 'CONNECT',
    path: 'www.google.com:80'
  };

  var req = http.request(options);
  req.end();

  req.on('connect', (res, socket, head) => {
    console.log('got connected!');

    // make a request over an HTTP tunnel
    socket.write('GET / HTTP/1.1\r\n' +
                 'Host: www.google.com:80\r\n' +
                 'Connection: close\r\n' +
                 '\r\n');
    socket.on('data', (chunk) => {
      console.log(chunk.toString());
    });
    socket.on('end', () => {
      proxy.close();
    });
  });
});

// ------------------------------------

const http = require('http');

// Create an HTTP server
var srv = http.createServer( (req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('okay');
});
srv.on('upgrade', (req, socket, head) => {
  socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n' +
               'Upgrade: WebSocket\r\n' +
               'Connection: Upgrade\r\n' +
               '\r\n');

  socket.pipe(socket); // echo back
});

// now that server is running
srv.listen(1337, '127.0.0.1', () => {

  // make a request
  var options = {
    port: 1337,
    hostname: '127.0.0.1',
    headers: {
      'Connection': 'Upgrade',
      'Upgrade': 'websocket'
    }
  };

  var req = http.request(options);
  req.end();

  req.on('upgrade', (res, socket, upgradeHead) => {
    console.log('got upgraded!');
    socket.end();
    process.exit(0);
  });
});

// ----------------------------------------

const http = require('http');

const server = http.createServer((req, res) => {
  res.end();
});
server.on('clientError', (err, socket) => {
  socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
server.listen(8000);

// ---------------------------------------

const server = http.createServer((req,res) => {
  res.setHeader('Content-Type', 'text/html');
  res.setHeader('X-Foo', 'bar');
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('ok');
});

// -------------------------------------------------------

var postData = querystring.stringify({
  'msg' : 'Hello World!'
});

var options = {
  hostname: 'www.google.com',
  port: 80,
  path: '/upload',
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Content-Length': Buffer.byteLength(postData)
  }
};

var req = http.request(options, (res) => {
  console.log(`STATUS: ${res.statusCode}`);
  console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
  res.setEncoding('utf8');
  res.on('data', (chunk) => {
    console.log(`BODY: ${chunk}`);
  });
  res.on('end', () => {
    console.log('No more data in response.');
  });
});

req.on('error', (e) => {
  console.log(`problem with request: ${e.message}`);
});

// write data to request body
req.write(postData);
req.end();

=======================================
| HTTPS -
=======================================

https.request(options, callback)

https.createServer(options[, requestListener])

https.Agent

https.Server

server.setTimeout(msecs, callback)

server.timeout

server.close([callback])

server.listen(handle[, callback])
server.listen(path[, callback])
server.listen(port[, host][, backlog][, callback])

https.get(options, callback)

https.globalAgent

// ---------------------------------------------

// curl -k https://localhost:8000/
const https = require('https');
const fs = require('fs');

const options = {
  key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
  cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};

https.createServer(options, (req, res) => {
  res.writeHead(200);
  res.end('hello world\n');
}).listen(8000);

// ---------------------------------------------

const https = require('https');
const fs = require('fs');

const options = {
  pfx: fs.readFileSync('server.pfx')
};

https.createServer(options, (req, res) => {
  res.writeHead(200);
  res.end('hello world\n');
}).listen(8000);

// ---------------------------------------------

const https = require('https');

https.get('https://encrypted.google.com/', (res) => {
  console.log('statusCode: ', res.statusCode);
  console.log('headers: ', res.headers);

  res.on('data', (d) => {
    process.stdout.write(d);
  });

}).on('error', (e) => {
  console.error(e);
});

// ---------------------------------------------

const https = require('https');

var options = {
  hostname: 'encrypted.google.com',
  port: 443,
  path: '/',
  method: 'GET'
};

var req = https.request(options, (res) => {
  console.log('statusCode: ', res.statusCode);
  console.log('headers: ', res.headers);

  res.on('data', (d) => {
    process.stdout.write(d);
  });
});
req.end();

req.on('error', (e) => {
  console.error(e);
});

// ---------------------------------------------

var options = {
  hostname: 'encrypted.google.com',
  port: 443,
  path: '/',
  method: 'GET',
  key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
  cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};
options.agent = new https.Agent(options);

var req = https.request(options, (res) => {
  ...
});

=======================================
| Timers - setTimeout, setInterval, setImmediate, ref, unref
=======================================

var timeout = setTimeout(function(){}, 5000[, ...arg])
clearTimeout(timeout)

var interval = setInterval(function(){}, 5000[, ...arg])
clearInterval(interval )

var immediate = setImmediate(function(){}[, ...arg])
clearImmediate(immediate)

timeout.ref()

timeout.unref()

=======================================
| URL - парсинг урлов
=======================================

http://user:pass@host.com:8080/p/a/t/h?query=string#hash

┌───────────────────────────────────────────────────────────┐
│                                                        href                                               │
├───── ─┬┬─────── ┬─────────────┬───────────────────┬──────┤
│ protocol ││   auth      │           host       │               path                │ hash   │
│             ││               ├─────────────┼───── ───┬──────────┤          │
│             ││               │ hostname │ port │ pathname │     search      │           │
│             ││               │               │        │               ├┬──────── ─┤          │
│             ││               │               │        │               │ │    query       │          │
│ http:      // user:pass @ host.com : 8080     /p/a/t/h   ?  query=string   #hash │
│             ││               │               │        │               │ │                   │         │
└───────┴┴─── ────┴────────┴────┴────── ─┴┴──────────┴─────┘

const url = require('url');

urlObject.href // 'http://user:pass@host.com:8080/p/a/t/h?query=string#hash'
urlObject.protocol // 'http:'
urlObject.slashes // true
urlObject.host // 'host.com:8080'
urlObject.auth // 'user:pass
urlObject.hostname // 'host.com'
urlObject.port // '8080'
urlObject.pathname // '/p/a/t/h'
urlObject.search // ?query=string'
urlObject.path // '/p/a/t/h?query=string'
urlObject.query // 'query=string' или {'query': 'string'}
urlObject.hash '#hash'

url.format(urlObject) // возвращает отформатированную строку в адресом
url.parse(urlString[, parseQueryString[, slashesDenoteHost]]) // берет строку с адресом и превращает её в объект URL object
url.resolve(from, to) // соединяет адрес страницы с адресом сайта

url.resolve('/one/two/three', 'four')               // '/one/two/four'
url.resolve('http://example.com/', '/one')      // 'http://example.com/one'
url.resolve('http://example.com/one', '/two') // 'http://example.com/two'

Пробелы и знаки < > " ` \r \n \t { } | \ ^ ' автоматически будут заэскейплены.

=======================================
| Query String - парсинг строки с параметрами из URL
=======================================

const querystring = require('querystring');

querystring.escape(str) // эскейпит строку символами с процентом

querystring.unescape(str) // анэскейпит (деколдирует) строку, содержащую символы с процентами

querystring.parse(str[, sep[, eq[, options]]]) // превращает строку с параметрами в объект

querystring.parse('foo=bar&abc=xyz&abc=123', '&', '=', {decodeURIComponent: querystring.unescape, maxKeys: 1000}) // {foo: 'bar', abc: ['xyz',

'123']}

querystring.stringify(obj[, sep[, eq[, options]]]) // преваращает объект с параметрами в строку

querystring.stringify({foo: 'bar', abc: ['xyz', '123']}, '&', '=', {encodeURIComponent: querystring.escape}) // 'foo=bar&abc=xyz&abc=123'
querystring.stringify({foo: 'bar', baz: ['qux', 'quux'], corge: ''}) // 'foo=bar&baz=qux&baz=quux&corge='
querystring.stringify({foo: 'bar', baz: 'qux'}, ';', ':') // 'foo:bar;baz:qux'

=======================================
| Utilities - утилиты format, debuglog, deprecate, inspect
=======================================

// debuglog()

const util = require('util');

const debuglog = util.debuglog('foo');

debuglog('hello from foo [%d]', 123); // Если программа запущена с ключом NODE_DEBUG=foo, то в консоли будет выведено FOO 3245: hello

from foo [123] иначе функция не выведен ничего

// deprecate()

const util = require('util');

exports.puts = util.deprecate(function() {
  for (var i = 0, len = arguments.length; i < len; ++i) {
    process.stdout.write(arguments[i] + '\n');
  }
}, 'util.puts: Use console.log instead'); // Выведет в консоли сообщение util.puts: Use console.log instead

// format()

Типы форматов:

%s - string
%d - number (integer и float).
%j - JSON
%% - символ '%'

util.format('%s:%s', 'foo'); // Returns 'foo:%s'

util.format('%s:%s', 'foo', 'bar', 'baz'); // 'foo:bar baz'

util.format(1, 2, 3); // '1 2 3'

// inspect()

console.log(util.inspect(util, { showHidden: true, depth: null })); // выведет структуру всех свойст объекта

// Custom inspect()

const util = require('util');

const obj = { name: 'nate' };

obj.inspect = function(depth) {
  return `{${this.name}}`;
};

util.inspect(obj); // "{nate}"

=======================================
| StringDecoder - декодирование объекта Buffer в строку
=======================================

new StringDecoder([encoding])
stringDecoder.end([buffer])
stringDecoder.write(buffer)

const StringDecoder = require('string_decoder').StringDecoder;
const decoder = new StringDecoder('utf8');

const cent = Buffer.from([0xC2, 0xA2]);
console.log(decoder.write(cent));

const euro = Buffer.from([0xE2, 0x82, 0xAC]);
console.log(decoder.write(euro));

четверг, 28 июля 2016 г.

Мой вариант JSON Schema

;(function (global, factory) {
    if (typeof define === 'function' && define.amd) {
        define(factory);
    } else if (typeof module === 'object' && typeof module.exports === 'object') {
        module.exports = factory();
    } else {
        global.JSONSchema = factory();
    }
})(this, function () {

    // Валидация данных в формате JSON согласно схеме проверки.

    // Допустимые для типы значений:
    // - types - массив с возможными вариантами типов значений
    // - type - тип значения: null, boolean, number, string, array, object
    // - min - минимальная длина значения или число элементов, входящих в него
    // - max - максимальная длина значения или число элементов, входящих в него
    // - regexp: {pattern: 'abcde', flags: 'gi'} - регулярное выражение для проверки содержимого строки
    // - items - объект, содержащий в себе описание схемы для проверки элементов внутри массива, имеющих числовые индексы
    // - allItems - массив, содержащий в себе описание схемы для проверки всех элементов внутри массива, имеющих одинаковый тип
    // - properties - объект, содержащий в себе описание схемы для проверки элементов внутри объекта
    // - optional: true - булево значение, всегда равное true и указывающее на то, что не является ошибкой, если данный элемент будет отсутствовать
    //
    // undefined:
    // - type
    // - optional: true
    //
    // null:
    // - type
    // - optional: true
    //
    // boolean:
    // - type
    // - optional: true
    //
    // number:
    // - type
    // - min
    // - max
    // - optional: true
    //
    // string:
    // - type
    // - min
    // - max
    // - regexp
    // - optional: true
    //
    // array:
    // - type
    // - min
    // - max
    // - items
    // - allItems
    // - optional: true
    //
    // object:
    // - type
    // - min
    // - max
    // - properties
    // - optional: true

    // Пример использования:
    //
    // var data = {
    //       undef: undefined
    //     , nul: null
    //     , bool: false
    //     , num: 5
    //     , str: 'ab1c2de'
    //     , arr: [1, 2, [3, 4]]
    //     , obj: {one: '', two: {three: 3}}
    //     , anyTypeFrom: 5
    //     , all: [1, 2, 3, 4]
    // };
    //
    // var schema = {
    //       undef: {type: 'undefined'}
    //     , nul: {type: 'null'}
    //     , bool: {type: 'boolean'}
    //     , num: {type: 'number', min: 0, max: 10}
    //     , str: {type: 'string', min: 0, max: 10, regexp: {pattern: 'abcde', flags: 'gi'}}
    //     , arr: {type: 'array', min: 0, max: 10, items: {
    //             '0': {type: 'number'}
    //           , '1': {type: 'number'}
    //           , '2': {type: 'array', items: {
    //                     '0': {type: 'number'}
    //                   , '1': {type: 'string'}
    //               }
    //             }
    //         }
    //       }
    //     , obj: {type: 'object', min: 0, max: 10, properties: {
    //              one: {type: 'number'}
    //            , two: {type: 'object', properties: {
    //                   three: {type: 'string'}
    //                 , four: {type: 'string', optional: true}
    //              }
    //            }
    //        }
    //       }
    //     , anyTypeFrom: {
    //         types: [
    //              {type: 'number', min: 0, max: 10}
    //            , {type: 'string', min: 0, max: 10, regexp: {pattern: 'abcde', flags: 'gi'}}
    //         ]
    //       }
    //     , all: {type: 'array', allItems: {type: 'number'}}
    //     , notExist1: {type: 'array', optional: true}
    //     , notExist2: {
    //           types: [
    //                  {type: 'number', min: 0, max: 10}
    //                , {type: 'string', min: 0, max: 10, regexp: {pattern: 'abcde', flags: 'gi'}}
    //           ]
    //         , optional: true
    //       }
    // };
    //
    // var result = JSONSchema.validate(data, schema);
    //
    // if (!result.valid) {
    //     for (var i = 0, len = result.errors.length; i < len; i++) {
    //         console.log('Ошибка: ' + result.errors[i].message);
    //         console.log('Путь до элемента: ' + result.errors[i].path);
    //     }
    // }

    function has (object, key) {
        return Object.prototype.hasOwnProperty.call(object, key);
    }

    function isTypeOf (value, type) {
        return Object.prototype.toString.call(value).toLowerCase().slice(8, -1) === type;
    }

    function objectLength (object) {
        var length = 0
            , key;
        for (key in object) {if (has(object, key)) {
            length++;
        }}
        return length;
    }

    function validate (json, schema) {

        var result = {
              valid: true
            , errors: []
        };

        function addError (path, message) {
            result.valid = false;
            result.errors.push({path: path, message: message});
        }

        if (!isTypeOf(json, 'object')) {
            addError('корневой элемент', 'Корневой элемент должен быть объектом.');
        } else {
            for (var key in schema) {if (has(schema, key)) {
                if (
                          !has(json, key)
                    && (
                            (has(schema[key], 'type') && schema[key].type !== 'undefined' && !has(schema[key], 'optional'))
                        || (has(schema[key], 'types') && !has(schema[key], 'optional'))
                    )
                ) {
                    addError('корневой объект', 'Элемент "' + key + '" должен присутствовать в корневом объекте.');
                }
                           if (has(schema[key], 'type')) {validateSingleRootType(json, schema, key);
                } else if (has(schema[key], 'types')) {validateManyRootTypes(json, schema, key);
                }
            }}
        }

        function validateSingleRootType (json, schema, key) {
            if (!has(schema[key], 'type')) {throw new Error('Описание "type" всегда должно присутствовать в схеме для элемента "' + key + '" корневого объекта.');}
                       if (schema[key].type === 'undefined') {validateUndefined(key, json[key], schema[key], '');
            } else if (schema[key].type === 'null') {validateNull(key, json[key], schema[key], '');
            } else if (schema[key].type === 'boolean') {validateBoolean(key, json[key], schema[key], '');
            } else if (schema[key].type === 'number') {validateNumber(key, json[key], schema[key], '');
            } else if (schema[key].type === 'string') {validateString(key, json[key], schema[key], '');
            } else if (schema[key].type === 'array') {validateArray(key, json[key], schema[key], '');
            } else if (schema[key].type === 'object') {validateObject(key, json[key], schema[key], '');
            }
        }

        function validateManyRootTypes (json, schema, key) {
            var elementType
                , requiredElements = [];
            for (var i = 0, len = schema[key].types.length; i < len; i++) {
                if (!has(schema[key].types[i], 'type')) {throw new Error('Описание "type" всегда должно присутствовать в схеме для элемента "' + key + '" корневого объекта.');}
                           if (schema[key].types[i].type === 'undefined' && isTypeOf(json[key], 'undefined')) {elementType = 'undefined'; break;
                } else if (schema[key].types[i].type === 'null' && isTypeOf(json[key], 'null')) {elementType = 'null'; break;
                } else if (schema[key].types[i].type === 'boolean' && isTypeOf(json[key], 'boolean')) {elementType = 'boolean'; break;
                } else if (schema[key].types[i].type === 'number' && isTypeOf(json[key], 'number')) {elementType = 'number'; validateNumber(key, json[key], schema[key].types[i], ''); break;
                } else if (schema[key].types[i].type === 'string' && isTypeOf(json[key], 'string')) {elementType = 'string'; validateString(key, json[key], schema[key].types[i], ''); break;
                } else if (schema[key].types[i].type === 'array' && isTypeOf(json[key], 'array')) {elementType = 'array'; validateArray(key, json[key], schema[key].types[i], ''); break;
                } else if (schema[key].types[i].type === 'object' && isTypeOf(json[key], 'object')) {elementType = 'object'; validateObject(key, json[key], schema[key].types[i], ''); break;
                } else {requiredElements.push('"' + schema[key].types[i].type + '"');
                }
            }
            if (elementType === undefined && !has(schema[key], 'optional')) {addError('корневой объект', 'Элемент "' + key + '" корневого объекта должен иметь значение с типом: ' + requiredElements.join(', ') + '.');}
        }

        function validateUndefined (/*key, value, schemaForUndefined, pathToElement*/) {
            // Сообщение об ошибке выводить не нужно. Если элемента нет, то это допустимо.
            // if (!isTypeOf(value, 'undefined') && !has(schemaForNull, 'optional')) {addError(pathToElement + key, 'Элемент "' + key + '" не должен присутствовать.');}
        }

        function validateNull (key, value, schemaForNull, pathToElement) {
            if (isTypeOf(value, 'undefined') && has(schemaForNull, 'optional')) {return;}
            if (!isTypeOf(value, 'null')) {addError(pathToElement + key, 'Элемент "' + key + '" должен иметь значение null.');}
        }

        function validateBoolean (key, bool, schemaForBoolean, pathToElement) {
            if (isTypeOf(bool, 'undefined') && has(schemaForBoolean, 'optional')) {return;}
            if (!isTypeOf(bool, 'boolean')) {addError(pathToElement + key, 'Элемент "' + key + '" должен иметь значение true или false.');}
        }

        function validateNumber (key, number, schemaForNumber, pathToElement) {
            if (isTypeOf(number, 'undefined') && has(schemaForNumber, 'optional')) {return;}
            if (!isTypeOf(number, 'number')) {addError(pathToElement + key, 'Элемент "' + key + '" должен иметь в качестве значения число.');
            } else {
                if (number !== number) {addError(pathToElement + key, 'Элемент "' + key + '" не должен иметь в качестве значения NaN.');}
                if (has(schemaForNumber, 'min') && number < schemaForNumber.min) {addError(pathToElement + key, 'Значение элемента "' + key + '" должно быть больше или равно ' + schemaForNumber.min + '.');}
                if (has(schemaForNumber, 'max') && number > schemaForNumber.max) {addError(pathToElement + key, 'Значение элемента "' + key + '" должно быть меньше или равно ' + schemaForNumber.max + '.');}
            }
        }

        function validateString (key, string, schemaForString, pathToElement) {
            if (isTypeOf(string, 'undefined') && has(schemaForString, 'optional')) {return;}
            if (!isTypeOf(string, 'string')) {addError(pathToElement + key, 'Элемент "' + key + '" должен иметь в качестве значения строку.');
            } else {
                if (has(schemaForString, 'min') && schemaForString.min === 0 && string.length === 0) {return;}
                if (has(schemaForString, 'min') && string.length < schemaForString.min) {addError(pathToElement + key, 'Число символов в строке "' + key + '" должно быть больше или равно ' + schemaForString.min + '.');}
                if (has(schemaForString, 'max') && string.length > schemaForString.max) {addError(pathToElement + key, 'Число символов в строке "' + key + '" должно быть меньше или равно ' + schemaForString.max + '.');}
                if (has(schemaForString, 'regexp') && !(new RegExp(schemaForString.regexp.pattern, schemaForString.regexp.flags).test(string))) {addError(pathToElement + key, 'Значение элемента "' + key + '" не соответствует регулярному выражению: new RegExp("' + schemaForString.regexp.pattern + '", "' + schemaForString.regexp.flags + '")');}
            }
        }

        function validateArray (key, array, schemaForArray, pathToElement) {
            var index
                , arrayLength;
            if (isTypeOf(array, 'undefined') && has(schemaForArray, 'optional')) {return;}
            if (!isTypeOf(array, 'array')) {addError(pathToElement + key, 'Элемент "' + key + '" должен иметь в качестве значения массив.');
            } else {
                if (has(schemaForArray, 'min') && schemaForArray.min === 0 && array.length === 0) {return;}
                if (has(schemaForArray, 'min') && array.length < schemaForArray.min) {addError(pathToElement + key, 'Число элементов в массиве "' + key + '" должно быть больше или равно ' + schemaForArray.min + '.');}
                if (has(schemaForArray, 'max') && array.length > schemaForArray.max) {addError(pathToElement + key, 'Число элементов в массиве "' + key + '" должно быть меньше или равно ' + schemaForArray.max + '.');}
                if (has(schemaForArray, 'items')) {
                    for (index in schemaForArray.items) {if (has(schemaForArray.items, index)) {
                        if (array[index] === undefined) {addError(pathToElement + key, 'Элемент с индексом "' + index + '" должен присутствовать в массиве "' + key + '".');}
                                   if (has(schemaForArray.items[index], 'type')) {validateSingleArrayType(array, schemaForArray, index, key, pathToElement);
                        } else if (has(schemaForArray.items[index], 'types')) {validateManyArrayTypes(array, schemaForArray, index, key, pathToElement);
                        }
                    }}
                }
                if (has(schemaForArray, 'allItems')) {
                    for (index = 0, arrayLength = array.length; index < arrayLength; index++) {
                        if (!has(schemaForArray.allItems, 'type')) {throw new Error('Описание "type" всегда должно присутствовать в схеме для всех элементов массива "' + key + '".');}
                                   if (schemaForArray.allItems.type === 'undefined') {validateUndefined(index, array[index], schemaForArray.allItems, pathToElement + key + '.');
                        } else if (schemaForArray.allItems.type === 'null') {validateNull(index, array[index], schemaForArray.allItems, pathToElement + key + '.');
                        } else if (schemaForArray.allItems.type === 'boolean') {validateBoolean(index, array[index], schemaForArray.allItems, pathToElement + key + '.');
                        } else if (schemaForArray.allItems.type === 'number') {validateNumber(index, array[index], schemaForArray.allItems, pathToElement + key + '.');
                        } else if (schemaForArray.allItems.type === 'string') {validateString(index, array[index], schemaForArray.allItems, pathToElement + key + '.');
                        } else if (schemaForArray.allItems.type === 'array') {validateArray(index, array[index], schemaForArray.allItems, pathToElement + key + '.');
                        } else if (schemaForArray.allItems.type === 'object') {validateObject(index, array[index], schemaForArray.allItems, pathToElement + key + '.');
                        }
                    }
                }
            }
        }

        function validateSingleArrayType (array, schemaForArray, index, key, pathToElement) {
            if (!has(schemaForArray.items[index], 'type')) {throw new Error('Описание "type" всегда должно присутствовать в схеме для элемента с индексом "' + index + '" массива "' + key + '".');}
                       if (schemaForArray.items[index].type === 'undefined') {validateUndefined(index, array[index], schemaForArray.items[index], pathToElement + key + '.');
            } else if (schemaForArray.items[index].type === 'null') {validateNull(index, array[index], schemaForArray.items[index], pathToElement + key + '.');
            } else if (schemaForArray.items[index].type === 'boolean') {validateBoolean(index, array[index], schemaForArray.items[index], pathToElement + key + '.');
            } else if (schemaForArray.items[index].type === 'number') {validateNumber(index, array[index], schemaForArray.items[index], pathToElement + key + '.');
            } else if (schemaForArray.items[index].type === 'string') {validateString(index, array[index], schemaForArray.items[index], pathToElement + key + '.');
            } else if (schemaForArray.items[index].type === 'array') {validateArray(index, array[index], schemaForArray.items[index], pathToElement + key + '.');
            } else if (schemaForArray.items[index].type === 'object') {validateObject(index, array[index], schemaForArray.items[index], pathToElement + key + '.');
            }
        }

        function validateManyArrayTypes (array, schemaForArray, index, key, pathToElement) {
            var elementType
                , requiredElements = [];
            for (var i = 0, len = schemaForArray.items[index].types.length; i < len; i++) {
                if (!has(schemaForArray.items[index].types[i], 'type')) {throw new Error('Описание "type" всегда должно присутствовать в схеме для элемента с индексом "' + index + '" массива "' + key + '".');}
                           if (schemaForArray.items[index].types[i].type === 'undefined' && isTypeOf(array[index], 'undefined')) {elementType = 'undefined'; break;
                } else if (schemaForArray.items[index].types[i].type === 'null' && isTypeOf(array[index], 'null')) {elementType = 'null'; break;
                } else if (schemaForArray.items[index].types[i].type === 'boolean' && isTypeOf(array[index], 'boolean')) {elementType = 'boolean'; break;
                } else if (schemaForArray.items[index].types[i].type === 'number' && isTypeOf(array[index], 'number')) {elementType = 'number'; validateNumber(index, array[index], schemaForArray.items[index].types[i], pathToElement + key + '.'); break;
                } else if (schemaForArray.items[index].types[i].type === 'string' && isTypeOf(array[index], 'string')) {elementType = 'string'; validateString(index, array[index], schemaForArray.items[index].types[i], pathToElement + key + '.'); break;
                } else if (schemaForArray.items[index].types[i].type === 'array' && isTypeOf(array[index], 'array')) {elementType = 'array'; validateArray(index, array[index], schemaForArray.items[index].types[i], pathToElement + key + '.'); break;
                } else if (schemaForArray.items[index].types[i].type === 'object' && isTypeOf(array[index], 'object')) {elementType = 'object'; validateObject(index, array[index], schemaForArray.items[index].types[i], pathToElement + key + '.'); break;
                } else {requiredElements.push('"' + schemaForArray.items[index].types[i].type + '"');
                }
            }
            if (elementType === undefined && !has(schemaForArray.items[index], 'optional')) {addError(pathToElement + key + '.' + index, 'Элемент с индексом "' + index + '" массива "' + key + '" должен иметь значение с типом: ' + requiredElements.join(', ') + '.');}
        }

        function validateObject (key, object, schemaForObject, pathToElement) {
            if (isTypeOf(object, 'undefined') && has(schemaForObject, 'optional')) {return;}
            if (!isTypeOf(object, 'object')) {addError(pathToElement + key, 'Элемент "' + key + '" должен иметь в качестве значения объект.');
            } else {
                if (has(schemaForObject, 'min') && schemaForObject.min === 0 && objectLength(object) === 0) {return;}
                if (has(schemaForObject, 'min') && objectLength(object) < schemaForObject.min) {addError(pathToElement + key, 'Число свойств в объекте "' + key + '" должно быть больше или равно ' + schemaForObject.min + '.');}
                if (has(schemaForObject, 'max') && objectLength(object) > schemaForObject.max) {addError(pathToElement + key, 'Число свойств в объекте "' + key + '" должно быть меньше или равно ' + schemaForObject.max + '.');}
                if (has(schemaForObject, 'properties')) {
                    for (var property in schemaForObject.properties) {if (has(schemaForObject.properties, property)) {
                        if (
                                  !has(object, property)
                            && (
                                    (has(schemaForObject.properties[property], 'type') && schemaForObject.properties[property].type !== 'undefined'  && !has(schemaForObject.properties[property], 'optional'))
                                || (has(schemaForObject.properties[property], 'types') && !has(schemaForObject.properties[property], 'optional'))
                            )
                        ) {
                            addError(pathToElement + key, 'Элемент "' + property + '" должен присутствовать в объекте "' + key + '".');
                        }
                                   if (has(schemaForObject.properties[property], 'type')) {validateSingleObjectType(object, schemaForObject, property, key, pathToElement);
                        } else if (has(schemaForObject.properties[property], 'types')) {validateManyObjectTypes(object, schemaForObject, property, key, pathToElement);
                        }
                    }}
                }
            }
        }

        function validateSingleObjectType (object, schemaForObject, property, key, pathToElement) {
            if (!has(schemaForObject.properties[property], 'type')) {throw new Error('Описание "type" всегда должно присутствовать в схеме для элемента "' + property + '" объекта "' + key + '".');}
                       if (schemaForObject.properties[property].type === 'undefined') {validateUndefined(property, object[property], schemaForObject.properties[property], pathToElement + key + '.');
            } else if (schemaForObject.properties[property].type === 'null') {validateNull(property, object[property], schemaForObject.properties[property], pathToElement + key + '.');
            } else if (schemaForObject.properties[property].type === 'boolean') {validateBoolean(property, object[property], schemaForObject.properties[property], pathToElement + key + '.');
            } else if (schemaForObject.properties[property].type === 'number') {validateNumber(property, object[property], schemaForObject.properties[property], pathToElement + key + '.');
            } else if (schemaForObject.properties[property].type === 'string') {validateString(property, object[property], schemaForObject.properties[property], pathToElement + key + '.');
            } else if (schemaForObject.properties[property].type === 'array') {validateArray(property, object[property], schemaForObject.properties[property], pathToElement + key + '.');
            } else if (schemaForObject.properties[property].type === 'object') {validateObject(property, object[property], schemaForObject.properties[property], pathToElement + key + '.');
            }
        }

        function validateManyObjectTypes (object, schemaForObject, property, key, pathToElement) {
            var elementType
                , requiredElements = [];
            for (var i = 0, len = schemaForObject.properties[property].types.length; i < len; i++) {
                if (!has(schemaForObject.properties[property].types[i], 'type')) {throw new Error('Описание "type" всегда должно присутствовать в схеме для элемента "' + property + '" объекта "' + key + '".');}
                           if (schemaForObject.properties[property].types[i].type === 'undefined' && isTypeOf(object[property], 'undefined')) {elementType = 'undefined'; break;
                } else if (schemaForObject.properties[property].types[i].type === 'null' && isTypeOf(object[property], 'null')) {elementType = 'null'; break;
                } else if (schemaForObject.properties[property].types[i].type === 'boolean' && isTypeOf(object[property], 'boolean')) {elementType = 'boolean'; break;
                } else if (schemaForObject.properties[property].types[i].type === 'number' && isTypeOf(object[property], 'number')) {elementType = 'number'; validateNumber(property, object[property], schemaForObject.properties[property].types[i], pathToElement + key + '.'); break;
                } else if (schemaForObject.properties[property].types[i].type === 'string' && isTypeOf(object[property], 'string')) {elementType = 'string'; validateString(property, object[property], schemaForObject.properties[property].types[i], pathToElement + key + '.'); break;
                } else if (schemaForObject.properties[property].types[i].type === 'array' && isTypeOf(object[property], 'array')) {elementType = 'array'; validateArray(property, object[property], schemaForObject.properties[property].types[i], pathToElement + key + '.'); break;
                } else if (schemaForObject.properties[property].types[i].type === 'object' && isTypeOf(object[property], 'object')) {elementType = 'object'; validateObject(property, object[property], schemaForObject.properties[property].types[i], pathToElement + key + '.'); break;
                } else {requiredElements.push('"' + schemaForObject.properties[property].types[i].type + '"');
                }
            }
            if (elementType === undefined && !has(schemaForObject.properties[property], 'optional')) {addError(pathToElement + key + '.' + property, 'Элемент "' + property + '" объекта "' + key + '" должен иметь значение с типом: ' + requiredElements.join(', ') + '.');}
        }

        if (result.errors.length > 0) {
            (function(){
                var pathElements
                    , pathElementsLength
                    , resultPath = ''
                    , separator
                    , errorsLength = result.errors.length
                    , i;
                while (errorsLength--) {
                    pathElements = result.errors[errorsLength].path.split('.');
                    pathElementsLength = pathElements.length;
                    for (i = 0; i < pathElementsLength; i++) {
                        if (i === 0) {
                            separator = '';
                        } else {
                            separator = '.';
                        }
                                   if ((/^\d+$/g).test(pathElements[i])) {resultPath += '[' + pathElements[i] + ']';
                        } else if ((/^\d+/g).test(pathElements[i])) {resultPath += '["' + pathElements[i] + '"]';
                        } else {resultPath += separator + pathElements[i];
                        }
                    }
                    result.errors[errorsLength].path = resultPath;
                    resultPath = '';
                }
            })();
        }

        return result;

    }

    return {validate: validate};

});