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

New EcmaScript 6 Class functions

// Class Definition

// ES 6
class Shape {
    constructor (id, x, y) {
        this.id = id
        this.move(x, y)
    }
    move (x, y) {
        this.x = x
        this.y = y
    }
}

// ES 5

var Shape = function (id, x, y) {
    this.id = id;
    this.move(x, y);
};
Shape.prototype.move = function (x, y) {
    this.x = x;
    this.y = y;
};

// Class Inheritance

// ES 6

class Rectangle extends Shape {
    constructor (id, x, y, width, height) {
        super(id, x, y)
        this.width  = width
        this.height = height
    }
}
class Circle extends Shape {
    constructor (id, x, y, radius) {
        super(id, x, y)
        this.radius = radius
    }
}

// ES 5

var Rectangle = function (id, x, y, width, height) {
    Shape.call(this, id, x, y);
    this.width  = width;
    this.height = height;
};
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
var Circle = function (id, x, y, radius) {
    Shape.call(this, id, x, y);
    this.radius = radius;
};
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;

// Class Inheritance, From Expressions

// ES 6

var aggregation = (baseClass, ...mixins) => {
    let base = class _Combined extends baseClass {
        constructor (...args) {
            super(...args)
            mixins.forEach((mixin) => {
                mixin.prototype.initializer.call(this)
            })
        }
    }
    let copyProps = (target, source) => {
        Object.getOwnPropertyNames(source)
            .concat(Object.getOwnPropertySymbols(source))
            .forEach((prop) => {
            if (prop.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/))
                return
            Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop))
        })
    }
    mixins.forEach((mixin) => {
        copyProps(base.prototype, mixin.prototype)
        copyProps(base, mixin)
    })
    return base
}

class Colored {
    initializer ()     { this._color = "white" }
    get color ()       { return this._color }
    set color (v)      { this._color = v }
}

class ZCoord {
    initializer ()     { this._z = 0 }
    get z ()           { return this._z }
    set z (v)          { this._z = v }
}

class Shape {
    constructor (x, y) { this._x = x; this._y = y }
    get x ()           { return this._x }
    set x (v)          { this._x = v }
    get y ()           { return this._y }
    set y (v)          { this._y = v }
}

class Rectangle extends aggregation(Shape, Colored, ZCoord) {}

var rect = new Rectangle(7, 42)
rect.z     = 1000
rect.color = "red"
console.log(rect.x, rect.y, rect.z, rect.color)

// ES 5

var aggregation = function (baseClass, mixins) {
    var base = function () {
        baseClass.apply(this, arguments);
        mixins.forEach(function (mixin) {
            mixin.prototype.initializer.call(this);
        }.bind(this));
    };
    base.prototype = Object.create(baseClass.prototype);
    base.prototype.constructor = base;
    var copyProps = function (target, source) {
        Object.getOwnPropertyNames(source).forEach(function (prop) {
            if (prop.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/))
                return
            Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop))
        })
    }
    mixins.forEach(function (mixin) {
        copyProps(base.prototype, mixin.prototype);
        copyProps(base, mixin);
    });
    return base;
};

var Colored = function () {};
Colored.prototype = {
    initializer: function ()  { this._color = "white"; },
    getColor:    function ()  { return this._color; },
    setColor:    function (v) { this._color = v; }
};

var ZCoord = function () {};
ZCoord.prototype = {
    initializer: function ()  { this._z = 0; },
    getZ:        function ()  { return this._z; },
    setZ:        function (v) { this._z = v; }
};

var Shape = function (x, y) {
    this._x = x; this._y = y;
};
Shape.prototype = {
    getX: function ()  { return this._x; },
    setX: function (v) { this._x = v; },
    getY: function ()  { return this._y; },
    setY: function (v) { this._y = v; }
}

var _Combined = aggregation(Shape, [ Colored, ZCoord ]);
var Rectangle = function (x, y) {
    _Combined.call(this, x, y);
};
Rectangle.prototype = Object.create(_Combined.prototype);
Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle(7, 42);
rect.setZ(1000);
rect.setColor("red");
console.log(rect.getX(), rect.getY(), rect.getZ(), rect.getColor());

// Base Class Access

// ES 6

class Shape {
    …
    toString () {
        return `Shape(${this.id})`
    }
}
class Rectangle extends Shape {
    constructor (id, x, y, width, height) {
        super(id, x, y)
        …
    }
    toString () {
        return "Rectangle > " + super.toString()
    }
}
class Circle extends Shape {
    constructor (id, x, y, radius) {
        super(id, x, y)
        …
    }
    toString () {
        return "Circle > " + super.toString()
    }
}

// ES 5

var Shape = function (id, x, y) {
    …
};
Shape.prototype.toString = function (x, y) {
    return "Shape(" + this.id + ")"
};
var Rectangle = function (id, x, y, width, height) {
    Shape.call(this, id, x, y);
    …
};
Rectangle.prototype.toString = function () {
    return "Rectangle > " + Shape.prototype.toString.call(this);
};
var Circle = function (id, x, y, radius) {
    Shape.call(this, id, x, y);
    …
};
Circle.prototype.toString = function () {
    return "Circle > " + Shape.prototype.toString.call(this);
};

// Static Members

// ES 6

class Rectangle extends Shape {
    …
    static defaultRectangle () {
        return new Rectangle("default", 0, 0, 100, 100)
    }
}
class Circle extends Shape {
    …
    static defaultCircle () {
        return new Circle("default", 0, 0, 100)
    }
}
var defRectangle = Rectangle.defaultRectangle()
var defCircle    = Circle.defaultCircle()

// ES 5

var Rectangle = function (id, x, y, width, height) {
    …
};
Rectangle.defaultRectangle = function () {
    return new Rectangle("default", 0, 0, 100, 100);
};
var Circle = function (id, x, y, width, height) {
    …
};
Circle.defaultCircle = function () {
    return new Circle("default", 0, 0, 100);
};
var defRectangle = Rectangle.defaultRectangle();
var defCircle    = Circle.defaultCircle();

// Getter/Setter

// ES 6

// ES 5

var Rectangle = function (width, height) {
    this.width  = width;
    this.height = height;
};
Rectangle.prototype = {
    set width  (width)  { this._width = width;             },
    get width  ()       { return this._width;              },
    set height (height) { this._height = height;           },
    get height ()       { return this._height;             },
    get area   ()       { return this.width * this.height; }
};
var r = new Rectangle(50, 20);
r.area === 1000;

// Range

function range (start, end, step) {
    var list = [];
    while (start < end) {
        list.push(start);
        start += step;
    }
    return list;
}

// isNaN and isFinite

var isNaN = function (n) {
    return n !== n;
};
var isFinite = function (v) {
    return (typeof v === "number" && isNaN(v) && v !== Infinity && v !== -Infinity);
};

// isSafeInteger

function isSafeInteger (n) {
    return (
           typeof n === 'number'
        && Math.round(n) === n
        && -(Math.pow(2, 53) - 1) <= n
        && n <= (Math.pow(2, 53) - 1)
    );
}
isSafeInteger(42) === true;
isSafeInteger(9007199254740992) === false;

// mathTrunc

function mathTrunc (x) {
    return (x < 0 ? Math.ceil(x) : Math.floor(x));
}
console.log(mathTrunc(42.7)) // 42
console.log(mathTrunc( 0.1)) // 0
console.log(mathTrunc(-0.1)) // -0

// mathSign

function mathSign (x) {
    return ((x === 0  isNaN(x)) ? x : (x > 0 ? 1 : -1));
}
console.log(mathSign(7))   // 1
console.log(mathSign(0))   // 0
console.log(mathSign(-0))  // -0
console.log(mathSign(-7))  // -1
console.log(mathSign(NaN)) // NaN

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

Require JS Module dependencies tree visualization

Файл rjs.js

// Порядок использования:
//
// 1. Загрузите данный файл первым в качестве зависимости внутри вашего главного JavaScript-файла.
// 2. Как только HTML-страница будет загружена выполните в консоли браузера команды:
//     window.rjs.buildTree();
//     console.log(window.rjs.tree);
//    В результате в объекте window.rjs.tree будет сформировано дерево зависимостей ваших модулей.
// 3. Для формирования списка зависимостей модулей вызовите метод window.rjs.toList().
//
// Методы доступные после загрузки HTML-страницы:
// rjs.buildTree()
// - Формирует карту зависимостей ваших модулей внутри объекта rjs.tree.
// - Выведите значение rjs.tree в консоли браузера, чтобы увидеть карту зависимостей модулей.
// rjs.toList()
// - Возвращает строку со списком зависимостей всех модулей.

// onResourceLoad - это внутренний крюк, который может быть использован для вывода сообщения о том, что модуль был
// создан или он экспортировал свое значение. Таким образом становится возможна отрисовка порядка загрузки модулей и их зависимостей.
requirejs.onResourceLoad = function (context, module, dependeciesArray) {

    // context - внутренний объект, испольуемый RequireJS для хранения экспортированных значений и состояния загрузки модулей.
    // В данном случае он никак не используется.
 
    // module - объект содержащий информацию о только что загруженном модуле.
    // Свойства объекта module:
    // module.name - нормализованное имя модуля. Для плагина это просто имя плагина.
    // module.url - URL исползуемый для загрузки файла модуля. Может быть относительным путем.
 
    // dependeciesArray - массив зависимостей определенных для данного модуля. Каждый элемент массива это объект module.
 
    // Сформировать глобальный объект window.rjs при загрузке первого модуля
    if (!window.rjs) {
        window.rjs = {
              tree: {} // дерево зависимостей модулей
            , buildTree: buildTree // функция формирования карты зависисмостей модулей (определена ниже)
            , toList: toList // функция формирования списка зависисмостей модулей (определена ниже)
            , toPaths: toPaths
            , buildPathsTree: buildPathsTree
            , getMainFileTree: getMainFileTree
            , drawTree: drawTree
        };
    }

    // Если модуля нет в дереве зависимостей, то добавить объект с данными нового модуля в дерево
    if (!window.rjs.tree[module.name]) {
        window.rjs.tree[module.name] = {moduleName: module.name, moduleDependeciesNames: [], moduleDependeciesTree: {}, modulePath: module.url ? module.url.split('?')[0] : ''};
        // moduleDependeciesNames - массив имен модулей, от которых зависит данный модуль
        // moduleMap - карта зависимостей данного модуля
    }
 
    // Для формирования полного дерева зависимостей модулей друг от друга
    var i, len;
    if (dependeciesArray) { // Если у модуля есть зависимости, то
        for (
            i = 0, len = dependeciesArray.length;
            i < len;
            i++
        ) { // добавить в массив имен зависимостей имена модулей, от которых зависит данный модуль
            window.rjs.tree[module.name].moduleDependeciesNames.push(dependeciesArray[i].name);
        }
    }
 
    // На данном этапе в дерево добавлены имена всех загруженных модулей.
    // В массив имен зависимостей всех модулей добавлены имена модулей, от которых они зависят.
    // Однако карта зависимостей у всех модулей пока пуста.

};

// Функция формирования карты дерева зависисмостей модулей
// На данном этапе карта зависимостей у всех модулей пока пуста.
// Поэтому данная функция заполняет занчения moduleMap для каждого модуля.
function buildTree () {
    var tree = window.rjs.tree
        , moduleName
        , currentModule
        , i
        , len
        , currentDependencyName;
    for (moduleName in tree) { // Для каждого имени модуля, содержащегося в дереве
        if (tree.hasOwnProperty(moduleName)) { // Если имя модуля содержится в дереве, то взять объект с данными данного модуля
            currentModule = tree[moduleName]; // вида {moduleName: module.name, moduleDependeciesNames: [], moduleMap: {}, modulePath: ''}
            for (
                i = 0, len = currentModule.moduleDependeciesNames.length;
                i < len;
                i++
            ) {
                currentDependencyName = currentModule.moduleDependeciesNames[i]; // Взять имя текущей зависимости данного модуля
                currentModule.moduleDependeciesTree[currentDependencyName] = tree[currentDependencyName]; // В карту данного модуля добавить объект текущего модуля из дерева
            }
        }
    }
}

// Фунцкия формирования списка зависисмостей модулей
// Данная функция возвращает строку, состоящую из списка зависисмостей модулей
function toList () {
    var list = []
        , tree = window.rjs.tree
        , moduleName
        , currentModule
        , i
        , len;
    for (moduleName in tree) { // Для каждого имени модуля, содержащегося в дереве
        if (tree.hasOwnProperty(moduleName)) { // Если имя модуля содержится в дереве, то взять объект с данными данного модуля
            currentModule = tree[moduleName]; // вида {moduleName: module.name, moduleDependeciesNames: [], moduleMap: {}, modulePath: ''}
            len = currentModule.moduleDependeciesNames.length;
            if (len) {
                for (i = 0; i < len; i++) {
                    list.push(moduleName + ' < ' + currentModule.moduleDependeciesNames[i]); // Добавить строку вида "moduleName > moduleDependecyName" для каждой зависимости данного модуля
                }
            } else {
                list.push(currentModule.moduleName); // Добавить строку вида "moduleName" для данного модуля
            }
        }
    }
    list.sort(function(a, b){if (a > b) {return 1} else if (a < b) {return -1;} else {return 0;}});
    return list.join('\n');
}

// Фунцкия формирования списка путей к файлам модулей
// Данная функция возвращает строку, состоящую из списка путей к файлам модулей
function toPaths () {
    var paths = []
        , tree = window.rjs.tree
        , moduleName
        , currentModule;
    for (moduleName in tree) { // Для каждого имени модуля, содержащегося в дереве
        if (tree.hasOwnProperty(moduleName)) { // Если имя модуля содержится в дереве, то взять объект с данными данного модуля
            currentModule = tree[moduleName]; // вида {moduleName: module.name, moduleDependeciesNames: [], moduleMap: {}, modulePath: ''}
            paths.push(
                   moduleName.search('.html') === -1
                ? currentModule.modulePath.replace('./', '')
                : 'js/' + moduleName
            ); // Добавить строку вида "modulePath" для данного модуля
        }
    }
    paths.sort(function(a, b){if (a > b) {return 1} else if (a < b) {return -1;} else {return 0;}});
    return paths.join('\n');
}

// Функция формирования дерева путей загрузки модулей
function buildPathsTree () {
    var paths = []
        , tree = window.rjs.tree
        , moduleName
        , currentModule;
    for (moduleName in tree) { // Для каждого имени модуля, содержащегося в дереве
        if (tree.hasOwnProperty(moduleName)) { // Если имя модуля содержится в дереве, то взять объект с данными данного модуля
            currentModule = tree[moduleName]; // вида {moduleName: module.name, moduleDependeciesNames: [], moduleMap: {}, modulePath: ''}
            paths.push(
                   moduleName.search('.html') === -1
                ? currentModule.modulePath.replace('./', '')
                : 'js/' + moduleName
            ); // Добавить строку вида "modulePath" для данного модуля
        }
    }
    paths.sort(function(a, b){if (a > b) {return 1} else if (a < b) {return -1;} else {return 0;}});
    return convertArrayOfDelimitedStringsIntoHierarchicalObject(paths, '/');
}

// Функция конвертации массива с разграниченными строками в иерархическое дерево
function convertArrayOfDelimitedStringsIntoHierarchicalObject (input, delimiter) {
    // input = ['Fred-Jim-Bob', 'Fred-Jim', 'Fred-Thomas-Rob', 'Fred'];
    // delimiter = '-';
    var output = []
        , i, j, k
        , inputLength
        , chain, chainLength
        , currentNode, currentNodeLength
        , wantedNode
        , lastNode
        , newNode;
    for (i = 0, inputLength = input.length; i < inputLength; i++) {
        chain = input[i].split(delimiter);
        currentNode = output;
        for (j = 0, chainLength = chain.length; j < chainLength; j++) {
            wantedNode = chain[j];
            lastNode = currentNode;
            for (k = 0, currentNodeLength = currentNode.length; k < currentNodeLength; k++) {
                if (currentNode[k].name === wantedNode) {
                    currentNode = currentNode[k].dependecies;
                    break;
                }
            }
            // If we couldn't find an item in this list of dependecies
            // that has the right name, create one:
            if (lastNode === currentNode) {
                currentNode[k] = {name: wantedNode, dependecies: []};
                newNode = currentNode[k];
                currentNode = newNode.dependecies;
            }
        }
    }
    return output.length > 0 ? output[0] : {};
}

// Функция выбора главного файла
function getMainFileTree () {

    function buildTreeObject (module) {
        var treeObject = {
              name: module.moduleName
            , path: module.modulePath
            , dependecies: []
        };
     
        for (var key in module.moduleDependeciesTree) {
            treeObject.dependecies.push(buildTreeObject(module.moduleDependeciesTree[key]));
        }
     
        treeObject.dependecies.sort(function(a, b){
            a = a.name;
            b = b.name;
            if (a > b) {return 1} else if (a < b) {return -1;} else {return 0;}
        });
 
        return treeObject;
    }

    return buildTreeObject(window.rjs.tree['main']);
 
}

// Функция отрисовки дерева зависимостей модулей
function drawTree (obj, prefix, options) {

    if (typeof obj === 'string') {obj = {name: obj};}
    if (prefix === undefined) {prefix = '';}
    if (options === undefined) {options = {};}

    function chr (s) {
        var chars = {
            '│' : '|',
            '└' : '`',
            '├' : '+',
            '─' : '-',
            '┬' : '-'
        };
        if (options.unicode === false)  {
            return chars[s];
        } else {
            return s;
        }
    };

    var dependecies = obj.dependecies || []
        , lines = (obj.name || '').split('\n')
        , splitter = '\n' + prefix + (dependecies.length ? chr('│') : ' ') + ' ';

    return prefix
           + lines.join(splitter) + '\n'
           + dependecies.map(function (module, index) {
                                    var last = (index === dependecies.length - 1)
                                       , more = (module.dependecies && module.dependecies.length)
                                       , prefix_ = prefix + (last ? ' ' : chr('│')) + ' ';

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

}


Файл index.js

require.config({
      paths: {
          onResourceLoad: 'lib/require/rjs'
        , main: './main'
      }
    , urlArgs: '_=' + Math.random()
});

require(
    [
          'onResourceLoad'
        , 'main'
    ]
    , function(
          onResourceLoad
        , main
    ) {
 
        main();

        if (console && console.log) {
            window.rjs.buildTree();
            //for (var key in window.rjs.tree) {console.log(key);}
            //console.log(window.rjs.toList());
            console.log(window.rjs.drawTree(window.rjs.getMainFileTree()));
            // console.log(window.rjs.toPaths());
            console.log(window.rjs.drawTree(window.rjs.buildPathsTree()));
        }

     
    }
);

Файл main.js

require.config({
      paths: {
          FirstModule: 'app/first/controller'
        , SecondModule: 'app/second/controller'
        , ThirdModule: 'app/third/controller'
      }
});

define(
    [
          'FirstModule'
        , 'SecondModule'
        , 'ThirdModule'
    ]
    , function(
          FirstModule
        , SecondModule
        , ThirdModule
    ) {

        return function () {
            FirstModule.init();
            SecondModule.init();
            ThirdModule.init();
        };
     
    }
);

пятница, 5 июня 2015 г.

JavaScript Build Require JS Tree Visualization

Файл rjs.js

// Порядок использования:
//
// 1. Загрузите данный файл первым в качестве зависимости внутри вашего главного JavaScript-файла.
//    (Подробнее смотрите на странице https://github.com/jrburke/requirejs/wiki/Internal-API:-onResourceLoad)
// 2. Как только HTML-страница будет загружена выполните в консоли браузера команды:
//     window.rjs.buildTree();
//     console.log(window.rjs.tree);
//    В результате в объекте window.rjs.tree будет сформировано дерево зависимостей ваших модулей.
// 3. Для формирования дерева зависимостей модулей в формате UML вызовите метод window.rjs.toUml().
//    Результат работы метода вы можете использовать здесь: http://yuml.me/diagram/scruffy/class/draw
//
// Методы доступные после загрузки HTML-страницы:
// rjs.buildTree()
// - Формирует карту зависимостей ваших модулей внутри объекта rjs.tree.
// - Выведите значение rjs.tree в консоли браузера, чтобы увидеть карту зависимостей модулей.
// rjs.toUml()
// - Возвращает строку в формате UML, которая может быть использована для построения UML-диаграммы.
// - UML Website: http://yuml.me/diagram/scruffy/class/draw

// onResourceLoad - это внутренний крюк, который может быть использован для вывода сообщения о том, что модуль был
// создан или он экспортировал свое значение. Таким образом становится возможна отрисовка порядка загрузки модулей и их зависимостей.
requirejs.onResourceLoad = function (context, module, dependeciesArray) {

    // context - внутренний объект, испольуемый RequireJS для хранения экспортированных значений и состояния загрузки модулей
    // В данном случае он никак не используется.
   
    // module - объект содержащий информацию о только что загруженном модуле.
    // Свойства объекта module:
    // module.name - нормализованное имя модуля. Для плагина это просто имя плагина.
    // module.parentMap - объект модуля, используемый для решения задачи относительных ID для данного модуля. Может быть null, но если существует, то имеет ту же структуру, что и объект module.
    // Дополнительно можно использовать следующие свойства объекта module:
    // module.url - URL исползуемый для загрузки файла модуля. Может быть относительным путем.
   
    // dependeciesArray - массив зависимостей определенных для данного модуля. Каждый элемент массива это объект module.
   
    // Сформировать глобальный объект window.rjs при загрузке первого модуля
    if (!window.rjs) {
        window.rjs = {
              tree: {} // дерево зависимостей модулей
            , buildTree: buildTree // функция формирования карты зависисмостей модулей (определена ниже)
            , toUml: toUml // функция формирования дерева зависисмостей модулей в формате UML (определена ниже)
        };
    }

    // Если модуля нет в дереве зависимостей, то добавить объект с данными нового модуля в дерево
    if (!window.rjs.tree[module.name]) {
        window.rjs.tree[module.name] = {moduleDependeciesNames: [], moduleMap: {}};
        // moduleDependeciesNames - массив имен модулей, от которых зависит данный модуль
        // moduleMap - карта зависимостей данного модуля
    }
   
    // Для формирования полного дерева зависимостей модулей друг от друга
    var i, len;
    if (dependeciesArray) { // Если у модуля есть зависимости, то
        for (
            i = 0, len = dependeciesArray.length;
            i < len;
            i++
        ) { // добавить в массив имен зависимостей имена модулей, от которых зависит данный модуль
            window.rjs.tree[module.name].moduleDependeciesNames.push(dependeciesArray[i].name);
        }
    }
   
    // На данном этапе в дерево добавлены имена всех загруженных модулей.
    // В массив имен зависимостей всех модулей добавлены имена модулей, от которых они зависят.
    // Однако карта зависимостей у всех модулей пока пуста.
   
// Для формирования простого дерева зависимостей данного модуля
//    if (module.parentMap && module.parentMap.name) {
//        if (!window.rjs.tree[module.parentMap.name]) {
//            window.rjs.tree[module.parentMap.name] = {moduleDependeciesNames: [], moduleMap: {}};
//        }
//        if (module.parentMap.name !== module.name) {
//            window.rjs.tree[module.parentMap.name].moduleDependeciesNames.push(module.name);
//        }
//    }

};

// Функция формирования карты дерева зависисмостей модулей
// На данном этапе карта зависимостей у всех модулей пока пуста.
// Поэтому данная функция заполняет занчения moduleMap для каждого модуля.
function buildTree () {
    var tree = window.rjs.tree
        , moduleName
        , currentModule
        , i
        , len
        , currentDependencyName;
    for (moduleName in tree) { // Для каждого имени модуля, содержащегося в дереве
        if (tree.hasOwnProperty(moduleName)) { // Если имя модуля содержится в дереве, то взять объект с данными данного модуля
            currentModule = tree[moduleName]; // вида {moduleDependeciesNames: [], moduleMap: {}}
            for (
                i = 0, len = currentModule.moduleDependeciesNames.length;
                i < len;
                i++
            ) {
                currentDependencyName = currentModule.moduleDependeciesNames[i]; // Взять имя текущей зависимости данного модуля
                currentModule.moduleMap[currentDependencyName] = tree[currentDependencyName]; // В карту данного модуля добавить объект текущего модуля из дерева
            }
        }
    }
}

// Фунцкия формирования дерева зависисмостей модулей в формате UML
function toUml () {
    var uml = []
        , tree = window.rjs.tree
        , moduleName
        , currentModule
        , i
        , len;
    for (moduleName in tree) { // Для каждого имени модуля, содержащегося в дереве
        if (tree.hasOwnProperty(moduleName)) { // Если имя модуля содержится в дереве, то взять объект с данными данного модуля
            currentModule = tree[moduleName]; // вида {moduleDependeciesNames: [], moduleMap: {}}
            for (
                i = 0, len = currentModule.moduleDependeciesNames.length;
                i < len;
                i++
            ) {
                uml.push('[' + moduleName + ']->[' + currentModule.moduleDependeciesNames[i] + ']'); // Добавить строку вида [moduleName]->[moduleDependecyName] для каждой зависимости данного модуля
            }
        }
    }
    return uml.join('\n');
}

Файл index.js

require.config({
      paths: {
          onResourceLoad: 'lib/require/rjs'
        , FirstModule: 'app/first/controller'
        , SecondModule: 'app/second/controller'
        , ThirdModule: 'app/third/controller'
      }
});

require(
    [
          'onResourceLoad'
        , 'FirstModule'
        , 'SecondModule'
        , 'ThirdModule'
    ]
    , function(
          onResourceLoad
        , FirstModule
        , SecondModule
        , ThirdModule
    ) {
   
        FirstModule.init();
        SecondModule.init();
        ThirdModule.init();
       
    }
);

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

JavaScript Build Folder Tree Visualization

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

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

/*

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

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

*/

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

function hierarchy (obj, prefix, opts) {

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

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

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

}

/*

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

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

console.log(result);

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

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

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

console.log(result);

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

*/

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

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

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

JavaScript File Download

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