<commit_before>// ## KumaScript template script loaders
//
// This module houses the machinery for template loading, compilation, and
// caching.

/*jshint node: true, esversion: 6, expr: false, boss: true */

// ### Prerequisites
var fs = require('fs'),
    path = require('path'),
    _ = require('underscore'),
    Memcached = require('memcached'),
    ks_templates = require(__dirname + '/templates'),
    ks_utils = require(__dirname + '/utils');

// ### BaseLoader
//
// The base API to template loading.
var BaseLoader = ks_utils.Class({

    initialize: function (options) {
        // Create a memcache instance, if necessary
        if (this.options.memcache) {
            var mo = this.options.memcache;
            this.memcached = new Memcached(mo.server, mo.options || {});
        } else {
            // If the configuration is missing, use the fake stub cache
            this.memcached = new ks_utils.FakeMemcached();
        }
    },

    // #### get(name, cb)
    //
    // Get the named template, pass it to the callback when available.
    // The callback should expect `(err, fn)` parameters
    //
    // Try not to override this method, since it takes care of basics like
    // trapping errors and using a cache if available. Instead, override
    // the `load()` method.
    get: function (name, cb) {
        var $this = this;
        try {
            $this.cache_fetch(name, function (err, source) {
                if (source && !err && $this.options.cache_control != 'no-cache') {
                    return $this.compile(source, cb, true);
                }
                $this.load(name, function (err, source) {
                    if (!source || err) {
                        return cb(err, null, false);
                    }
                    $this.cache_store(name, source, function (err) {
                        return $this.compile(source, cb, false);
                    });
                });
            });
        } catch (e) {
            return cb(e, null);
        }
    },

    // #### compile(source, cb)
    //
    // Compile the given source using the configured template class, call
    // cb(error, tmpl_instance)
    compile: function (source, cb, cache_hit) {
        var $this = this;
        try {
            var tmpl_cls = $this.options.template_class;
            cb(null, new tmpl_cls({ source: source }), cache_hit);
        } catch (e) {
            cb(e, null, cache_hit);
        }
    },

    // #### cache_fetch(name, cb)
    //
    // Attempt to fetch the named template from cache.
    //
    // If it's a miss, the callback gets an error. Otherwise, the callback gets
    // the cached template.
    cache_fetch: function (name, cb) {
        var key = 'kumascript:loader:' + ks_utils.md5(name);
        this.memcached.get(key, function (err, result) {
            if (err || !result) {
                cb(err, null);
            } else {
                var source = (new Buffer(result, 'base64')).toString('utf-8');
                cb(null, source);
            }
        });
    },

    // #### cache_store(name, cb)
    //
    // Store the named template into the cache. This should really just be a
    // pass-through and not telegraph any errors onto the callback.
    cache_store: function (name, source, cb) {
        var key = 'kumascript:loader:' + ks_utils.md5(name);
        var timeout = this.options.cache_timeout || 3600;
        var cache_source = (new Buffer(source,'utf8')).toString('base64');
        this.memcached.set(key, cache_source, timeout, function (err, result) {
            return (err) ? cb(err, null) : cb(null, source);
        });
    },

    // By default, a loader doesn't know what macros are available
    can_list_macros: false,

    // #### macros_data()
    //
    // Return data about the macros known to this loader
    macros_data: function () {
        return {
            'can_list_macros': this.can_list_macros,
            'macros': this.macros_details(),
        };
    },

    // ### macros_details()
    //
    // Return details of macros known by this loader.
    macros_details: function () {
        // By default, loader doesn't know what macros are available
        return [];
    }
});

// ### FileLoader
var FileLoader = ks_utils.Class(BaseLoader, {

    default_options: {
        // Root directory for templates (relative to the repo dir).
        root_dir: 'macros',
        // Class used to render templates
        template_class: ks_templates.EJSTemplate
    },

    initialize: function (options) {
        // Build the template map, associating lowercase template names
        // with their paths on the filesystem.

        var dir = null,
            duplicates = {},
            template_map = this.template_map = {},
            repo_dir = path.join(__dirname, '..', '..'),
            dirs = [path.join(repo_dir, this.options.root_dir)];

        function getTemplateName(fp) {
            // Returns the lowercase basename, without the extension.
            return path.parse(fp).name.toLowerCase();
        }

        function processFilename(fn) {
            // If the given filename is a directory, push it onto
            // the queue, otherwise consider it a template.
            var fp = path.join(dir, fn);
            if (fs.statSync(fp).isDirectory()) {
                dirs.push(fp);
            } else {
                var name = getTemplateName(fn);
                if (_.has(template_map, name)) {
                    // Keep track of all duplicates and throw error later.
                    if (!(_.has(duplicates, name))) {
                        duplicates[name] = [template_map[name]];
                    }
                    duplicates[name].push(fp);
                } else {
                    template_map[name] = fp;
                }
            }
        }

        // Walk the directory tree under the specified root directory.
        while (dirs.length > 0) {
            dir = dirs.shift();
            fs.readdirSync(dir).forEach(processFilename);
        }

        if (_.isEmpty(template_map)) {
            // Let's throw an error if no macros could be discovered, since
            // for now this is the only time we check and this loader is
            // useless if there are no macros.
            var root_dir = path.resolve(repo_dir, this.options.root_dir);
            throw new Error(
                `no macros could be found in "${root_dir}"`
            );
        }

        if (!(_.isEmpty(duplicates))) {
            // Duplicate template names
            var msg = "duplicate macros:";
            _.keys(duplicates).forEach(function(name) {
                msg += "\n" + name + ": " + duplicates[name].join(", ");
            });
            throw new Error(msg);
        }
    },

    // Don't use caching, as we're loading from the local filesystem.
    get: function (name, cb) {
        var $this = this;
        try {
            // Replace all colons in macro names with dashes.
            var lc_name = name.replace(/:/g, '-').toLowerCase();
            if (_.has(this.template_map, lc_name)) {
                var fp = this.template_map[lc_name];
                fs.readFile(fp, 'utf8', function (err, source) {
                    if (err || !source) {
                        cb(err, null, false);
                    } else {
                        $this.compile(source, cb, false);
                    }
                });
            } else {
                cb(new Error('unable to load: ' + name), null, false);
            }
        } catch (e) {
            cb(e, null, false);
        }
    },

    // FileLoader does have a list of all known macros
    can_list_macros: true,

    // Returns list of macro detail objects with these key: value pairs:
    //  name: the mixed-case name of the macro
    //  filename: path to filename in macros folder
    macros_details: function() {
        var macros = [],
            repo_dir = path.join(__dirname, '..', '..'),
            macro_dir = path.join(repo_dir, this.options.root_dir) + path.sep;
        for (var lc_name in this.template_map) {
            var fp = this.template_map[lc_name],
                name = path.parse(fp).name,
                short_path = fp.slice(macro_dir.length).replace(path.sep, '/');
            macros.push({
                'name': name,
                'filename': short_path
            });
        }
        return macros;
    }
});

// ### Exported public API
module.exports = {
    BaseLoader: BaseLoader,
    FileLoader: FileLoader,
};
<commit_msg>accomodate absolute paths for macro directory<commit_after>// ## KumaScript template script loaders
//
// This module houses the machinery for template loading, compilation, and
// caching.

/*jshint node: true, esversion: 6, expr: false, boss: true */

// ### Prerequisites
var fs = require('fs'),
    path = require('path'),
    _ = require('underscore'),
    Memcached = require('memcached'),
    ks_templates = require(__dirname + '/templates'),
    ks_utils = require(__dirname + '/utils');

// ### BaseLoader
//
// The base API to template loading.
var BaseLoader = ks_utils.Class({

    initialize: function (options) {
        // Create a memcache instance, if necessary
        if (this.options.memcache) {
            var mo = this.options.memcache;
            this.memcached = new Memcached(mo.server, mo.options || {});
        } else {
            // If the configuration is missing, use the fake stub cache
            this.memcached = new ks_utils.FakeMemcached();
        }
    },

    // #### get(name, cb)
    //
    // Get the named template, pass it to the callback when available.
    // The callback should expect `(err, fn)` parameters
    //
    // Try not to override this method, since it takes care of basics like
    // trapping errors and using a cache if available. Instead, override
    // the `load()` method.
    get: function (name, cb) {
        var $this = this;
        try {
            $this.cache_fetch(name, function (err, source) {
                if (source && !err && $this.options.cache_control != 'no-cache') {
                    return $this.compile(source, cb, true);
                }
                $this.load(name, function (err, source) {
                    if (!source || err) {
                        return cb(err, null, false);
                    }
                    $this.cache_store(name, source, function (err) {
                        return $this.compile(source, cb, false);
                    });
                });
            });
        } catch (e) {
            return cb(e, null);
        }
    },

    // #### compile(source, cb)
    //
    // Compile the given source using the configured template class, call
    // cb(error, tmpl_instance)
    compile: function (source, cb, cache_hit) {
        var $this = this;
        try {
            var tmpl_cls = $this.options.template_class;
            cb(null, new tmpl_cls({ source: source }), cache_hit);
        } catch (e) {
            cb(e, null, cache_hit);
        }
    },

    // #### cache_fetch(name, cb)
    //
    // Attempt to fetch the named template from cache.
    //
    // If it's a miss, the callback gets an error. Otherwise, the callback gets
    // the cached template.
    cache_fetch: function (name, cb) {
        var key = 'kumascript:loader:' + ks_utils.md5(name);
        this.memcached.get(key, function (err, result) {
            if (err || !result) {
                cb(err, null);
            } else {
                var source = (new Buffer(result, 'base64')).toString('utf-8');
                cb(null, source);
            }
        });
    },

    // #### cache_store(name, cb)
    //
    // Store the named template into the cache. This should really just be a
    // pass-through and not telegraph any errors onto the callback.
    cache_store: function (name, source, cb) {
        var key = 'kumascript:loader:' + ks_utils.md5(name);
        var timeout = this.options.cache_timeout || 3600;
        var cache_source = (new Buffer(source,'utf8')).toString('base64');
        this.memcached.set(key, cache_source, timeout, function (err, result) {
            return (err) ? cb(err, null) : cb(null, source);
        });
    },

    // By default, a loader doesn't know what macros are available
    can_list_macros: false,

    // #### macros_data()
    //
    // Return data about the macros known to this loader
    macros_data: function () {
        return {
            'can_list_macros': this.can_list_macros,
            'macros': this.macros_details(),
        };
    },

    // ### macros_details()
    //
    // Return details of macros known by this loader.
    macros_details: function () {
        // By default, loader doesn't know what macros are available
        return [];
    }
});

// ### FileLoader
var FileLoader = ks_utils.Class(BaseLoader, {

    default_options: {
        // Root directory for templates (relative to the repo dir).
        root_dir: 'macros',
        // Class used to render templates
        template_class: ks_templates.EJSTemplate
    },

    initialize: function (options) {
        // Build the template map, associating lowercase template names
        // with their paths on the filesystem.

        var dirs = [],
            dir = null,
            duplicates = {},
            template_map = this.template_map = {},
            macro_dir = this.macro_dir = path.resolve(
                __dirname,
                '..', '..',
                this.options.root_dir
            );

        function getTemplateName(fp) {
            // Returns the lowercase basename, without the extension.
            return path.parse(fp).name.toLowerCase();
        }

        function processFilename(fn) {
            // If the given filename is a directory, push it onto
            // the queue, otherwise consider it a template.
            var fp = path.join(dir, fn);
            if (fs.statSync(fp).isDirectory()) {
                dirs.push(fp);
            } else {
                var name = getTemplateName(fn);
                if (_.has(template_map, name)) {
                    // Keep track of all duplicates and throw error later.
                    if (!(_.has(duplicates, name))) {
                        duplicates[name] = [template_map[name]];
                    }
                    duplicates[name].push(fp);
                } else {
                    template_map[name] = fp;
                }
            }
        }

        dirs.push(macro_dir);

        // Walk the directory tree under the specified root directory.
        while (dirs.length > 0) {
            dir = dirs.shift();
            fs.readdirSync(dir).forEach(processFilename);
        }

        if (_.isEmpty(template_map)) {
            // Let's throw an error if no macros could be discovered, since
            // for now this is the only time we check and this loader is
            // useless if there are no macros.
            throw new Error(
                `no macros could be found in "${macro_dir}"`
            );
        }

        if (!(_.isEmpty(duplicates))) {
            // Duplicate template names
            var msg = "duplicate macros:";
            _.keys(duplicates).forEach(function(name) {
                msg += "\n" + name + ": " + duplicates[name].join(", ");
            });
            throw new Error(msg);
        }
    },

    // Don't use caching, as we're loading from the local filesystem.
    get: function (name, cb) {
        var $this = this;
        try {
            // Replace all colons in macro names with dashes.
            var lc_name = name.replace(/:/g, '-').toLowerCase();
            if (_.has(this.template_map, lc_name)) {
                var fp = this.template_map[lc_name];
                fs.readFile(fp, 'utf8', function (err, source) {
                    if (err || !source) {
                        cb(err, null, false);
                    } else {
                        $this.compile(source, cb, false);
                    }
                });
            } else {
                cb(new Error('unable to load: ' + name), null, false);
            }
        } catch (e) {
            cb(e, null, false);
        }
    },

    // FileLoader does have a list of all known macros
    can_list_macros: true,

    // Returns list of macro detail objects with these key: value pairs:
    //  name: the mixed-case name of the macro
    //  filename: path to filename in macros folder
    macros_details: function() {
        var macros = [],
            macro_dir = this.macro_dir + path.sep;
        for (var lc_name in this.template_map) {
            var fp = this.template_map[lc_name],
                name = path.parse(fp).name,
                short_path = fp.slice(macro_dir.length).replace(path.sep, '/');
            macros.push({
                'name': name,
                'filename': short_path
            });
        }
        return macros;
    }
});

// ### Exported public API
module.exports = {
    BaseLoader: BaseLoader,
    FileLoader: FileLoader,
};
<|endoftext|>


<commit_before>const languageConstructs = {
  'if': true,
  'then': true,
  'else': true,
  'const': true,
  'let': true,
  'where': true,
  'in': true,
  'push': true,
  'pop': true,
  'shift': true,
  'unshift': true,
  'true': true,
  'false': true,
  'null': true
}

module.exports = languageConstructs
<commit_msg>Add `do`<commit_after>const languageConstructs = {
  'if': true,
  'then': true,
  'else': true,
  'const': true,
  'let': true,
  'where': true,
  'in': true,
  'push': true,
  'pop': true,
  'shift': true,
  'unshift': true,
  'true': true,
  'false': true,
  'null': true,
  'do': true
}

module.exports = languageConstructs
<|endoftext|>


<commit_before>/**
 * dummyLayout.js
 *
 * This takes an existing graph and lays it out simply in O(n*m) time, where n
 * is the number of vertices in the top level and m is the maximal number of
 * ports that any of those vertices has.
 *
 * @author John O'Brien.
 */

var CardinalPortPosition = require('./../model/cardinalPortPosition');
var CardinalDirection = require('./../enum/cardinalDirection');

// Lay out an existing graph instance.

function layOutGraph(graph) {
  var vertices = graph.get('vertices');
  //loop for laying out the vertices
  var a = 0;
  //console.log(vertices.length);
  for(var i = 0; i < Math.sqrt(vertices.length) && a < vertices.length - 1; i++){
	for(var g = 0; g < Math.sqrt(vertices.length) && a < vertices.length - 1; g++){
      if (vertices[i].get('isPinned') === false) {
        vertices[a].set('position', {x: i * 100, y: g * 100});

        /*var ins = vertices[i].get('inputs');
        var outs = vertices[i].get('outputs');

        var position;

        for(var iterator = 0; iterator < ins.length; iterator++) {
          position = new CardinalPortPosition({
            direction: CardinalDirection.EAST,
            percentage: 0.5
          });
          ins[iterator].set('position', position);
          }

          for(iterator = 0; iterator < outs.length; iterator++) {
          position = new CardinalPortPosition({
          direction: CardinalDirection.WEST,
          percentage: 0.5
          });
          outs[iterator].set('position', position);
        }*/
      }
      //console.log(a);
      a++;
	}
  }
}
module.exports = layOutGraph;
<commit_msg>rewrote dummy<commit_after>/**
 * dummyLayout.js
 *
 * This takes an existing graph and lays it out simply in O(n*m) time, where n
 * is the number of vertices in the top level and m is the maximal number of
 * ports that any of those vertices has.
 *
 * @author John O'Brien.
 */

var CardinalPortPosition = require('./../model/cardinalPortPosition');
var CardinalDirection = require('./../enum/cardinalDirection');

// Lay out an existing graph instance.

function layOutGraph(graph) {
  var vertices = graph.get('vertices');
  //loop for laying out the vertices
  var a = 0;
  //console.log(vertices.length);
  /*for(var i = 0; i < Math.sqrt(vertices.length) && a < vertices.length - 1; i++){
	for(var g = 0; g < Math.sqrt(vertices.length) && a < vertices.length - 1; g++){
      if (vertices[i].get('isPinned') === false) {
        vertices[a].set('position', {x: i * 100, y: g * 100});

        /*var ins = vertices[i].get('inputs');
        var outs = vertices[i].get('outputs');

        var position;

        for(var iterator = 0; iterator < ins.length; iterator++) {
          position = new CardinalPortPosition({
            direction: CardinalDirection.EAST,
            percentage: 0.5
          });
          ins[iterator].set('position', position);
          }

          for(iterator = 0; iterator < outs.length; iterator++) {
          position = new CardinalPortPosition({
          direction: CardinalDirection.WEST,
          percentage: 0.5
          });
          outs[iterator].set('position', position);
        }
      }
      //console.log(a);
      a++;
	}*/
	var sidelength = Math.round(Math.sqrt(vertices.length));
	for(var i=0; i< vertices.length; i++){
		if(vertices[i].get('isPinned') ==false){
			 vertices[i].set('position', {x: Math.floor(i / height) * 100, y: (i % sidelength)  * 100});
		}
	}
  }
}
module.exports = layOutGraph;
<|endoftext|>


<commit_before>module.exports = {
    extractId: function(href) {
        return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' )  // Remove protocol & domain
            .replace(/[\?\&]livereload=\w+/, '' )  // Remove LiveReload cachebuster
            .replace(/^\//,                  '' )  // Remove root /
            .replace(/\.[a-zA-Z]+$/,         '' )  // Remove simple extension
            .replace(/[^\.\w-]+/g,           '-')  // Replace illegal characters
            .replace(/\./g,                  ':'); // Replace dots with colons(for valid id)
    },
    addDataAttr: function(options, tag) {
        for (var opt in tag.dataset) {
            if (tag.dataset.hasOwnProperty(opt)) {
                if (opt === "env" || opt === "dumpLineNumbers" || opt === "rootpath" || opt === "errorReporting") {
                    options[opt] = tag.dataset[opt];
                } else {
                    try {
                        options[opt] = JSON.parse(tag.dataset[opt]);
                    }
                    catch(_) {}
                }
            }
        }
    }
};
<commit_msg>Fix jscs violations, happening on travis<commit_after>module.exports = {
    extractId: function(href) {
        return href.replace(/^[a-z-]+:\/+?[^\/]+/, '')  // Remove protocol & domain
            .replace(/[\?\&]livereload=\w+/, '')        // Remove LiveReload cachebuster
            .replace(/^\//, '')                         // Remove root /
            .replace(/\.[a-zA-Z]+$/, '')                // Remove simple extension
            .replace(/[^\.\w-]+/g, '-')                 // Replace illegal characters
            .replace(/\./g, ':');                       // Replace dots with colons(for valid id)
    },
    addDataAttr: function(options, tag) {
        for (var opt in tag.dataset) {
            if (tag.dataset.hasOwnProperty(opt)) {
                if (opt === "env" || opt === "dumpLineNumbers" || opt === "rootpath" || opt === "errorReporting") {
                    options[opt] = tag.dataset[opt];
                } else {
                    try {
                        options[opt] = JSON.parse(tag.dataset[opt]);
                    }
                    catch(_) {}
                }
            }
        }
    }
};
<|endoftext|>


<commit_before>/* global atom */

// Dependencies
var helpers = require('atom-linter')
var linter = require('./utils/linter')

module.exports = function () {
  var self = this

  return {
    grammarScopes: self.scope,
    scope: 'file',
    lintOnFly: true,
    regex:
      '^.*?:(?<line>[0-9]+):(?<col>[0-9]+):' +
      '((?<message>.*?(?=\\())((?<error>.+undefined.*?)|(?<warning>.*?)))$',
    lint: function (textEditor) {
      var filePath = textEditor.getPath()
      var fileContent = textEditor.getText()
      var settings = self.cache.get(filePath)

      // Sane check
      if (!settings || !settings.args || !settings.styleObj) {
        atom.notifications.addWarning('Something went wrong internally.', {
          detail: 'No sweat, just re-open this file and this annoying warning shouldn\'t appear anymore',
          dismissable: true
        })

        return []
      }

      var boundLinter = linter.bind({
        regex: this.regex,
        file: {
          path: filePath,
          content: fileContent
        }
      })

      var execPath = settings.styleObj.execPath
      var args = settings.args.concat(['-'])
      var opts = { stdin: fileContent }

      return helpers.execNode(execPath, args, opts)
      .then(boundLinter)
      .catch(function (err) {
        atom.notifications.addError('Couldn\'t execute linter bin', {
          detail: err.message,
          dismissable: true
        })
      })
    }
  }
}
<commit_msg>pass throwOnStdErr option to false<commit_after>/* global atom */

// Dependencies
var helpers = require('atom-linter')
var linter = require('./utils/linter')

module.exports = function () {
  var self = this

  return {
    grammarScopes: self.scope,
    scope: 'file',
    lintOnFly: true,
    regex:
      '^.*?:(?<line>[0-9]+):(?<col>[0-9]+):' +
      '((?<message>.*?(?=\\())((?<error>.+undefined.*?)|(?<warning>.*?)))$',
    lint: function (textEditor) {
      var filePath = textEditor.getPath()
      var fileContent = textEditor.getText()
      var settings = self.cache.get(filePath)

      // Sane check
      if (!settings || !settings.args || !settings.styleObj) {
        atom.notifications.addWarning('Something went wrong internally.', {
          detail: 'No sweat, just re-open this file and this annoying warning shouldn\'t appear anymore',
          dismissable: true
        })

        return []
      }

      var boundLinter = linter.bind({
        regex: this.regex,
        file: {
          path: filePath,
          content: fileContent
        }
      })

      var execPath = settings.styleObj.execPath
      var args = settings.args.concat(['-'])
      var opts = { stdin: fileContent, throwOnStdErr: false }

      return helpers.execNode(execPath, args, opts)
      .then(boundLinter)
      .catch(function (err) {
        atom.notifications.addError('Couldn\'t execute linter bin', {
          detail: err.message,
          dismissable: true
        })
      })
    }
  }
}
<|endoftext|>


<commit_before>var path = require('path');
var alias = require('../util/alias');
var Command = require('cli-define').Command;
var ErrorDefinition = require('cli-error').ErrorDefinition;

function getModulePath(cmd) {
  if(cmd.parent() === this) {
    return cmd.key();
  }else{
    // build name from all parent commands
    var p = cmd.parent();
    var list = [];
    while(p !== this) {
      list.push(p.key());
      p = p.parent();
    }
    list.reverse();
    list.push(cmd.key());
    return path.join.apply(path, list);
  }
}

// lazily load a command action definition from a module
function lazy(arg) {
  var conf = this.configure();
  var action = arg.action();
  if(!conf.command.dir || action !== undefined) return false;
  var file = path.join(conf.command.dir, getModulePath.call(this, arg))
  try{
    action = require(file);
    if(typeof action !== 'function') {
      return this.raise(this.errors.ECMD_TYPE, [file]);
    }
    arg.action(action);
  }catch(e) {
    return this.raise(this.errors.ECMD_REQUIRE, [file, e.message], e);
  }
}

function invoke(obj, req, next) {
  var keys = Object.keys(obj);
  if(!keys.length) return next();
  var i, key, arg;
  for(i = 0;i < keys.length;i++) {
    key = keys[i];
    arg = obj[key];
    if(key === req.command.args[0]) {
      req.command.parents = req.command.parents || [];
      req.command.parents.push(req.command.name);
      req.command.name = key;
      req.command.args.shift();
      if(typeof arg === 'function') {
        return arg.call(this, req.command, req, next);
      }else if(arg instanceof Command) {
        lazy.call(this, arg);
        var action = arg.action();
        if(typeof action === 'function') {
          return action.call(this, req.command, req, next);
        }
      }
    }
  }
  return next();
}

/**
 *  Searches the raw arguments looking for the first argument
 *  that matches a known command and invokes an action associated
 *  with the command.
 */
module.exports = function() {
  return function command(req, next) {
    if(!arguments.length) return;
    var scope = this, conf = this.configure();
    function callback(obj) {
      //console.dir('callback');
      //console.dir(arguments);

      // proceed to next middleware
      if(!arguments.length) {
        return next();
      // stop middleware execution
      }else if((obj instanceof Error)
        || (obj instanceof ErrorDefinition)
        || typeof obj === 'string') {
        // error raised
        return next.apply(null, arguments);
      }else if(obj === null || typeof obj === 'boolean') {
        return next(obj);
      // execute subcommand
      }else if(obj
        && typeof obj === 'object' && !Array.isArray(obj)) {
        // TODO: allow passing of subcommand object map
        // TODO: and subcommands associated with the command
        return invoke.call(scope, obj, req, callback);
      }else if(Array.isArray(obj)) {
        return next(scope.errors.ECMD_ARRAY);
      }
      // unknown response
      next.apply(null, arguments);
    }
    var i, raw = req.unparsed.slice(0), action, cmd, arg, ind, file;
    for(i = 0;i < raw.length;i++) {
      cmd = raw[i]; arg = alias(cmd, this._commands);
      //console.log('testing for command with %s', cmd);
      if(arg) {
        raw.splice(i, 1);
        lazy.call(this, arg);
        action = arg.action();
        if(typeof action == 'function') {
          ind = req.args.indexOf(cmd);
          //req.args.splice(ind, 1);
          req.command = {name: cmd, cmd: arg, args: raw};
          req.command.validate = function(cb) {
            var subcommands = this.commands();
            var sub = raw[0], k, names;
            for(k in subcommands) {
              names = subcommands[k].names();
              if(~names.indexOf(sub)) return cb();
            }
            cb(scope.errors.EUNKNOWN_SUBCOMMAND, [sub, cmd]);
          }.bind(arg);
          //console.dir(req.command);
          action.call(this, req.command, req, callback);
          return false;
        }
      }
    }
    if(conf && conf.command.required && raw.length) {
      return next(scope.errors.EUNKNOWN_COMMAND, [raw[0]]);
    }
    next();
  }
}
<commit_msg>Support invoking subcommands by alias name.<commit_after>var path = require('path');
var alias = require('../util/alias');
var Command = require('cli-define').Command;
var ErrorDefinition = require('cli-error').ErrorDefinition;

function getModulePath(cmd) {
  if(cmd.parent() === this) {
    return cmd.key();
  }else{
    // build name from all parent commands
    var p = cmd.parent();
    var list = [];
    while(p !== this) {
      list.push(p.key());
      p = p.parent();
    }
    list.reverse();
    list.push(cmd.key());
    return path.join.apply(path, list);
  }
}

// lazily load a command action definition from a module
function lazy(arg) {
  var conf = this.configure();
  var action = arg.action();
  if(!conf.command.dir || action !== undefined) return false;
  var file = path.join(conf.command.dir, getModulePath.call(this, arg))
  try{
    action = require(file);
    if(typeof action !== 'function') {
      return this.raise(this.errors.ECMD_TYPE, [file]);
    }
    arg.action(action);
  }catch(e) {
    return this.raise(this.errors.ECMD_REQUIRE, [file, e.message], e);
  }
}

function invoke(obj, req, next) {
  var keys = Object.keys(obj);
  if(!keys.length) return next();
  var i, key, arg, names;
  for(i = 0;i < keys.length;i++) {
    key = keys[i];
    arg = obj[key];
    names = arg.names();
    //console.log('invoke subcommand %s, %s', key, arg.toString(null));
    if(~names.indexOf(key)) {
      req.command.parents = req.command.parents || [];
      req.command.parents.push(req.command.name);
      req.command.name = key;
      req.command.args.shift();
      if(typeof arg === 'function') {
        return arg.call(this, req.command, req, next);
      }else if(arg instanceof Command) {
        lazy.call(this, arg);
        var action = arg.action();
        if(typeof action === 'function') {
          return action.call(this, req.command, req, next);
        }
      }
    }
  }
  return next();
}

/**
 *  Searches the raw arguments looking for the first argument
 *  that matches a known command and invokes an action associated
 *  with the command.
 */
module.exports = function() {
  return function command(req, next) {
    if(!arguments.length) return;
    var scope = this, conf = this.configure();
    function callback(obj) {
      //console.dir('callback');
      //console.dir(arguments);

      // proceed to next middleware
      if(!arguments.length) {
        return next();
      // stop middleware execution
      }else if((obj instanceof Error)
        || (obj instanceof ErrorDefinition)
        || typeof obj === 'string') {
        // error raised
        return next.apply(null, arguments);
      }else if(obj === null || typeof obj === 'boolean') {
        return next(obj);
      // execute subcommand
      }else if(obj
        && typeof obj === 'object' && !Array.isArray(obj)) {
        // TODO: allow passing of subcommand object map
        // TODO: and subcommands associated with the command
        return invoke.call(scope, obj, req, callback);
      }else if(Array.isArray(obj)) {
        return next(scope.errors.ECMD_ARRAY);
      }
      // unknown response
      next.apply(null, arguments);
    }
    var i, raw = req.unparsed.slice(0), action, cmd, arg, ind, file;
    for(i = 0;i < raw.length;i++) {
      cmd = raw[i]; arg = alias(cmd, this._commands);
      //console.log('testing for command with %s', cmd);
      if(arg) {
        raw.splice(i, 1);
        lazy.call(this, arg);
        action = arg.action();
        if(typeof action == 'function') {
          ind = req.args.indexOf(cmd);
          //req.args.splice(ind, 1);
          req.command = {name: cmd, cmd: arg, args: raw};
          req.command.validate = function(cb) {
            var subcommands = this.commands();
            var sub = raw[0], k, names;
            for(k in subcommands) {
              names = subcommands[k].names();
              if(~names.indexOf(sub)) return cb();
            }
            cb(scope.errors.EUNKNOWN_SUBCOMMAND, [sub, cmd]);
          }.bind(arg);
          //console.dir(req.command);
          action.call(this, req.command, req, callback);
          return false;
        }
      }
    }
    if(conf && conf.command.required && raw.length) {
      return next(scope.errors.EUNKNOWN_COMMAND, [raw[0]]);
    }
    next();
  }
}
<|endoftext|>


<commit_before>/**
 * Middleware to initialise a standard API response.
 *
 * Adds `res.apiResonse` and `res.apiError` methods.
 *
 * ####Example:
 *
 *     app.all('/api*', initAPI);
 *
 * @param {app.request} req
 * @param {app.response} res
 * @param {function} next
 * @api public
 */

// The exported function returns a closure that retains
// a reference to the keystone instance, so it can be
// passed as middeware to the express app.

exports = module.exports = function(keystone) {
	return function initAPI(req, res, next) {

		res.apiResponse = function(status) {
			if (req.query.callback)
				res.jsonp(status);
			else
				res.json(status);
		};

		res.apiError = function(key, err, msg) {
			msg = msg || 'Error';
			key = key || 'unknown error';
			msg += ' (' + key + ')';
			if (keystone.get('logger')) {
				console.log(msg + (err ? ':' : ''));
				if (err) {
					console.log(err);
				}
			}
			res.status(500);
			res.apiResponse({ error: key || 'error', detail: err });
		};

		next();
	};
};
<commit_msg>You specify error code to send on res.apiError ( added as a parameter  )<commit_after>/**
 * Middleware to initialise a standard API response.
 *
 * Adds `res.apiResonse` and `res.apiError` methods.
 *
 * ####Example:
 *
 *     app.all('/api*', initAPI);
 *
 * @param {app.request} req
 * @param {app.response} res
 * @param {function} next
 * @api public
 */

// The exported function returns a closure that retains
// a reference to the keystone instance, so it can be
// passed as middeware to the express app.

exports = module.exports = function(keystone) {
	return function initAPI(req, res, next) {

		res.apiResponse = function(status) {
			if (req.query.callback)
				res.jsonp(status);
			else
				res.json(status);
		};

		res.apiError = function(key, err, msg, code) {
			msg = msg || 'Error';
			key = key || 'unknown error';
			msg += ' (' + key + ')';
			if (keystone.get('logger')) {
				console.log(msg + (err ? ':' : ''));
				if (err) {
					console.log(err);
				}
			}
			res.status(code || 500);
			res.apiResponse({ error: key || 'error', detail: err });
		};

		next();
	};
};
<|endoftext|>


<commit_before>/*
 * DealPort
 * Copyright (c) 2015  DealPort B.V.
 *
 * This file is part of DealPort
 *
 * DealPort is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * DealPort is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with DealPort.  If not, see <http://www.gnu.org/licenses/>.
 *
 * In addition, the following supplemental terms apply, based on section 7 of
 * the GNU Affero General Public License (version 3):
 * a) Preservation of all legal notices and author attributions
 */

'use strict';

var P = require('bluebird');
var merge = require('merge');
var equal = require('deep-equal');
var ObjectID = require('mongodb').ObjectID;

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

var jobClassificationData = require('../resource/static/job-classification.json').value;
var salaryRangeData = require('../resource/static/salary-range.json').value.euro;
var jobRoleData = require('../resource/static/job-role.json').value;

/**
 *
 * Looks like in mongo:
 *
 * {
 *  "_id" : "object id hex string",
 *  "company": "object id hex of company",
 *  "name": "Used in the url, unique in combination with 'company'",
 *  "title": "Single line text",
 *  "teaser": "Single line text",
 *  "description": "Multi line text",
 *  "jobClassification": "enum value, see job-classification.json",
 *  "salaryRange": { see salary-range.json },
 *  "primaryRole": "enum value, see job-role.json",
 *  "secondaryRole": "enum value, see job-role.json",
 *  "skills" : [
 *      "javascript",
 *      "less"
 *  ],
 *  "private" : {
 *      "editableByUser" : [
 *          "object id hex"
 *      ],
 *      "createdByUser" : ObjectId(...)
 *  }
 * }
 *
 *
 * Virtual keys:
 *
 * editableByCurrentUser
 *
 * @param modelIndex
 * @constructor
 */

function VacancyModel(modelIndex)
{
        Model.call(this, modelIndex);
}

module.exports = VacancyModel;
require('inherits')(VacancyModel, Model);

VacancyModel.prototype.cleanupName = function(name)
{
        if (!name)
        {
                return '';
        }

        name = name.toString();
        name = name.replace(/[\x00-\x1F]+/g, ''); // ascii control chars
        name = name.trim();

        // replace spaces with dashes
        name = name.replace(/\s+/g, '-');

        name = name.toLowerCase();
        return name;
};

VacancyModel.prototype.isEditableByUser = function(vacancy, user)
{
        var ret = false;

        if (user &&
            user.private &&
            user.private.allPermissions)
        {
                return true;
        }

        if (user &&
            vacancy.private &&
            vacancy.private.editableByUser)
        {
                vacancy.private.editableByUser.forEach(function(userId)
                {
                        if (user._id === userId)
                        {
                                ret = true;
                        }
                });
        }

        return ret;
};

VacancyModel.prototype.isVisibleToUser = function(vacancy, user)
{
        if (vacancy.visible || vacancy.visible === undefined)
        {
                return true;
        }

        return this.isEditableByUser(vacancy, user);
};

VacancyModel.prototype.addVirtualFields = function(id, data, user)
{
        data._id = id;

        data.editableByCurrentUser = this.isEditableByUser(data, user);

        if (data.visible === undefined) { data.visible = true; }
};

VacancyModel.prototype.filterDocument = P.method(function(docId, data, user)
{
        this.addVirtualFields(docId, data.data, user);
        data.data.private = undefined; // delete is slow

});

VacancyModel.prototype.validateReadingAllowed = P.method(function(docId, user)
{
});

VacancyModel.prototype._validateComponent = function(comp)
{
        if (!Array.isArray(comp.p))
        {
                console.error(comp);
                throw Error('Invalid operation, missing path');
        }

        function assert(cond)
        {
                if (!cond)
                {
                        throw Error('Invalid operation for path ' + comp.p.join(', '));
                }
        }

        // todo: make this stuff more generic
        var cleanComp = {p : comp.p};

        // allow complete replacements for these keys
        if (comp.p.length === 1 && 'oi' in comp)
        {
                switch (comp.p[0])
                {
                        case 'title':
                        case 'teaser':
                        case 'description':
                        case 'location':
                                if ('oi' in comp)
                                {
                                        assert(typeof comp.oi === 'string');
                                        cleanComp.oi = comp.oi;
                                }

                                if ('od' in comp)
                                {
                                        assert(typeof comp.od === 'string');
                                        cleanComp.od = comp.od;

                                        if (!('oi' in cleanComp))
                                        {
                                                // make sure the key is not deleted (turn it into a replacement)
                                                cleanComp.oi = '';
                                        }
                                }
                                break;
                        case 'skills':
                                if ('oi' in comp)
                                {
                                        assert(Array.isArray(comp.oi));
                                        comp.oi.forEach(function(item){ assert(typeof item === 'string'); });

                                        cleanComp.oi = comp.oi;
                                }

                                if ('od' in comp)
                                {
                                        assert(Array.isArray(comp.od));
                                        comp.od.forEach(function(item){ assert(typeof item === 'string'); });

                                        cleanComp.od = comp.od;

                                        if (!('oi' in cleanComp))
                                        {
                                                // make sure the key is not deleted
                                                cleanComp.oi = false;
                                        }
                                }
                                break;

                        case 'jobClassification':
                        case 'salaryRange':
                        case 'primaryRole':
                        case 'secondaryRole':
                                var allowedValues = null;
                                switch (comp.p[0])
                                {
                                        case 'jobClassification':
                                                allowedValues = jobClassificationData;
                                                break;
                                        case 'salaryRange':
                                                allowedValues = salaryRangeData;
                                                break;
                                        case 'primaryRole':
                                        case 'secondaryRole':
                                                allowedValues = jobRoleData;
                                                break;
                                }

                                if ('oi' in comp)
                                {
                                        allowedValues.forEach(function(entry)
                                        {
                                                if (equal(entry.value, comp.oi, {strict: true}))
                                                {
                                                        cleanComp.oi = comp.oi;
                                                }
                                        });
                                }

                                if ('od' in comp)
                                {
                                        allowedValues.forEach(function(entry)
                                        {
                                                if (equal(entry.value, comp.od, {strict: true}))
                                                {
                                                        cleanComp.od = comp.od;
                                                }
                                        });

                                        if (!('oi' in cleanComp))
                                        {
                                                // make sure the key is not deleted (turn it into a replacement)
                                                cleanComp.oi = allowedValues[0].value;
                                        }
                                }
                                break;
                }

                return cleanComp;
        }

        switch(comp.p[0])
        {
                case 'title': // strings
                case 'teaser':
                case 'description':
                case 'location':
                        assert(comp.p.length === 2);
                        assert(Number.isFinite(comp[1]));

                        if ('si' in comp)
                        {
                                assert(typeof comp.si === 'string');
                                cleanComp.si = comp.si;
                        }

                        if ('sd' in comp)
                        {
                                assert(typeof comp.sd === 'string');
                                cleanComp.sd = comp.si;
                        }

                        break;
                case 'skills':

                        if ('li' in comp)
                        {
                                assert(comp.p.length === 2);
                                assert(Number.isFinite(comp[1]));
                                assert(typeof comp.li === 'string');
                                cleanComp.li = comp.li;
                        }

                        if ('ld' in comp)
                        {
                                assert(comp.p.length === 2);
                                assert(Number.isFinite(comp[1]));
                                assert(typeof comp.ld === 'string');
                                cleanComp.ld = comp.ld;
                        }

                        if ('si' in comp)
                        {
                                assert(comp.p.length === 3);
                                assert(Number.isFinite(comp[1]));
                                assert(typeof comp.si === 'string');
                                cleanComp.si = comp.si;
                        }

                        if ('sd' in comp)
                        {
                                assert(comp.p.length === 3);
                                assert(Number.isFinite(comp[1]));
                                assert(typeof comp.si === 'string');
                                cleanComp.sd = comp.si;
                        }
        }

        return cleanComp;
};

VacancyModel.prototype.validateOperation = P.method(function(docId, opData, user)
{
        return P.using(this.modelIndex.acquire(), function(db)
        {
                return db.vacancy.findOneAsync({_id: docId}, {_id: 1, private: 1})
                .bind(this)
                .then(function(vacancyPrivateOnly)
                {
                        if (!vacancyPrivateOnly)
                        {
                                throw Error('Invalid vacancy id: ' + docId);
                        }

                        if (!this.isEditableByUser(vacancyPrivateOnly, user))
                        {
                                throw Error('You are not allowed to edit the vacancy: ' + docId);
                        }

                        opData.op.forEach(function(comp, index)
                        {
                                opData.op[index] = this._validateComponent(comp);
                        }, this);

                        return true;
                });
        }.bind(this));
});

VacancyModel.prototype.afterSubmit = P.method(function(docId, opData, snapshot, user)
{
        var titleChanged = false;

        opData.op.forEach(function(comp)
        {
                if (comp.p &&
                    comp.p[0] === 'title')
                {
                        titleChanged = true;
                }
        }, this);

        if (titleChanged)
        {
                return this.findUniqueName(snapshot.data.company, snapshot.data.title, docId)
                .bind(this)
                .then(function(uniqueName)
                {
                        if (!uniqueName)
                        {
                                uniqueName = docId; // fallback
                        }

                        return this.modelIndex.livedb.submitAsync('vacancy', docId, {
                                op: [
                                        {
                                                p : ['name'],
                                                od: snapshot.data.name,
                                                oi: uniqueName
                                        }
                                ],
                                v : snapshot.v
                        });
                });
        }

        return null;
});

VacancyModel.prototype.findUniqueName = P.method(function(companyId, title, findForVacancyId)
{
        var name = this.cleanupName(title);

        return P.using(this.modelIndex.acquire(), function(db)
        {
                var attempts = [];
                for (var i = 1; i <= 25; ++i)
                {
                        var nameAttempt = name + (i > 1 ? '-' + i : '');

                        if (/^[0-9a-zA-Z]{24}$/.test(nameAttempt))
                        {
                                // ObjectIDs are reserved
                                nameAttempt += 'x';
                        }

                        attempts.push(P.join(
                                nameAttempt,
                                db.vacancy.findOneAsync({company: companyId, name: nameAttempt}, {_id: 1})
                        ));
                }

                return P.all(attempts)
                .then(function(attempts)
                {
                        for (var i = 0; i < attempts.length; ++i)
                        {
                                var attempt = attempts[i];
                                var foundVacancyId = attempt[1] && attempt[1]._id;

                                if (!foundVacancyId ||
                                    foundVacancyId === findForVacancyId)
                                {
                                        return attempt[0];
                                }
                        }

                        // give up
                        return null;
                });
        });
});

var vacancyDefaultFields = {
        title: '{ New vacancy! }',
        name: 'new-vacancy',
        company: null,
        teaser: 'A short teaser for your vacancy',
        description: 'Describe your vacancy in detail here',
        skills: ['list some', 'skills', 'here'],
        location: 'Rotterdam',
        jobClassification: 'temporary',
        salaryRange: { 'min':  40001, 'max':  80000 },
        primaryRole: 'software-engineer',
        secondaryRole: '',
        private: {
                editableByUser: [],
                createdByUser: null
        }
};

VacancyModel.prototype.newVacancy = P.method(function(initialFields)
{
        var fields = merge.recursive(vacancyDefaultFields, initialFields);
        var id = new ObjectID().toString();

        if (!fields.company)
        {
                throw Error('Company field is required');
        }

        return this.findUniqueName(fields.company, fields.title)
        .bind(this)
        .then(function(uniqueName)
        {
                fields.name = uniqueName || id;

                return this.modelIndex.livedb.submitAsync('vacancy', id, {
                        create: {
                                type: 'json0',
                                data: fields
                        }
                })
                .bind(this)
                .spread(function(version, transformedByOps, snapshot)
                {
                        this.addVirtualFields(id, snapshot.data, null);
                        return snapshot.data;
                });
        });
});

VacancyModel.prototype.removeVacancy = function(id)
{
        var livedb = this.modelIndex.livedb;
        return livedb.submitAsync('vacancy', id, {del: true});
};<commit_msg>Better defaults for freshly created vacancies<commit_after>/*
 * DealPort
 * Copyright (c) 2015  DealPort B.V.
 *
 * This file is part of DealPort
 *
 * DealPort is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * DealPort is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with DealPort.  If not, see <http://www.gnu.org/licenses/>.
 *
 * In addition, the following supplemental terms apply, based on section 7 of
 * the GNU Affero General Public License (version 3):
 * a) Preservation of all legal notices and author attributions
 */

'use strict';

var P = require('bluebird');
var merge = require('merge');
var equal = require('deep-equal');
var ObjectID = require('mongodb').ObjectID;

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

var jobClassificationData = require('../resource/static/job-classification.json').value;
var salaryRangeData = require('../resource/static/salary-range.json').value.euro;
var jobRoleData = require('../resource/static/job-role.json').value;

/**
 *
 * Looks like in mongo:
 *
 * {
 *  "_id" : "object id hex string",
 *  "company": "object id hex of company",
 *  "name": "Used in the url, unique in combination with 'company'",
 *  "title": "Single line text",
 *  "teaser": "Single line text",
 *  "description": "Multi line text",
 *  "jobClassification": "enum value, see job-classification.json",
 *  "salaryRange": { see salary-range.json },
 *  "primaryRole": "enum value, see job-role.json",
 *  "secondaryRole": "enum value, see job-role.json",
 *  "skills" : [
 *      "javascript",
 *      "less"
 *  ],
 *  "private" : {
 *      "editableByUser" : [
 *          "object id hex"
 *      ],
 *      "createdByUser" : ObjectId(...)
 *  }
 * }
 *
 *
 * Virtual keys:
 *
 * editableByCurrentUser
 *
 * @param modelIndex
 * @constructor
 */

function VacancyModel(modelIndex)
{
        Model.call(this, modelIndex);
}

module.exports = VacancyModel;
require('inherits')(VacancyModel, Model);

VacancyModel.prototype.cleanupName = function(name)
{
        if (!name)
        {
                return '';
        }

        name = name.toString();
        name = name.replace(/[\x00-\x1F]+/g, ''); // ascii control chars
        name = name.trim();

        // replace spaces with dashes
        name = name.replace(/\s+/g, '-');

        name = name.toLowerCase();
        return name;
};

VacancyModel.prototype.isEditableByUser = function(vacancy, user)
{
        var ret = false;

        if (user &&
            user.private &&
            user.private.allPermissions)
        {
                return true;
        }

        if (user &&
            vacancy.private &&
            vacancy.private.editableByUser)
        {
                vacancy.private.editableByUser.forEach(function(userId)
                {
                        if (user._id === userId)
                        {
                                ret = true;
                        }
                });
        }

        return ret;
};

VacancyModel.prototype.isVisibleToUser = function(vacancy, user)
{
        if (vacancy.visible || vacancy.visible === undefined)
        {
                return true;
        }

        return this.isEditableByUser(vacancy, user);
};

VacancyModel.prototype.addVirtualFields = function(id, data, user)
{
        data._id = id;

        data.editableByCurrentUser = this.isEditableByUser(data, user);

        if (data.visible === undefined) { data.visible = true; }
};

VacancyModel.prototype.filterDocument = P.method(function(docId, data, user)
{
        this.addVirtualFields(docId, data.data, user);
        data.data.private = undefined; // delete is slow

});

VacancyModel.prototype.validateReadingAllowed = P.method(function(docId, user)
{
});

VacancyModel.prototype._validateComponent = function(comp)
{
        if (!Array.isArray(comp.p))
        {
                console.error(comp);
                throw Error('Invalid operation, missing path');
        }

        function assert(cond)
        {
                if (!cond)
                {
                        throw Error('Invalid operation for path ' + comp.p.join(', '));
                }
        }

        // todo: make this stuff more generic
        var cleanComp = {p : comp.p};

        // allow complete replacements for these keys
        if (comp.p.length === 1 && 'oi' in comp)
        {
                switch (comp.p[0])
                {
                        case 'title':
                        case 'teaser':
                        case 'description':
                        case 'location':
                                if ('oi' in comp)
                                {
                                        assert(typeof comp.oi === 'string');
                                        cleanComp.oi = comp.oi;
                                }

                                if ('od' in comp)
                                {
                                        assert(typeof comp.od === 'string');
                                        cleanComp.od = comp.od;

                                        if (!('oi' in cleanComp))
                                        {
                                                // make sure the key is not deleted (turn it into a replacement)
                                                cleanComp.oi = '';
                                        }
                                }
                                break;
                        case 'skills':
                                if ('oi' in comp)
                                {
                                        assert(Array.isArray(comp.oi));
                                        comp.oi.forEach(function(item){ assert(typeof item === 'string'); });

                                        cleanComp.oi = comp.oi;
                                }

                                if ('od' in comp)
                                {
                                        assert(Array.isArray(comp.od));
                                        comp.od.forEach(function(item){ assert(typeof item === 'string'); });

                                        cleanComp.od = comp.od;

                                        if (!('oi' in cleanComp))
                                        {
                                                // make sure the key is not deleted
                                                cleanComp.oi = false;
                                        }
                                }
                                break;

                        case 'jobClassification':
                        case 'salaryRange':
                        case 'primaryRole':
                        case 'secondaryRole':
                                var allowedValues = null;
                                switch (comp.p[0])
                                {
                                        case 'jobClassification':
                                                allowedValues = jobClassificationData;
                                                break;
                                        case 'salaryRange':
                                                allowedValues = salaryRangeData;
                                                break;
                                        case 'primaryRole':
                                        case 'secondaryRole':
                                                allowedValues = jobRoleData;
                                                break;
                                }

                                if ('oi' in comp)
                                {
                                        allowedValues.forEach(function(entry)
                                        {
                                                if (equal(entry.value, comp.oi, {strict: true}))
                                                {
                                                        cleanComp.oi = comp.oi;
                                                }
                                        });
                                }

                                if ('od' in comp)
                                {
                                        allowedValues.forEach(function(entry)
                                        {
                                                if (equal(entry.value, comp.od, {strict: true}))
                                                {
                                                        cleanComp.od = comp.od;
                                                }
                                        });

                                        if (!('oi' in cleanComp))
                                        {
                                                // make sure the key is not deleted (turn it into a replacement)
                                                cleanComp.oi = allowedValues[0].value;
                                        }
                                }
                                break;
                }

                return cleanComp;
        }

        switch(comp.p[0])
        {
                case 'title': // strings
                case 'teaser':
                case 'description':
                case 'location':
                        assert(comp.p.length === 2);
                        assert(Number.isFinite(comp[1]));

                        if ('si' in comp)
                        {
                                assert(typeof comp.si === 'string');
                                cleanComp.si = comp.si;
                        }

                        if ('sd' in comp)
                        {
                                assert(typeof comp.sd === 'string');
                                cleanComp.sd = comp.si;
                        }

                        break;
                case 'skills':

                        if ('li' in comp)
                        {
                                assert(comp.p.length === 2);
                                assert(Number.isFinite(comp[1]));
                                assert(typeof comp.li === 'string');
                                cleanComp.li = comp.li;
                        }

                        if ('ld' in comp)
                        {
                                assert(comp.p.length === 2);
                                assert(Number.isFinite(comp[1]));
                                assert(typeof comp.ld === 'string');
                                cleanComp.ld = comp.ld;
                        }

                        if ('si' in comp)
                        {
                                assert(comp.p.length === 3);
                                assert(Number.isFinite(comp[1]));
                                assert(typeof comp.si === 'string');
                                cleanComp.si = comp.si;
                        }

                        if ('sd' in comp)
                        {
                                assert(comp.p.length === 3);
                                assert(Number.isFinite(comp[1]));
                                assert(typeof comp.si === 'string');
                                cleanComp.sd = comp.si;
                        }
        }

        return cleanComp;
};

VacancyModel.prototype.validateOperation = P.method(function(docId, opData, user)
{
        return P.using(this.modelIndex.acquire(), function(db)
        {
                return db.vacancy.findOneAsync({_id: docId}, {_id: 1, private: 1})
                .bind(this)
                .then(function(vacancyPrivateOnly)
                {
                        if (!vacancyPrivateOnly)
                        {
                                throw Error('Invalid vacancy id: ' + docId);
                        }

                        if (!this.isEditableByUser(vacancyPrivateOnly, user))
                        {
                                throw Error('You are not allowed to edit the vacancy: ' + docId);
                        }

                        opData.op.forEach(function(comp, index)
                        {
                                opData.op[index] = this._validateComponent(comp);
                        }, this);

                        return true;
                });
        }.bind(this));
});

VacancyModel.prototype.afterSubmit = P.method(function(docId, opData, snapshot, user)
{
        var titleChanged = false;

        opData.op.forEach(function(comp)
        {
                if (comp.p &&
                    comp.p[0] === 'title')
                {
                        titleChanged = true;
                }
        }, this);

        if (titleChanged)
        {
                return this.findUniqueName(snapshot.data.company, snapshot.data.title, docId)
                .bind(this)
                .then(function(uniqueName)
                {
                        if (!uniqueName)
                        {
                                uniqueName = docId; // fallback
                        }

                        return this.modelIndex.livedb.submitAsync('vacancy', docId, {
                                op: [
                                        {
                                                p : ['name'],
                                                od: snapshot.data.name,
                                                oi: uniqueName
                                        }
                                ],
                                v : snapshot.v
                        });
                });
        }

        return null;
});

VacancyModel.prototype.findUniqueName = P.method(function(companyId, title, findForVacancyId)
{
        var name = this.cleanupName(title);

        return P.using(this.modelIndex.acquire(), function(db)
        {
                var attempts = [];
                for (var i = 1; i <= 25; ++i)
                {
                        var nameAttempt = name + (i > 1 ? '-' + i : '');

                        if (/^[0-9a-zA-Z]{24}$/.test(nameAttempt))
                        {
                                // ObjectIDs are reserved
                                nameAttempt += 'x';
                        }

                        attempts.push(P.join(
                                nameAttempt,
                                db.vacancy.findOneAsync({company: companyId, name: nameAttempt}, {_id: 1})
                        ));
                }

                return P.all(attempts)
                .then(function(attempts)
                {
                        for (var i = 0; i < attempts.length; ++i)
                        {
                                var attempt = attempts[i];
                                var foundVacancyId = attempt[1] && attempt[1]._id;

                                if (!foundVacancyId ||
                                    foundVacancyId === findForVacancyId)
                                {
                                        return attempt[0];
                                }
                        }

                        // give up
                        return null;
                });
        });
});

var vacancyDefaultFields = {
        title: 'Fill in a short title here',
        name: 'new-vacancy',
        company: null,
        teaser: 'A short one-line teaser for your vacancy',
        description: 'Describe your job vacancy in detail here...\n\n' +
                   'Maybe you could describe the atmosphere in your team...\n\n' +
                   'Or the passion that drives your company...',
        skills: ['list some', 'required skills', 'here'],
        location: 'Rotterdam',
        jobClassification: 'temporary',
        salaryRange: { 'min':  0, 'max':  0 },
        primaryRole: '',
        secondaryRole: '',
        private: {
                editableByUser: [],
                createdByUser: null
        }
};

VacancyModel.prototype.newVacancy = P.method(function(initialFields)
{
        var fields = merge.recursive(vacancyDefaultFields, initialFields);
        var id = new ObjectID().toString();

        if (!fields.company)
        {
                throw Error('Company field is required');
        }

        return this.findUniqueName(fields.company, fields.title)
        .bind(this)
        .then(function(uniqueName)
        {
                fields.name = uniqueName || id;

                return this.modelIndex.livedb.submitAsync('vacancy', id, {
                        create: {
                                type: 'json0',
                                data: fields
                        }
                })
                .bind(this)
                .spread(function(version, transformedByOps, snapshot)
                {
                        this.addVirtualFields(id, snapshot.data, null);
                        return snapshot.data;
                });
        });
});

VacancyModel.prototype.removeVacancy = function(id)
{
        var livedb = this.modelIndex.livedb;
        return livedb.submitAsync('vacancy', id, {del: true});
};<|endoftext|>


<commit_before>'use strict';
var mongoose = require('mongoose');
var ShortId = require('mongoose-shortid');
var timestamps = require('mongoose-timestamp');
var BBPromise = require('bluebird');
var mongooseDelete = require('mongoose-delete');
var keygen = require('keygenerator');

var Schema = mongoose.Schema;
var ApplicationSchema = new Schema({
  schemaVersion: String,
  //the org that owns this application
  organisation: {
    type: ShortId,
    required: 'Application must belong to an Organisation',
    index: true,
    ref: 'Organisation'
  },
  //friendly name
  name: {
    type: String
  },
  _id: {
    type: ShortId,
    base: 64,
    retries: 4,
    len: 20
  },
  //url alias
  alias: {
    type: [String],
    index: true
  },

  //the app.hoi.io sub domain for hosting
  subDomain: {
    type: String,
    unique: true,
    index: true,
    required: true,
    default: function () {
      return require('moniker').choose();
    }
  },

  //api key
  apiKey: {
    type: String,
    unique: true,
    index: true,
    required: true,
    default: function () {
      return keygen._();
    }
  },
  //the storage for data
  dataKey: {
    type: String,
    unique: true,
    required: true,
    default: function () {
      return keygen._({
        forceLowercase: true
      });
    }
  },
  //anonymous

  anonymousPermissions: {
    dev: [{
      type: String
    }],
    test: [{
      type: String
    }],
    live: [{
      type: String
    }]
  },
  //settings from the hoist.json files
  settings: {
    dev: {
      type: Schema.Types.Mixed
    },
    test: {
      type: Schema.Types.Mixed
    },
    live: {
      type: Schema.Types.Mixed
    },
  },
  //when was the code last released
  lastDeploy: {
    dev: {
      type: Date
    },
    test: {
      type: Date
    },
    live: {
      type: Date
    }
  },
  //should this application get preview features
  isCanary: {
    type: Boolean,
    default: false
  },
  //path to the git repository
  gitRepo: {
    type: String,
    index: true
  }

});

ApplicationSchema.plugin(timestamps);
ApplicationSchema.plugin(mongooseDelete);

mongoose.model('Application', ApplicationSchema);

var Application = mongoose.model('Application');
BBPromise.promisifyAll(Application);
BBPromise.promisifyAll(Application.prototype);
exports.name = 'Application';
exports.model = Application;
<commit_msg>add loggly settings<commit_after>'use strict';
var mongoose = require('mongoose');
var ShortId = require('mongoose-shortid');
var timestamps = require('mongoose-timestamp');
var BBPromise = require('bluebird');
var mongooseDelete = require('mongoose-delete');
var keygen = require('keygenerator');

var Schema = mongoose.Schema;
var ApplicationSchema = new Schema({
  schemaVersion: String,
  //the org that owns this application
  organisation: {
    type: ShortId,
    required: 'Application must belong to an Organisation',
    index: true,
    ref: 'Organisation'
  },
  //friendly name
  name: {
    type: String
  },
  _id: {
    type: ShortId,
    base: 64,
    retries: 4,
    len: 20
  },
  //url alias
  alias: {
    type: [String],
    index: true
  },
  loggly: {
    _id: false,
    token: String,
    subdomain: String
  },
  //the app.hoi.io sub domain for hosting
  subDomain: {
    type: String,
    unique: true,
    index: true,
    required: true,
    default: function () {
      return require('moniker').choose();
    }
  },

  //api key
  apiKey: {
    type: String,
    unique: true,
    index: true,
    required: true,
    default: function () {
      return keygen._();
    }
  },
  //the storage for data
  dataKey: {
    type: String,
    unique: true,
    required: true,
    default: function () {
      return keygen._({
        forceLowercase: true
      });
    }
  },
  //anonymous

  anonymousPermissions: {
    dev: [{
      type: String
    }],
    test: [{
      type: String
    }],
    live: [{
      type: String
    }]
  },
  //settings from the hoist.json files
  settings: {
    dev: {
      type: Schema.Types.Mixed
    },
    test: {
      type: Schema.Types.Mixed
    },
    live: {
      type: Schema.Types.Mixed
    },
  },
  //when was the code last released
  lastDeploy: {
    dev: {
      type: Date
    },
    test: {
      type: Date
    },
    live: {
      type: Date
    }
  },
  //should this application get preview features
  isCanary: {
    type: Boolean,
    default: false
  },
  //path to the git repository
  gitRepo: {
    type: String,
    index: true
  }

});

ApplicationSchema.plugin(timestamps);
ApplicationSchema.plugin(mongooseDelete);

mongoose.model('Application', ApplicationSchema);

var Application = mongoose.model('Application');
BBPromise.promisifyAll(Application);
BBPromise.promisifyAll(Application.prototype);
exports.name = 'Application';
exports.model = Application;
<|endoftext|>


<commit_before>var from = require('from');
var log = require('./utils/log');
var es   = require('event-stream');

module.exports = function(streams) {

  var isEmptyStream = false;
  return from(function getChunk(count, next) {
    var _this = this;

    var streamQueue = [];
    var currentStream = 0;

    // Emit everything available in the queue
    function emitData() {
      for(var i=0;i<streamQueue.length;i++) {
        var dataQueue = streamQueue[i];

        if(dataQueue.length == 0) {
          return;
        }

        for(var j=0;j<dataQueue.length;j++) {
          var data = dataQueue[j];
          if(!!data) {
            _this.emit('data', data);
            dataQueue[j] = null;

            return emitData();
          }
        }
      }

      if(currentStream == streamQueue.length) {
        _this.emit('end');
      }
    }

    function processStream(index, stream) {
      stream.on('data', function(data) {
        streamQueue[index].push(data);
        emitData();
      });
      stream.on('end', function() {
        currentStream++;

        // The stream was empty and didn't send any data
        if(streamQueue[index].length == 0) {
          isEmptyStream = true;
          streamQueue[index].push(null);
        }

        //If we have received the last end event, end the whole stream.
        if(currentStream == streamQueue.length) {
          emitData();
        }
      })

      stream.resume();
    }

    for(var i=0;i<streams.length;i++) {
      streamQueue.push([]);
    }

    for(var j=0;j<streams.length;j++) {
      processStream(j, streams[j]);
    }

  }).pipe(es.map(function(file, cb) {
    if(isEmptyStream) {
      log('[Info] Non-matching include directive found in "' + file.path + '"');
      cb(null, file);
    }
  }));
};<commit_msg>Prevent some false-positives for non-matching directive info messages<commit_after>var from = require('from');
var log = require('./utils/log');
var es   = require('event-stream');
var path = require('path');

module.exports = function(streams) {

  var isEmptyStream = false;
  return from(function getChunk(count, next) {
    var _this = this;

    var streamQueue = [];
    var currentStream = 0;

    // Emit everything available in the queue
    function emitData() {
      for(var i=0;i<streamQueue.length;i++) {
        var dataQueue = streamQueue[i];

        if(dataQueue.length == 0) {
          return;
        }

        for(var j=0;j<dataQueue.length;j++) {
          var data = dataQueue[j];
          if(!!data) {
            _this.emit('data', data);
            dataQueue[j] = null;

            return emitData();
          }
        }
      }

      if(currentStream == streamQueue.length) {
        _this.emit('end');
      }
    }

    function processStream(index, stream) {
      stream.on('data', function(data) {
        streamQueue[index].push(data);
        emitData();
      });
      stream.on('end', function() {
        currentStream++;

        // The stream was empty and didn't send any data
        if(streamQueue[index].length == 0) {
          isEmptyStream = true;
          streamQueue[index].push(null);
        }

        //If we have received the last end event, end the whole stream.
        if(currentStream == streamQueue.length) {
          emitData();
        }
      })

      stream.resume();
    }

    for(var i=0;i<streams.length;i++) {
      streamQueue.push([]);
    }

    for(var j=0;j<streams.length;j++) {
      processStream(j, streams[j]);
    }

  }).pipe(es.map(function(file, cb) {
    // no matching directives if empty stream and file path does not start with
    // a path seperator (and is therefore an absolute path). Directive globs
    // create a relative path
    if(isEmptyStream && file.path.indexOf(path.sep) !== 0) {
      log('[Info] Non-matching include directive found in "' + file.path + '"');
      cb(null, file);
    }
  }));
};<|endoftext|>


<commit_before>/*!
 * Snakeskin
 * https://github.com/SnakeskinTpl/Snakeskin
 *
 * Released under the MIT license
 * https://github.com/SnakeskinTpl/Snakeskin/blob/master/LICENSE
 */

/**
 * The class for parsing SS templates
 *
 * @constructor
 * @implements {$$SnakeskinParser}
 */
export class Parser {
	/**
	 * @param {string} src - the source text of templates
	 * @param {$$SnakeskinParserParams} params - additional parameters
	 */
	construtor(src, params) {
		$C(this).forEach((el, key) => {
			if (el && el.init) {
				this[key] = el.init();
			}

		}, {notOwn: true});

		/** @type {Parser} */
		this.parent = params.parent || null;

		/** @type {boolean} */
		this.throws = params.throws;

		/** @type {boolean} */
		this.useStrict = params.useStrict;

		/** @type {?function(!Error)} */
		this.onError = params.onError || null;

		/** @type {!Array} */
		this.scope = params.scope || [];

		/** @type {Object} */
		this.proto = params.proto || null;

		/** @type {!Object} */
		this.info = params.info;

		/** @type {boolean} */
		this.needPrfx = params.needPrfx || false;

		/** @type {!Array} */
		this.lines = params.lines || [''];

		/** @type {string} */
		this.eol = params.eol;

		/** @type {string} */
		this.renderMode = params.renderMode;

		/** @type {boolean} */
		this.tolerateWhitespace = params.tolerateWhitespace;

		/** @type {boolean} */
		this.inlineIterators = params.inlineIterators;

		/** @type {(string|boolean)} */
		this.doctype = params.doctype;

		/** @type {boolean} */
		this.replaceUndef = params.replaceUndef;

		/** @type {boolean} */
		this.escapeOutput = params.escapeOutput;

		/** @type {(?string|undefined)} */
		this.renderAs = params.renderAs;

		/** @type {string} */
		this.exports = params.exports;

		/** @type {boolean} */
		this.autoReplace = params.autoReplace !== false;

		/** @type {(Object|undefined)} */
		this.macros = params.macros;

		/** @type {string} */
		this.bemFilter = params.bemFilter;

		/** @type {boolean} */
		this.localization = params.localization;

		/** @type {string} */
		this.i18nFn = params.i18nFn;

		/** @type {(Object|undefined)} */
		this.language = params.language;

		/** @type {(RegExp|undefined)} */
		this.ignore = params.ignore;

		/**
		 * Stack parameters that can be changed in a code
		 * @type {!Array}
		 */
		this.params = [
			{
				'@root': true,
				renderMode: this.renderMode,
				inlineIterators: this.inlineIterators,
				doctype: this.doctype,
				escapeOutput: this.escapeOutput,
				renderAs: this.renderAs,
				replaceUndef: this.replaceUndef,
				autoReplace: this.autoReplace,
				macros: this.macros,
				localization: this.localization,
				i18nFn: this.i18nFn,
				bemFilter: this.bemFilter,
				language: this.language,
				ignore: this.ignore,
				tolerateWhitespace: this.tolerateWhitespace
			}
		];

		if (params.consts) {
			/** @type {(Array|undefined)} */
			this.consts = params.consts;
		}

		/**
		 * If is true, then compiling will be broken
		 * @type {boolean}
		 */
		this.brk = false;

		/**
		 * The name of the active directive
		 * @type {?string}
		 */
		this.name = null;

		/**
		 * The map of directives, which can go after the active
		 * @type {Object}
		 */
		this.after = null;

		/**
		 * If is false, then template can't be inserted into the resulting JS string
		 * @type {boolean}
		 */
		this.canWrite = true;

		// Whitespace
		// >>>

		/** @type {boolean} */
		this.space = false;

		/** @type {boolean} */
		this.skipSpace = false;

		/** @type {boolean} */
		this.prevSpace = false;

		/** @type {boolean} */
		this.chainSpace = false;

		/** @type {number} */
		this.strongSpace = 0;

		/** @type {boolean} */
		this.sysSpace = false;

		/** @type {number} */
		this.freezeLine = 0;

		/** @type {boolean} */
		this.attr = false;

		/** @type {boolean} */
		this.attrEscape = false;

		// <<<

		/**
		 * The number of the active iteration
		 * @type {number}
		 */
		this.i = -1;

		/**
		 * The tree of blocks: proto, block, const
		 * @type {Object}
		 */
		this.blockStructure = null;

		/**
		 * The map for blocks: proto, block, const
		 * @type {Object}
		 */
		this.blockTable = null;

		/**
		 * The cache of outer prototypes / blocks
		 * @type {!Object}
		 */
		this.preDefs = {};

		/**
		 * The name of the active outer prototype / block
		 * @type {?string}
		 */
		this.outerLink = null;

		/**
		 * The template structure
		 * @type {!Object}
		 */
		this.structure = {
			name: 'root',

			/**
			 * @type {?{
			 *   name:
			 *   string,
			 *   parent: Object,
			 *   params: !Object,
			 *   stack: !Array,
			 *   vars: Object,
			 *   children: Array,
			 *   sys: boolean,
			 *   strong: boolean
			 * }}
			 */
			parent: null,

			params: {},
			stack: [],

			vars: params.vars || {},
			children: [],

			sys: false,
			strong: false
		};

		/**
		 * If is true, then the active directive is inline
		 * @type {?boolean}
		 */
		this.inline = null;

		/**
		 * If is true, then the active directive has a text type
		 * @type {boolean}
		 */
		this.text = false;

		/**
		 * The content of Escaper blocks
		 * @type {!Array}
		 */
		this.quotContent = [];

		/**
		 * The content of directives (for replaceTplVars)
		 * @type {!Array}
		 */
		this.dirContent = [];

		/**
		 * The content of CDATA blocks
		 * @type {!Array}
		 */
		this.cDataContent = [];

		/**
		 * The map of included files
		 * @type {!Object}
		 */
		this.files = {};

		/**
		 * The module object
		 * @type {{exports, require, id, key, root, filename, parent, children, loaded}}
		 */
		this.module = {
			exports: {},
			require: IS_NODE ?
				require : null,

			id: 0,
			key: [],

			root: null,
			filename: this.info.file,
			parent: IS_NODE ?
				module : null,

			children: [],
			loaded: true
		};

		/**
		 * The source text of templates
		 * @type {string}
		 */
		this.source = this.replaceCData(String(src));

		/**
		 * The final JS string
		 * @type {string}
		 */
		this.res = '';

		if (!this.proto) {
			this.res += /* cbws */`
				This code is generated automatically, don\'t alter it. */
				(function () {
					${this.useStrict ? '\'use strict\';' : ''}

					var
						__IS_NODE__ = false,
						__AMD__ = typeof define === 'function' && define.amd,
						__HAS_EXPORTS__ = typeof exports !== 'undefined',
						__EXPORTS__ = __HAS_EXPORTS__ ? exports : __AMD__ ? {} : this;

					try {
						__IS_NODE__ = typeof process === 'object' && Object().toString.call(process) === '[object process]';

					} catch (ignore) {}

					var Snakeskin = (__IS_NODE__ ? global : this).Snakeskin;

					function __INIT__(obj) {
						Snakeskin = Snakeskin ||
							(obj instanceof Object ? obj : void 0);

						if (__HAS_EXPORTS__) {
							delete __EXPORTS__.init;
						}

						if (__AMD__) {
							define(['Snakeskin'], function (ss) {
								Snakeskin = Snakeskin || ss;
								__EXEC__.call(__EXPORTS__);
								return __EXPORTS__;
							});

						} else {
							if (__IS_NODE__) {
								Snakeskin = Snakeskin || require(obj);
							}

							__EXEC__.call(__EXPORTS__);
							return __EXPORTS__;
						}
					}

					if (__HAS_EXPORTS__) {
						__EXPORTS__.init = __INIT__;
					}

					function __EXEC__() {
						var
							__ROOT__ = this,
							self = this;

						var
							__APPEND__ = Snakeskin.appendChild,
							__FILTERS__ = Snakeskin.Filters,
							__VARS__ = Snakeskin.Vars,
							__LOCAL__ = Snakeskin.LocalVars;

						${this.multiDeclVar('$_')}
			`;
		}
	};
}
<commit_msg>Bugfix<commit_after>/*!
 * Snakeskin
 * https://github.com/SnakeskinTpl/Snakeskin
 *
 * Released under the MIT license
 * https://github.com/SnakeskinTpl/Snakeskin/blob/master/LICENSE
 */

import { IS_NODE } from '../consts/hacks'

/**
 * The class for parsing SS templates
 *
 * @constructor
 * @implements {$$SnakeskinParser}
 */
export class Parser {
	/**
	 * @param {string} src - the source text of templates
	 * @param {$$SnakeskinParserParams} params - additional parameters
	 */
	construtor(src, params) {
		$C(this).forEach((el, key) => {
			if (el && el.init) {
				this[key] = el.init();
			}

		}, {notOwn: true});

		/** @type {Parser} */
		this.parent = params.parent || null;

		/** @type {boolean} */
		this.throws = params.throws;

		/** @type {boolean} */
		this.useStrict = params.useStrict;

		/** @type {?function(!Error)} */
		this.onError = params.onError || null;

		/** @type {!Array} */
		this.scope = params.scope || [];

		/** @type {Object} */
		this.proto = params.proto || null;

		/** @type {!Object} */
		this.info = params.info;

		/** @type {boolean} */
		this.needPrfx = params.needPrfx || false;

		/** @type {!Array} */
		this.lines = params.lines || [''];

		/** @type {string} */
		this.eol = params.eol;

		/** @type {string} */
		this.renderMode = params.renderMode;

		/** @type {boolean} */
		this.tolerateWhitespace = params.tolerateWhitespace;

		/** @type {boolean} */
		this.inlineIterators = params.inlineIterators;

		/** @type {(string|boolean)} */
		this.doctype = params.doctype;

		/** @type {boolean} */
		this.replaceUndef = params.replaceUndef;

		/** @type {boolean} */
		this.escapeOutput = params.escapeOutput;

		/** @type {(?string|undefined)} */
		this.renderAs = params.renderAs;

		/** @type {string} */
		this.exports = params.exports;

		/** @type {boolean} */
		this.autoReplace = params.autoReplace !== false;

		/** @type {(Object|undefined)} */
		this.macros = params.macros;

		/** @type {string} */
		this.bemFilter = params.bemFilter;

		/** @type {boolean} */
		this.localization = params.localization;

		/** @type {string} */
		this.i18nFn = params.i18nFn;

		/** @type {(Object|undefined)} */
		this.language = params.language;

		/** @type {(RegExp|undefined)} */
		this.ignore = params.ignore;

		/**
		 * Stack parameters that can be changed in a code
		 * @type {!Array}
		 */
		this.params = [
			{
				'@root': true,
				renderMode: this.renderMode,
				inlineIterators: this.inlineIterators,
				doctype: this.doctype,
				escapeOutput: this.escapeOutput,
				renderAs: this.renderAs,
				replaceUndef: this.replaceUndef,
				autoReplace: this.autoReplace,
				macros: this.macros,
				localization: this.localization,
				i18nFn: this.i18nFn,
				bemFilter: this.bemFilter,
				language: this.language,
				ignore: this.ignore,
				tolerateWhitespace: this.tolerateWhitespace
			}
		];

		if (params.consts) {
			/** @type {(Array|undefined)} */
			this.consts = params.consts;
		}

		/**
		 * If is true, then compiling will be broken
		 * @type {boolean}
		 */
		this.brk = false;

		/**
		 * The name of the active directive
		 * @type {?string}
		 */
		this.name = null;

		/**
		 * The map of directives, which can go after the active
		 * @type {Object}
		 */
		this.after = null;

		/**
		 * If is false, then template can't be inserted into the resulting JS string
		 * @type {boolean}
		 */
		this.canWrite = true;

		// Whitespace
		// >>>

		/** @type {boolean} */
		this.space = false;

		/** @type {boolean} */
		this.skipSpace = false;

		/** @type {boolean} */
		this.prevSpace = false;

		/** @type {boolean} */
		this.chainSpace = false;

		/** @type {number} */
		this.strongSpace = 0;

		/** @type {boolean} */
		this.sysSpace = false;

		/** @type {number} */
		this.freezeLine = 0;

		/** @type {boolean} */
		this.attr = false;

		/** @type {boolean} */
		this.attrEscape = false;

		// <<<

		/**
		 * The number of the active iteration
		 * @type {number}
		 */
		this.i = -1;

		/**
		 * The tree of blocks: proto, block, const
		 * @type {Object}
		 */
		this.blockStructure = null;

		/**
		 * The map for blocks: proto, block, const
		 * @type {Object}
		 */
		this.blockTable = null;

		/**
		 * The cache of outer prototypes / blocks
		 * @type {!Object}
		 */
		this.preDefs = {};

		/**
		 * The name of the active outer prototype / block
		 * @type {?string}
		 */
		this.outerLink = null;

		/**
		 * The template structure
		 * @type {!Object}
		 */
		this.structure = {
			name: 'root',

			/**
			 * @type {?{
			 *   name:
			 *   string,
			 *   parent: Object,
			 *   params: !Object,
			 *   stack: !Array,
			 *   vars: Object,
			 *   children: Array,
			 *   sys: boolean,
			 *   strong: boolean
			 * }}
			 */
			parent: null,

			params: {},
			stack: [],

			vars: params.vars || {},
			children: [],

			sys: false,
			strong: false
		};

		/**
		 * If is true, then the active directive is inline
		 * @type {?boolean}
		 */
		this.inline = null;

		/**
		 * If is true, then the active directive has a text type
		 * @type {boolean}
		 */
		this.text = false;

		/**
		 * The content of Escaper blocks
		 * @type {!Array}
		 */
		this.quotContent = [];

		/**
		 * The content of directives (for replaceTplVars)
		 * @type {!Array}
		 */
		this.dirContent = [];

		/**
		 * The content of CDATA blocks
		 * @type {!Array}
		 */
		this.cDataContent = [];

		/**
		 * The map of included files
		 * @type {!Object}
		 */
		this.files = {};

		/**
		 * The module object
		 * @type {{exports, require, id, key, root, filename, parent, children, loaded}}
		 */
		this.module = {
			exports: {},
			require: IS_NODE ?
				require : null,

			id: 0,
			key: [],

			root: null,
			filename: this.info.file,
			parent: IS_NODE ?
				module : null,

			children: [],
			loaded: true
		};

		/**
		 * The source text of templates
		 * @type {string}
		 */
		this.source = this.replaceCData(String(src));

		/**
		 * The final JS string
		 * @type {string}
		 */
		this.res = '';

		if (!this.proto) {
			this.res += /* cbws */`
				This code is generated automatically, don\'t alter it. */
				(function () {
					${this.useStrict ? '\'use strict\';' : ''}

					var
						__IS_NODE__ = false,
						__AMD__ = typeof define === 'function' && define.amd,
						__HAS_EXPORTS__ = typeof exports !== 'undefined',
						__EXPORTS__ = __HAS_EXPORTS__ ? exports : __AMD__ ? {} : this;

					try {
						__IS_NODE__ = typeof process === 'object' && Object().toString.call(process) === '[object process]';

					} catch (ignore) {}

					var Snakeskin = (__IS_NODE__ ? global : this).Snakeskin;

					function __INIT__(obj) {
						Snakeskin = Snakeskin ||
							(obj instanceof Object ? obj : void 0);

						if (__HAS_EXPORTS__) {
							delete __EXPORTS__.init;
						}

						if (__AMD__) {
							define(['Snakeskin'], function (ss) {
								Snakeskin = Snakeskin || ss;
								__EXEC__.call(__EXPORTS__);
								return __EXPORTS__;
							});

						} else {
							if (__IS_NODE__) {
								Snakeskin = Snakeskin || require(obj);
							}

							__EXEC__.call(__EXPORTS__);
							return __EXPORTS__;
						}
					}

					if (__HAS_EXPORTS__) {
						__EXPORTS__.init = __INIT__;
					}

					function __EXEC__() {
						var
							__ROOT__ = this,
							self = this;

						var
							__APPEND__ = Snakeskin.appendChild,
							__FILTERS__ = Snakeskin.Filters,
							__VARS__ = Snakeskin.Vars,
							__LOCAL__ = Snakeskin.LocalVars;

						${this.multiDeclVar('$_')}
			`;
		}
	};
}
<|endoftext|>