<commit_before>/*
 * grunt-tt2-bem
 * https://github.com/Wu-Wu/grunt-tt2-bem
 *
 * Copyright (c) 2014 Anton Gerasimov
 * Licensed under the MIT license.
 */

'use strict';

var bn = require('bem-naming'),
    _ = require('lodash');

var BemDecl = function (options) {
    options = options || {};

    this.options = _.defaults(options, {
        debug: false,
        prefixes: [ 'b', 'i', 'l' ],    // used prefixes
        allowed: []                     // allowed block names
    });

    this.debug = this.options.debug;
    this.allowed = this.options.allowed;

    var literal = '[-a-zA-Z0-9]',
        modSep = '_',
        elemSep = '__',
        word = literal + '+',
        block = '((' + this.options.prefixes.join('|') + ')-' + word + ')',
        mod = '(?:' + modSep + '(' + word + '))?',
        elem = '(?:' + elemSep + '(' + word + '))?';

    this.re = new RegExp('\\b' + block + elem + mod + mod, 'g');

    // console.log('re:', this.re);

    this.clear();
};

BemDecl.prototype.clear = function() {
    this.stash = [];
    this.seen = {};
    this.matched = [];
};

BemDecl.prototype.parse = function (text) {
    this.clear();
    this.matched = text.match(this.re);
    _.each(this.matched, this.push, this);
};

BemDecl.prototype.push = function (item) {
    var parsed;

    try {
        parsed = bn.parse(item);
    }
    catch (e) {
        parsed = { block : false };
    }

    if (!parsed.block) {
        return;
    }

    if (!parsed.elem && !parsed.modName && !this.seen[parsed.block]) {
        this.seen[parsed.block] = true;
    }
    this.stash.push(parsed);
};

BemDecl.prototype.found = function () {
    return this.matched;
};

BemDecl.prototype.isSeen = function (item) {
    return this.seen[item.block] ? true : false;
};

BemDecl.prototype.isAllowed = function (item) {
    if (this.allowed.length) {
        return _.indexOf(this.allowed, item.block) !== -1 ? true : false;
    }
    else {
        return true;
    }
};

BemDecl.prototype.parsed = function() {
    return _.chain(this.stash)
                .filter(this.isSeen, this)
                .filter(this.isAllowed, this)
                .compact()
                .uniq(function (item) { return JSON.stringify(item); })
                .value();
};

// representations:

// a) elem: 'foo'
// b) elems: [ 'foo', 'bar' ]
// c) elems: [ { elem: 'foo' }, { elem: 'bar' } ]

// transition table:

// (1) elem            | (2) elem          | representation
// --------------------+-------------------+---------------
// foo                 | (none)            | -> a)
// foo                 | foo               | -> a) -> a)
// foo                 | bar               | -> a) -> b)
// foo (+ mod*)        | bar               | -> c)

// * state by handleElemMod()

BemDecl.prototype.handleElemOnly = function (block, entity) {
    var singular = _.has(block, 'elem'),
        plural   = _.has(block, 'elems');

    if (!singular && !plural) {
        // +element to singular
        block.elem = entity.elem;
    }
    else if (singular) {
        // cast singular to plural
        var prev = block.elem;
        if (prev !== entity.elem) {
            delete block.elem;
            block.elems = [ prev, entity.elem ];
        }
    }
    else if (plural) {
        // +element to plural
        if (_.isObject(block.elems[0])) {
            // "elems" like [ { elem: "a" }, { elem: "b" } ] (Array-Of-Objects)
            if (_.findIndex(block.elems, { elem: entity.elem }) === -1) {
                // not yet exists in "elems"
                block.elems.push({ elem: entity.elem });
            }
        }
        else {
            // "elems" like [ "a", "b" ] (Array-Of-Scalars)
            if (_.indexOf(block.elems, entity.elem) === -1) {
                // not yet exists in "elems"
                block.elems.push(entity.elem);
            }
        }
    }

    return block;
};

// representations:

// a) mod: 'foo'
// b) mods: [ { mod: 'foo', val: 'bar' } ]
// c) mods: [ { mod: 'foo', vals: [ 'bar', 'baz' ] } ]
// d) mods: [ { mod: 'foo', val: 'bar' }, { mod: 'baz', val: true } ]
// e) mods: [ { mod: 'foo', vals: [ true, 'bar' ] } ]

// transition table:

// (1) mod/val         | (2) mod/val       | representation
// --------------------+-------------------+---------------
// foo/(true)          | (none)            | -> a)
// foo/bar             | (none)            | -> b)
// foo/bar             | foo/baz           | -> b) -> c)
// foo/bar             | baz/(true)        | -> b) -> d)
// foo/bar             | baz/bar           | -> b) -> d)
// foo/(true)          | foo/bar           | -> a) -> e)
// foo/(true)          | baz/(true)        | -> a) -> d)
// foo/(true)          | baz/bar           | -> a) -> d)

BemDecl.prototype.handleModOnly = function (block, entity) {
    var singular = _.has(block, 'mod'),
        plural   = _.has(block, 'mods'),
        hasVal   = ! _.isBoolean(entity.modVal);

    if (!singular && !plural) {
        // not exists "mod", nor "mods"
        var mod = this.toMod(entity);
        // -> a) && -> b)
        block[ mod.key ] = mod.val;
    }
    else if (singular) {
        // exists "mod"
        var prev = block.mod;
        delete block.mod;

        if (hasVal && prev === entity.modName) {
            // a) -> e)
            block.mods = [
                { mod: prev, vals: [ true, entity.modVal ] }
            ];
        }
        else {
            // a) -> d)
            block.mods = [
                { mod: prev, val: true },
                { mod: entity.modName, val: entity.modVal }
            ];
        }
    }
    else if (plural) {
        // exists "mods"
        var pos = _.findIndex(block.mods, { mod: entity.modName });

        if (pos !== -1) {
            var mods = block.mods[pos];
            // c) -> c) && b) -> c)
            mods = this.transformVal(mods, entity);
        }
        else {
            // b) -> d)
            block.mods.push({ mod: entity.modName, val: entity.modVal });
        }
    }

    return block;
};

BemDecl.prototype.toMod = function (entity) {
    // default - plural
    var hasVal = ! _.isBoolean(entity.modVal),
        mod = {
            key: 'mods',
            val: [
                { mod: entity.modName, val: entity.modVal }
            ]
        };

    if (!hasVal) {
        // switch to singular
        mod.key = 'mod';
        mod.val = entity.modName;
    }

    return mod;
};

// handle elem + mod if present "elems"
BemDecl.prototype.emPluralElem = function (elems, entity) {
    var hasModVal = ! _.isBoolean(entity.modVal),
        mod;

    var posAsObject = _.findIndex(elems, { elem: entity.elem }),
        posAsString = _.indexOf(elems, entity.elem);

    if (posAsObject !== -1 && posAsString === -1) {
        // "elems" as array of objects
        // found current elem in "elems"
        var me = elems[posAsObject],
            singular = _.has(me, 'mod'),
            plural = _.has(me, 'mods');

        if (singular && !plural) {
            // exists "mod" (only)
            if (me.mod !== entity.modName) {
                // different mods
                // cast "mod" -> "mods"
                var was = me.mod;
                delete me.mod;

                me.mods = [
                    { mod: was },
                    { mod: entity.modName }
                ];

                if (hasModVal) {
                    // set new mod's "val"
                    me.mods[1].val = entity.modVal;
                }
            }
            else {
                // the same mod
                if (hasModVal) {
                    // cast "mod" -> "mods"
                    delete me.mod;

                    // TODO: should be saved boolean?
                    me.mods = [
                        { mod: entity.modName, vals: [ true, entity.modVal ] }
                    ];
                }
            }
        }
        else if (!singular && plural) {
            // exists "mods" (only)
            var current = _.findIndex(elems[posAsObject].mods, { mod: entity.modName });

            if (current !== -1) {
                // the current mod found in "mods"
                var exMod = elems[posAsObject].mods[current];
                exMod = this.transformVal(exMod, entity);
            }
            else {
                // the current mod not found in "mods"
                // add new mod object to "mods"
                elems[posAsObject].mods.push({ mod: entity.modName, val: entity.modVal });
            }
        }
    }
    else if (posAsString !== -1 && posAsObject === -1) {
        // "elems" as array of strings
        elems = _.map(elems, function (elem) {
            var el = { elem: elem };

            if (elem === entity.elem) {
                var modX = this.toMod(entity);

                el[ modX.key ] = modX.val;
            }

            return el;
        }, this);
    }
    else {
        // element not found in "elems"
        elems.push({ elem: entity.elem });
        // new element's index
        mod = this.toMod(entity);

        elems[ elems.length - 1 ][ mod.key ] = mod.val;
    }

    return elems;
};

BemDecl.prototype.handleElemMod = function (block, entity) {
    var singular = _.has(block, 'elem'),
        plural = _.has(block, 'elems'),
        mod,
        elemIndex = 0;

    if (!singular && !plural) {
        // absent both forms
        block.elems = [];

        block.elems.push({ elem: entity.elem });

        mod = this.toMod(entity);
        block.elems[ elemIndex ][ mod.key ] = mod.val;
    }
    else if (singular) {
        // present singular (elem)
        var prev = block.elem;

        delete block.elem;
        block.elems = [];

        // save previous element
        block.elems.push({ elem: prev });

        if (prev !== entity.elem) {
            // current element to this list
            block.elems.push({ elem: entity.elem });
            // add "mod" to this element
            elemIndex = 1;
        }

        // save "mod"
        mod = this.toMod(entity);
        block.elems[ elemIndex ][ mod.key ] = mod.val;
    }
    else if (plural) {
        block.elems = this.emPluralElem(block.elems, entity);
    }

    return block;
};

// transforms "val" to "vals" based on mod's state and current entity
BemDecl.prototype.transformVal = function (mod, entity) {
    var singular = _.has(mod, 'val'),
        plural = _.has(mod, 'vals');

    if (singular && !plural) {
        // exists "val"
        if (mod.val !== entity.modVal) {
            // cast "val" -> "vals"
            var savedVal = mod.val;
            delete mod.val;
            mod.vals = [ savedVal, entity.modVal ];
        }
    }
    else if (!singular && plural) {
        // exists "vals"
        if (_.indexOf(mod.vals, entity.modVal) === -1) {
            // add new val to list
            mod.vals.push(entity.modVal);
        }
    }
    else {
        // not exists "val", nor "vals"
        if (! _.isBoolean(entity.modVal)) {
            // cast empty -> "vals" (if has new val)
            // TODO: should be saved boolean?
            mod.vals = [ true, entity.modVal ];
        }
    }

    return mod;
};

BemDecl.prototype.decl = function() {
    var order = [],
        decl = [];

    _.each(this.parsed(), function (item) {
        var index    = _.indexOf(order, item.block),
            hasElem = ! _.isUndefined(item.elem),
            hasMod  = ! _.isUndefined(item.modName);

        if (index === -1) {
            order.push(item.block);
            index = order.length - 1;
            decl.push({ block: item.block });
        }

        // exists element & modifier
        if (hasElem && hasMod) {
            decl[index] = this.handleElemMod(decl[index], item);
        }
        // exists element
        else if (hasElem) {
            decl[index] = this.handleElemOnly(decl[index], item);
        }
        // exists modifier
        else if (hasMod) {
            decl[index] = this.handleModOnly(decl[index], item);
        }

    }, this);

    return decl;
};

module.exports = BemDecl;
<commit_msg>fixes when "elems" and no "mod" or "mods"<commit_after>/*
 * grunt-tt2-bem
 * https://github.com/Wu-Wu/grunt-tt2-bem
 *
 * Copyright (c) 2014 Anton Gerasimov
 * Licensed under the MIT license.
 */

'use strict';

var bn = require('bem-naming'),
    _ = require('lodash');

_.mixin({ 'gap': function (count) {
    var gap = '';
    while (count-- > 0) {
        gap += '  ';
    }
    return gap;
}}, { chain: false });

var BemDecl = function (options) {
    options = options || {};

    this.options = _.defaults(options, {
        debug: false,
        prefixes: [ 'b', 'i', 'l' ],    // used prefixes
        allowed: []                     // allowed block names
    });

    this.debug = this.options.debug;
    this.allowed = this.options.allowed;

    var literal = '[-a-zA-Z0-9]',
        modSep = '_',
        elemSep = '__',
        word = literal + '+',
        block = '((' + this.options.prefixes.join('|') + ')-' + word + ')',
        mod = '(?:' + modSep + '(' + word + '))?',
        elem = '(?:' + elemSep + '(' + word + '))?';

    this.re = new RegExp('\\b' + block + elem + mod + mod, 'g');

    // console.log('re:', this.re);

    this.clear();
};

BemDecl.prototype.clear = function() {
    this.stash = [];
    this.seen = {};
    this.matched = [];
};

BemDecl.prototype.parse = function (text) {
    this.clear();
    this.matched = text.match(this.re);
    _.each(this.matched, this.push, this);
};

BemDecl.prototype.push = function (item) {
    var parsed;

    try {
        parsed = bn.parse(item);
    }
    catch (e) {
        parsed = { block : false };
    }

    if (!parsed.block) {
        return;
    }

    if (!parsed.elem && !parsed.modName && !this.seen[parsed.block]) {
        this.seen[parsed.block] = true;
    }
    this.stash.push(parsed);
};

BemDecl.prototype.found = function () {
    return this.matched;
};

BemDecl.prototype.isSeen = function (item) {
    return this.seen[item.block] ? true : false;
};

BemDecl.prototype.isAllowed = function (item) {
    if (this.allowed.length) {
        return _.indexOf(this.allowed, item.block) !== -1 ? true : false;
    }
    else {
        return true;
    }
};

BemDecl.prototype.parsed = function() {
    return _.chain(this.stash)
                .filter(this.isSeen, this)
                .filter(this.isAllowed, this)
                .compact()
                .uniq(function (item) { return JSON.stringify(item); })
                .value();
};

BemDecl.prototype.logg = function (level, message) {
    if (this.debug) {
        console.log(_.gap(level) + message);
    }
};

// representations:

// a) elem: 'foo'
// b) elems: [ 'foo', 'bar' ]
// c) elems: [ { elem: 'foo' }, { elem: 'bar' } ]

// transition table:

// (1) elem            | (2) elem          | representation
// --------------------+-------------------+---------------
// foo                 | (none)            | -> a)
// foo                 | foo               | -> a) -> a)
// foo                 | bar               | -> a) -> b)
// foo (+ mod*)        | bar               | -> c)

// * state by handleElemMod()

BemDecl.prototype.handleElemOnly = function (block, entity) {
    var singular = _.has(block, 'elem'),
        plural   = _.has(block, 'elems');

    if (!singular && !plural) {
        // +element to singular
        this.logg(1, 'no singular or plural: -> "elem"');
        block.elem = entity.elem;
    }
    else if (singular) {
        // cast singular to plural
        var prev = block.elem;
        this.logg(1, 'present singular: E = ' + prev);
        if (prev !== entity.elem) {
            this.logg(2, 'not equal to E = ' + entity.elem + ': cast "elem" -> "elems"');
            delete block.elem;
            block.elems = [ prev, entity.elem ];
        }
    }
    else if (plural) {
        // +element to plural
        this.logg(1, 'present plural');
        if (_.isObject(block.elems[0])) {
            this.logg(2, 'as Array of Objects...');
            // "elems" like [ { elem: "a" }, { elem: "b" } ] (Array-Of-Objects)
            if (_.findIndex(block.elems, { elem: entity.elem }) === -1) {
                this.logg(3, 'push E = ' + entity.elem + ' as Object');
                // not yet exists in "elems"
                block.elems.push({ elem: entity.elem });
            }
        }
        else {
            this.logg(2, 'as Array of Strings...');
            // "elems" like [ "a", "b" ] (Array-Of-Scalars)
            if (_.indexOf(block.elems, entity.elem) === -1) {
                this.logg(3, 'push E = ' + entity.elem + ' as String');
                // not yet exists in "elems"
                block.elems.push(entity.elem);
            }
        }
    }

    return block;
};

// representations:

// a) mod: 'foo'
// b) mods: [ { mod: 'foo', val: 'bar' } ]
// c) mods: [ { mod: 'foo', vals: [ 'bar', 'baz' ] } ]
// d) mods: [ { mod: 'foo', val: 'bar' }, { mod: 'baz', val: true } ]
// e) mods: [ { mod: 'foo', vals: [ true, 'bar' ] } ]

// transition table:

// (1) mod/val         | (2) mod/val       | representation
// --------------------+-------------------+---------------
// foo/(true)          | (none)            | -> a)
// foo/bar             | (none)            | -> b)
// foo/bar             | foo/baz           | -> b) -> c)
// foo/bar             | baz/(true)        | -> b) -> d)
// foo/bar             | baz/bar           | -> b) -> d)
// foo/(true)          | foo/bar           | -> a) -> e)
// foo/(true)          | baz/(true)        | -> a) -> d)
// foo/(true)          | baz/bar           | -> a) -> d)

BemDecl.prototype.handleModOnly = function (block, entity) {
    var singular = _.has(block, 'mod'),
        plural   = _.has(block, 'mods'),
        hasVal   = ! _.isBoolean(entity.modVal);

    if (!singular && !plural) {
        this.logg(1, 'no singular or plural');
        // not exists "mod", nor "mods"
        var mod = this.toMod(entity);
        // -> a) && -> b)
        block[ mod.key ] = mod.val;
        this.logg(2, '-> "' + mod.key + '"');
    }
    else if (singular) {
        // exists "mod"
        var prev = block.mod;
        delete block.mod;

        this.logg(1, 'present singular: M = ' +  prev + ': cast "mod" -> "mods"');

        if (hasVal && prev === entity.modName) {
            this.logg(2, 'equal to M = ' + entity.modName + ': cast "val" -> "vals"');
            // a) -> e)
            block.mods = [
                { mod: prev, vals: [ true, entity.modVal ] }
            ];
        }
        else {
            this.logg(2, 'push M = ' + entity.modName + ' to "mods"');
            // a) -> d)
            block.mods = [
                { mod: prev, val: true },
                { mod: entity.modName, val: entity.modVal }
            ];
        }
    }
    else if (plural) {
        // exists "mods"
        this.logg(1, 'present plural');
        var pos = _.findIndex(block.mods, { mod: entity.modName });

        if (pos !== -1) {
            this.logg(2, 'found M = ' + entity.modName + 'in "mods"');
            var mods = block.mods[pos];
            // c) -> c) && b) -> c)
            mods = this.transformVal(mods, entity);
        }
        else {
            this.logg(2, 'not found M = ' + entity.modName + 'in "mods"');
            // b) -> d)
            block.mods.push({ mod: entity.modName, val: entity.modVal });
        }
    }

    return block;
};

BemDecl.prototype.toMod = function (entity) {
    // default - plural
    var hasVal = ! _.isBoolean(entity.modVal),
        mod = {
            key: 'mods',
            val: [
                { mod: entity.modName, val: entity.modVal }
            ]
        };

    if (!hasVal) {
        // switch to singular
        mod.key = 'mod';
        mod.val = entity.modName;
    }

    return mod;
};

// handle elem + mod if present "elems"
BemDecl.prototype.emPluralElem = function (elems, entity) {
    var hasModVal = ! _.isBoolean(entity.modVal),
        mod;

    var posAsObject = _.findIndex(elems, { elem: entity.elem }),
        posAsString = _.indexOf(elems, entity.elem);

    this.logg(1, 'present plural');

    if (posAsObject !== -1 && posAsString === -1) {
        // "elems" as array of objects
        // found current elem in "elems"
        var me = elems[posAsObject],
            singular = _.has(me, 'mod'),
            plural = _.has(me, 'mods');

        this.logg(2, 'found E = ' + entity.elem + ' in "elems" as Object');

        if (!singular && !plural) {
            this.logg(3, 'absent singular or plural');

            var modY = this.toMod(entity);
            me[ modY.key ] = modY.val;
            this.logg(4, 'M = ' + entity.modName + ': add "' + modY.key + '"');
        }
        else if (singular && !plural) {
            this.logg(3, 'present "mod"');
            // exists "mod" (only)
            if (me.mod !== entity.modName) {
                // different mods
                // cast "mod" -> "mods"
                var was = me.mod;
                delete me.mod;

                this.logg(4, was + ' not equal M = ' + entity.modName + ': cast "mod" -> "mods"');

                me.mods = [
                    { mod: was },
                    { mod: entity.modName }
                ];

                if (hasModVal) {
                    // set new mod's "val"
                    me.mods[1].val = entity.modVal;
                    this.logg(5, 'set value ' + entity.modVal + ' for this');
                }
            }
            else {
                // the same mod
                if (hasModVal) {
                    // cast "mod" -> "mods"
                    delete me.mod;

                    // TODO: should be saved boolean?
                    me.mods = [
                        { mod: entity.modName, vals: [ true, entity.modVal ] }
                    ];

                    this.logg(4, ' not equal M = ' + entity.modName + ': cast "val" -> "vals"');
                }
            }
        }
        else if (!singular && plural) {
            this.logg(3, 'present "mods"');
            // exists "mods" (only)
            var current = _.findIndex(elems[posAsObject].mods, { mod: entity.modName });

            if (current !== -1) {
                this.logg(4, 'found M = ' + entity.modName);
                // the current mod found in "mods"
                var exMod = elems[posAsObject].mods[current];
                exMod = this.transformVal(exMod, entity);
            }
            else {
                this.logg(4, 'not found M = ' + entity.modName);
                // the current mod not found in "mods"
                // add new mod object to "mods"
                elems[posAsObject].mods.push({ mod: entity.modName, val: entity.modVal });
            }
        }
    }
    else if (posAsString !== -1 && posAsObject === -1) {
        this.logg(2, 'found E = ' + entity.elem + ' in "elems" as String');
        // "elems" as array of strings
        elems = _.map(elems, function (elem) {
            var el = { elem: elem };

            if (elem === entity.elem) {
                var modX = this.toMod(entity);

                el[ modX.key ] = modX.val;
            }

            return el;
        }, this);
    }
    else {
        this.logg(2, 'not found E = ' + entity.elem + ' in "elems"');
        // element not found in "elems"
        elems.push({ elem: entity.elem });
        // new element's index
        mod = this.toMod(entity);

        elems[ elems.length - 1 ][ mod.key ] = mod.val;
    }

    return elems;
};

BemDecl.prototype.handleElemMod = function (block, entity) {
    var singular = _.has(block, 'elem'),
        plural = _.has(block, 'elems'),
        mod,
        elemIndex = 0;

    if (!singular && !plural) {
        // absent both forms
        this.logg(1, 'no singular or plural');
        block.elems = [];

        block.elems.push({ elem: entity.elem });
        this.logg(1, 'push E = ' + entity.elem + ' to "elems" as Object');

        mod = this.toMod(entity);
        block.elems[ elemIndex ][ mod.key ] = mod.val;
        this.logg(1, 'set M to "' + mod.key + '"');
    }
    else if (singular) {
        // present singular (elem)
        this.logg(1, 'present singular: cast "elem" -> "elems"');
        var prev = block.elem;

        delete block.elem;
        block.elems = [];

        // save previous element
        block.elems.push({ elem: prev });

        if (prev !== entity.elem) {
            this.logg(1, 'push E = ' + entity.elem + ' to "elems" as Object');
            // current element to this list
            block.elems.push({ elem: entity.elem });
            // add "mod" to this element
            elemIndex = 1;
        }

        // save "mod"
        mod = this.toMod(entity);
        block.elems[ elemIndex ][ mod.key ] = mod.val;
        this.logg(1, 'set M to "' + mod.key + '"');
    }
    else if (plural) {
        block.elems = this.emPluralElem(block.elems, entity);
    }

    return block;
};

// transforms "val" to "vals" based on mod's state and current entity
BemDecl.prototype.transformVal = function (mod, entity) {
    var singular = _.has(mod, 'val'),
        plural = _.has(mod, 'vals');

    if (singular && !plural) {
        // exists "val"
        if (mod.val !== entity.modVal) {
            // cast "val" -> "vals"
            var savedVal = mod.val;
            delete mod.val;
            mod.vals = [ savedVal, entity.modVal ];
        }
    }
    else if (!singular && plural) {
        // exists "vals"
        if (_.indexOf(mod.vals, entity.modVal) === -1) {
            // add new val to list
            mod.vals.push(entity.modVal);
        }
    }
    else {
        // not exists "val", nor "vals"
        if (! _.isBoolean(entity.modVal)) {
            // cast empty -> "vals" (if has new val)
            // TODO: should be saved boolean?
            mod.vals = [ true, entity.modVal ];
        }
    }

    return mod;
};

BemDecl.prototype.decl = function() {
    var order = [],
        decl = [];

    _.each(this.parsed(), function (item) {
        var index    = _.indexOf(order, item.block),
            hasElem = ! _.isUndefined(item.elem),
            hasMod  = ! _.isUndefined(item.modName);

        if (index === -1) {
            order.push(item.block);
            index = order.length - 1;
            decl.push({ block: item.block });
        }

        // exists element & modifier
        if (hasElem && hasMod) {
            this.logg(0, 'elem-mod: E = ' + item.elem + '; M = ' + item.modName);
            decl[index] = this.handleElemMod(decl[index], item);
        }
        // exists element
        else if (hasElem) {
            this.logg(0, 'elem-only: E = ' + item.elem);
            decl[index] = this.handleElemOnly(decl[index], item);
        }
        // exists modifier
        else if (hasMod) {
            this.logg(0, 'mod-only: M = ' + item.modName);
            decl[index] = this.handleModOnly(decl[index], item);
        }

    }, this);

    return decl;
};

module.exports = BemDecl;
<|endoftext|>


<commit_before>'use strict';

<commit_msg>impl the business plan<commit_after>'use strict';

var BigNumber = require('bignumber.js');

var minPrice = BigNumber(15).times(5);
var max = function(x, y) { return x.comparedTo(y) > 0 ? x : y };

module.exports = {

  cycles: {
    $subscription: {
      storageUsedMb: {},
      users: {}
    },
    monthly: {
      $duration: "1 month",
      usd: {
        $cost: function(users) { return max(BigNumber(users).times(15), minPrice) }
      }
    }
  },

  notifications: {
    usdBelow0: function(usd) { return BigNumber(usd).lt(0) }
  }

};
<|endoftext|>


<commit_before>"use strict";

var io = require('./sockets-manager.js').getSocketsManager(),
    _  = require('underscore');

function Channel(channelID, channelsManager) {
    if (!channelID) {
        throw new Error('channelID is required');
    }

    this.channelID = channelID;
    this.channelNamespace = '/' + channelID;
    this.ownerSocketID = null;
    this.channel = io.of(this.channelNamespace);
    this.channelsManager = channelsManager;

    this._init();
}

Channel.prototype._init = function () {
    this.channel.on('connection', function (socket) {
        if (!this.ownerSocketID) {
            // Owner connection
            this.ownerSocketID = socket.id;
            console.info('Accepted [' + socket.id + '] as owner of ' + this.channelNamespace);

            // Receiving code
            socket.on('codeText', function (data) {
                socket.broadcast.emit('codeText', data);
            });

            // Disconnect all sockets in the namespace when
            // the owner socket disconnects
            socket.on('disconnect', function () {
                console.log('Owner socket of ' + this.channelNamespace + ' disconnected');
                this.unload();
            }.bind(this));
        } else {
            // Guest connection
            console.info('Accepted [' + socket.id + '] as guest of ' + this.channelNamespace);
        }
    }.bind(this));
};

Channel.prototype.unload = function () {
    var sockets = this.channel.sockets;

    _.values(sockets).forEach(function (socket) {
        if (socket.id !== this.ownerSocketID) {
            socket.disconnect();
        }
    }.bind(this));

    //unregister all event listeners
    this.channel.removeAllListeners();

    this.channelsManager.notifyChannelDeleted(this.channelID);
};

module.exports = {
    Channel       :   Channel
};<commit_msg>bug fix<commit_after>"use strict";

var io = require('./sockets-manager.js').getSocketsManager(),
    _  = require('underscore');

function Channel(channelID, channelsManager) {
    if (!channelID) {
        throw new Error('channelID is required');
    }

    this.channelID = channelID;
    this.channelNamespace = '/' + channelID;
    this.ownerSocketID = null;
    this.channel = io.of(this.channelNamespace);
    this.channelsManager = channelsManager;

    this._init();
}

Channel.prototype._init = function () {
    this.channel.on('connection', function (socket) {
        if (!this.ownerSocketID) {
            // Owner connection
            this.ownerSocketID = socket.id;
            console.info('Accepted [' + socket.id + '] as owner of ' + this.channelNamespace);

            // Receiving code
            socket.on('codeText', function (data) {
                socket.broadcast.to(this.channelID).emit('codeText', data);
            });

            // Disconnect all sockets in the namespace when
            // the owner socket disconnects
            socket.on('disconnect', function () {
                console.log('Owner socket of ' + this.channelNamespace + ' disconnected');
                this.unload();
            }.bind(this));
        } else {
            // Guest connection
            console.info('Accepted [' + socket.id + '] as guest of ' + this.channelNamespace);
        }
    }.bind(this));
};

Channel.prototype.unload = function () {
    var sockets = this.channel.sockets;

    _.values(sockets).forEach(function (socket) {
        if (socket.id !== this.ownerSocketID) {
            socket.disconnect();
        }
    }.bind(this));

    //unregister all event listeners
    this.channel.removeAllListeners();

    this.channelsManager.notifyChannelDeleted(this.channelID);
};

module.exports = {
    Channel       :   Channel
};<|endoftext|>


<commit_before>module.exports = {
  getHomeDir: function() {
    return process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];
  },
  getUserName: function() {
    process.env[(process.platform == 'win32') ? 'USERNAME' : 'USER'];
  }
}
<commit_msg>bugfix<commit_after>module.exports = {
  getHomeDir: function() {
    return process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];
  },
  getUserName: function() {
    return process.env[(process.platform == 'win32') ? 'USERNAME' : 'USER'];
  }
};
<|endoftext|>


<commit_before>var hoxy = require('hoxy');
var URL = require("url");
var http = require("http");
var objectAssign = require("object-assign");
var DEFAULT_OPTIONS = {
    port: 8087,
    mockFileDir: process.cwd()
};
function CocProxy(options) {
    this.options = objectAssign(DEFAULT_OPTIONS, options);
}

CocProxy.prototype.start = function () {
    this.proxy = new hoxy.Proxy().listen(8087);
    // local or reverse proxy
    require("./request-interceptor").interceptRequest(this.proxy, this.options);
    this.proxy.log('error warn', function (event) {
        console.error(event.level + ': ' + event.message);
        if (event.error) {
            console.error(event.error.stack);
        }
    });
};
CocProxy.prototype.exit = function () {
    if (!this.proxy) {
        throw new Error("Proxy didn't start yet!");
    }
    this.proxy.close();
};
module.exports.CocProxy = CocProxy;<commit_msg>refactor(proxy): remove moot require<commit_after>var hoxy = require('hoxy');
var objectAssign = require("object-assign");
var DEFAULT_OPTIONS = {
    port: 8087,
    mockFileDir: process.cwd()
};
function CocProxy(options) {
    this.options = objectAssign(DEFAULT_OPTIONS, options);
}

CocProxy.prototype.start = function () {
    this.proxy = new hoxy.Proxy().listen(8087);
    // local or reverse proxy
    require("./request-interceptor").interceptRequest(this.proxy, this.options);
    this.proxy.log('error warn', function (event) {
        console.error(event.level + ': ' + event.message);
        if (event.error) {
            console.error(event.error.stack);
        }
    });
};
CocProxy.prototype.exit = function () {
    if (!this.proxy) {
        throw new Error("Proxy didn't start yet!");
    }
    this.proxy.close();
};
module.exports.CocProxy = CocProxy;<|endoftext|>


<commit_before>'use babel';

import CoeditorView from './coeditor-view';
import { CompositeDisposable, Range } from 'atom';
import ShareDB from 'sharedb/lib/client';
import otText from 'ot-text';
import WebSocket from 'ws';
import crypto from 'crypto';

ShareDB.types.register(otText.type);
console.log(ShareDB.types.map);

export default {
  coeditorView: null,
  subscriptions: null,

  socket: null,
  push: null,
  doc: null,
  editor: null,
  buffer: null,
  marker: null,

  initialized: null,

  activate(state) {
    this.initial(state);
  },

  deactivate() {
    this.disconnect();
  },

  serialize() {
    return {
    };
  },

  initial(state) {
    if (this.initialized) {
      return;
    }

    this.coeditorView = new CoeditorView(state.viewState);
    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable();

    // Register command that toggles this view
    atom.commands.add('atom-workspace', {
      'coeditor:toggle': () => this.toggle()
    });
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'coeditor:disconnect': () => this.disconnect()
    }));
    this.subscriptions.add(atom.commands.add(this.coeditorView.element, {
      'core:confirm': () => this.getConfigFromInput(),
      'core:cancel': () => this.coeditorView.close()
    }));
    this.initialized = true;
  },

  disconnect() {
    if (!this.initialized) {
      return;
    }
    this.coeditorView.destroy();
    this.subscriptions.dispose();
    if (this.socket) {
      this.socket.close();
      this.socket = null;
      atom.notifications.addSuccess('Disconnected');
    } else {
      atom.notifications.addWarning("No active socket/connection");
    }
    this.initialized = false;
  },

  toggle() {
    this.initial({});
    let defaultConfig = {
      serverAddress : '192.168.1.175:9090',
      sessionId     : crypto.randomBytes(20).toString('hex'),
      clientId      : crypto.randomBytes(5).toString('hex')
    };
    this.coeditorView.setConfig(defaultConfig);
    this.coeditorView.show();
  },

  getConfigFromInput() {
    this.coeditorView.close();
    console.log('server address: ' + this.coeditorView.serverEditor.getText());
    console.log('session id: ' + this.coeditorView.sessionIdEditor.getText());
    console.log('client id: ' + this.coeditorView.clientIdEditor.getText());
    let config = {
      serverAddress : this.coeditorView.serverEditor.getText(),
      sessionId     : this.coeditorView.sessionIdEditor.getText(),
      clientId      : this.coeditorView.clientIdEditor.getText()
    };
    this.connect(config);
  },

  connect(config) {

    this.editor = atom.workspace.getActiveTextEditor();
    this.buffer = this.editor.getBuffer();
    // TODO set address and portNumber
    let address = config.serverAddress;
    this.socket = new WebSocket('ws://' + address);
    this.socket.on('open', () => {
      atom.notifications.addSuccess('connected to: ' + this.socket.url);
      var connection = new ShareDB.Connection(this.socket);
      this.doc = connection.get('examples', config.sessionId);
      this.doc.subscribe( (error) => {
        if (error) throw error;
        if (this.doc.type === null) {
          this.doc.create(this.buffer.getText(), 'text');
        } else {
          let data = this.doc.data;
          this.doBufferChange( () => {
            this.buffer.setText(data);
          })
        }
      });

      this.push = true;
      this.subscriptions.add(this.listenToBufferChanges());
      this.subscriptions.add(this.listenToEditorChanges());
      this.listenToServerChanges();

      // TODO: get sessionId from user input
      let initData = {
        a         : 'meta',
        type      : 'init',
        sessionId : config.sessionId,
        clientId  : config.clientId
      };
      this.sendSocketMsg( () => {
        this.socket.send(JSON.stringify(initData));
      });
    });
  },

  listenToEditorChanges() {
    this.subscriptions.add(
      this.editor.onDidSave( (event) => {
        let saveData = {
          a: 'meta',
          type: 'save',
          path: event.path
        };
        setTimeout( () => {
          this.sendSocketMsg( () => {
            this.socket.send(JSON.stringify(saveData));
          });
        }, 0);
      })
    );

    return this.editor.onDidChangeSelectionRange( (event) => {
      let selectionData = {
        a: 'meta',
        type: 'select',
        selectRange: event.newBufferRange
      };
      setTimeout( () => {
        this.sendSocketMsg( () => {
          this.socket.send(JSON.stringify(selectionData));
        });
      }, 0);
    });
  },

  listenToBufferChanges() {
    return this.buffer.onDidChange((event) => {
      if (!this.push) {
        return;
      }
      // remove text event
      if (event['oldText'].length !== 0) {
        let startPoint = event['oldRange'].start;
        let position = this.buffer.characterIndexForPosition(startPoint);
        let op = [position, {d: event['oldText'].length}];
        this.doc.submitOp(op);
      }

      // insert text event
      if (event['newText'].length !== 0) {
        let startPoint = event['newRange'].start;
        let position = this.buffer.characterIndexForPosition(startPoint);
        let op = [position, event['newText']];
        this.doc.submitOp(op);
      }

    })
  },

  listenToServerChanges() {
    this.socket.on('message', (msg) => {
      let data = JSON.parse(msg);
      console.log(data);
      if (data.a !== 'meta') {
        return;
      }
      switch (data.type) {
        case 'select':
          this.onSelect(data.selectRange);
          break;
        case 'save':
          this.onSave();
          break;
        case 'close':
          this.onClose();
          break;
        case 'socketClose':
          this.onSocketClose(data.clientId);
          break;
        default:
      }
    });
    this.doc.on('op', (op, source) => {
      if (source) {
        console.log('Got your own changes, I am not doing anything...');
        return;
      }
      console.log('changing...');
      console.log(op);

      var pos = 0;
      var spos = 0;
      for (var i = 0; i < op.length; i++) {
        var component = op[i];
        switch (typeof component) {
          case 'number':
            pos += component;
            spos += component;
            break;
          case 'string':
            this.onInsert(pos, component);
            pos += component.length;
            break;
          case 'object':
            this.onRemove(pos, component.d);
            spos += component.d;
            break;
          default:
        }
      }
    });
  },

  doBufferChange(callback) {
    this.push = false;
    callback();
    this.push = true;
  },

  sendSocketMsg(callback) {
    if (this.socket) {
      callback();
    }
  },

  onInsert(position, text) {
    this.doBufferChange( () => {
      let from = this.buffer.positionForCharacterIndex(position);
      this.buffer.insert(from, text);
    })
  },

  onRemove(position, length) {
    this.doBufferChange( () => {
      let from = this.buffer.positionForCharacterIndex(position);
      let to = this.buffer.positionForCharacterIndex(position + length);
      this.buffer.delete([from.toArray(), to.toArray()]);
    })
  },

  onSelect(range) {
    console.log('onSelect: begin');
    if (this.marker !== null) {
      this.marker.destroy();
    }
    this.marker = this.editor.markBufferRange(Range.fromObject(range), {
      invalidate: 'never'
    });
    this.editor.decorateMarker(this.marker, {
      type: 'highlight',
      class: 'coeditor-selection'
    });
    console.log('onSelect: end');
  },

  onSave() {
    // atom.workspace.getActivePane().saveItem(atom.workspace.getActivePaneItem());
    this.editor.save();
  },

  onClose() {
    atom.workspace.getActivePaneItem().destroy();
  },

  onSocketClose(clientId) {
    atom.notifications.addInfo(clientId + ' left.');
  }

};
<commit_msg>send relative file path<commit_after>'use babel';

import CoeditorView from './coeditor-view';
import { CompositeDisposable, Range } from 'atom';
import ShareDB from 'sharedb/lib/client';
import otText from 'ot-text';
import WebSocket from 'ws';
import crypto from 'crypto';
import path from 'path';

ShareDB.types.register(otText.type);
console.log(ShareDB.types.map);

export default {
  coeditorView: null,
  subscriptions: null,

  socket: null,
  push: null,
  doc: null,
  editor: null,
  buffer: null,
  marker: null,

  initialized: null,

  activate(state) {
    this.initial(state);
  },

  deactivate() {
    this.disconnect();
  },

  serialize() {
    return {
    };
  },

  initial(state) {
    if (this.initialized) {
      return;
    }

    this.coeditorView = new CoeditorView(state.viewState);
    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable();

    // Register command that toggles this view
    atom.commands.add('atom-workspace', {
      'coeditor:toggle': () => this.toggle()
    });
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'coeditor:disconnect': () => this.disconnect()
    }));
    this.subscriptions.add(atom.commands.add(this.coeditorView.element, {
      'core:confirm': () => this.getConfigFromInput(),
      'core:cancel': () => this.coeditorView.close()
    }));
    this.initialized = true;
  },

  disconnect() {
    if (!this.initialized) {
      return;
    }
    this.coeditorView.destroy();
    this.subscriptions.dispose();
    if (this.socket) {
      this.socket.close();
      this.socket = null;
      atom.notifications.addSuccess('Disconnected');
    } else {
      atom.notifications.addWarning("No active socket/connection");
    }
    this.initialized = false;
  },

  toggle() {
    this.initial({});
    let defaultConfig = {
      serverAddress : '192.168.1.175:9090',
      sessionId     : crypto.randomBytes(20).toString('hex'),
      clientId      : crypto.randomBytes(5).toString('hex')
    };
    this.coeditorView.setConfig(defaultConfig);
    this.coeditorView.show();
  },

  getConfigFromInput() {
    this.coeditorView.close();
    console.log('server address: ' + this.coeditorView.serverEditor.getText());
    console.log('session id: ' + this.coeditorView.sessionIdEditor.getText());
    console.log('client id: ' + this.coeditorView.clientIdEditor.getText());
    let config = {
      serverAddress : this.coeditorView.serverEditor.getText(),
      sessionId     : this.coeditorView.sessionIdEditor.getText(),
      clientId      : this.coeditorView.clientIdEditor.getText()
    };
    this.connect(config);
  },

  connect(config) {

    this.editor = atom.workspace.getActiveTextEditor();
    this.buffer = this.editor.getBuffer();
    // TODO set address and portNumber
    let address = config.serverAddress;
    this.socket = new WebSocket('ws://' + address);
    this.socket.on('open', () => {
      atom.notifications.addSuccess('connected to: ' + this.socket.url);
      var connection = new ShareDB.Connection(this.socket);
      this.doc = connection.get('examples', config.sessionId);
      this.doc.subscribe( (error) => {
        if (error) throw error;
        if (this.doc.type === null) {
          this.doc.create(this.buffer.getText(), 'text');
        } else {
          let data = this.doc.data;
          this.doBufferChange( () => {
            this.buffer.setText(data);
          })
        }
      });

      this.push = true;
      this.subscriptions.add(this.listenToBufferChanges());
      this.subscriptions.add(this.listenToEditorChanges());
      this.listenToServerChanges();

      // TODO: get sessionId from user input
      let initData = {
        a         : 'meta',
        type      : 'init',
        sessionId : config.sessionId,
        clientId  : config.clientId
      };
      this.sendSocketMsg( () => {
        this.socket.send(JSON.stringify(initData));
      });
    });
  },

  listenToEditorChanges() {
    this.subscriptions.add(
      this.editor.onDidSave( (event) => {
        let saveData = {
          a: 'meta',
          type: 'save',
          path: atom.project.relativizePath(event.path);
        };
        setTimeout( () => {
          this.sendSocketMsg( () => {
            this.socket.send(JSON.stringify(saveData));
          });
        }, 0);
      })
    );

    return this.editor.onDidChangeSelectionRange( (event) => {
      let selectionData = {
        a: 'meta',
        type: 'select',
        selectRange: event.newBufferRange
      };
      setTimeout( () => {
        this.sendSocketMsg( () => {
          this.socket.send(JSON.stringify(selectionData));
        });
      }, 0);
    });
  },

  listenToBufferChanges() {
    return this.buffer.onDidChange((event) => {
      if (!this.push) {
        return;
      }
      // remove text event
      if (event['oldText'].length !== 0) {
        let startPoint = event['oldRange'].start;
        let position = this.buffer.characterIndexForPosition(startPoint);
        let op = [position, {d: event['oldText'].length}];
        this.doc.submitOp(op);
      }

      // insert text event
      if (event['newText'].length !== 0) {
        let startPoint = event['newRange'].start;
        let position = this.buffer.characterIndexForPosition(startPoint);
        let op = [position, event['newText']];
        this.doc.submitOp(op);
      }

    })
  },

  listenToServerChanges() {
    this.socket.on('message', (msg) => {
      let data = JSON.parse(msg);
      console.log(data);
      if (data.a !== 'meta') {
        return;
      }
      switch (data.type) {
        case 'select':
          this.onSelect(data.selectRange);
          break;
        case 'save':
          this.onSave(data.path);
          break;
        case 'close':
          this.onClose();
          break;
        case 'socketClose':
          this.onSocketClose(data.clientId);
          break;
        default:
      }
    });
    this.doc.on('op', (op, source) => {
      if (source) {
        console.log('Got your own changes, I am not doing anything...');
        return;
      }
      console.log('changing...');
      console.log(op);

      var pos = 0;
      var spos = 0;
      for (var i = 0; i < op.length; i++) {
        var component = op[i];
        switch (typeof component) {
          case 'number':
            pos += component;
            spos += component;
            break;
          case 'string':
            this.onInsert(pos, component);
            pos += component.length;
            break;
          case 'object':
            this.onRemove(pos, component.d);
            spos += component.d;
            break;
          default:
        }
      }
    });
  },

  doBufferChange(callback) {
    this.push = false;
    callback();
    this.push = true;
  },

  sendSocketMsg(callback) {
    if (this.socket) {
      callback();
    }
  },

  onInsert(position, text) {
    this.doBufferChange( () => {
      let from = this.buffer.positionForCharacterIndex(position);
      this.buffer.insert(from, text);
    })
  },

  onRemove(position, length) {
    this.doBufferChange( () => {
      let from = this.buffer.positionForCharacterIndex(position);
      let to = this.buffer.positionForCharacterIndex(position + length);
      this.buffer.delete([from.toArray(), to.toArray()]);
    })
  },

  onSelect(range) {
    console.log('onSelect: begin');
    if (this.marker !== null) {
      this.marker.destroy();
    }
    this.marker = this.editor.markBufferRange(Range.fromObject(range), {
      invalidate: 'never'
    });
    this.editor.decorateMarker(this.marker, {
      type: 'highlight',
      class: 'coeditor-selection'
    });
    console.log('onSelect: end');
  },

  onSave(relativePath) {
    // atom.workspace.getActivePane().saveItem(atom.workspace.getActivePaneItem());
    console.log(atom.project.getPaths());
    if (typeof this.editor.getPath() !== 'undefined') {
      this.editor.save();
    } else {
      let absolutePath = path.join(atom.project.getPaths(), relativePath);
      this.editor.saveAs(absolutePath);
    }
  },

  onClose() {
    atom.workspace.getActivePaneItem().destroy();
  },

  onSocketClose(clientId) {
    atom.notifications.addInfo(clientId + ' left.');
  }

};
<|endoftext|>


<commit_before>/*
  lib/browser/tag/mkdom.js

  Includes hacks needed for the Internet Explorer version 9 and below
  See: http://kangax.github.io/compat-table/es5/#ie8
       http://codeplanet.io/dropping-ie8/
*/
var mkdom = (function (checkIE) {
  var
    reHasYield  = /<yield\b/i,
    reYieldAll  = /<yield\s*(?:\/>|>([\S\s]*?)<\/yield\s*>)/ig,
    reYieldCls  = /<yield\s+to=[^>]+>[\S\s]*?<\/yield\s*>\s*/ig,
    reYieldDest = /<yield\s+from=['"]?([-\w]+)['"]?\s*(?:\/>|>([\S\s]*?)<\/yield\s*>)/ig,
    rsYieldSrc  = '<yield\\s+to=[\'"]@[\'"]\\s*>([\\S\\s]*?)</yield\\s*>',
    rootEls = { tr: 'tbody', th: 'tr', td: 'tr', col: 'colgroup' }

  checkIE = checkIE && checkIE < 10
  var tblTags = checkIE
    ? SPECIAL_TAGS_REGEX : /^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?)$/

  /**
   * Creates a DOM element to wrap the given content. Normally an `DIV`, but can be
   * also a `TABLE`, `SELECT`, `TBODY`, `TR`, or `COLGROUP` element.
   *
   * @param   {string} impl   - Tag implementation with the template and root attributes
   * @param   {string} [html] - HTML content that comes from the DOM element where you
   *           will mount the tag, mostly the original tag in the page
   * @param   {object} [attr] - Plain object where to store the root attributes
   * @returns {HTMLElement} DOM element with _templ_ merged through `YIELD` with the _html_.
   */
  function _mkdom(impl, html, attr) {

    var templ = impl.tmpl,
      match   = templ && templ.match(/^\s*<([-\w]+)/),
      tagName = match && match[1].toLowerCase(),
      el = mkEl('div')

    if (!html) html = ''

    if (impl.attrs) attr.attrs = replaceYield(impl.attrs, html)

    // replace all the yield tags with the tag inner html
    templ = replaceYield(templ, html)

    /* istanbul ignore next */
    if (tblTags.test(tagName))
      el = specialTags(el, templ, tagName)
    else
      el.innerHTML = templ

    el.stub = true

    return el
  }

  /*
    Creates the root element for table or select child elements:
    tr/th/td/thead/tfoot/tbody/caption/col/colgroup/option/optgroup
  */
  function specialTags(el, templ, tagName) {
    var
      select = tagName[0] === 'o',
      parent = select ? 'select>' : 'table>'

    // trim() is important here, this ensures we don't have artifacts,
    // so we can check if we have only one element inside the parent
    el.innerHTML = '<' + parent + templ.trim() + '</' + parent
    parent = el.firstChild

    // returns the immediate parent if tr/th/td/col is the only element, if not
    // returns the whole tree, as this can include additional elements
    if (select) {
      parent.selectedIndex = -1  // for IE9, compatible w/current riot behavior
    } else {
      var tname = rootEls[tagName]
      if (tname && parent.children.length === 1) parent = $(tname, parent)
    }
    return parent
  }

  /*
    Replace the yield tag from any tag template with the innerHTML of the
    original tag in the page
  */
  function replaceYield(templ, html) {
    // do nothing if no yield
    if (!reHasYield.test(templ)) return templ

    // be careful with #1343 - string on the source having `$1`
    var n = 1
    templ = templ.replace(reYieldDest, function (_, ref, def) {
      var m = html.match(RegExp(rsYieldSrc.replace('@', ref), 'i'))
      n = 0
      return (m ? m[1] : def) || ''
    })

    // yield without any "from", replace yield in templ with the innerHTML
    if (n || reHasYield.test(templ)) {
      if (html) html = html.replace(reYieldCls, '').trim()
      templ = templ.replace(reYieldAll, function (_, def) {
        return html || def || ''
      })
    }

    return templ
  }

  return _mkdom

})(IE_VERSION)
<commit_msg>Enhanced yield<commit_after>/*
  lib/browser/tag/mkdom.js

  Includes hacks needed for the Internet Explorer version 9 and below
  See: http://kangax.github.io/compat-table/es5/#ie8
       http://codeplanet.io/dropping-ie8/
*/
var mkdom = (function (checkIE) {
  var
    reYieldAll  = /<yield\s*(?:\/>|>([\S\s]*?)<\/yield\s*>)/ig,
    reYieldCls  = /<yield\s+to=[^>]+>[\S\s]*?<\/yield\s*>\s*/ig,
    reYieldDest = /<yield\s+from=['"]?([-\w]+)['"]?\s*(?:\/>|>([\S\s]*?)<\/yield\s*>)/ig,
    rsYieldSrc  = '<yield\\s+to=[\'"]@[\'"]\\s*>([\\S\\s]*?)</yield\\s*>',
    rootEls = { tr: 'tbody', th: 'tr', td: 'tr', col: 'colgroup' }

  checkIE = checkIE && checkIE < 10
  var tblTags = checkIE
    ? SPECIAL_TAGS_REGEX : /^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?)$/

  /**
   * Creates a DOM element to wrap the given content. Normally an `DIV`, but can be
   * also a `TABLE`, `SELECT`, `TBODY`, `TR`, or `COLGROUP` element.
   *
   * @param   {string} impl   - Tag implementation with the template and root attributes
   * @param   {string} [html] - HTML content that comes from the DOM element where you
   *           will mount the tag, mostly the original tag in the page
   * @param   {object} [attr] - Plain object where to store the root attributes
   * @returns {HTMLElement} DOM element with _templ_ merged through `YIELD` with the _html_.
   */
  function _mkdom(impl, html, attr) {

    var templ = impl.tmpl,
      match   = templ && templ.match(/^\s*<([-\w]+)/),
      tagName = match && match[1].toLowerCase(),
      el = mkEl('div')

    if (!html) html = ''

    if (impl.attrs) attr.attrs = replaceYield(impl.attrs, html)

    // replace all the yield tags with the tag inner html
    templ = replaceYield(templ, html)

    /* istanbul ignore next */
    if (tblTags.test(tagName))
      el = specialTags(el, templ, tagName)
    else
      el.innerHTML = templ

    el.stub = true

    return el
  }

  /*
    Creates the root element for table or select child elements:
    tr/th/td/thead/tfoot/tbody/caption/col/colgroup/option/optgroup
  */
  function specialTags(el, templ, tagName) {
    var
      select = tagName[0] === 'o',
      parent = select ? 'select>' : 'table>'

    // trim() is important here, this ensures we don't have artifacts,
    // so we can check if we have only one element inside the parent
    el.innerHTML = '<' + parent + templ.trim() + '</' + parent
    parent = el.firstChild

    // returns the immediate parent if tr/th/td/col is the only element, if not
    // returns the whole tree, as this can include additional elements
    if (select) {
      parent.selectedIndex = -1  // for IE9, compatible w/current riot behavior
    } else {
      var tname = rootEls[tagName]
      if (tname && parent.children.length === 1) parent = $(tname, parent)
    }
    return parent
  }

  /*
    Replace the yield tag from any tag template with the innerHTML of the
    original tag in the page
  */
  function replaceYield(templ, html) {
    // do nothing if no yield
    if (!/<yield\b/i.test(templ)) return templ

    // be careful with #1343 - string on the source having `$1`
    var n = 1
    templ = templ.replace(reYieldDest, function (_, ref, def) {
      var m = html.match(RegExp(rsYieldSrc.replace('@', ref), 'i'))
      n = 0
      return (m ? m[1] : def) || ''
    })

    // yield without any "from", replace yield in templ with the innerHTML
    if (n || reHasYield.test(templ)) {
      if (html) html = html.replace(reYieldCls, '').trim()
      templ = templ.replace(reYieldAll, function (_, def) {
        return html || def || ''
      })
    }

    return templ
  }

  return _mkdom

})(IE_VERSION)
<|endoftext|>


<commit_before>import {Record, List, Map} from 'immutable';
import {MODE_IDLE} from './constants';

export const Guide = Record({
  id: "",
  type: "",
  properties: Map()
}, 'Guide');

export const DefaultGuides = new Map({
  'h1': new Guide({
    id: 'h1',
    type: 'horizontal-streak',
    properties: new Map({
      step: 20,
      colors: ['#808080', '#ddd', '#ddd', '#ddd', '#ddd']
    })
  }),
  'v1': new Guide({
    id: 'v1',
    type: 'vertical-streak',
    properties: new Map({
      step: 20,
      colors: ['#808080', '#ddd', '#ddd', '#ddd', '#ddd']
    })
  })
});

export const ElementsSet = Record({
  vertices: new List(),
  lines: new List(),
  holes: new List(),
  areas: new List(),
  items: new List(),
}, 'ElementsSet');

export const Layer = Record({
  id: "",
  altitude: 0,
  name: "",
  vertices: new Map(),
  lines: new Map(),
  holes: new Map(),
  areas: new Map(),
  items: new Map(),
  selected: new ElementsSet(),
  visible: true
}, 'Layer');

export const Scene = Record({
  unit: "cm",
  layers: new Map({
    'layer-1': new Layer({id: 'layer-1', name: 'default layer'})
  }),
  guides: DefaultGuides,
  selectedLayer: 'layer-1',
  width: 3000,
  height: 2000
}, 'Scene');

export const Vertex = Record({
  id: "",
  x: -1,
  y: -1,
  prototype: "vertices",
  selected: false,
  lines: new List(),
  areas: new List(),
}, 'Vertex');

export const Line = Record({
  id: "",
  type: "",
  prototype: "lines",
  vertices: new List(),
  holes: new List(),
  selected: false,
  properties: new Map()
}, 'Line');

export const Hole = Record({
  id: "",
  type: "",
  prototype: "holes",
  offset: -1,
  line: "",
  selected: false,
  properties: new Map(),
}, 'Hole');

export const Area = Record({
  id: "",
  type: "",
  prototype: "areas",
  vertices: new List(),
  selected: false,
  properties: new Map()
}, 'Area');

export const Item = Record({
  id: "",
  prototype: 'items',
  type: "",
  properties: new Map(),
  selected: false,
  x: 0,
  y: 0,
  rotation: 0
}, 'Item');

export const State = Record({
  mode: MODE_IDLE,
  scene: new Scene(),
  viewer2D: new Map(),

  snapElements: new List(),
  activeSnapElement: null,

  drawingSupport: Map(),
  draggingSupport: Map(),
  rotatingSupport: Map()
}, 'State');

<commit_msg>adds scene history<commit_after>import {Record, List, Map} from 'immutable';
import {MODE_IDLE} from './constants';

export const Guide = Record({
  id: "",
  type: "",
  properties: Map()
}, 'Guide');

export const DefaultGuides = new Map({
  'h1': new Guide({
    id: 'h1',
    type: 'horizontal-streak',
    properties: new Map({
      step: 20,
      colors: ['#808080', '#ddd', '#ddd', '#ddd', '#ddd']
    })
  }),
  'v1': new Guide({
    id: 'v1',
    type: 'vertical-streak',
    properties: new Map({
      step: 20,
      colors: ['#808080', '#ddd', '#ddd', '#ddd', '#ddd']
    })
  })
});

export const ElementsSet = Record({
  vertices: new List(),
  lines: new List(),
  holes: new List(),
  areas: new List(),
  items: new List(),
}, 'ElementsSet');

export const Layer = Record({
  id: "",
  altitude: 0,
  name: "",
  vertices: new Map(),
  lines: new Map(),
  holes: new Map(),
  areas: new Map(),
  items: new Map(),
  selected: new ElementsSet(),
  visible: true
}, 'Layer');

export const Scene = Record({
  unit: "cm",
  layers: new Map({
    'layer-1': new Layer({id: 'layer-1', name: 'default layer'})
  }),
  guides: DefaultGuides,
  selectedLayer: 'layer-1',
  width: 3000,
  height: 2000
}, 'Scene');

export const Vertex = Record({
  id: "",
  x: -1,
  y: -1,
  prototype: "vertices",
  selected: false,
  lines: new List(),
  areas: new List(),
}, 'Vertex');

export const Line = Record({
  id: "",
  type: "",
  prototype: "lines",
  vertices: new List(),
  holes: new List(),
  selected: false,
  properties: new Map()
}, 'Line');

export const Hole = Record({
  id: "",
  type: "",
  prototype: "holes",
  offset: -1,
  line: "",
  selected: false,
  properties: new Map(),
}, 'Hole');

export const Area = Record({
  id: "",
  type: "",
  prototype: "areas",
  vertices: new List(),
  selected: false,
  properties: new Map()
}, 'Area');

export const Item = Record({
  id: "",
  prototype: 'items',
  type: "",
  properties: new Map(),
  selected: false,
  x: 0,
  y: 0,
  rotation: 0
}, 'Item');

export const State = Record({
  mode: MODE_IDLE,
  scene: new Scene(),
  viewer2D: new Map(),

  snapElements: new List(),
  activeSnapElement: null,

  drawingSupport: Map(),
  draggingSupport: Map(),
  rotatingSupport: Map(),
  sceneHistory: new List()
}, 'State');

<|endoftext|>


<commit_before>(function(dust) {

dust.compile = function(source, name, minify) {
  try {
    if (minify) {
      source = source.replace(/^\s+/mg, '').replace(/\n/mg, '');
      console.log(source);
    }
    var ast = filterAST(dust.parse(source));
    return compile(ast, name);
  }
  catch(err)
  {
    if(!err.line || !err.column) throw err;
    throw new SyntaxError(err.message + " At line : " + err.line + ", column : " + err.column);
  }
};

function filterAST(ast) {
  var context = {};
  return dust.filterNode(context, ast);
}

dust.filterNode = function(context, node) {
  return dust.optimizers[node[0]](context, node);
}

dust.optimizers = {
  body:      compactBuffers,
  buffer:    noop,
  special:   convertSpecial,
  format:    nullify,        // TODO: convert format
  reference: visit,
  "#":       visit,
  "?":       visit,
  "^":       visit,
  "<":       visit,
  "+":       visit,
  "@":       visit,
  "%":       visit,
  partial:   visit,
  context:   visit,
  params:    visit,
  bodies:    visit,
  param:     visit,
  filters:   noop,
  key:       noop,
  path:      noop,
  literal:   noop,
  comment:   nullify
}

dust.pragmas = {
  esc: function(compiler, context, bodies, params) {
    var old = compiler.auto;
    if (!context) context = 'h';
    compiler.auto = (context === 's') ? '' : context;
    var out = compileParts(compiler, bodies.block);
    compiler.auto = old;
    return out;
  }
}

function visit(context, node) {
  var out = [node[0]];
  for (var i=1, len=node.length; i<len; i++) {
    var res = dust.filterNode(context, node[i]);
    if (res) out.push(res);
  }
  return out;
}

// Compacts consecutive buffer nodes into a single node
function compactBuffers(context, node) {
  var out = [node[0]], memo;
  for (var i=1, len=node.length; i<len; i++) {
    var res = dust.filterNode(context, node[i]);
    if (res) {
      if (res[0] === 'buffer') {
        if (memo) {
          memo[1] += res[1];
        } else {
          memo = res;
          out.push(res);
        }
      } else {
        memo = null;
        out.push(res);
      }
    }
  }
  return out;
}

var specialChars = {
  "s": " ",
  "n": "\n",
  "r": "\r",
  "lb": "{",
  "rb": "}"
};

function convertSpecial(context, node) { return ['buffer', specialChars[node[1]]] }
function noop(context, node) { return node }
function nullify(){}

function compile(ast, name) {
  var context = {
    name: name,
    bodies: [],
    blocks: {},
    index: 0,
    auto: "h"
  }

  return "(function(){dust.register("
    + (name ? "\"" + name + "\"" : "null") + ","
    + dust.compileNode(context, ast)
    + ");"
    + compileBlocks(context)
    + compileBodies(context)
    + "return body_0;"
    + "})();";
}

function compileBlocks(context) {
  var out = [],
      blocks = context.blocks;

  for (var name in blocks) {
    out.push("'" + name + "':" + blocks[name]);
  }
  if (out.length) {
    context.blocks = "ctx=ctx.shiftBlocks(blocks);";
    return "var blocks={" + out.join(',') + "};";
  }
  return context.blocks = "";
}

function compileBodies(context) {
  var out = [],
      bodies = context.bodies,
      blx = context.blocks;

  for (var i=0, len=bodies.length; i<len; i++) {
    out[i] = "function body_" + i + "(chk,ctx){"
      + blx + "return chk" + bodies[i] + ";}";
  }
  return out.join('');
}

function compileParts(context, body) {
  var parts = '';
  for (var i=1, len=body.length; i<len; i++) {
    parts += dust.compileNode(context, body[i]);
  }
  return parts;
}

dust.compileNode = function(context, node) {
  return dust.nodes[node[0]](context, node);
}

dust.nodes = {
  body: function(context, node) {
    var id = context.index++, name = "body_" + id;
    context.bodies[id] = compileParts(context, node);
    return name;
  },

  buffer: function(context, node) {
    return ".write(" + escape(node[1]) + ")";
  },

  format: function(context, node) {
    return ".write(" + escape(node[1] + node[2]) + ")";
  },

  reference: function(context, node) {
    return ".reference(" + dust.compileNode(context, node[1])
      + ",ctx," + dust.compileNode(context, node[2]) + ")";
  },

  "#": function(context, node) {
    return compileSection(context, node, "section");
  },

  "?": function(context, node) {
    return compileSection(context, node, "exists");
  },

  "^": function(context, node) {
    return compileSection(context, node, "notexists");
  },

  "<": function(context, node) {
    var bodies = node[4];
    for (var i=1, len=bodies.length; i<len; i++) {
      var param = bodies[i],
          type = param[1][1];
      if (type === "block") {
        context.blocks[node[1].text] = dust.compileNode(context, param[2]);
        return '';
      }
    }
    return '';
  },

  "+": function(context, node) {
    if(typeof(node[1].text) === "undefined"  && typeof(node[4]) === "undefined"){
      return ".block(ctx.getBlock("
      + dust.compileNode(context, node[1])
      + ",chk, ctx)," + dust.compileNode(context, node[2]) + ", {},"
      + dust.compileNode(context, node[3])
      + ")";
    }else {
      return ".block(ctx.getBlock("
      + escape(node[1].text)
      + ")," + dust.compileNode(context, node[2]) + ","
      + dust.compileNode(context, node[4]) + ","
      + dust.compileNode(context, node[3])
      + ")";
    }
  },

  "@": function(context, node) {
    return ".helper("
      + escape(node[1].text)
      + "," + dust.compileNode(context, node[2]) + ","
      + dust.compileNode(context, node[4]) + ","
      + dust.compileNode(context, node[3])
      + ")";
  },

  "%": function(context, node) {
    // TODO: Move these hacks into pragma precompiler
    var name = node[1][1];
    if (!dust.pragmas[name]) return '';

    var rawBodies = node[4];
    var bodies = {};
    for (var i=1, len=rawBodies.length; i<len; i++) {
      var b = rawBodies[i];
      bodies[b[1][1]] = b[2];
    }

    var rawParams = node[3];
    var params = {};
    for (var i=1, len=rawParams.length; i<len; i++) {
      var p = rawParams[i];
      params[p[1][1]] = p[2][1];
    }

    var ctx = node[2][1] ? node[2][1].text : null;

    return dust.pragmas[name](context, ctx, bodies, params);
  },

  partial: function(context, node) {
    return ".partial("
      + dust.compileNode(context, node[1])
      + "," + dust.compileNode(context, node[2])
      + "," + dust.compileNode(context, node[3]) + ")";
  },

  context: function(context, node) {
    if (node[1]) {
      return "ctx.rebase(" + dust.compileNode(context, node[1]) + ")";
    }
    return "ctx";
  },

  params: function(context, node) {
    var out = [];
    for (var i=1, len=node.length; i<len; i++) {
      out.push(dust.compileNode(context, node[i]));
    }
    if (out.length) {
      return "{" + out.join(',') + "}";
    }
    return "null";
  },

  bodies: function(context, node) {
    var out = [];
    for (var i=1, len=node.length; i<len; i++) {
      out.push(dust.compileNode(context, node[i]));
    }
    return "{" + out.join(',') + "}";
  },

  param: function(context, node) {
    return dust.compileNode(context, node[1]) + ":" + dust.compileNode(context, node[2]);
  },

  filters: function(context, node) {
    var list = [];
    for (var i=1, len=node.length; i<len; i++) {
      var filter = node[i];
      list.push("\"" + filter + "\"");
    }
    return "\"" + context.auto + "\""
      + (list.length ? ",[" + list.join(',') + "]" : '');
  },

  key: function(context, node) {
    return "ctx.get(\"" + node[1] + "\")";
  },

  path: function(context, node) {
    var current = node[1],
        keys = node[2],
        list = [];

    for (var i=0,len=keys.length; i<len; i++) {
      list.push("\"" + keys[i] + "\"");
    }
    return "ctx.getPath(" + current + ",[" + list.join(',') + "])";
  },

  literal: function(context, node) {
    return escape(node[1]);
  }
}

function compileSection(context, node, cmd) {
  return "." + cmd + "("
    + dust.compileNode(context, node[1])
    + "," + dust.compileNode(context, node[2]) + ","
    + dust.compileNode(context, node[4]) + ","
    + dust.compileNode(context, node[3])
    + ")";
}

var escape = (typeof JSON === "undefined")
  ? function(str) { return "\"" + dust.escapeJs(str) + "\"" }
  : JSON.stringify;

})(typeof exports !== 'undefined' ? exports : getGlobal());
<commit_msg>Remove console.log<commit_after>(function(dust) {

dust.compile = function(source, name, minify) {
  try {
    if (minify) {
      source = source.replace(/^\s+/mg, '').replace(/\n/mg, '');
    }
    var ast = filterAST(dust.parse(source));
    return compile(ast, name);
  }
  catch(err)
  {
    if(!err.line || !err.column) throw err;
    throw new SyntaxError(err.message + " At line : " + err.line + ", column : " + err.column);
  }
};

function filterAST(ast) {
  var context = {};
  return dust.filterNode(context, ast);
}

dust.filterNode = function(context, node) {
  return dust.optimizers[node[0]](context, node);
}

dust.optimizers = {
  body:      compactBuffers,
  buffer:    noop,
  special:   convertSpecial,
  format:    nullify,        // TODO: convert format
  reference: visit,
  "#":       visit,
  "?":       visit,
  "^":       visit,
  "<":       visit,
  "+":       visit,
  "@":       visit,
  "%":       visit,
  partial:   visit,
  context:   visit,
  params:    visit,
  bodies:    visit,
  param:     visit,
  filters:   noop,
  key:       noop,
  path:      noop,
  literal:   noop,
  comment:   nullify
}

dust.pragmas = {
  esc: function(compiler, context, bodies, params) {
    var old = compiler.auto;
    if (!context) context = 'h';
    compiler.auto = (context === 's') ? '' : context;
    var out = compileParts(compiler, bodies.block);
    compiler.auto = old;
    return out;
  }
}

function visit(context, node) {
  var out = [node[0]];
  for (var i=1, len=node.length; i<len; i++) {
    var res = dust.filterNode(context, node[i]);
    if (res) out.push(res);
  }
  return out;
}

// Compacts consecutive buffer nodes into a single node
function compactBuffers(context, node) {
  var out = [node[0]], memo;
  for (var i=1, len=node.length; i<len; i++) {
    var res = dust.filterNode(context, node[i]);
    if (res) {
      if (res[0] === 'buffer') {
        if (memo) {
          memo[1] += res[1];
        } else {
          memo = res;
          out.push(res);
        }
      } else {
        memo = null;
        out.push(res);
      }
    }
  }
  return out;
}

var specialChars = {
  "s": " ",
  "n": "\n",
  "r": "\r",
  "lb": "{",
  "rb": "}"
};

function convertSpecial(context, node) { return ['buffer', specialChars[node[1]]] }
function noop(context, node) { return node }
function nullify(){}

function compile(ast, name) {
  var context = {
    name: name,
    bodies: [],
    blocks: {},
    index: 0,
    auto: "h"
  }

  return "(function(){dust.register("
    + (name ? "\"" + name + "\"" : "null") + ","
    + dust.compileNode(context, ast)
    + ");"
    + compileBlocks(context)
    + compileBodies(context)
    + "return body_0;"
    + "})();";
}

function compileBlocks(context) {
  var out = [],
      blocks = context.blocks;

  for (var name in blocks) {
    out.push("'" + name + "':" + blocks[name]);
  }
  if (out.length) {
    context.blocks = "ctx=ctx.shiftBlocks(blocks);";
    return "var blocks={" + out.join(',') + "};";
  }
  return context.blocks = "";
}

function compileBodies(context) {
  var out = [],
      bodies = context.bodies,
      blx = context.blocks;

  for (var i=0, len=bodies.length; i<len; i++) {
    out[i] = "function body_" + i + "(chk,ctx){"
      + blx + "return chk" + bodies[i] + ";}";
  }
  return out.join('');
}

function compileParts(context, body) {
  var parts = '';
  for (var i=1, len=body.length; i<len; i++) {
    parts += dust.compileNode(context, body[i]);
  }
  return parts;
}

dust.compileNode = function(context, node) {
  return dust.nodes[node[0]](context, node);
}

dust.nodes = {
  body: function(context, node) {
    var id = context.index++, name = "body_" + id;
    context.bodies[id] = compileParts(context, node);
    return name;
  },

  buffer: function(context, node) {
    return ".write(" + escape(node[1]) + ")";
  },

  format: function(context, node) {
    return ".write(" + escape(node[1] + node[2]) + ")";
  },

  reference: function(context, node) {
    return ".reference(" + dust.compileNode(context, node[1])
      + ",ctx," + dust.compileNode(context, node[2]) + ")";
  },

  "#": function(context, node) {
    return compileSection(context, node, "section");
  },

  "?": function(context, node) {
    return compileSection(context, node, "exists");
  },

  "^": function(context, node) {
    return compileSection(context, node, "notexists");
  },

  "<": function(context, node) {
    var bodies = node[4];
    for (var i=1, len=bodies.length; i<len; i++) {
      var param = bodies[i],
          type = param[1][1];
      if (type === "block") {
        context.blocks[node[1].text] = dust.compileNode(context, param[2]);
        return '';
      }
    }
    return '';
  },

  "+": function(context, node) {
    if(typeof(node[1].text) === "undefined"  && typeof(node[4]) === "undefined"){
      return ".block(ctx.getBlock("
      + dust.compileNode(context, node[1])
      + ",chk, ctx)," + dust.compileNode(context, node[2]) + ", {},"
      + dust.compileNode(context, node[3])
      + ")";
    }else {
      return ".block(ctx.getBlock("
      + escape(node[1].text)
      + ")," + dust.compileNode(context, node[2]) + ","
      + dust.compileNode(context, node[4]) + ","
      + dust.compileNode(context, node[3])
      + ")";
    }
  },

  "@": function(context, node) {
    return ".helper("
      + escape(node[1].text)
      + "," + dust.compileNode(context, node[2]) + ","
      + dust.compileNode(context, node[4]) + ","
      + dust.compileNode(context, node[3])
      + ")";
  },

  "%": function(context, node) {
    // TODO: Move these hacks into pragma precompiler
    var name = node[1][1];
    if (!dust.pragmas[name]) return '';

    var rawBodies = node[4];
    var bodies = {};
    for (var i=1, len=rawBodies.length; i<len; i++) {
      var b = rawBodies[i];
      bodies[b[1][1]] = b[2];
    }

    var rawParams = node[3];
    var params = {};
    for (var i=1, len=rawParams.length; i<len; i++) {
      var p = rawParams[i];
      params[p[1][1]] = p[2][1];
    }

    var ctx = node[2][1] ? node[2][1].text : null;

    return dust.pragmas[name](context, ctx, bodies, params);
  },

  partial: function(context, node) {
    return ".partial("
      + dust.compileNode(context, node[1])
      + "," + dust.compileNode(context, node[2])
      + "," + dust.compileNode(context, node[3]) + ")";
  },

  context: function(context, node) {
    if (node[1]) {
      return "ctx.rebase(" + dust.compileNode(context, node[1]) + ")";
    }
    return "ctx";
  },

  params: function(context, node) {
    var out = [];
    for (var i=1, len=node.length; i<len; i++) {
      out.push(dust.compileNode(context, node[i]));
    }
    if (out.length) {
      return "{" + out.join(',') + "}";
    }
    return "null";
  },

  bodies: function(context, node) {
    var out = [];
    for (var i=1, len=node.length; i<len; i++) {
      out.push(dust.compileNode(context, node[i]));
    }
    return "{" + out.join(',') + "}";
  },

  param: function(context, node) {
    return dust.compileNode(context, node[1]) + ":" + dust.compileNode(context, node[2]);
  },

  filters: function(context, node) {
    var list = [];
    for (var i=1, len=node.length; i<len; i++) {
      var filter = node[i];
      list.push("\"" + filter + "\"");
    }
    return "\"" + context.auto + "\""
      + (list.length ? ",[" + list.join(',') + "]" : '');
  },

  key: function(context, node) {
    return "ctx.get(\"" + node[1] + "\")";
  },

  path: function(context, node) {
    var current = node[1],
        keys = node[2],
        list = [];

    for (var i=0,len=keys.length; i<len; i++) {
      list.push("\"" + keys[i] + "\"");
    }
    return "ctx.getPath(" + current + ",[" + list.join(',') + "])";
  },

  literal: function(context, node) {
    return escape(node[1]);
  }
}

function compileSection(context, node, cmd) {
  return "." + cmd + "("
    + dust.compileNode(context, node[1])
    + "," + dust.compileNode(context, node[2]) + ","
    + dust.compileNode(context, node[4]) + ","
    + dust.compileNode(context, node[3])
    + ")";
}

var escape = (typeof JSON === "undefined")
  ? function(str) { return "\"" + dust.escapeJs(str) + "\"" }
  : JSON.stringify;

})(typeof exports !== 'undefined' ? exports : getGlobal());
<|endoftext|>


<commit_before>const reactions = require('./reactions.js');

function UserMessage(user_id, text){
  this.user_id = user_id;
  this.text = text;
}

UserMessage.prototype.equals = function (message) {
  return this.user_id == message.user_id &&
    this.text == message.text;
};

function NoMessage(){

}

function Rule(expression, reaction){
  this.expression = expression;
  this.reaction = reaction;
}

Rule.prototype.toString = function(){
  return 'Rule [' + this.expression + ', ' + this.reaction + ']'
}

function Rules(){
  this.rules = new Array();
}

Rules.prototype.add = function(rule){
  this.rules.push(rule);
}

module.exports = {
  UserMessage,
  NoMessage,
  Rules,
  Rule
};
<commit_msg>Linting for #1.<commit_after>function UserMessage(userId, text) {
  this.userId = userId;
  this.text = text;
}

UserMessage.prototype.equals = function (message) {
  return this.userId === message.userId &&
    this.text === message.text;
};

function NoMessage() {

}

function Rule(expression, reaction) {
  this.expression = expression;
  this.reaction = reaction;
}

function Rules() {
  this.rules = [];
}

Rules.prototype.add = function (rule) {
  this.rules.push(rule);
};

module.exports = {
  UserMessage,
  NoMessage,
  Rules,
  Rule,
};
<|endoftext|>