<commit_before>(function($) {

    $.fn.dynamo = function() {

        return this.each(function(i, v) {
            var v = $(v);
            var delay = parseInt(v.attr('data-delay')) || 3000;
            var speed = parseInt(v.attr('data-speed')) || 350;

            var lines = v.attr('data-lines').split(',');

            // wrap the original contents in a span
            v.html($('').text(v.text()));

            // grab the width of the span
            var max = v.find('span:eq(0)').width();

            // for each item in data-lines, create a span with item as its content
            // compare the width of this span with the max
            for (var k in lines) {
                var span = $('').text(lines[k]);
                v.append(span);
                max = Math.max(max, span.width());
            }

            // replace all the spans with inline-div's
            v.find('span').each(function(i, ele) {

                var s = $(ele).remove();
                var d = $('

').text(s.text());
                d.width(max);
                v.append(d);
            });

            // set the height of the dynamo container
            var height = v.find('>:first-child').height();

            // style
            v
                .width(max)
                .height(height)
                .css({
                    'display' : 'inline-block',
                    'position' : 'relative',
                    'overflow' : 'hidden',
                    'vertical-align' : 'bottom',
                    'text-align' : 'left'
                });

            // manually center it if we need to
            if (v.attr('data-center'))
                v.css('text-align', 'center');

            // now, animate it
            var transition = function() {
                v.find('div:first').slideUp(speed, function() {
                    v.append($(this).show());
                });
            };

            setInterval(transition, delay);
        });
    };

    // automatically initiate cycles on elements of class 'dynamo'
    $('.dynamo').dynamo();

})(jQuery);<commit_msg>Added support for data-delimiter. If there's no data-delimiter specified, default (,) delimiter is used<commit_after>(function($) {

    $.fn.dynamo = function() {

        return this.each(function(i, v) {
            var v = $(v);
            var delay = parseInt(v.attr('data-delay')) || 300;
            var speed = parseInt(v.attr('data-speed')) || 350;

            var lines = v.attr('data-lines').split(v.attr('data-delimiter') || ',');

            // wrap the original contents in a span
            v.html($('').text(v.text()));

            // grab the width of the span
            var max = v.find('span:eq(0)').width();

            // for each item in data-lines, create a span with item as its content
            // compare the width of this span with the max
            for (var k in lines) {
                var span = $('').text(lines[k]);
                v.append(span);
                max = Math.max(max, span.width());
            }

            // replace all the spans with inline-div's
            v.find('span').each(function(i, ele) {

                var s = $(ele).remove();
                var d = $('

').text(s.text());
                d.width(max);
                v.append(d);
            });

            // set the height of the dynamo container
            var height = v.find('>:first-child').height();

            // style
            v
                .width(max)
                .height(height)
                .css({
                    'display' : 'inline-block',
                    'position' : 'relative',
                    'overflow' : 'hidden',
                    'vertical-align' : 'bottom',
                    'text-align' : 'left'
                });

            // manually center it if we need to
            if (v.attr('data-center'))
                v.css('text-align', 'center');

            // now, animate it
            var transition = function() {
                v.find('div:first').slideUp(speed, function() {
                    v.append($(this).show());
                });
            };

            setInterval(transition, delay);
        });
    };

    // automatically initiate cycles on elements of class 'dynamo'
    $('.dynamo').dynamo();

})(jQuery);<|endoftext|>


<commit_before>
var deepExtend = require('deep-extend')

var methods = {
  createReadStream  : {type: 'readable'},
  readStream        : {type: 'readable'},
  createValueStream : {type: 'readable'},
  valueStream       : {type: 'readable'},
  createKeyStream   : {type: 'readable'},
  keyStream         : {type: 'readable'},
  createWriteStream : {type: 'writable'},
  writeStream       : {type: 'writable'},
  isOpen            : {type: 'sync'},
  isClosed          : {type: 'sync'},
  put               : {type: 'async'},
  get               : {type: 'async'},
  del               : {type: 'async'},
  batch             : {type: 'async'},
  approximateSize   : {type: 'async'}
}

//valid types:
//readable, writable, duplex, sync, async, error

//error means that this method is disabled in the client
//the client should throw

function getMethods (db) {
  var _methods = db.methods || {}
  return merge(merge({}, methods), _methods)
}

module.exports = function manifest (db, terse) {
  var man = {}
  if(db.methods || !terse) man.methods = {}
  if(!terse) {
    deepExtend(deepExtend(man.methods, methods), db.methods)
  } else
    deepExtend(man.methods, db.methods)

  if(db.sublevels) {
    man.sublevels = {}
    for(var name in db.sublevels) {
      man.sublevels[name] = manifest(db.sublevels[name], terse)
    }
  }
  return man
}

<commit_msg>show _sep and _prefix<commit_after>
var deepExtend = require('deep-extend')

var methods = {
  createReadStream  : {type: 'readable'},
  readStream        : {type: 'readable'},
  createValueStream : {type: 'readable'},
  valueStream       : {type: 'readable'},
  createKeyStream   : {type: 'readable'},
  keyStream         : {type: 'readable'},
  createWriteStream : {type: 'writable'},
  writeStream       : {type: 'writable'},
  isOpen            : {type: 'sync'},
  isClosed          : {type: 'sync'},
  put               : {type: 'async'},
  get               : {type: 'async'},
  del               : {type: 'async'},
  batch             : {type: 'async'},
  approximateSize   : {type: 'async'}
}

//valid types:
//readable, writable, duplex, sync, async, error

//error means that this method is disabled in the client
//the client should throw

function getMethods (db) {
  var _methods = db.methods || {}
  return merge(merge({}, methods), _methods)
}

module.exports = function manifest (db, terse) {
  var man = {}
  if(db.methods || !terse) man.methods = {}
  if(!terse) {
    deepExtend(deepExtend(man.methods, methods), db.methods)
  } else
    deepExtend(man.methods, db.methods)

  if(db._sep)
    man._sep = _sep
  if(db._prefix)
    man._prefix = db._prefix

  if(db.sublevels) {
    man.sublevels = {}
    for(var name in db.sublevels) {
      man.sublevels[name] = manifest(db.sublevels[name], terse)
    }
  }
  return man
}

<|endoftext|>


<commit_before>/*!
 * verb <https://github.com/assemble/verb>
 *
 * Copyright (c) 2014 Jon Schlinkert, Brian Woodward, contributors.
 * Licensed under the MIT license.
 */

// process.env.DEBUG = 'verb:*'

'use strict';

var path = require('path');
var vfs = require('vinyl-fs');
var File = require('gulp-util').File;
var es = require('event-stream');
var load = require('load-plugins');
var chalk = require('chalk');
var debug = require('debug')('verb');
var session = require('session-cache')('verb');
var Template = require('template');
var Config = require('orchestrator');
var stack = require('./lib/stack');
var utils = require('./lib/utils');
var _ = require('lodash');
var extend = _.extend;

/**
 * Create an instance of `Verb` with the given `options`.
 *
 * ```js
 * var verb = new Verb();
 * ```
 *
 * @param {Object} `options`
 * @constructor
 */

var Verb = module.exports = Template.extend({
  constructor: function (options) {
    Verb.__super__.constructor.call(this, options);
    Config.call(this);
    this._initialize();
  }
});

extend(Verb.prototype, Config.prototype);

/**
 * Initialize all configuration settings.
 *
 * @api private
 */

Verb.prototype._initialize = function() {
  this.fns = {};

  // extension must be loaded first
  this.loadPlugins();
  this.loadHelpers();

  this._defaultSettings();
  this._defaultConfig();
  this._defaultTransforms();
  this._defaultDelims();
  this._defaultTemplates();
  this._defaultMiddleware();
  this._defaultHelpers();
  this._defaultAsyncHelpers();
};

/**
 * Initialize default template types
 *
 * @api private
 */

Verb.prototype._defaultConfig = function() {
  this.option('base', process.cwd());
  this.option('cwd', process.cwd());
  this.option('viewEngine', '.md');
  this.option('destExt', '.md');
  this.option('escapeDelims', {
    from: ['{%%', '%}'],
    to: ['{%', '%}']
  });
  this.option('defaults', {
    isRenderable: true,
    isPartial: true,
    engine: '.md',
    ext: '.md'
  });
};

/**
 * Initialize default template types
 *
 * @api private
 */

Verb.prototype._defaultSettings = function() {
  this.enable('debug');
  this.disable('debugEngine');

  this.enable('src:init plugin');
  this.enable('dest:render plugin');
  this.enable('dest:readme plugin');

  this.disable('dest:travis plugin');
  this.disable('travis badge');
};

/**
 * Load default transforms. Transforms are used to extend or
 * modify the `cache.data` object, but really anything on the
 * `this` object can be tranformed.
 *
 * @api private
 */

Verb.prototype._defaultTransforms = function() {
  this.transform('pkg', require('./lib/transforms/pkg'));
  this.transform('author', require('./lib/transforms/author'));
  this.transform('username', require('./lib/transforms/username'));
  this.transform('travis', require('./lib/transforms/travis'));
  this.transform('runner', require('./lib/transforms/runner'));
};

/**
 * Load default middleware
 *
 * @api private
 */

Verb.prototype._defaultMiddleware = function() {
  this.use(require('./lib/middleware/ext')(this));
};

/**
 * Register default template delimiters.
 *
 *   - `['{%', '%}']` => default template delimiters
 *   - `['<<%', '%>>']` => default template delimiters
 *
 * @api private
 */

Verb.prototype._defaultDelims = function() {
  this.addDelims('md', ['{%', '%}'], ['<<%', '%>>']);
};

/**
 * Initialize default template types
 *
 * @api private
 */

Verb.prototype._defaultTemplates = function() {
  var opts = this.option('defaults');
  this.create('doc', opts);

  var createBase = require('./lib/create/base')(this, opts);
  createBase('include', require('verb-readme-includes'));
  createBase('badge', require('verb-readme-badges'));

  this.create('file', extend(opts, {
    renameKey: function (fp) {
      return fp;
    }
  }));
};

/**
 * Initialize default helpers.
 *
 * @api private
 */

Verb.prototype._defaultHelpers = function() {
  this.helper('date', require('helper-date'));
  this.helper('license', require('helper-license'));
  this.helper('copyright', require('helper-copyright'));

  this.helpers(require('logging-helpers'));
  this.helpers(require('./lib/helpers/deprecated'));
  this.helper('strip', require('./lib/helpers/strip'));
  this.helper('comments', require('./lib/helpers/comments'));
};

/**
 * Initialize async helpers.
 *
 * @api private
 */

Verb.prototype._defaultAsyncHelpers = function() {
  this.asyncHelper('include', require('./lib/helpers/include')(this));
  this.asyncHelper('badge', require('./lib/helpers/badge')(this));
  this.asyncHelper('docs', require('./lib/helpers/docs')(this));
};

/**
 * Load plugins. Called in the constructor to load plugins from
 * `node_modules` using the given `namespace`. You may also call
 * the method directly.
 *
 * **Example**
 *
 * Namespace `foo` would load plugins using the
 * `foo-*` glob pattern, e.g:
 *
 * ```js
 * verb.loadPlugins('foo-*');
 * ```
 *
 * @param  {String} `pattern` Optionally pass a glob pattern when calling the method directly.
 * @return {Object} Returns an object of plugins loaded from `node_modules`.
 * @api private
 */

Verb.prototype.loadPlugins = function() {
  this.loadType('async-helper', 'async');
  this.loadType('helper', 'helpers');
};

/**
 * Register helpers that are automatically loaded.
 *
 * @api private
 */

Verb.prototype.loadHelpers = function() {
  debug('loading helpers: %j', arguments);
  var fn, name;

  var helpers = Object.keys(this.fns.helpers);
  var len = helpers.length;
  var i = 0;

  while (i < len) {
    name = helpers[i++];
    fn = this.fns.helpers[name];
    this.helper(name, fn);
  }

  var async = Object.keys(this.fns.async);
  var alen = async.length;
  var j = 0;

  while (j < len) {
    name = async[j++];
    fn = this.fns.async[name];
    this.asyncHelper(name, fn);
  }

  return this;
};

/**
 * Private method to create a plugin loader for the
 * given plugin `type`, e.g. "helper"
 *
 * @param  {String} `type` The plugin type, e.g. "helper"
 * @param  {String} `plural` Plural form of `type`, e.g. "helpers"
 * @return {Object} `fns` Object of plugins, key-value pairs. The value is a function.
 * @api private
 */

Verb.prototype.loadType = function(type, plural) {
  debug('loading type: %s', type);

  this.fns[plural] = this.fns[plural] || {};
  extend(this.fns[plural], load(type + '*', {
    strip: type,
    cwd: process.cwd()
  }));

  return this.fns[plural];
};

/**
 * Convenience method for looking up a template
 * on the cache by:
 *
 *   1. `name`, as-is
 *   2. If `name` has an extension, try without it
 *   3. If `name` does not have an extension, try `name.md`
 *
 * @param {String} `plural` The collection to search.
 * @param {String} `name` The name of the template.
 * @api private
 */

Verb.prototype.lookup = function(plural, name) {
  debug('lookup [plural]: %s, [name]: %s', plural, name);

  var base = path.basename(name, path.extname(name));
  var cache = this.cache[plural];

  var ext = this.option('ext');
  if (ext[0] !== '.') {
    ext = '.' + ext;
  }

  if (cache.hasOwnProperty(name)) {
    debug('lookup name: %s', name);
    return cache[name];
  }

  if (/\./.test(name) && cache.hasOwnProperty(base)) {
    debug('lookup base: %s', base);
    return cache[base];
  }

  if (cache.hasOwnProperty(name + ext)) {
    debug('lookup name + ext: %s', name + ext);
    return cache[name + ext];
  }

  return null;
};

/**
 * Run an array of tasks.
 *
 * ```js
 * verb.run(['foo', 'bar']);
 * ```
 *
 * @param {Array} `tasks`
 * @api private
 */

Verb.prototype.run = function () {
  var tasks = arguments.length ? arguments : ['default'];
  this.start.apply(this, tasks);
};

/**
 * Wrapper around Config._runTask to enable `sessions`
 *
 * @param  {Object} `task` Task to run
 * @api private
 */

Verb.prototype._runTask = function(task) {
  var verb = this;
  session.run(function () {
    session.set('task_name', task.name);
    Config.prototype._runTask.call(verb, task);
  });
};

/**
 * Convert `value` to a vinyl file.
 *
 * @param  {Object} `value`
 * @return {Object} Returns a vinyl file.
 * @api private
 */

Verb.prototype.toVinyl = function(value, options) {
  debug('toVinyl: %j', arguments);
  var opts = extend({}, this.options, options);

  var file = new File({path: value.path});
  if (value.content) {
    file.contents = new Buffer(value.content);
  }

  // create string props
  createProps(file, value, opts, ['base', 'cwd', 'relative', 'ext']);
  // create object props
  createProps(file, value, opts, ['options', 'locals', 'data'], {});
  return file;
};

/**
 * Glob patterns or filepaths to source files.
 *
 * ```js
 * verb.src('src/*.hbs', {layout: 'default'})
 * ```
 *
 * **Example usage**
 *
 * ```js
 * verb.task('site', function() {
 *   verb.src('src/*.hbs', {layout: 'default'})
 *     verb.dest('dist')
 * });
 * ```
 *
 * @param {String|Array} `glob` Glob patterns or file paths to source files.
 * @param {Object} `options` Options or locals to merge into the context and/or pass to `src` plugins
 * @api public
 */

Verb.prototype.src = function (glob, options) {
  return es.pipe.apply(es, utils.arrayify([
    vfs.src(glob, options),
    stack.src(this, glob, options)
  ]));
};

/**
 * Specify a destination for processed files.
 *
 * ```js
 * verb.dest('dist', {ext: '.xml'})
 * ```
 *
 * **Example usage**
 *
 * ```js
 * verb.task('sitemap', function() {
 *   verb.src('src/*.txt')
 *     verb.dest('dist', {ext: '.xml'})
 * });
 * ```
 *
 * @param {String|Function} `dest` File path or rename function.
 * @param {Object} `options` Options or locals to merge into the context and/or pass to `dest` plugins
 * @api public
 */

Verb.prototype.dest = function (dest, options) {
  return es.pipe.apply(es, utils.arrayify([
    stack.dest(this, dest, options),
    vfs.dest(dest, options)
  ]));
};

/**
 * Define a Verb task.
 *
 * ```js
 * verb.task('docs', function() {
 *   verb.src(['.verb.md', 'docs/*.md'])
 *     .pipe(verb.dest('./'));
 * });
 * ```
 *
 * @param {String} `name`
 * @param {Function} `fn`
 * @api public
 */

Verb.prototype.task = Verb.prototype.add;

/**
 * Re-run the specified task(s) when a file changes.
 *
 * ```js
 * verb.task('watch', function() {
 *   verb.watch('docs/*.md', ['docs']);
 * });
 * ```
 *
 * @param  {String|Array} `glob` Filepaths or glob patterns.
 * @param  {Function} `fn` Task(s) to watch.
 * @api public
 */

Verb.prototype.watch = function (glob, opts, fn) {
  if (Array.isArray(opts) || typeof opts === 'function') {
    fn = opts;
    opts = null;
  }

  if (Array.isArray(fn)) {
    return vfs.watch(glob, opts, function () {
      this.start.apply(this, fn);
    }.bind(this));
  }
  return vfs.watch(glob, opts, fn);
};

/**
 * Create default properties on a vinyl file object.
 *
 * @param  {Object} `file`
 * @param  {Object} `value`
 * @param  {Object} `opts`
 * @param  {Array} `props` Array of properties to add
 * @param  {*} `defaultVal` optionally pass a default value
 * @return {Object}
 */

function createProps(file, value, opts, props, defaultVal) {
  props = Array.isArray(props) ? props : [props];
  var len = props.length;
  var i = 0;

  while (len--) {
    var prop = props[i++];
    if (value.hasOwnProperty(prop)) {
      file[prop] = value[prop];

    } else if (opts.hasOwnProperty(prop)) {
      file[prop] = opts[prop];

    } else if (defaultVal) {
      file[prop] = defaultVal;

    } else {
      continue;
    }
  }
}
/**
 * Expose `verb.Verb`
 */

Verb.prototype.Verb = Verb;

/**
 * Expose `verb`
 */

module.exports = new Verb();
<commit_msg>adds default routes/middlware to escape delimiters<commit_after>/*!
 * verb <https://github.com/assemble/verb>
 *
 * Copyright (c) 2014 Jon Schlinkert, Brian Woodward, contributors.
 * Licensed under the MIT license.
 */

// process.env.DEBUG = 'verb:*'

'use strict';

var fs = require('fs');
var path = require('path');
var vfs = require('vinyl-fs');
var File = require('gulp-util').File;
var es = require('event-stream');
var load = require('load-plugins');
var chalk = require('chalk');
var debug = require('debug')('verb');
var session = require('session-cache')('verb');
var Template = require('template');
var tmplutil = require('template-utils');
var Config = require('orchestrator');
var stack = require('./lib/stack');
var utils = require('./lib/utils');
var _ = require('lodash');
var extend = _.extend;

/**
 * Create an instance of `Verb` with the given `options`.
 *
 * ```js
 * var verb = new Verb();
 * ```
 *
 * @param {Object} `options`
 * @constructor
 */

var Verb = module.exports = Template.extend({
  constructor: function (options) {
    Verb.__super__.constructor.call(this, options);
    Config.call(this);
    this._initialize();
  }
});

extend(Verb.prototype, Config.prototype);

/**
 * Initialize all configuration settings.
 *
 * @api private
 */

Verb.prototype._initialize = function() {
  this.fns = {};

  // load extensions first
  this.loadPlugins();
  this.loadHelpers();

  // load all defaults
  this._defaultSettings();
  this._defaultConfig();
  this._defaultTransforms();
  this._defaultDelims();
  this._defaultTemplates();
  this._defaultMiddleware();
  this._defaultHelpers();
  this._defaultAsyncHelpers();
};

/**
 * Initialize default template types
 *
 * @api private
 */

Verb.prototype._defaultConfig = function() {
  this.option('base', process.cwd());
  this.option('cwd', process.cwd());
  this.option('viewEngine', '.md');
  this.option('destExt', '.md');
  this.option('escapeDelims', {
    from: ['{%%', '%}'],
    to: ['{%', '%}']
  });
  this.option('defaults', {
    isRenderable: true,
    isPartial: true,
    engine: '.md',
    ext: '.md'
  });
};

/**
 * Initialize default template types
 *
 * @api private
 */

Verb.prototype._defaultSettings = function() {
  this.enable('debug');
  this.disable('debugEngine');

  this.enable('src:init plugin');
  this.enable('dest:render plugin');
  this.enable('dest:readme plugin');

  this.disable('dest:travis plugin');
  this.disable('travis badge');
};

/**
 * Load default transforms. Transforms are used to extend or
 * modify the `cache.data` object, but really anything on the
 * `this` object can be tranformed.
 *
 * @api private
 */

Verb.prototype._defaultTransforms = function() {
  this.transform('pkg', require('./lib/transforms/pkg'));
  this.transform('author', require('./lib/transforms/author'));
  this.transform('username', require('./lib/transforms/username'));
  this.transform('travis', require('./lib/transforms/travis'));
  this.transform('runner', require('./lib/transforms/runner'));
};

/**
 * Load default middleware
 *
 * @api private
 */

Verb.prototype._defaultMiddleware = function() {
  var escaper = tmplutil.escape;
  this.route(/\.*/).before(escaper.escape(this));
  this.route(/\.*/).after(escaper.unescape(this));
  this.use(require('./lib/middleware/ext')(this));
};

/**
 * Register default template delimiters.
 *
 *   - `['{%', '%}']` => default template delimiters
 *   - `['<<%', '%>>']` => default template delimiters
 *
 * @api private
 */

Verb.prototype._defaultDelims = function() {
  this.addDelims('md', ['{%', '%}'], ['<<%', '%>>']);
};

/**
 * Initialize default template types
 *
 * @api private
 */

Verb.prototype._defaultTemplates = function() {
  var opts = this.option('defaults');
  this.create('doc', opts);

  var createBase = require('./lib/create/base')(this, opts);
  createBase('include', require('verb-readme-includes'));
  createBase('badge', require('verb-readme-badges'));

  this.create('file', extend(opts, {
    renameKey: function (fp) {
      return fp;
    }
  }));
};

/**
 * Initialize default helpers.
 *
 * @api private
 */

Verb.prototype._defaultHelpers = function() {
  this.helper('date', require('helper-date'));
  this.helper('license', require('helper-license'));
  this.helper('copyright', require('helper-copyright'));
  this.helpers(require('logging-helpers'));
  this.helpers(require('./lib/helpers/deprecated'));
  this.helper('strip', require('./lib/helpers/strip'));
  this.helper('comments', require('./lib/helpers/comments'));
  this.helper('read', function (fp) {
    return fs.readFileSync(fp, 'utf8');
  });
};

/**
 * Initialize async helpers.
 *
 * @api private
 */

Verb.prototype._defaultAsyncHelpers = function() {
  this.asyncHelper('include', require('./lib/helpers/include')(this));
  this.asyncHelper('badge', require('./lib/helpers/badge')(this));
  this.asyncHelper('docs', require('./lib/helpers/docs')(this));
};

/**
 * Load plugins. Called in the constructor to load plugins from
 * `node_modules` using the given `namespace`. You may also call
 * the method directly.
 *
 * **Example**
 *
 * Namespace `foo` would load plugins using the
 * `foo-*` glob pattern, e.g:
 *
 * ```js
 * verb.loadPlugins('foo-*');
 * ```
 *
 * @param  {String} `pattern` Optionally pass a glob pattern when calling the method directly.
 * @return {Object} Returns an object of plugins loaded from `node_modules`.
 * @api private
 */

Verb.prototype.loadPlugins = function() {
  this.loadType('async-helper', 'async');
  this.loadType('helper', 'helpers');
};

/**
 * Register helpers that are automatically loaded.
 *
 * @api private
 */

Verb.prototype.loadHelpers = function() {
  debug('loading helpers: %j', arguments);
  var fn, name;

  var helpers = Object.keys(this.fns.helpers);
  var len = helpers.length;
  var i = 0;

  while (i < len) {
    name = helpers[i++];
    fn = this.fns.helpers[name];
    this.helper(name, fn);
  }

  var async = Object.keys(this.fns.async);
  var alen = async.length;
  var j = 0;

  while (j < len) {
    name = async[j++];
    fn = this.fns.async[name];
    this.asyncHelper(name, fn);
  }

  return this;
};

/**
 * Private method to create a plugin loader for the
 * given plugin `type`, e.g. "helper"
 *
 * @param  {String} `type` The plugin type, e.g. "helper"
 * @param  {String} `plural` Plural form of `type`, e.g. "helpers"
 * @return {Object} `fns` Object of plugins, key-value pairs. The value is a function.
 * @api private
 */

Verb.prototype.loadType = function(type, plural) {
  debug('loading type: %s', type);

  this.fns[plural] = this.fns[plural] || {};
  extend(this.fns[plural], load(type + '*', {
    strip: type,
    cwd: process.cwd()
  }));

  return this.fns[plural];
};

/**
 * Convenience method for looking up a template
 * on the cache by:
 *
 *   1. `name`, as-is
 *   2. If `name` has an extension, try without it
 *   3. If `name` does not have an extension, try `name.md`
 *
 * @param {String} `plural` The collection to search.
 * @param {String} `name` The name of the template.
 * @api private
 */

Verb.prototype.lookup = function(plural, name) {
  debug('lookup [plural]: %s, [name]: %s', plural, name);

  var base = path.basename(name, path.extname(name));
  var cache = this.cache[plural];

  var ext = this.option('ext');
  if (ext[0] !== '.') {
    ext = '.' + ext;
  }

  if (cache.hasOwnProperty(name)) {
    debug('lookup name: %s', name);
    return cache[name];
  }

  if (/\./.test(name) && cache.hasOwnProperty(base)) {
    debug('lookup base: %s', base);
    return cache[base];
  }

  if (cache.hasOwnProperty(name + ext)) {
    debug('lookup name + ext: %s', name + ext);
    return cache[name + ext];
  }

  return null;
};

/**
 * Run an array of tasks.
 *
 * ```js
 * verb.run(['foo', 'bar']);
 * ```
 *
 * @param {Array} `tasks`
 * @api private
 */

Verb.prototype.run = function () {
  var tasks = arguments.length ? arguments : ['default'];
  this.start.apply(this, tasks);
};

/**
 * Wrapper around Config._runTask to enable `sessions`
 *
 * @param  {Object} `task` Task to run
 * @api private
 */

Verb.prototype._runTask = function(task) {
  var verb = this;
  session.run(function () {
    session.set('task_name', task.name);
    Config.prototype._runTask.call(verb, task);
  });
};

/**
 * Convert `value` to a vinyl file.
 *
 * @param  {Object} `value`
 * @return {Object} Returns a vinyl file.
 * @api private
 */

Verb.prototype.toVinyl = function(value, options) {
  debug('toVinyl: %j', arguments);
  var opts = extend({}, this.options, options);

  var file = new File({
    cwd: value.cwd,
    base: value.base,
    path: value.path,
  });

  if (value.content) {
    file.contents = new Buffer(value.content);
  }

  // create string props
  createProps(file, value, opts, ['ext']);
  // create object props
  createProps(file, value, opts, ['options', 'locals', 'data'], {});
  return file;
};

/**
 * Glob patterns or filepaths to source files.
 *
 * ```js
 * verb.src('src/*.hbs', {layout: 'default'})
 * ```
 *
 * **Example usage**
 *
 * ```js
 * verb.task('site', function() {
 *   verb.src('src/*.hbs', {layout: 'default'})
 *     verb.dest('dist')
 * });
 * ```
 *
 * @param {String|Array} `glob` Glob patterns or file paths to source files.
 * @param {Object} `options` Options or locals to merge into the context and/or pass to `src` plugins
 * @api public
 */

Verb.prototype.src = function (glob, options) {
  return es.pipe.apply(es, utils.arrayify([
    vfs.src(glob, options),
    stack.src(this, glob, options)
  ]));
};

/**
 * Specify a destination for processed files.
 *
 * ```js
 * verb.dest('dist', {ext: '.xml'})
 * ```
 *
 * **Example usage**
 *
 * ```js
 * verb.task('sitemap', function() {
 *   verb.src('src/*.txt')
 *     verb.dest('dist', {ext: '.xml'})
 * });
 * ```
 *
 * @param {String|Function} `dest` File path or rename function.
 * @param {Object} `options` Options or locals to merge into the context and/or pass to `dest` plugins
 * @api public
 */

Verb.prototype.dest = function (dest, options) {
  return es.pipe.apply(es, utils.arrayify([
    stack.dest(this, dest, options),
    vfs.dest(dest, options)
  ]));
};

/**
 * Define a Verb task.
 *
 * ```js
 * verb.task('docs', function() {
 *   verb.src(['.verb.md', 'docs/*.md'])
 *     .pipe(verb.dest('./'));
 * });
 * ```
 *
 * @param {String} `name`
 * @param {Function} `fn`
 * @api public
 */

Verb.prototype.task = Verb.prototype.add;

/**
 * Re-run the specified task(s) when a file changes.
 *
 * ```js
 * verb.task('watch', function() {
 *   verb.watch('docs/*.md', ['docs']);
 * });
 * ```
 *
 * @param  {String|Array} `glob` Filepaths or glob patterns.
 * @param  {Function} `fn` Task(s) to watch.
 * @api public
 */

Verb.prototype.watch = function (glob, opts, fn) {
  if (Array.isArray(opts) || typeof opts === 'function') {
    fn = opts;
    opts = null;
  }

  if (Array.isArray(fn)) {
    return vfs.watch(glob, opts, function () {
      this.start.apply(this, fn);
    }.bind(this));
  }
  return vfs.watch(glob, opts, fn);
};

/**
 * Create default properties on a vinyl file object.
 *
 * @param  {Object} `file`
 * @param  {Object} `value`
 * @param  {Object} `opts`
 * @param  {Array} `props` Array of properties to add
 * @param  {*} `defaultVal` optionally pass a default value
 * @return {Object}
 */

function createProps(file, value, opts, props, defaultVal) {
  props = Array.isArray(props) ? props : [props];
  var len = props.length;
  var i = 0;

  while (len--) {
    var prop = props[i++];
    if (value.hasOwnProperty(prop)) {
      file[prop] = value[prop];

    } else if (opts.hasOwnProperty(prop)) {
      file[prop] = opts[prop];

    } else if (defaultVal) {
      file[prop] = defaultVal;

    } else {
      continue;
    }
  }
}
/**
 * Expose `verb.Verb`
 */

Verb.prototype.Verb = Verb;

/**
 * Expose `verb`
 */

module.exports = new Verb();
<|endoftext|>


<commit_before>let debug = require('./debug')('indexd')
let dbwrapper = require('./dbwrapper')
let { EventEmitter } = require('events')
let parallel = require('run-parallel')
let rpcUtil = require('./rpc')

let FeeIndex = require('./indexes/fee')
let MtpIndex = require('./indexes/mediantime')
let ScriptIndex = require('./indexes/script')
let TxIndex = require('./indexes/tx')
let TxinIndex = require('./indexes/txin')
let TxoIndex = require('./indexes/txo')

function txoToString ({ txId, vout }) {
  return `${txId}:${vout}`
}

function Indexd (db, rpc) {
  this.db = dbwrapper(db)
  this.rpc = rpc
  this.emitter = new EventEmitter() // TODO: bind to this
  this.emitter.setMaxListeners(Infinity)
  this.indexes = {
    fee: new FeeIndex(),
    mtp: new MtpIndex(),
    script: new ScriptIndex(),
    tx: new TxIndex(),
    txin: new TxinIndex(),
    txo: new TxoIndex()
  }
}

Indexd.prototype.tips = function (callback) {
  let tasks = {}

  for (let indexName in this.indexes) {
    let index = this.indexes[indexName]
    tasks[indexName] = (next) => index.tip(this.db, next)
  }

  parallel(tasks, callback)
}

// recurses until `nextBlockId` is falsy
Indexd.prototype.connectFrom = function (prevBlockId, blockId, callback) {
  this.tips((err, tips) => {
    if (err) return callback(err)

    let todo = {}
    for (let indexName in tips) {
      let tip = tips[indexName]
      if (tip && tip.blockId !== prevBlockId) continue
      if (indexName === 'fee') {
        if (!tips.txo) continue
        if (tip && tips.fee.height > tips.txo.height) continue
      }

      todo[indexName] = true
    }

    let todoList = Object.keys(todo)
    if (todoList.length === 0) return callback(new RangeError('Misconfiguration'))

    debug(`Downloading ${blockId} (for ${todoList})`)

    rpcUtil.block(this.rpc, blockId, (err, block) => {
      if (err) return callback(err)

      let atomic = this.db.atomic()
      let events // TODO
      let { height } = block
      debug(`Connecting ${blockId} @ ${height}`)

      // connect block to relevant chain tips
      for (let indexName in todo) {
        let index = this.indexes[indexName]
        if (!index.connect) continue

        index.connect(atomic, block, events)
      }

      atomic.write((err) => {
        if (err) return callback(err)
        debug(`Connected ${blockId} @ ${height}`)

        let self = this
        function loop (err) {
          if (err) return callback(err)

          // recurse until nextBlockId is falsy
          if (!block.nextBlockId) return callback(null, true)
          self.connectFrom(blockId, block.nextBlockId, callback)
        }

        if (!todo.fee) return loop()

        debug(`Connecting ${blockId} (2nd Order)`)
        let atomic2 = this.db.atomic()
        this.indexes.fee.connect2ndOrder(this.db, this.indexes.txo, atomic2, block, (err) => {
          if (err) return loop(err)

          debug(`Connected ${blockId} (2nd Order)`)
          atomic2.write(loop)
        })
      })
    })
  })
}

Indexd.prototype.disconnect = function (blockId, callback) {
  debug(`Disconnecting ${blockId}`)

  function fin (err) {
    if (err) return callback(err)
    debug(`Disconnected ${blockId}`)
    callback()
  }

  this.tips((err, tips) => {
    if (err) return fin(err)

    // TODO: fetch lazily
    rpcUtil.block(this.rpc, blockId, (err, block) => {
      if (err) return fin(err)

      let atomic = this.db.atomic()

      // disconnect block from relevant chain tips
      for (let indexName in this.indexes) {
        let index = this.indexes[indexName]
        let tip = tips[indexName]
        if (!tip) continue
        if (tip.blockId !== block.blockId) continue

        index.disconnect(atomic, block)
      }

      atomic.write(fin)
    })
  })
}

// empties the mempool
Indexd.prototype.clear = function () {
  for (let indexName in this.indexes) {
    this.indexes[indexName].constructor()
  }
}

Indexd.prototype.__resync = function (done) {
  debug('resynchronizing')

  let self = this
  function lowestTip (callback) {
    self.tips((err, tips) => {
      if (err) return callback(err)

      let lowest
      for (let key in tips) {
        let tip = tips[key]
        if (!tip) return callback()
        if (!lowest) lowest = tip
        if (lowest.height < tip.height) continue
        lowest = tip
      }

      callback(null, lowest)
    })
  }

  parallel({
    bitcoind: (f) => rpcUtil.tip(this.rpc, f),
    indexd: (f) => lowestTip(f)
  }, (err, r) => {
    if (err) return done(err)

    // Step 0, genesis?
    if (!r.indexd) {
      debug('genesis')
      return rpcUtil.blockIdAtHeight(this.rpc, 0, (err, genesisId) => {
        if (err) return done(err)

        this.connectFrom(null, genesisId, done)
      })
    }

    // Step 1, equal?
    debug('...', r)
    if (r.bitcoind.blockId === r.indexd.blockId) return done()

    // Step 2, is indexd behind? [aka, does bitcoind have the indexd tip]
    rpcUtil.headerJSON(this.rpc, r.indexd.blockId, (err, common) => {
//        if (err && /not found/.test(err.message)) return fin(err) // uh, burn it to the ground
      if (err) return done(err)

      // forked?
      if (common.confirmations === -1) {
        debug('forked')
        return this.disconnect(r.indexd.blockId, (err) => {
          if (err) return done(err)

          this.__resync(done)
        })
      }

      // yes, indexd is behind
      debug('bitcoind is ahead')
      this.connectFrom(common.blockId, common.nextBlockId, done)
    })
  })
}

Indexd.prototype.tryResync = function (callback) {
  if (callback) {
    this.emitter.once('resync', callback)
  }

  if (this.syncing) return
  this.syncing = true

  let self = this
  function fin (err) {
    self.syncing = false
    self.emitter.emit('resync', err)
  }

  this.__resync((err, updated) => {
    if (err) return fin(err)
    if (updated) return this.tryResyncMempool(fin)
    fin()
  })
}

Indexd.prototype.tryResyncMempool = function (callback) {
  rpcUtil.mempool(this.rpc, (err, txIds) => {
    if (err) return callback(err)

    this.clear()
    parallel(txIds.map((txId) => (next) => this.notify(txId, next)), callback)
  })
}

Indexd.prototype.notify = function (txId, callback) {
  rpcUtil.transaction(this.rpc, txId, (err, tx) => {
    if (err) return callback(err)

    for (let indexName in this.indexes) {
      let index = this.indexes[indexName]

      if (!index.mempool) continue
      index.mempool(tx)
    }

    callback()
  })
}

// QUERIES
Indexd.prototype.blockIdByTransactionId = function (txId, callback) {
  this.indexes.tx.heightBy(this.db, txId, (err, height) => {
    if (err) return callback(err)
    if (height === -1) return callback()

    rpcUtil.blockIdAtHeight(this.rpc, height, callback)
  })
}

Indexd.prototype.latestFeesForNBlocks = function (nBlocks, callback) {
  this.indexes.fee.latestFeesFor(this.db, nBlocks, callback)
}

// returns a txo { txId, vout, value, script }, by key { txId, vout }
Indexd.prototype.txoByTxo = function (txo, callback) {
  this.indexes.txo.txoBy(this.db, txo, callback)
}

// returns the height at scId was first-seen (-1 if unconfirmed)
Indexd.prototype.firstSeenScriptId = function (scId, callback) {
  this.indexes.script.firstSeenScriptId(this.db, scId, callback)
}

// returns a list of txIds with inputs/outputs from/to a { scId, heightRange, ?mempool }
Indexd.prototype.transactionIdsByScriptRange = function (scRange, dbLimit, callback) {
  this.txosByScriptRange(scRange, dbLimit, (err, txos) => {
    if (err) return callback(err)

    let txIdSet = {}
    let tasks = txos.map((txo) => {
      txIdSet[txo.txId] = true
      return (next) => this.indexes.txin.txinBy(this.db, txo, next)
    })

    parallel(tasks, (err, txins) => {
      if (err) return callback(err)

      txins.forEach((txin) => {
        if (!txin) return
        txIdSet[txin.txId] = true
      })

      callback(null, Object.keys(txIdSet))
    })
  })
}

// returns a list of txos { txId, vout, height, value } by { scId, heightRange, ?mempool }
Indexd.prototype.txosByScriptRange = function (scRange, dbLimit, callback) {
  this.indexes.script.txosBy(this.db, scRange, dbLimit, callback)
}

// returns a list of (unspent) txos { txId, vout, height, value }, by { scId, heightRange, ?mempool }
// XXX: despite txo queries being bound by heightRange, the UTXO status is up-to-date
Indexd.prototype.utxosByScriptRange = function (scRange, dbLimit, callback) {
  this.txosByScriptRange(scRange, dbLimit, (err, txos) => {
    if (err) return callback(err)

    let taskMap = {}
    let unspentMap = {}

    txos.forEach((txo) => {
      let txoId = txoToString(txo)
      unspentMap[txoId] = txo
      taskMap[txoId] = (next) => this.indexes.txin.txinBy(this.db, txo, next)
    })

    parallel(taskMap, (err, txinMap) => {
      if (err) return callback(err)

      let unspents = []
      for (let txoId in txinMap) {
        let txin = txinMap[txoId]

        // has a txin, therefore spent
        if (txin) continue

        unspents.push(unspentMap[txoId])
      }

      callback(null, unspents)
    })
  })
}

module.exports = Indexd
<commit_msg>Expose lowestTip method, e.g. for querying indexing status<commit_after>let debug = require('./debug')('indexd')
let dbwrapper = require('./dbwrapper')
let { EventEmitter } = require('events')
let parallel = require('run-parallel')
let rpcUtil = require('./rpc')

let FeeIndex = require('./indexes/fee')
let MtpIndex = require('./indexes/mediantime')
let ScriptIndex = require('./indexes/script')
let TxIndex = require('./indexes/tx')
let TxinIndex = require('./indexes/txin')
let TxoIndex = require('./indexes/txo')

function txoToString ({ txId, vout }) {
  return `${txId}:${vout}`
}

function Indexd (db, rpc) {
  this.db = dbwrapper(db)
  this.rpc = rpc
  this.emitter = new EventEmitter() // TODO: bind to this
  this.emitter.setMaxListeners(Infinity)
  this.indexes = {
    fee: new FeeIndex(),
    mtp: new MtpIndex(),
    script: new ScriptIndex(),
    tx: new TxIndex(),
    txin: new TxinIndex(),
    txo: new TxoIndex()
  }
}

Indexd.prototype.tips = function (callback) {
  let tasks = {}

  for (let indexName in this.indexes) {
    let index = this.indexes[indexName]
    tasks[indexName] = (next) => index.tip(this.db, next)
  }

  parallel(tasks, callback)
}

// recurses until `nextBlockId` is falsy
Indexd.prototype.connectFrom = function (prevBlockId, blockId, callback) {
  this.tips((err, tips) => {
    if (err) return callback(err)

    let todo = {}
    for (let indexName in tips) {
      let tip = tips[indexName]
      if (tip && tip.blockId !== prevBlockId) continue
      if (indexName === 'fee') {
        if (!tips.txo) continue
        if (tip && tips.fee.height > tips.txo.height) continue
      }

      todo[indexName] = true
    }

    let todoList = Object.keys(todo)
    if (todoList.length === 0) return callback(new RangeError('Misconfiguration'))

    debug(`Downloading ${blockId} (for ${todoList})`)

    rpcUtil.block(this.rpc, blockId, (err, block) => {
      if (err) return callback(err)

      let atomic = this.db.atomic()
      let events // TODO
      let { height } = block
      debug(`Connecting ${blockId} @ ${height}`)

      // connect block to relevant chain tips
      for (let indexName in todo) {
        let index = this.indexes[indexName]
        if (!index.connect) continue

        index.connect(atomic, block, events)
      }

      atomic.write((err) => {
        if (err) return callback(err)
        debug(`Connected ${blockId} @ ${height}`)

        let self = this
        function loop (err) {
          if (err) return callback(err)

          // recurse until nextBlockId is falsy
          if (!block.nextBlockId) return callback(null, true)
          self.connectFrom(blockId, block.nextBlockId, callback)
        }

        if (!todo.fee) return loop()

        debug(`Connecting ${blockId} (2nd Order)`)
        let atomic2 = this.db.atomic()
        this.indexes.fee.connect2ndOrder(this.db, this.indexes.txo, atomic2, block, (err) => {
          if (err) return loop(err)

          debug(`Connected ${blockId} (2nd Order)`)
          atomic2.write(loop)
        })
      })
    })
  })
}

Indexd.prototype.disconnect = function (blockId, callback) {
  debug(`Disconnecting ${blockId}`)

  function fin (err) {
    if (err) return callback(err)
    debug(`Disconnected ${blockId}`)
    callback()
  }

  this.tips((err, tips) => {
    if (err) return fin(err)

    // TODO: fetch lazily
    rpcUtil.block(this.rpc, blockId, (err, block) => {
      if (err) return fin(err)

      let atomic = this.db.atomic()

      // disconnect block from relevant chain tips
      for (let indexName in this.indexes) {
        let index = this.indexes[indexName]
        let tip = tips[indexName]
        if (!tip) continue
        if (tip.blockId !== block.blockId) continue

        index.disconnect(atomic, block)
      }

      atomic.write(fin)
    })
  })
}

// empties the mempool
Indexd.prototype.clear = function () {
  for (let indexName in this.indexes) {
    this.indexes[indexName].constructor()
  }
}

Indexd.prototype.lowestTip = function(callback) {
  this.tips((err, tips) => {
    if (err) return callback(err)

    let lowest
    for (let key in tips) {
      let tip = tips[key]
      if (!tip) return callback()
      if (!lowest) lowest = tip
      if (lowest.height < tip.height) continue
      lowest = tip
    }

    callback(null, lowest)
  })
}

Indexd.prototype.__resync = function (done) {
  debug('resynchronizing')

  parallel({
    bitcoind: (f) => rpcUtil.tip(this.rpc, f),
    indexd: (f) => this.lowestTip(f)
  }, (err, r) => {
    if (err) return done(err)

    // Step 0, genesis?
    if (!r.indexd) {
      debug('genesis')
      return rpcUtil.blockIdAtHeight(this.rpc, 0, (err, genesisId) => {
        if (err) return done(err)

        this.connectFrom(null, genesisId, done)
      })
    }

    // Step 1, equal?
    debug('...', r)
    if (r.bitcoind.blockId === r.indexd.blockId) return done()

    // Step 2, is indexd behind? [aka, does bitcoind have the indexd tip]
    rpcUtil.headerJSON(this.rpc, r.indexd.blockId, (err, common) => {
//        if (err && /not found/.test(err.message)) return fin(err) // uh, burn it to the ground
      if (err) return done(err)

      // forked?
      if (common.confirmations === -1) {
        debug('forked')
        return this.disconnect(r.indexd.blockId, (err) => {
          if (err) return done(err)

          this.__resync(done)
        })
      }

      // yes, indexd is behind
      debug('bitcoind is ahead')
      this.connectFrom(common.blockId, common.nextBlockId, done)
    })
  })
}

Indexd.prototype.tryResync = function (callback) {
  if (callback) {
    this.emitter.once('resync', callback)
  }

  if (this.syncing) return
  this.syncing = true

  let self = this
  function fin (err) {
    self.syncing = false
    self.emitter.emit('resync', err)
  }

  this.__resync((err, updated) => {
    if (err) return fin(err)
    if (updated) return this.tryResyncMempool(fin)
    fin()
  })
}

Indexd.prototype.tryResyncMempool = function (callback) {
  rpcUtil.mempool(this.rpc, (err, txIds) => {
    if (err) return callback(err)

    this.clear()
    parallel(txIds.map((txId) => (next) => this.notify(txId, next)), callback)
  })
}

Indexd.prototype.notify = function (txId, callback) {
  rpcUtil.transaction(this.rpc, txId, (err, tx) => {
    if (err) return callback(err)

    for (let indexName in this.indexes) {
      let index = this.indexes[indexName]

      if (!index.mempool) continue
      index.mempool(tx)
    }

    callback()
  })
}

// QUERIES
Indexd.prototype.blockIdByTransactionId = function (txId, callback) {
  this.indexes.tx.heightBy(this.db, txId, (err, height) => {
    if (err) return callback(err)
    if (height === -1) return callback()

    rpcUtil.blockIdAtHeight(this.rpc, height, callback)
  })
}

Indexd.prototype.latestFeesForNBlocks = function (nBlocks, callback) {
  this.indexes.fee.latestFeesFor(this.db, nBlocks, callback)
}

// returns a txo { txId, vout, value, script }, by key { txId, vout }
Indexd.prototype.txoByTxo = function (txo, callback) {
  this.indexes.txo.txoBy(this.db, txo, callback)
}

// returns the height at scId was first-seen (-1 if unconfirmed)
Indexd.prototype.firstSeenScriptId = function (scId, callback) {
  this.indexes.script.firstSeenScriptId(this.db, scId, callback)
}

// returns a list of txIds with inputs/outputs from/to a { scId, heightRange, ?mempool }
Indexd.prototype.transactionIdsByScriptRange = function (scRange, dbLimit, callback) {
  this.txosByScriptRange(scRange, dbLimit, (err, txos) => {
    if (err) return callback(err)

    let txIdSet = {}
    let tasks = txos.map((txo) => {
      txIdSet[txo.txId] = true
      return (next) => this.indexes.txin.txinBy(this.db, txo, next)
    })

    parallel(tasks, (err, txins) => {
      if (err) return callback(err)

      txins.forEach((txin) => {
        if (!txin) return
        txIdSet[txin.txId] = true
      })

      callback(null, Object.keys(txIdSet))
    })
  })
}

// returns a list of txos { txId, vout, height, value } by { scId, heightRange, ?mempool }
Indexd.prototype.txosByScriptRange = function (scRange, dbLimit, callback) {
  this.indexes.script.txosBy(this.db, scRange, dbLimit, callback)
}

// returns a list of (unspent) txos { txId, vout, height, value }, by { scId, heightRange, ?mempool }
// XXX: despite txo queries being bound by heightRange, the UTXO status is up-to-date
Indexd.prototype.utxosByScriptRange = function (scRange, dbLimit, callback) {
  this.txosByScriptRange(scRange, dbLimit, (err, txos) => {
    if (err) return callback(err)

    let taskMap = {}
    let unspentMap = {}

    txos.forEach((txo) => {
      let txoId = txoToString(txo)
      unspentMap[txoId] = txo
      taskMap[txoId] = (next) => this.indexes.txin.txinBy(this.db, txo, next)
    })

    parallel(taskMap, (err, txinMap) => {
      if (err) return callback(err)

      let unspents = []
      for (let txoId in txinMap) {
        let txin = txinMap[txoId]

        // has a txin, therefore spent
        if (txin) continue

        unspents.push(unspentMap[txoId])
      }

      callback(null, unspents)
    })
  })
}

module.exports = Indexd
<|endoftext|>


<commit_before>'use strict';
var Dropbox = require('dropbox');
var options;

var queue = {};

function addToDropboxQueue (message) {
  var user = message.user;
  var fileContent = message.file;
  var fileName = message.fileName;
  var saveObject = {
    file: fileContent,
    token: user.dropbox_token, // jshint ignore:line
    timeout: setTimeout(function(){
      saveToDropBox(fileContent, fileName, user);
    }, options.delayTime)
  };
  if (queue[fileName]) {
    clearTimeout(queue[fileName].timeout);
  }
  queue[fileName] = saveObject;
}

function saveToDropBox (file, name, user) {
  var client = new Dropbox.Client({
    key: options.id,
    secret: options.secret,
    token: user.dropbox_token // jshint ignore:line
  });

  client.writeFile(name, file, function (err) {
    if (err) {
      process.send({
        error: err,
        user: user
      });
    }
  });

}

function handleMessages(message) {
  if (message.file) {
    addToDropboxQueue(message);
  } else
  if (message.options) {
    options = message.options;
    options.delayTime *= 1000;
  }
}

module.exports = function (socket) {
  socket.on('message', function (msg) {
    handleMessages(JSON.parse(msg.toString()));
  });
};
<commit_msg>Set config via module.exports not message<commit_after>'use strict';
var Dropbox = require('dropbox');
var options;

var queue = {};

function addToDropboxQueue (message) {
  var user = message.user;
  var fileContent = message.file;
  var fileName = message.fileName;
  var saveObject = {
    file: fileContent,
    token: user.dropbox_token, // jshint ignore:line
    timeout: setTimeout(function(){
      saveToDropBox(fileContent, fileName, user);
    }, options.delayTime)
  };
  if (queue[fileName]) {
    clearTimeout(queue[fileName].timeout);
  }
  queue[fileName] = saveObject;
}

function saveToDropBox (file, name, user) {
  var client = new Dropbox.Client({
    key: options.id,
    secret: options.secret,
    token: user.dropbox_token // jshint ignore:line
  });

  client.writeFile(name, file, function (err) {
    if (err) {
      process.send({
        error: err,
        user: user
      });
    }
  });

}

function handleMessages(message) {
  if (message.file) {
    addToDropboxQueue(message);
  }
}

module.exports = function (socket, config) {
  options = config;
  options.delayTime *= 1000;
  socket.on('message', function (msg) {
    handleMessages(JSON.parse(msg.toString()));
  });
};
<|endoftext|>


<commit_before>'use strict';

var delaysqs = function (sqs, queueUrl, messageCallback, errorCallback) {

    var polling = false;

    var _processMessage = function (message, next) {
        var deliveryTimestamp = (message.MessageAttributes && message.MessageAttributes.deliveryTimestamp) ?
            parseInt(message.MessageAttributes.deliveryTimestamp.StringValue) : 0;
        if (deliveryTimestamp) {
            var delay = deliveryTimestamp - new Date().getTime();
            if (delay > 0) {
                _resendToQueue(message, delay, next);
            } else {
                _notifyAndDelete(message, next);
            }
        } else {
            _notifyAndDelete(message, next);
        }
    };

    var _notifyAndDelete = function (message, next) {
        messageCallback(message.Body);
        sqs.deleteMessage({
            ReceiptHandle: message.ReceiptHandle
        }, function (err, data) {
            if (err) {
                errorCallback(err);
            }
            next();
        });
    };

    var _resendToQueue = function(message, delay, next) {

        var delayInSeconds = Math.min(Math.floor(delay / 1000), 900);
        sqs.sendMessage({
            MessageBody: message.Body,
            DelaySeconds: delayInSeconds,
            MessageAttributes: message.MessageAttributes,
            QueueUrl: queueUrl
        }, function (err, data) {
            if (err) {
                errorCallback(err);
                next();
            } else {
                console.info("Message with id %s put back to queue, deleting current instance", data.MessageId);
                sqs.deleteMessage({
                    ReceiptHandle: message.ReceiptHandle
                }, function (err, data) {
                    if (err) {
                        errorCallback(err);
                    }
                    next();
                });
            }
        });
    };

    var _enqueueMessage = function (payload, deliveryTimestamp, callback) {
        var delay = deliveryTimestamp - new Date().getTime();
        var delayInSeconds = Math.min(Math.floor(delay / 1000), 900);
        if (delay > 0) {
            sqs.sendMessage({
                QueueUrl: queueUrl,
                MessageBody: payload,
                DelaySeconds: delayInSeconds,
                MessageAttributes: {
                    deliveryTimestamp: {
                        DataType: "Number",
                        StringValue: deliveryTimestamp.toString()
                    }
                }
            },
            function (err, data) {
                if (err) {
                    callback(err, null);
                } else {
                    callback(null, data.MessageId);
                }
            });
        } else {
            messageCallback(payload);
            callback(null, null);
        }
    };

    var _pollQueueForMessages = function () {
        if (polling) {
            console.info("Starting the long poll for messages");
            sqs.receiveMessage({
                WaitTimeSeconds: 20,
                VisibilityTimeout: 10,
                MaxNumberOfMessages: 5,
                MessageAttributeNames: ['All'],
                QueueUrl: queueUrl

            }, function (err, data) {
                if (err) {
                    console.warn("An error occurred while retrieving messages");
                    errorCallback(err);
                    process.nextTick(_pollQueueForMessages);
                } else {
                    if (data.Messages) {
                        console.info("Received %d message(s)", data.Messages.length);
                        var completed = 0;
                        for (var i = 0; i < data.Messages.length; i++) {
                            _processMessage(data.Messages[i], function () {
                                completed++;
                                if (completed == data.Messages.length) {
                                    process.nextTick(_pollQueueForMessages);
                                }
                            });
                        }
                    } else {
                        console.info("Returning from long poll with no messages");
                        process.nextTick(_pollQueueForMessages);
                    }
                }
            });
        }
    };

    var _startPolling = function () {
        if (!polling) {
            polling = true;
            process.nextTick(_pollQueueForMessages);
        }
    };

    var _stopPolling = function () {
        polling = false;
    };

    var _isPolling = function () {
        return polling;
    };

    return ({
        startPolling: _startPolling,
        stopPolling: _stopPolling,
        enqueueMessage: _enqueueMessage,
        isPolling: _isPolling
    });

};

module.exports = delaysqs;

<commit_msg>refactored code to delete message<commit_after>'use strict';

var delaysqs = function (sqs, queueUrl, messageCallback, errorCallback) {

    var polling = false;

    var _processMessage = function (message, next) {
        var deliveryTimestamp = (message.MessageAttributes && message.MessageAttributes.deliveryTimestamp) ?
            parseInt(message.MessageAttributes.deliveryTimestamp.StringValue) : 0;
        if (deliveryTimestamp) {
            var delay = deliveryTimestamp - new Date().getTime();
            if (delay > 0) {
                _resendToQueue(message, delay, next);
            } else {
                _notifyAndDelete(message, next);
            }
        } else {
            _notifyAndDelete(message, next);
        }
    };

    var _notifyAndDelete = function (message, next) {
        messageCallback(message.Body);
        _deleteMessage(message.ReceiptHandle, next);
    };

    var _deleteMessage = function (receiptHandle, next) {
        sqs.deleteMessage({
            ReceiptHandle: receiptHandle
        }, function (err, data) {
            if (err) {
                errorCallback(err);
            }
            next();
        });
    };

    var _resendToQueue = function(message, delay, next) {

        var delayInSeconds = Math.min(Math.floor(delay / 1000), 900);
        sqs.sendMessage({
            MessageBody: message.Body,
            DelaySeconds: delayInSeconds,
            MessageAttributes: message.MessageAttributes,
            QueueUrl: queueUrl
        }, function (err, data) {
            if (err) {
                errorCallback(err);
                next();
            } else {
                console.info("Message with id %s put back to queue, deleting current instance", data.MessageId);
                _deleteMessage(message.ReceiptHandle, next);
            }
        });
    };

    var _enqueueMessage = function (payload, deliveryTimestamp, callback) {
        var delay = deliveryTimestamp - new Date().getTime();
        var delayInSeconds = Math.min(Math.floor(delay / 1000), 900);
        if (delay > 0) {
            sqs.sendMessage({
                QueueUrl: queueUrl,
                MessageBody: payload,
                DelaySeconds: delayInSeconds,
                MessageAttributes: {
                    deliveryTimestamp: {
                        DataType: "Number",
                        StringValue: deliveryTimestamp.toString()
                    }
                }
            },
            function (err, data) {
                if (err) {
                    callback(err, null);
                } else {
                    callback(null, data.MessageId);
                }
            });
        } else {
            messageCallback(payload);
            callback(null, null);
        }
    };

    var _pollQueueForMessages = function () {
        if (polling) {
            console.info("Starting the long poll for messages");
            sqs.receiveMessage({
                WaitTimeSeconds: 20,
                VisibilityTimeout: 10,
                MaxNumberOfMessages: 5,
                MessageAttributeNames: ['All'],
                QueueUrl: queueUrl

            }, function (err, data) {
                if (err) {
                    console.warn("An error occurred while retrieving messages");
                    errorCallback(err);
                    process.nextTick(_pollQueueForMessages);
                } else {
                    if (data.Messages) {
                        console.info("Received %d message(s)", data.Messages.length);
                        var completed = 0;
                        for (var i = 0; i < data.Messages.length; i++) {
                            _processMessage(data.Messages[i], function () {
                                completed++;
                                if (completed == data.Messages.length) {
                                    process.nextTick(_pollQueueForMessages);
                                }
                            });
                        }
                    } else {
                        console.info("Returning from long poll with no messages");
                        process.nextTick(_pollQueueForMessages);
                    }
                }
            });
        }
    };

    var _startPolling = function () {
        if (!polling) {
            polling = true;
            process.nextTick(_pollQueueForMessages);
        }
    };

    var _stopPolling = function () {
        polling = false;
    };

    var _isPolling = function () {
        return polling;
    };

    return ({
        startPolling: _startPolling,
        stopPolling: _stopPolling,
        enqueueMessage: _enqueueMessage,
        isPolling: _isPolling
    });

};

module.exports = delaysqs;

<|endoftext|>


<commit_before>var editor = []
var menu
var output, html, css, js

const {
  remote,
  clipboard
} = require('electron')

const {
  Menu,
  MenuItem
} = remote

// Global variables
var scripts = ''
var styles = ''

// Functions for AddIns
const getScr = () => scripts

const getSty = () => styles

function newFile () {
  fileEntry = null
  hasWriteAccess = false
}

function handleNewButton (i) {
  if (false) {
    newFile()
    editor[i].setValue('')
  } else {
    window.open('file://' + __dirname + '/index.html')
  }
}

// Context menu init()
function initContextMenu (i) {
  menu = new Menu()
  menu.append(new MenuItem({
    label: 'Copy',
    click: function () {
      clipboard.writeText(editor[i].getSelection(), 'copy')
    }
  }))
  menu.append(new MenuItem({
    label: 'Cut',
    click: function () {
      clipboard.writeText(editor[i].getSelection(), 'copy')
      editor[i].replaceSelection('')
    }
  }))
  menu.append(new MenuItem({
    label: 'Paste',
    click: function () {
      editor[i].replaceSelection(clipboard.readText('copy'))
    }
  }))

  window.addEventListener('contextmenu', function (ev) {
    ev.preventDefault()
    menu.popup(remote.getCurrentWindow(), ev.x, ev.y)
  }, false)
}

// Main Functions for Electron
onload = function () {
  initContextMenu()

  document.getElementById('min-button').addEventListener('click', function (e) {
    const window = remote.getCurrentWindow()
    window.minimize()
    console.log('Minimize Triggered!')
  })

  document.getElementById('max-button').addEventListener('click', function (e) {
    const window = remote.getCurrentWindow()
    if (!window.isMaximized()) {
      window.maximize()
    } else {
      window.unmaximize()
    }
    console.log('Maximize Triggered!')
    console.log(document.getElementById('close-button'))
  })

  document.getElementById('close-button').addEventListener('click', function (e) {
    const window = remote.getCurrentWindow()
    window.close()
  })

  document.getElementById('close-window').addEventListener('click', function (e) {
    const window = remote.getCurrentWindow()
    window.close()
  })
  document.getElementById('new').addEventListener('click', handleNewButton)

  editor[0] = CodeMirror(
    document.getElementById('html-editor'), {
      mode: {
        name: 'htmlmixed'
      },
      lineNumbers: true,
      lineWrapping: true,
      autofocus: true,
      indentWithTabs: true,
      scrollbarStyle: 'overlay',
      theme: 'base16-ocean-dark'
    })

  editor[1] = CodeMirror(
    document.getElementById('css-editor'), {
      mode: {
        name: 'css'
      },
      lineNumbers: true,
      lineWrapping: true,
      indentWithTabs: true,
      // Having issues with smartIndent. Therfore, turned off.
      smartIndent: false,
      scrollbarStyle: 'overlay',
      theme: 'base16-ocean-dark'
    })

  editor[2] = CodeMirror(
    document.getElementById('js-editor'), {
      mode: {
        name: 'javascript',
        json: true
      },
      lineNumbers: true,
      lineWrapping: true,
      indentWithTabs: true,
      scrollbarStyle: 'overlay',
      theme: 'base16-ocean-dark'
    })

  output = document.getElementById('output')
  editorLabels = document.getElementsByClassName('editor-label')
  html = editor[0]
  css = editor[1]
  js = editor[2]

  html.on('change', function (css, change) {
    paint()
  })

  css.on('change', function (css, change) {
    paint()
  })

  js.on('change', function (css, change) {
    paint()
  })

  html.on('focus', function (css, change) {
    removeFocus(editorLabels)
    editorLabels[0].classList.add('editor-focus')
  })

  css.on('focus', function (css, change) {
    removeFocus(editorLabels)
    editorLabels[1].classList.add('editor-focus')
  })

  js.on('focus', function (css, change) {
    removeFocus(editorLabels)
    editorLabels[2].classList.add('editor-focus')
  })

  addScript()
  addStyle()
  newFile()
  onresize()
}

function removeFocus (editor) {
  for (var i = 0; i < editor.length; i++) {
    editor[i].classList.remove('editor-focus')
  }
}
onresize = function () {
  for (var i = 0; i < 3; i++) {
    editor[i].refresh()
  }
}

function paint () {
  output.srcdoc = '' + '' + getSty() + '' + '' + '' + html.getValue() + getScr() + '' + '' + ''
  console.log(output.srcdoc)
}

function toggleStatus (i, span) {
  if (span[i].classList.contains('status-active')) {
    span[i].classList.remove('status-active')
  } else {
    span[i].classList.add('status-active')
  }
}

function addScript () {
  var jsMenu = document.getElementById('js-menu')
  var jsButtons = jsMenu.getElementsByTagName('a')
  let jsSpan = jsMenu.querySelectorAll('span')
  var ScrFlags = [0, 0, 0]
  jsButtons[0].addEventListener('click', function (e) {
    toggleStatus(0, jsSpan)
    if (ScrFlags[1] === 0) {
      var jQStr = ""
      scripts += bootjsStr
      ScrFlags[0] = 1
      console.log('Bootstrap added!')
    }
  })
  jsButtons[1].addEventListener('click', function (e) {
    toggleStatus(1, jsSpan)
    if (ScrFlags[1] === 0) {
      var jQStr = ""
      scripts += jQStr
      ScrFlags[1] = 1
      console.log('jQuery added!')
    }
  })
  jsButtons[2].addEventListener('click', function (e) {
    toggleStatus(2, jsSpan)
    if (ScrFlags[2] === 0) {
      var js3Str = ""
      scripts += js3Str
      ScrFlags[2] = 1
      console.log('Three.js added!')
    }
  })
};

function addStyle () {
  var cssMenu = document.getElementById('css-menu')
  var cssButtons = cssMenu.getElementsByTagName('a')
  let cssSpan = cssMenu.querySelectorAll('span')
  var StyFlags = [0, 0, 0, 0]
  cssButtons[0].addEventListener('click', function (e) {
    toggleStatus(0, cssSpan)
    if (StyFlags[0] === 0) {
      var aniStr = "<link rel='stylesheet' type='text/css' href='lib/animate.css'>"
      styles += aniStr
      StyFlags[0] = 1
      console.log('Animate added!')
    }
  })
  cssButtons[1].addEventListener('click', function (e) {
    toggleStatus(1, cssSpan)
    if (StyFlags[1] === 0) {
      var bootStr = "<link rel='stylesheet' type='text/css' href='lib/bootstrap.min.css'>"
      styles += bootStr
      StyFlags[1] = 1
      console.log('Bootstrap added!')
    }
  })
  cssButtons[2].addEventListener('click', function (e) {
    toggleStatus(2, cssSpan)
    if (StyFlags[2] === 0) {
      var faStr = "<link rel='stylesheet' type='text/css' href='lib/font-awesome.min.css'>"
      styles += faStr
      StyFlags[2] = 1
      console.log('Font Awesome added!')
    }
  })
  cssButtons[3].addEventListener('click', function (e) {
    toggleStatus(3, cssSpan)
    if (StyFlags[3] === 0) {
      var matStr = "<link href='lib/materialize.min.css'>"
      styles += matStr
      StyFlags[3] = 1
      console.log('Materialize added!')
    }
  })
};
<commit_msg>localised context function added<commit_after>var editor = []
var menu
var output, html, css, js

const {
  remote,
  clipboard
} = require('electron')

const {
  Menu,
  MenuItem
} = remote

// Global variables
var scripts = ''
var styles = ''
var currentEditor = ''

// Functions for AddIns
const getScr = () => scripts
const getSty = () => styles
const getCurEditor = () => {getEditor(); return currentEditor}

function getEditor()
{
  if(html.hasFocus())
  {
     currentEditor = html;
     console.warn('html');
  }
  if(css.hasFocus())
  {
     currentEditor = css;
     console.warn('css');
  }
  if(js.hasFocus())
  {
     currentEditor = js;
     console.warn('js');
  }
}

function newFile () {
  fileEntry = null
  hasWriteAccess = false
}

function handleNewButton (i) {
  if (false) {
    newFile()
    editor[i].setValue('')
  } else {
    window.open('file://' + __dirname + '/index.html')
  }
}

// Context menu init()
function initContextMenu () {
  menu = new Menu()
  menu.append(new MenuItem({
    label: 'Copy',
    click: function () {
      var editor = getCurEditor();
      console.info(editor);
      var text = editor.getSelection();
      clipboard.writeText(text)
    }
  }))
  menu.append(new MenuItem({
    label: 'Cut',
    click: function () {
      var editor = getCurEditor();
      console.info(editor);
      var text = editor.getSelection();
      clipboard.writeText(text);
      editor.replaceSelection('')
    }
  }))
  menu.append(new MenuItem({
    label: 'Paste',
    click: function () {
      var editor = getCurEditor();
      console.info(editor);
      editor.replaceSelection(clipboard.readText())
    }
  }))

  window.addEventListener('contextmenu', function (ev) {
    ev.preventDefault()
    menu.popup(remote.getCurrentWindow(), ev.x, ev.y)
  }, false)
}

// Main Functions for Electron
onload = function () {
  initContextMenu()

  document.getElementById('min-button').addEventListener('click', function (e) {
    const window = remote.getCurrentWindow()
    window.minimize()
    console.log('Minimize Triggered!')
  })

  document.getElementById('max-button').addEventListener('click', function (e) {
    const window = remote.getCurrentWindow()
    if (!window.isMaximized()) {
      window.maximize()
    } else {
      window.unmaximize()
    }
    console.log('Maximize Triggered!')
    console.log(document.getElementById('close-button'))
  })

  document.getElementById('close-button').addEventListener('click', function (e) {
    const window = remote.getCurrentWindow()
    window.close()
  })

  document.getElementById('close-window').addEventListener('click', function (e) {
    const window = remote.getCurrentWindow()
    window.close()
  })
  document.getElementById('new').addEventListener('click', handleNewButton)

  editor[0] = CodeMirror(
    document.getElementById('html-editor'), {
      mode: {
        name: 'htmlmixed'
      },
      lineNumbers: true,
      lineWrapping: true,
      autofocus: true,
      indentWithTabs: true,
      scrollbarStyle: 'overlay',
      theme: 'base16-ocean-dark'
    })

  editor[1] = CodeMirror(
    document.getElementById('css-editor'), {
      mode: {
        name: 'css'
      },
      lineNumbers: true,
      lineWrapping: true,
      indentWithTabs: true,
      // Having issues with smartIndent. Therfore, turned off.
      smartIndent: false,
      scrollbarStyle: 'overlay',
      theme: 'base16-ocean-dark'
    })

  editor[2] = CodeMirror(
    document.getElementById('js-editor'), {
      mode: {
        name: 'javascript',
        json: true
      },
      lineNumbers: true,
      lineWrapping: true,
      indentWithTabs: true,
      scrollbarStyle: 'overlay',
      theme: 'base16-ocean-dark'
    })

  output = document.getElementById('output')
  editorLabels = document.getElementsByClassName('editor-label')
  html = editor[0]
  css = editor[1]
  js = editor[2]

  html.on('change', function (css, change) {
    paint()
  })

  css.on('change', function (css, change) {
    paint()
  })

  js.on('change', function (css, change) {
    paint()
  })

  html.on('focus', function (css, change) {
    removeFocus(editorLabels)
    editorLabels[0].classList.add('editor-focus')
  })

  css.on('focus', function (css, change) {
    removeFocus(editorLabels)
    editorLabels[1].classList.add('editor-focus')
  })

  js.on('focus', function (css, change) {
    removeFocus(editorLabels)
    editorLabels[2].classList.add('editor-focus')
  })

  addScript()
  addStyle()
  newFile()
  onresize()
}

function removeFocus (editor) {
  for (var i = 0; i < editor.length; i++) {
    editor[i].classList.remove('editor-focus')
  }
}
onresize = function () {
  for (var i = 0; i < 3; i++) {
    editor[i].refresh()
  }
}

function paint () {
  output.srcdoc = '' + '' + getSty() + '' + '' + '' + html.getValue() + getScr() + '' + '' + ''
  console.log(output.srcdoc)
}

function toggleStatus (i, span) {
  if (span[i].classList.contains('status-active')) {
    span[i].classList.remove('status-active')
  } else {
    span[i].classList.add('status-active')
  }
}

function addScript () {
  var jsMenu = document.getElementById('js-menu')
  var jsButtons = jsMenu.getElementsByTagName('a')
  let jsSpan = jsMenu.querySelectorAll('span')
  var ScrFlags = [0, 0, 0]
  jsButtons[0].addEventListener('click', function (e) {
    toggleStatus(0, jsSpan)
    if (ScrFlags[1] === 0) {
      var jQStr = ""
      scripts += bootjsStr
      ScrFlags[0] = 1
      console.log('Bootstrap added!')
    }
  })
  jsButtons[1].addEventListener('click', function (e) {
    toggleStatus(1, jsSpan)
    if (ScrFlags[1] === 0) {
      var jQStr = ""
      scripts += jQStr
      ScrFlags[1] = 1
      console.log('jQuery added!')
    }
  })
  jsButtons[2].addEventListener('click', function (e) {
    toggleStatus(2, jsSpan)
    if (ScrFlags[2] === 0) {
      var js3Str = ""
      scripts += js3Str
      ScrFlags[2] = 1
      console.log('Three.js added!')
    }
  })
};

function addStyle () {
  var cssMenu = document.getElementById('css-menu')
  var cssButtons = cssMenu.getElementsByTagName('a')
  let cssSpan = cssMenu.querySelectorAll('span')
  var StyFlags = [0, 0, 0, 0]
  cssButtons[0].addEventListener('click', function (e) {
    toggleStatus(0, cssSpan)
    if (StyFlags[0] === 0) {
      var aniStr = "<link rel='stylesheet' type='text/css' href='lib/animate.css'>"
      styles += aniStr
      StyFlags[0] = 1
      console.log('Animate added!')
    }
  })
  cssButtons[1].addEventListener('click', function (e) {
    toggleStatus(1, cssSpan)
    if (StyFlags[1] === 0) {
      var bootStr = "<link rel='stylesheet' type='text/css' href='lib/bootstrap.min.css'>"
      styles += bootStr
      StyFlags[1] = 1
      console.log('Bootstrap added!')
    }
  })
  cssButtons[2].addEventListener('click', function (e) {
    toggleStatus(2, cssSpan)
    if (StyFlags[2] === 0) {
      var faStr = "<link rel='stylesheet' type='text/css' href='lib/font-awesome.min.css'>"
      styles += faStr
      StyFlags[2] = 1
      console.log('Font Awesome added!')
    }
  })
  cssButtons[3].addEventListener('click', function (e) {
    toggleStatus(3, cssSpan)
    if (StyFlags[3] === 0) {
      var matStr = "<link href='lib/materialize.min.css'>"
      styles += matStr
      StyFlags[3] = 1
      console.log('Materialize added!')
    }
  })
};
<|endoftext|>


<commit_before>'use strict';

const express = require('express');
const bodyParser = require('body-parser');

const restService = express();
restService.use(bodyParser.json());

var data={};

restService.post('/hook', function (req, res) {

    console.log('hook request');

    try {

        if (req.body) {
            var body = req.body;
            var mailId='';

            switch(body.result.action){
                case 'asdaItem':
                for(var i of body.result.contexts){
                    if(i.name=='mail-set'){
                        mailId=i.parameters.mailId;
                    }
                }
                data[mailId]={'item':body.result.parameters.item, 'tab':'browse', 'orderLocation':null};
                return res.json({
                        speech: "Please open asda app on mobile. Would you like to view related offers?",
                        displayText: "Please open asda app on mobile. Would you like to view related offers?",
                        source: 'apiai-webhook'
                    });
                break;

                case 'asdaOffers':
                for(var i of body.result.contexts){
                    if(i.name=='mail-set'){
                        mailId=i.parameters.mailId;
                    }
                }
                data[mailId].tab='offers';
                data[mailId].orderLocation=null;
                return res.json({
                        speech: "Offers displayed on app",
                        displayText: "Offers displayed on app",
                        source: 'apiai-webhook'
                    });
                break;

                case 'asdaTrack':
                for(var i of body.result.contexts){
                    if(i.name=='mail-set'){
                        mailId=i.parameters.mailId;
                    }
                }
                data[mailId].tab='track';
                data[mailId].orderLocation='53.792033,-1.545054';
                return res.json({
                        speech: "Order tracking info displayed on app",
                        displayText: "Order tracking info displayed on app",
                        source: 'apiai-webhook'
                    });
                break;
            }
        }

        return res.json({
            speech: "Webhook input error",
            displayText: "Webhook input error",
            source: 'apiai-webhook'
        });
    } catch (err) {
        console.error("Can't process request", err);

        return res.status(400).json({
            status: {
                code: 400,
                errorType: err.message
            }
        });
    }
});

restService.post('/mobileapp', function (req, res) {

    var mailId='';
    console.log('mobileapp request');

    try {

        if (req.body) {
            mailId=req.body.mailId;
        }

        if(data[mailId]){
            return res.json({
            mailId: mailId,
            item: data[mailId].item.toUpperCase(),
            tab: data[mailId].tab,
            orderLocation: data[mailId].orderLocation,
            status: 'Success'
        });
        }
        else {
            return res.json({
            mailId: mailId,
            item: null,
            tab: null,
            orderLocation: null,
            status: 'Mail Id not found'
        });
        }

    } catch (err) {
        console.error("Can't process request", err);

        return res.status(400).json({
            status: {
                code: 400,
                errorType: err.message
            }
        });
    }
});

restService.listen((process.env.PORT || 5000), function () {
    console.log("Server listening");
});
<commit_msg>Stable<commit_after>'use strict';

const express = require('express');
const bodyParser = require('body-parser');

const restService = express();
restService.use(bodyParser.json());

var data={};

restService.post('/hook', function (req, res) {

    console.log('hook request');

    try {

        if (req.body) {
            var body = req.body;
            var mailId='';

            switch(body.result.action){
                case 'asdaItem':
                for(var i of body.result.contexts){
                    if(i.name=='mail-set'){
                        mailId=i.parameters.mailId;
                    }
                }
                data[mailId]={'item':body.result.parameters.item, 'tab':'browse', 'orderLocation':null};
                return res.json({
                        speech: 'Please open asda app on mobile. Would you like to view related offers?',
                        displayText: 'Please open asda app on mobile. Would you like to view related offers?',
                        source: 'apiai-webhook'
                    });
                break;

                case 'asdaOffers':
                for(var i of body.result.contexts){
                    if(i.name=='mail-set'){
                        mailId=i.parameters.mailId;
                    }
                }
                data[mailId].tab='offers';
                data[mailId].orderLocation=null;
                return res.json({
                        speech: 'Offers displayed on app',
                        displayText: 'Offers displayed on app',
                        source: 'apiai-webhook'
                    });
                break;

                case 'asdaTrack':
                for(var i of body.result.contexts){
                    if(i.name=='mail-set'){
                        mailId=i.parameters.mailId;
                    }
                }
                data[mailId].tab='track';
                data[mailId].orderLocation='53.792033,-1.545054';
                return res.json({
                        speech: 'Order tracking info displayed on app',
                        displayText: 'Order tracking info displayed on app',
                        source: 'apiai-webhook'
                    });
                break;
            }
        }

        return res.json({
            speech: 'Webhook input error',
            displayText: 'Webhook input error',
            source: 'apiai-webhook'
        });
    } catch (err) {
        console.error("Can't process request", err);

        return res.status(400).json({
            status: {
                code: 400,
                errorType: err.message
            }
        });
    }
});

restService.post('/mobileapp', function (req, res) {

    var mailId='';
    console.log('mobileapp request');

    try {

        if (req.body) {
            mailId=req.body.mailId;
        }

        if(data[mailId]){
            return res.json({
            mailId: mailId,
            item: data[mailId].item.toUpperCase(),
            tab: data[mailId].tab,
            orderLocation: data[mailId].orderLocation,
            status: 'Success'
        });
        }
        else {
            return res.json({
            mailId: mailId,
            item: null,
            tab: null,
            orderLocation: null,
            status: 'Mail Id not found'
        });
        }

    } catch (err) {
        console.error("Can't process request", err);

        return res.status(400).json({
            status: {
                code: 400,
                errorType: err.message
            }
        });
    }
});

restService.listen((process.env.PORT || 5000), function () {
    console.log('Server listening');
});
<|endoftext|>