<commit_before>
var assert = require('assert');
var exec = require('child_process').exec;
var fs = require('fs');
var mkdirp = require('mkdirp');
var mocha = require('mocha');
var path = require('path');
var request = require('supertest');
var rimraf = require('rimraf');
var spawn = require('child_process').spawn;

var binPath = path.resolve(__dirname, '../bin/express');
var tempDir = path.resolve(__dirname, '../temp');

describe('express(1)', function () {
  mocha.before(function (done) {
    this.timeout(30000);
    cleanup(done);
  });

  mocha.after(function (done) {
    this.timeout(30000);
    cleanup(done);
  });

  describe('(no args)', function () {
    var dir;
    var files;

    mocha.before(function (done) {
      createEnvironment(function (err, newDir) {
        if (err) return done(err);
        dir = newDir;
        done();
      });
    });

    mocha.after(function (done) {
      this.timeout(30000);
      cleanup(dir, done);
    });

    it('should create basic app', function (done) {
      run(dir, [], function (err, stdout) {
        if (err) return done(err);
        files = parseCreatedFiles(stdout, dir);
        assert.equal(files.length, 17);
        done();
      });
    });

    it('should have basic files', function () {
      assert.notEqual(files.indexOf('bin/www'), -1);
      assert.notEqual(files.indexOf('app.js'), -1);
      assert.notEqual(files.indexOf('package.json'), -1);
    });

    it('should have jade templates', function () {
      assert.notEqual(files.indexOf('views/error.jade'), -1);
      assert.notEqual(files.indexOf('views/index.jade'), -1);
      assert.notEqual(files.indexOf('views/layout.jade'), -1);
    });

    it('should have a package.json file', function () {
      var file = path.resolve(dir, 'package.json');
      var contents = fs.readFileSync(file, 'utf8');
      assert.equal(contents, '{\n'
        + '  "name": ' + JSON.stringify(path.basename(dir)) + ',\n'
        + '  "version": "0.0.0",\n'
        + '  "private": true,\n'
        + '  "scripts": {\n'
        + '    "start": "node ./bin/www"\n'
        + '  },\n'
        + '  "dependencies": {\n'
        + '    "body-parser": "~1.12.0",\n'
        + '    "cookie-parser": "~1.3.4",\n'
        + '    "debug": "~2.1.1",\n'
        + '    "express": "~4.12.2",\n'
        + '    "jade": "~1.9.2",\n'
        + '    "morgan": "~1.5.1",\n'
        + '    "serve-favicon": "~2.2.0"\n'
        + '  }\n'
        + '}');
    });

    it('should have installable dependencies', function (done) {
      this.timeout(30000);
      npmInstall(dir, done);
    });

    it('should export an express app from app.js', function () {
      var file = path.resolve(dir, 'app.js');
      var app = require(file);
      assert.equal(typeof app, 'function');
      assert.equal(typeof app.handle, 'function');
    });

    it('should respond to HTTP request', function (done) {
      var file = path.resolve(dir, 'app.js');
      var app = require(file);

      request(app)
      .get('/')
      .expect(200, /<title>Express<\/title>/, done);
    });
  });

  describe('--ejs', function () {
    var dir;
    var files;

    mocha.before(function (done) {
      createEnvironment(function (err, newDir) {
        if (err) return done(err);
        dir = newDir;
        done();
      });
    });

    mocha.after(function (done) {
      this.timeout(30000);
      cleanup(dir, done);
    });

    it('should create basic app with ejs templates', function (done) {
      run(dir, ['--ejs'], function (err, stdout) {
        if (err) return done(err);
        files = parseCreatedFiles(stdout, dir);
        assert.equal(files.length, 16, 'should have 16 files');
        done();
      });
    });

    it('should have basic files', function () {
      assert.notEqual(files.indexOf('bin/www'), -1, 'should have bin/www file');
      assert.notEqual(files.indexOf('app.js'), -1, 'should have app.js file');
      assert.notEqual(files.indexOf('package.json'), -1, 'should have package.json file');
    });

    it('should have ejs templates', function () {
      assert.notEqual(files.indexOf('views/error.ejs'), -1, 'should have views/error.ejs file');
      assert.notEqual(files.indexOf('views/index.ejs'), -1, 'should have views/index.ejs file');
    });

    it('should have installable dependencies', function (done) {
      this.timeout(30000);
      npmInstall(dir, done);
    });

    it('should export an express app from app.js', function () {
      var file = path.resolve(dir, 'app.js');
      var app = require(file);
      assert.equal(typeof app, 'function');
      assert.equal(typeof app.handle, 'function');
    });

    it('should respond to HTTP request', function (done) {
      var file = path.resolve(dir, 'app.js');
      var app = require(file);

      request(app)
      .get('/')
      .expect(200, /<title>Express<\/title>/, done);
    });
  });

  describe('--git', function () {
    var dir;
    var files;

    mocha.before(function (done) {
      createEnvironment(function (err, newDir) {
        if (err) return done(err);
        dir = newDir;
        done();
      });
    });

    mocha.after(function (done) {
      this.timeout(30000);
      cleanup(dir, done);
    });

    it('should create basic app with git files', function (done) {
      run(dir, ['--git'], function (err, stdout) {
        if (err) return done(err);
        files = parseCreatedFiles(stdout, dir);
        assert.equal(files.length, 18, 'should have 18 files');
        done();
      });
    });

    it('should have basic files', function () {
      assert.notEqual(files.indexOf('bin/www'), -1, 'should have bin/www file');
      assert.notEqual(files.indexOf('app.js'), -1, 'should have app.js file');
      assert.notEqual(files.indexOf('package.json'), -1, 'should have package.json file');
    });

    it('should have .gitignore', function () {
      assert.notEqual(files.indexOf('.gitignore'), -1, 'should have .gitignore file');
    });

    it('should have jade templates', function () {
      assert.notEqual(files.indexOf('views/error.jade'), -1);
      assert.notEqual(files.indexOf('views/index.jade'), -1);
      assert.notEqual(files.indexOf('views/layout.jade'), -1);
    });
  });

  describe('-h', function () {
    var dir;

    mocha.before(function (done) {
      createEnvironment(function (err, newDir) {
        if (err) return done(err);
        dir = newDir;
        done();
      });
    });

    mocha.after(function (done) {
      this.timeout(30000);
      cleanup(dir, done);
    });

    it('should print usage', function (done) {
      run(dir, ['-h'], function (err, stdout) {
        if (err) return done(err);
        var files = parseCreatedFiles(stdout, dir);
        assert.equal(files.length, 0);
        assert.ok(/Usage: express/.test(stdout));
        assert.ok(/--help/.test(stdout));
        assert.ok(/--version/.test(stdout));
        done();
      });
    });
  });

  describe('--hbs', function () {
    var dir;
    var files;

    mocha.before(function (done) {
      createEnvironment(function (err, newDir) {
        if (err) return done(err);
        dir = newDir;
        done();
      });
    });

    mocha.after(function (done) {
      this.timeout(30000);
      cleanup(dir, done);
    });

    it('should create basic app with hbs templates', function (done) {
      run(dir, ['--hbs'], function (err, stdout) {
        if (err) return done(err);
        files = parseCreatedFiles(stdout, dir);
        assert.equal(files.length, 17);
        done();
      });
    });

    it('should have basic files', function () {
      assert.notEqual(files.indexOf('bin/www'), -1);
      assert.notEqual(files.indexOf('app.js'), -1);
      assert.notEqual(files.indexOf('package.json'), -1);
    });

    it('should have hbs in package dependencies', function () {
      var file = path.resolve(dir, 'package.json');
      var contents = fs.readFileSync(file, 'utf8');
      var dependencies = JSON.parse(contents).dependencies;
      assert.ok(typeof dependencies.hbs === 'string');
    });

    it('should have hbs templates', function () {
      assert.notEqual(files.indexOf('views/error.hbs'), -1);
      assert.notEqual(files.indexOf('views/index.hbs'), -1);
      assert.notEqual(files.indexOf('views/layout.hbs'), -1);
    });

    it('should have installable dependencies', function (done) {
      this.timeout(30000);
      npmInstall(dir, done);
    });

    it('should export an express app from app.js', function () {
      var file = path.resolve(dir, 'app.js');
      var app = require(file);
      assert.equal(typeof app, 'function');
      assert.equal(typeof app.handle, 'function');
    });

    it('should respond to HTTP request', function (done) {
      var file = path.resolve(dir, 'app.js');
      var app = require(file);

      request(app)
      .get('/')
      .expect(200, /<title>Express<\/title>/, done);
    });
  });

  describe('--help', function () {
    var dir;

    mocha.before(function (done) {
      createEnvironment(function (err, newDir) {
        if (err) return done(err);
        dir = newDir;
        done();
      });
    });

    mocha.after(function (done) {
      this.timeout(30000);
      cleanup(dir, done);
    });

    it('should print usage', function (done) {
      run(dir, ['--help'], function (err, stdout) {
        if (err) return done(err);
        var files = parseCreatedFiles(stdout, dir);
        assert.equal(files.length, 0);
        assert.ok(/Usage: express/.test(stdout));
        assert.ok(/--help/.test(stdout));
        assert.ok(/--version/.test(stdout));
        done();
      });
    });
  });
});

function cleanup(dir, callback) {
  if (typeof dir === 'function') {
    callback = dir;
    dir = tempDir;
  }

  rimraf(tempDir, function (err) {
    callback(err);
  });
}

function createEnvironment(callback) {
  var num = process.pid + Math.random();
  var dir = path.join(tempDir, ('app-' + num));

  mkdirp(dir, function ondir(err) {
    if (err) return callback(err);
    callback(null, dir);
  });
}

function npmInstall(dir, callback) {
  exec('npm install', {cwd: dir}, function (err, stderr) {
    if (err) {
      err.message += stderr;
      callback(err);
      return;
    }

    callback();
  });
}

function parseCreatedFiles(output, dir) {
  var files = [];
  var lines = output.split(/[\r\n]+/);
  var match;

  for (var i = 0; i < lines.length; i++) {
    if ((match = /create.*?: (.*)$/.exec(lines[i]))) {
      var file = match[1];

      if (dir) {
        file = path.resolve(dir, file);
        file = path.relative(dir, file);
      }

      file = file.replace(/\\/g, '/');
      files.push(file);
    }
  }

  return files;
}

function run(dir, args, callback) {
  var argv = [binPath].concat(args);
  var exec = process.argv[0];
  var stderr = '';
  var stdout = '';

  var child = spawn(exec, argv, {
    cwd: dir
  });

  child.stdout.setEncoding('utf8');
  child.stdout.on('data', function ondata(str) {
    stdout += str;
  });
  child.stderr.setEncoding('utf8');
  child.stderr.on('data', function ondata(str) {
    process.stderr.write(str);
    stderr += str;
  });

  child.on('close', onclose);
  child.on('error', callback);

  function onclose(code) {
    var err = null;

    try {
      assert.equal(stderr, '');
      assert.strictEqual(code, 0);
    } catch (e) {
      err = e;
    }

    callback(err, stdout.replace(/\x1b\[(\d+)m/g, '_color_$1_'));
  }
}
<commit_msg>tests: add test for 404 path in app<commit_after>
var assert = require('assert');
var exec = require('child_process').exec;
var fs = require('fs');
var mkdirp = require('mkdirp');
var mocha = require('mocha');
var path = require('path');
var request = require('supertest');
var rimraf = require('rimraf');
var spawn = require('child_process').spawn;

var binPath = path.resolve(__dirname, '../bin/express');
var tempDir = path.resolve(__dirname, '../temp');

describe('express(1)', function () {
  mocha.before(function (done) {
    this.timeout(30000);
    cleanup(done);
  });

  mocha.after(function (done) {
    this.timeout(30000);
    cleanup(done);
  });

  describe('(no args)', function () {
    var dir;
    var files;

    mocha.before(function (done) {
      createEnvironment(function (err, newDir) {
        if (err) return done(err);
        dir = newDir;
        done();
      });
    });

    mocha.after(function (done) {
      this.timeout(30000);
      cleanup(dir, done);
    });

    it('should create basic app', function (done) {
      run(dir, [], function (err, stdout) {
        if (err) return done(err);
        files = parseCreatedFiles(stdout, dir);
        assert.equal(files.length, 17);
        done();
      });
    });

    it('should have basic files', function () {
      assert.notEqual(files.indexOf('bin/www'), -1);
      assert.notEqual(files.indexOf('app.js'), -1);
      assert.notEqual(files.indexOf('package.json'), -1);
    });

    it('should have jade templates', function () {
      assert.notEqual(files.indexOf('views/error.jade'), -1);
      assert.notEqual(files.indexOf('views/index.jade'), -1);
      assert.notEqual(files.indexOf('views/layout.jade'), -1);
    });

    it('should have a package.json file', function () {
      var file = path.resolve(dir, 'package.json');
      var contents = fs.readFileSync(file, 'utf8');
      assert.equal(contents, '{\n'
        + '  "name": ' + JSON.stringify(path.basename(dir)) + ',\n'
        + '  "version": "0.0.0",\n'
        + '  "private": true,\n'
        + '  "scripts": {\n'
        + '    "start": "node ./bin/www"\n'
        + '  },\n'
        + '  "dependencies": {\n'
        + '    "body-parser": "~1.12.0",\n'
        + '    "cookie-parser": "~1.3.4",\n'
        + '    "debug": "~2.1.1",\n'
        + '    "express": "~4.12.2",\n'
        + '    "jade": "~1.9.2",\n'
        + '    "morgan": "~1.5.1",\n'
        + '    "serve-favicon": "~2.2.0"\n'
        + '  }\n'
        + '}');
    });

    it('should have installable dependencies', function (done) {
      this.timeout(30000);
      npmInstall(dir, done);
    });

    it('should export an express app from app.js', function () {
      var file = path.resolve(dir, 'app.js');
      var app = require(file);
      assert.equal(typeof app, 'function');
      assert.equal(typeof app.handle, 'function');
    });

    it('should respond to HTTP request', function (done) {
      var file = path.resolve(dir, 'app.js');
      var app = require(file);

      request(app)
      .get('/')
      .expect(200, /<title>Express<\/title>/, done);
    });

    it('should generate a 404', function (done) {
      var file = path.resolve(dir, 'app.js');
      var app = require(file);

      request(app)
      .get('/does_not_exist')
      .expect(404, /
Not Found<\/h1>/, done);
    });
  });

  describe('--ejs', function () {
    var dir;
    var files;

    mocha.before(function (done) {
      createEnvironment(function (err, newDir) {
        if (err) return done(err);
        dir = newDir;
        done();
      });
    });

    mocha.after(function (done) {
      this.timeout(30000);
      cleanup(dir, done);
    });

    it('should create basic app with ejs templates', function (done) {
      run(dir, ['--ejs'], function (err, stdout) {
        if (err) return done(err);
        files = parseCreatedFiles(stdout, dir);
        assert.equal(files.length, 16, 'should have 16 files');
        done();
      });
    });

    it('should have basic files', function () {
      assert.notEqual(files.indexOf('bin/www'), -1, 'should have bin/www file');
      assert.notEqual(files.indexOf('app.js'), -1, 'should have app.js file');
      assert.notEqual(files.indexOf('package.json'), -1, 'should have package.json file');
    });

    it('should have ejs templates', function () {
      assert.notEqual(files.indexOf('views/error.ejs'), -1, 'should have views/error.ejs file');
      assert.notEqual(files.indexOf('views/index.ejs'), -1, 'should have views/index.ejs file');
    });

    it('should have installable dependencies', function (done) {
      this.timeout(30000);
      npmInstall(dir, done);
    });

    it('should export an express app from app.js', function () {
      var file = path.resolve(dir, 'app.js');
      var app = require(file);
      assert.equal(typeof app, 'function');
      assert.equal(typeof app.handle, 'function');
    });

    it('should respond to HTTP request', function (done) {
      var file = path.resolve(dir, 'app.js');
      var app = require(file);

      request(app)
      .get('/')
      .expect(200, /<title>Express<\/title>/, done);
    });

    it('should generate a 404', function (done) {
      var file = path.resolve(dir, 'app.js');
      var app = require(file);

      request(app)
      .get('/does_not_exist')
      .expect(404, /
Not Found<\/h1>/, done);
    });
  });

  describe('--git', function () {
    var dir;
    var files;

    mocha.before(function (done) {
      createEnvironment(function (err, newDir) {
        if (err) return done(err);
        dir = newDir;
        done();
      });
    });

    mocha.after(function (done) {
      this.timeout(30000);
      cleanup(dir, done);
    });

    it('should create basic app with git files', function (done) {
      run(dir, ['--git'], function (err, stdout) {
        if (err) return done(err);
        files = parseCreatedFiles(stdout, dir);
        assert.equal(files.length, 18, 'should have 18 files');
        done();
      });
    });

    it('should have basic files', function () {
      assert.notEqual(files.indexOf('bin/www'), -1, 'should have bin/www file');
      assert.notEqual(files.indexOf('app.js'), -1, 'should have app.js file');
      assert.notEqual(files.indexOf('package.json'), -1, 'should have package.json file');
    });

    it('should have .gitignore', function () {
      assert.notEqual(files.indexOf('.gitignore'), -1, 'should have .gitignore file');
    });

    it('should have jade templates', function () {
      assert.notEqual(files.indexOf('views/error.jade'), -1);
      assert.notEqual(files.indexOf('views/index.jade'), -1);
      assert.notEqual(files.indexOf('views/layout.jade'), -1);
    });
  });

  describe('-h', function () {
    var dir;

    mocha.before(function (done) {
      createEnvironment(function (err, newDir) {
        if (err) return done(err);
        dir = newDir;
        done();
      });
    });

    mocha.after(function (done) {
      this.timeout(30000);
      cleanup(dir, done);
    });

    it('should print usage', function (done) {
      run(dir, ['-h'], function (err, stdout) {
        if (err) return done(err);
        var files = parseCreatedFiles(stdout, dir);
        assert.equal(files.length, 0);
        assert.ok(/Usage: express/.test(stdout));
        assert.ok(/--help/.test(stdout));
        assert.ok(/--version/.test(stdout));
        done();
      });
    });
  });

  describe('--hbs', function () {
    var dir;
    var files;

    mocha.before(function (done) {
      createEnvironment(function (err, newDir) {
        if (err) return done(err);
        dir = newDir;
        done();
      });
    });

    mocha.after(function (done) {
      this.timeout(30000);
      cleanup(dir, done);
    });

    it('should create basic app with hbs templates', function (done) {
      run(dir, ['--hbs'], function (err, stdout) {
        if (err) return done(err);
        files = parseCreatedFiles(stdout, dir);
        assert.equal(files.length, 17);
        done();
      });
    });

    it('should have basic files', function () {
      assert.notEqual(files.indexOf('bin/www'), -1);
      assert.notEqual(files.indexOf('app.js'), -1);
      assert.notEqual(files.indexOf('package.json'), -1);
    });

    it('should have hbs in package dependencies', function () {
      var file = path.resolve(dir, 'package.json');
      var contents = fs.readFileSync(file, 'utf8');
      var dependencies = JSON.parse(contents).dependencies;
      assert.ok(typeof dependencies.hbs === 'string');
    });

    it('should have hbs templates', function () {
      assert.notEqual(files.indexOf('views/error.hbs'), -1);
      assert.notEqual(files.indexOf('views/index.hbs'), -1);
      assert.notEqual(files.indexOf('views/layout.hbs'), -1);
    });

    it('should have installable dependencies', function (done) {
      this.timeout(30000);
      npmInstall(dir, done);
    });

    it('should export an express app from app.js', function () {
      var file = path.resolve(dir, 'app.js');
      var app = require(file);
      assert.equal(typeof app, 'function');
      assert.equal(typeof app.handle, 'function');
    });

    it('should respond to HTTP request', function (done) {
      var file = path.resolve(dir, 'app.js');
      var app = require(file);

      request(app)
      .get('/')
      .expect(200, /<title>Express<\/title>/, done);
    });

    it('should generate a 404', function (done) {
      var file = path.resolve(dir, 'app.js');
      var app = require(file);

      request(app)
      .get('/does_not_exist')
      .expect(404, /
Not Found<\/h1>/, done);
    });
  });

  describe('--help', function () {
    var dir;

    mocha.before(function (done) {
      createEnvironment(function (err, newDir) {
        if (err) return done(err);
        dir = newDir;
        done();
      });
    });

    mocha.after(function (done) {
      this.timeout(30000);
      cleanup(dir, done);
    });

    it('should print usage', function (done) {
      run(dir, ['--help'], function (err, stdout) {
        if (err) return done(err);
        var files = parseCreatedFiles(stdout, dir);
        assert.equal(files.length, 0);
        assert.ok(/Usage: express/.test(stdout));
        assert.ok(/--help/.test(stdout));
        assert.ok(/--version/.test(stdout));
        done();
      });
    });
  });
});

function cleanup(dir, callback) {
  if (typeof dir === 'function') {
    callback = dir;
    dir = tempDir;
  }

  rimraf(tempDir, function (err) {
    callback(err);
  });
}

function createEnvironment(callback) {
  var num = process.pid + Math.random();
  var dir = path.join(tempDir, ('app-' + num));

  mkdirp(dir, function ondir(err) {
    if (err) return callback(err);
    callback(null, dir);
  });
}

function npmInstall(dir, callback) {
  exec('npm install', {cwd: dir}, function (err, stderr) {
    if (err) {
      err.message += stderr;
      callback(err);
      return;
    }

    callback();
  });
}

function parseCreatedFiles(output, dir) {
  var files = [];
  var lines = output.split(/[\r\n]+/);
  var match;

  for (var i = 0; i < lines.length; i++) {
    if ((match = /create.*?: (.*)$/.exec(lines[i]))) {
      var file = match[1];

      if (dir) {
        file = path.resolve(dir, file);
        file = path.relative(dir, file);
      }

      file = file.replace(/\\/g, '/');
      files.push(file);
    }
  }

  return files;
}

function run(dir, args, callback) {
  var argv = [binPath].concat(args);
  var exec = process.argv[0];
  var stderr = '';
  var stdout = '';

  var child = spawn(exec, argv, {
    cwd: dir
  });

  child.stdout.setEncoding('utf8');
  child.stdout.on('data', function ondata(str) {
    stdout += str;
  });
  child.stderr.setEncoding('utf8');
  child.stderr.on('data', function ondata(str) {
    process.stderr.write(str);
    stderr += str;
  });

  child.on('close', onclose);
  child.on('error', callback);

  function onclose(code) {
    var err = null;

    try {
      assert.equal(stderr, '');
      assert.strictEqual(code, 0);
    } catch (e) {
      err = e;
    }

    callback(err, stdout.replace(/\x1b\[(\d+)m/g, '_color_$1_'));
  }
}
<|endoftext|>


<commit_before>$(function() {
  var $tabs = $('#search-results-tabs'),
      $unified = $('#unified-results'),
      $searchForm = $('.js-search-hash');

  if($tabs.length > 0){
    $tabs.tabs({ 'defaultTab' : getSelectedSearchTabIndex(), scrollOnload: true });
  }

  if ($unified.length > 0) {

    $('.js-openable-filter').each(function(){
      new GOVUK.CheckboxFilter({el:$(this)});
    })
    GOVUK.liveSearch.init();
  };

  // Returns the index of the tab, or defaults to 0 (ie the first tab)
  //
  // To do this, it looks at which tab has the "active" class.
  // This might be the one that the user has selected, or one that the server
  // has defaulted to (eg the first tab might have no results, so we'd set the
  // second one as active)
  function getSelectedSearchTabIndex(){
    var tabIds = $('.search-navigation a').map(function(i, el){
      return $(el).attr('href').split('#').pop();
    });

    var activeTabId = $(".search-navigation li.active a").first()
                        .attr("href").replace("#", "");

    var tabIndex = $.inArray(activeTabId, tabIds);
    return tabIndex > -1 ? tabIndex : 0;
  }

  if($searchForm.length){
    $tabs.on('tabChanged', updateSearchForms);
  }

  // Update the forms so that the selected tab is remembered for the next search.
  // This function applies to any form with the "tab" hidden input in it:
  //   * form at the top
  //   * government filtering form
  function updateSearchForms(e, hash){
    if(typeof hash !== 'undefined'){
      var $selectedTab = $('input[name=tab]');

      if($selectedTab.length === 0){
        $selectedTab = $('<input type="hidden" name="tab">');
        $searchForm.prepend($selectedTab);
      }

      $selectedTab.val(hash);
    }
  }

  (function trackSearchClicks(){
    if(($tabs.length === 0 && $unified.length === 0) || !GOVUK.cookie){
      return false;
    }
    $('#services-information-results-enhanced a, #departments-policy-results-enhanced a, #top-results a, #unified-results a').click(function(e){
      var $link = $(e.target),
          sublink = '',
          gaParams = ['_setCustomVar', 21, 'searchPosition', '', 3],
          position, href;

      if($link.closest('ul').hasClass('sections')){
        href = $link.attr('href');
        if(href.indexOf('#') > -1){
          sublink = '&sublink='+href.split('#')[1];
        }
        $link = $link.closest('ul');
      }

      position = $link.closest('li').index() + 1; // +1 so it isn't zero offset

      if($unified.length === 0 && $link.closest('#top-results').length === 0){
        position = position + 3; // to allow for the top results
      }
      gaParams[3] = 'position='+position+sublink;
      GOVUK.cookie('ga_nextpage_params', gaParams.join(','));
    });
  }());

});
<commit_msg>Track click positions after ajax updates<commit_after>$(function() {
  var $tabs = $('#search-results-tabs'),
      $unified = $('#unified-results'),
      $searchForm = $('.js-search-hash');

  if($tabs.length > 0){
    $tabs.tabs({ 'defaultTab' : getSelectedSearchTabIndex(), scrollOnload: true });
  }

  if ($unified.length > 0) {

    $('.js-openable-filter').each(function(){
      new GOVUK.CheckboxFilter({el:$(this)});
    })
    GOVUK.liveSearch.init();
  };

  // Returns the index of the tab, or defaults to 0 (ie the first tab)
  //
  // To do this, it looks at which tab has the "active" class.
  // This might be the one that the user has selected, or one that the server
  // has defaulted to (eg the first tab might have no results, so we'd set the
  // second one as active)
  function getSelectedSearchTabIndex(){
    var tabIds = $('.search-navigation a').map(function(i, el){
      return $(el).attr('href').split('#').pop();
    });

    var activeTabId = $(".search-navigation li.active a").first()
                        .attr("href").replace("#", "");

    var tabIndex = $.inArray(activeTabId, tabIds);
    return tabIndex > -1 ? tabIndex : 0;
  }

  if($searchForm.length){
    $tabs.on('tabChanged', updateSearchForms);
  }

  // Update the forms so that the selected tab is remembered for the next search.
  // This function applies to any form with the "tab" hidden input in it:
  //   * form at the top
  //   * government filtering form
  function updateSearchForms(e, hash){
    if(typeof hash !== 'undefined'){
      var $selectedTab = $('input[name=tab]');

      if($selectedTab.length === 0){
        $selectedTab = $('<input type="hidden" name="tab">');
        $searchForm.prepend($selectedTab);
      }

      $selectedTab.val(hash);
    }
  }

  (function trackSearchClicks(){
    if(($tabs.length === 0 && $unified.length === 0) || !GOVUK.cookie){
      return false;
    }
    $('#services-information-results-enhanced, #departments-policy-results-enhanced, #top-results, #unified-results').on('click', 'a', function(e){
      var $link = $(e.target),
          sublink = '',
          gaParams = ['_setCustomVar', 21, 'searchPosition', '', 3],
          position, href;

      if($link.closest('ul').hasClass('sections')){
        href = $link.attr('href');
        if(href.indexOf('#') > -1){
          sublink = '&sublink='+href.split('#')[1];
        }
        $link = $link.closest('ul');
      }

      position = $link.closest('li').index() + 1; // +1 so it isn't zero offset

      if($unified.length === 0 && $link.closest('#top-results').length === 0){
        position = position + 3; // to allow for the top results
      }
      gaParams[3] = 'position='+position+sublink;
      GOVUK.cookie('ga_nextpage_params', gaParams.join(','));
    });
  }());

});
<|endoftext|>


<commit_before>const webdriver = require('selenium-webdriver');
const bindAll = require('lodash.bindall');
require('chromedriver');
const chromedriverVersion = require('chromedriver').version;

const headless = process.env.SMOKE_HEADLESS || false;
const remote = process.env.SMOKE_REMOTE || false;
const ci = process.env.CI || false;
const usingCircle = process.env.CIRCLECI || false;
const buildID = process.env.CIRCLE_BUILD_NUM || '0000';
const {SAUCE_USERNAME, SAUCE_ACCESS_KEY} = process.env;
const {By, Key, until} = webdriver;

const DEFAULT_TIMEOUT_MILLISECONDS = 20 * 1000;

class SeleniumHelper {
    constructor () {
        bindAll(this, [
            'buildDriver',
            'clickButton',
            'clickCss',
            'clickText',
            'clickXpath',
            'containsClass',
            'dragFromXpathToXpath',
            'findByCss',
            'findByXpath',
            'findText',
            'getKey',
            'getDriver',
            'getLogs',
            'getSauceDriver',
            'loadPageUntilVisible',
            'signIn',
            'urlMatches',
            'waitUntilGone'
        ]);
    }
    buildDriver (name) {
        if (remote === 'true'){
            let nameToUse;
            if (ci === 'true'){
                let ciName = usingCircle ? 'circleCi ' : 'unknown ';
                nameToUse = ciName + buildID + ' : ' + name;
            } else {
                nameToUse = name;
            }
            this.driver = this.getSauceDriver(SAUCE_USERNAME, SAUCE_ACCESS_KEY, nameToUse);
        } else {
            this.driver = this.getDriver();
        }
        return this.driver;
    }

    getDriver () {
        const chromeCapabilities = webdriver.Capabilities.chrome();
        let args = [];
        if (headless) {
            args.push('--headless');
            args.push('window-size=1024,1680');
            args.push('--no-sandbox');
        }
        chromeCapabilities.set('chromeOptions', {args});
        let driver = new webdriver.Builder()
            .forBrowser('chrome')
            .withCapabilities(chromeCapabilities)
            .build();
        return driver;
    }

    getChromeVersionNumber () {
        const versionFinder = /\d+\.\d+/;
        const versionArray = versionFinder.exec(chromedriverVersion);
        if (versionArray === null) {
            throw new Error('couldn\'t find version of chromedriver');
        }
        return versionArray[0];
    }

    getSauceDriver (username, accessKey, name) {
        const chromeVersion = this.getChromeVersionNumber();
        // Driver configs can be generated with the Sauce Platform Configurator
        // https://wiki.saucelabs.com/display/DOCS/Platform+Configurator
        let driverConfig = {
            browserName: 'chrome',
            platform: 'macOS 10.14',
            version: chromeVersion
        };
        var driver = new webdriver.Builder()
            .withCapabilities({
                browserName: driverConfig.browserName,
                platform: driverConfig.platform,
                version: driverConfig.version,
                username: username,
                accessKey: accessKey,
                name: name
            })
            .usingServer(`http://${username}:${accessKey
            }@ondemand.saucelabs.com:80/wd/hub`)
            .build();
        return driver;
    }

    getKey (keyName) {
        return Key[keyName];
    }

    findByXpath (xpath, timeoutMessage = `findByXpath timed out for path: ${xpath}`) {
        return this.driver.wait(until.elementLocated(By.xpath(xpath)), DEFAULT_TIMEOUT_MILLISECONDS, timeoutMessage)
            .then(el => (
                this.driver.wait(el.isDisplayed(), DEFAULT_TIMEOUT_MILLISECONDS, `${xpath} is not visible`)
                    .then(() => el)
            ));
    }

    waitUntilGone (element) {
        return this.driver.wait(until.stalenessOf(element));
    }

    clickXpath (xpath) {
        return this.findByXpath(xpath).then(el => el.click());
    }

    clickText (text) {
        return this.clickXpath(`//*[contains(text(), '${text}')]`);
    }

    findText (text) {
        return this.driver.wait(until.elementLocated(By.xpath(`//*[contains(text(), '${text}')]`), 5 * 1000));
    }

    clickButton (text) {
        return this.clickXpath(`//button[contains(text(), '${text}')]`);
    }

    findByCss (css) {
        return this.driver.wait(until.elementLocated(By.css(css), 1000 * 5));
    }

    clickCss (css) {
        return this.findByCss(css).then(el => el.click());
    }

    dragFromXpathToXpath (startXpath, endXpath) {
        return this.findByXpath(startXpath).then(startEl => {
            return this.findByXpath(endXpath).then(endEl => {
                return this.driver.actions()
                    .dragAndDrop(startEl, endEl)
                    .perform();
            });
        });
    }

    // must be used on a www page
    async signIn (username, password) {
        await this.clickXpath('//li[@class="link right login-item"]/a');
        let name = await this.findByXpath('//input[@id="frc-username-1088"]');
        await name.sendKeys(username);
        let word = await this.findByXpath('//input[@id="frc-password-1088"]');
        await word.sendKeys(password + this.getKey('ENTER'));
        await this.findByXpath('//span[contains(@class, "profile-name")]');
    }

    urlMatches (regex) {
        return this.driver.wait(until.urlMatches(regex), 1000 * 5);
    }

    getLogs (whitelist) {
        return this.driver.manage()
            .logs()
            .get('browser')
            .then((entries) => {
                return entries.filter((entry) => {
                    const message = entry.message;
                    for (let i = 0; i < whitelist.length; i++) {
                        if (message.indexOf(whitelist[i]) !== -1) {
                            // eslint-disable-next-line no-console
                            // console.warn('Ignoring whitelisted error: ' + whitelist[i]);
                            return false;
                        } else if (entry.level !== 'SEVERE') {
                            // eslint-disable-next-line no-console
                            // console.warn('Ignoring non-SEVERE entry: ' + message);
                            return false;
                        }
                        return true;
                    }
                    return true;
                });
            });
    }

    async containsClass (element, cl) {
        let classes = await element.getAttribute('class');
        let classList = classes.split(' ');
        if (classList.includes(cl)){
            return true;
        }
        return false;
    }

    async waitUntilVisible (element, driver) {
        await driver.wait(until.elementIsVisible(element));
    }

}

module.exports = SeleniumHelper;
<commit_msg>remove reference to 'loadPageUntilVisible' from selenium-helpers<commit_after>const webdriver = require('selenium-webdriver');
const bindAll = require('lodash.bindall');
require('chromedriver');
const chromedriverVersion = require('chromedriver').version;

const headless = process.env.SMOKE_HEADLESS || false;
const remote = process.env.SMOKE_REMOTE || false;
const ci = process.env.CI || false;
const usingCircle = process.env.CIRCLECI || false;
const buildID = process.env.CIRCLE_BUILD_NUM || '0000';
const {SAUCE_USERNAME, SAUCE_ACCESS_KEY} = process.env;
const {By, Key, until} = webdriver;

const DEFAULT_TIMEOUT_MILLISECONDS = 20 * 1000;

class SeleniumHelper {
    constructor () {
        bindAll(this, [
            'buildDriver',
            'clickButton',
            'clickCss',
            'clickText',
            'clickXpath',
            'containsClass',
            'dragFromXpathToXpath',
            'findByCss',
            'findByXpath',
            'findText',
            'getKey',
            'getDriver',
            'getLogs',
            'getSauceDriver',
            'signIn',
            'urlMatches',
            'waitUntilGone'
        ]);
    }
    buildDriver (name) {
        if (remote === 'true'){
            let nameToUse;
            if (ci === 'true'){
                let ciName = usingCircle ? 'circleCi ' : 'unknown ';
                nameToUse = ciName + buildID + ' : ' + name;
            } else {
                nameToUse = name;
            }
            this.driver = this.getSauceDriver(SAUCE_USERNAME, SAUCE_ACCESS_KEY, nameToUse);
        } else {
            this.driver = this.getDriver();
        }
        return this.driver;
    }

    getDriver () {
        const chromeCapabilities = webdriver.Capabilities.chrome();
        let args = [];
        if (headless) {
            args.push('--headless');
            args.push('window-size=1024,1680');
            args.push('--no-sandbox');
        }
        chromeCapabilities.set('chromeOptions', {args});
        let driver = new webdriver.Builder()
            .forBrowser('chrome')
            .withCapabilities(chromeCapabilities)
            .build();
        return driver;
    }

    getChromeVersionNumber () {
        const versionFinder = /\d+\.\d+/;
        const versionArray = versionFinder.exec(chromedriverVersion);
        if (versionArray === null) {
            throw new Error('couldn\'t find version of chromedriver');
        }
        return versionArray[0];
    }

    getSauceDriver (username, accessKey, name) {
        const chromeVersion = this.getChromeVersionNumber();
        // Driver configs can be generated with the Sauce Platform Configurator
        // https://wiki.saucelabs.com/display/DOCS/Platform+Configurator
        let driverConfig = {
            browserName: 'chrome',
            platform: 'macOS 10.14',
            version: chromeVersion
        };
        var driver = new webdriver.Builder()
            .withCapabilities({
                browserName: driverConfig.browserName,
                platform: driverConfig.platform,
                version: driverConfig.version,
                username: username,
                accessKey: accessKey,
                name: name
            })
            .usingServer(`http://${username}:${accessKey
            }@ondemand.saucelabs.com:80/wd/hub`)
            .build();
        return driver;
    }

    getKey (keyName) {
        return Key[keyName];
    }

    findByXpath (xpath, timeoutMessage = `findByXpath timed out for path: ${xpath}`) {
        return this.driver.wait(until.elementLocated(By.xpath(xpath)), DEFAULT_TIMEOUT_MILLISECONDS, timeoutMessage)
            .then(el => (
                this.driver.wait(el.isDisplayed(), DEFAULT_TIMEOUT_MILLISECONDS, `${xpath} is not visible`)
                    .then(() => el)
            ));
    }

    waitUntilGone (element) {
        return this.driver.wait(until.stalenessOf(element));
    }

    clickXpath (xpath) {
        return this.findByXpath(xpath).then(el => el.click());
    }

    clickText (text) {
        return this.clickXpath(`//*[contains(text(), '${text}')]`);
    }

    findText (text) {
        return this.driver.wait(until.elementLocated(By.xpath(`//*[contains(text(), '${text}')]`), 5 * 1000));
    }

    clickButton (text) {
        return this.clickXpath(`//button[contains(text(), '${text}')]`);
    }

    findByCss (css) {
        return this.driver.wait(until.elementLocated(By.css(css), 1000 * 5));
    }

    clickCss (css) {
        return this.findByCss(css).then(el => el.click());
    }

    dragFromXpathToXpath (startXpath, endXpath) {
        return this.findByXpath(startXpath).then(startEl => {
            return this.findByXpath(endXpath).then(endEl => {
                return this.driver.actions()
                    .dragAndDrop(startEl, endEl)
                    .perform();
            });
        });
    }

    // must be used on a www page
    async signIn (username, password) {
        await this.clickXpath('//li[@class="link right login-item"]/a');
        let name = await this.findByXpath('//input[@id="frc-username-1088"]');
        await name.sendKeys(username);
        let word = await this.findByXpath('//input[@id="frc-password-1088"]');
        await word.sendKeys(password + this.getKey('ENTER'));
        await this.findByXpath('//span[contains(@class, "profile-name")]');
    }

    urlMatches (regex) {
        return this.driver.wait(until.urlMatches(regex), 1000 * 5);
    }

    getLogs (whitelist) {
        return this.driver.manage()
            .logs()
            .get('browser')
            .then((entries) => {
                return entries.filter((entry) => {
                    const message = entry.message;
                    for (let i = 0; i < whitelist.length; i++) {
                        if (message.indexOf(whitelist[i]) !== -1) {
                            // eslint-disable-next-line no-console
                            // console.warn('Ignoring whitelisted error: ' + whitelist[i]);
                            return false;
                        } else if (entry.level !== 'SEVERE') {
                            // eslint-disable-next-line no-console
                            // console.warn('Ignoring non-SEVERE entry: ' + message);
                            return false;
                        }
                        return true;
                    }
                    return true;
                });
            });
    }

    async containsClass (element, cl) {
        let classes = await element.getAttribute('class');
        let classList = classes.split(' ');
        if (classList.includes(cl)){
            return true;
        }
        return false;
    }

    async waitUntilVisible (element, driver) {
        await driver.wait(until.elementIsVisible(element));
    }

}

module.exports = SeleniumHelper;
<|endoftext|>


<commit_before>'use strict';

var mongoose = require('mongoose'),
    extend   = require('util')._extend;

module.exports = exports = function mongooseIntl(schema, options) {
    if (!options || !options.languages || !Array.isArray(options.languages) || !options.languages.length) {
        throw new mongoose.Error('Required languages array is missing');
    }

    // plugin options to be set under schema options
    schema.options.mongooseIntl = {};
    var pluginOptions = schema.options.mongooseIntl;

    pluginOptions.languages = options.languages.slice(0);

    // the first available language will be used as default if it's not set or unknown value passed
    if (!options.defaultLanguage || pluginOptions.languages.indexOf(options.defaultLanguage) === -1) {
        pluginOptions.defaultLanguage = pluginOptions.languages[0];
    } else {
        pluginOptions.defaultLanguage = options.defaultLanguage.slice(0);
    }

    schema.eachPath(function (path, schemaType) {
        if (schemaType.schema) { // propagate plugin initialization for sub-documents schemas
            schemaType.schema.plugin(mongooseIntl, pluginOptions);
            return;
        }

        if (!schemaType.options.intl) {
            return;
        }

        if (!(schemaType instanceof mongoose.Schema.Types.String)) {
            throw new mongoose.Error('Mongoose-intl plugin can be used with String type only');
        }

        var pathArray = path.split('.'),
            key       = pathArray.pop(),
            prefix    = pathArray.join('.');

        if (prefix) prefix += '.';

        // removing real path, it will be changed to virtual later
        schema.remove(path);

        // schema.remove removes path from paths object only, but doesn't update tree
        // sounds like a bug, removing item from the tree manually
        var tree = pathArray.reduce(function (mem, part) {
            return mem[part];
        }, schema.tree);
        delete tree[key];

        schema.virtual(path)
            .get(function () {
                // embedded and sub-documents will use language methods from the top level document
                var owner = this.ownerDocument ? this.ownerDocument() : this,
                    lang = owner.getLanguage(),
                    langSubDoc = this.getValue(path);

                if (langSubDoc.hasOwnProperty(lang)) {
                    return langSubDoc[lang];
                }

                // are there any other languages defined?
                for (var prop in langSubDoc) {
                    if (langSubDoc.hasOwnProperty(prop)) {
                        return null; // some other languages exist, but the required is not - return null value
                    }
                }
                return void 0; // no languages defined - the entire field is undefined
            })
            .set(function (value) {
                // multiple languages are set as an object
                if (typeof value === 'object') {
                    var languages = this.schema.options.mongooseIntl.languages;
                    languages.forEach(function (lang) {
                        if (!value[lang]) { return; }
                        this.set(path + '.' + lang, value[lang]);
                    }, this);
                    return;
                }

                // embedded and sub-documents will use language methods from the top level document
                var owner = this.ownerDocument ? this.ownerDocument() : this;
                this.set(path + '.' + owner.getLanguage(), value);
            });

        // intl option is not needed for the current path any more,
        // and is unwanted for all child lang-properties
        delete schemaType.options.intl;

        var intlObject = {};
        intlObject[key] = {};
        pluginOptions.languages.forEach(function (lang) {
            var langOptions = extend({}, schemaType.options);
            if (lang !== options.defaultLanguage) {
                delete langOptions.default;
                delete langOptions.required;
            }

            if (schemaType.options.defaultAll) {
                langOptions.default = schemaType.options.defaultAll;
            }

            if (schemaType.options.requiredAll) {
                langOptions.required = schemaType.options.requiredAll;
            }
            this[lang] = langOptions;
        }, intlObject[key]);

        // intlObject example:
        // { fieldName: {
        //     en: '',
        //     de: ''
        // }}
        schema.add(intlObject, prefix);
    });

    // document methods to set the language for each model instance (document)
    schema.method({
        getLanguages: function () {
            return this.schema.options.mongooseIntl.languages;
        },
        getLanguage: function () {
            return this.docLanguage || this.schema.options.mongooseIntl.defaultLanguage;
        },
        setLanguage: function (lang) {
            if (lang && this.getLanguages().indexOf(lang) !== -1) {
                this.docLanguage = lang;
            }
        },
        unsetLanguage: function () {
            delete this.docLanguage;
        }
    });

    // model methods to set the language for the current schema
    schema.static({
        getLanguages: function () {
            return this.schema.options.mongooseIntl.languages;
        },
        getDefaultLanguage: function () {
            return this.schema.options.mongooseIntl.defaultLanguage;
        },
        setDefaultLanguage: function (lang) {

            function updateLanguage(schema, lang) {
                schema.options.mongooseIntl.defaultLanguage = lang.slice(0);

                // default language change for sub-documents schemas
                schema.eachPath(function (path, schemaType) {
                    if (schemaType.schema) {
                        updateLanguage(schemaType.schema, lang);
                    }
                });

            }
            if (lang && this.getLanguages().indexOf(lang) !== -1) {
                updateLanguage(this.schema, lang);
            }
        }
    });

    // Mongoose will emit 'init' event once the schema will be attached to the model
    schema.on('init', function (model) {
        // no actions are required in the global method is already defined
        if (model.db.setDefaultLanguage) {
            return;
        }

        // define a global method to change the language for all models (and their schemas)
        // created for the current mongo connection
        model.db.setDefaultLanguage = function (lang) {
            var model, modelName;
            for (modelName in this.models) {
                if (this.models.hasOwnProperty(modelName)) {
                    model = this.models[modelName];
                    model.setDefaultLanguage && model.setDefaultLanguage(lang);
                }
            }
        };

        // create an alias for the global change language method attached to the default connection
        if (!mongoose.setDefaultLanguage) {
            mongoose.setDefaultLanguage = mongoose.connection.setDefaultLanguage;
        }
    });
};
<commit_msg>Fix #12: skip undefined fields<commit_after>'use strict';

var mongoose = require('mongoose'),
    extend   = require('util')._extend;

module.exports = exports = function mongooseIntl(schema, options) {
    if (!options || !options.languages || !Array.isArray(options.languages) || !options.languages.length) {
        throw new mongoose.Error('Required languages array is missing');
    }

    // plugin options to be set under schema options
    schema.options.mongooseIntl = {};
    var pluginOptions = schema.options.mongooseIntl;

    pluginOptions.languages = options.languages.slice(0);

    // the first available language will be used as default if it's not set or unknown value passed
    if (!options.defaultLanguage || pluginOptions.languages.indexOf(options.defaultLanguage) === -1) {
        pluginOptions.defaultLanguage = pluginOptions.languages[0];
    } else {
        pluginOptions.defaultLanguage = options.defaultLanguage.slice(0);
    }

    schema.eachPath(function (path, schemaType) {
        if (schemaType.schema) { // propagate plugin initialization for sub-documents schemas
            schemaType.schema.plugin(mongooseIntl, pluginOptions);
            return;
        }

        if (!schemaType.options.intl) {
            return;
        }

        if (!(schemaType instanceof mongoose.Schema.Types.String)) {
            throw new mongoose.Error('Mongoose-intl plugin can be used with String type only');
        }

        var pathArray = path.split('.'),
            key       = pathArray.pop(),
            prefix    = pathArray.join('.');

        if (prefix) prefix += '.';

        // removing real path, it will be changed to virtual later
        schema.remove(path);

        // schema.remove removes path from paths object only, but doesn't update tree
        // sounds like a bug, removing item from the tree manually
        var tree = pathArray.reduce(function (mem, part) {
            return mem[part];
        }, schema.tree);
        delete tree[key];

        schema.virtual(path)
            .get(function () {
                // embedded and sub-documents will use language methods from the top level document
                var owner = this.ownerDocument ? this.ownerDocument() : this,
                    lang = owner.getLanguage(),
                    langSubDoc = this.getValue(path);

                if (langSubDoc === null || langSubDoc === void 0) {
                    return langSubDoc;
                }

                if (langSubDoc.hasOwnProperty(lang)) {
                    return langSubDoc[lang];
                }

                // are there any other languages defined?
                for (var prop in langSubDoc) {
                    if (langSubDoc.hasOwnProperty(prop)) {
                        return null; // some other languages exist, but the required is not - return null value
                    }
                }
                return void 0; // no languages defined - the entire field is undefined
            })
            .set(function (value) {
                // multiple languages are set as an object
                if (typeof value === 'object') {
                    var languages = this.schema.options.mongooseIntl.languages;
                    languages.forEach(function (lang) {
                        if (!value[lang]) { return; }
                        this.set(path + '.' + lang, value[lang]);
                    }, this);
                    return;
                }

                // embedded and sub-documents will use language methods from the top level document
                var owner = this.ownerDocument ? this.ownerDocument() : this;
                this.set(path + '.' + owner.getLanguage(), value);
            });

        // intl option is not needed for the current path any more,
        // and is unwanted for all child lang-properties
        delete schemaType.options.intl;

        var intlObject = {};
        intlObject[key] = {};
        pluginOptions.languages.forEach(function (lang) {
            var langOptions = extend({}, schemaType.options);
            if (lang !== options.defaultLanguage) {
                delete langOptions.default;
                delete langOptions.required;
            }

            if (schemaType.options.defaultAll) {
                langOptions.default = schemaType.options.defaultAll;
            }

            if (schemaType.options.requiredAll) {
                langOptions.required = schemaType.options.requiredAll;
            }
            this[lang] = langOptions;
        }, intlObject[key]);

        // intlObject example:
        // { fieldName: {
        //     en: '',
        //     de: ''
        // }}
        schema.add(intlObject, prefix);
    });

    // document methods to set the language for each model instance (document)
    schema.method({
        getLanguages: function () {
            return this.schema.options.mongooseIntl.languages;
        },
        getLanguage: function () {
            return this.docLanguage || this.schema.options.mongooseIntl.defaultLanguage;
        },
        setLanguage: function (lang) {
            if (lang && this.getLanguages().indexOf(lang) !== -1) {
                this.docLanguage = lang;
            }
        },
        unsetLanguage: function () {
            delete this.docLanguage;
        }
    });

    // model methods to set the language for the current schema
    schema.static({
        getLanguages: function () {
            return this.schema.options.mongooseIntl.languages;
        },
        getDefaultLanguage: function () {
            return this.schema.options.mongooseIntl.defaultLanguage;
        },
        setDefaultLanguage: function (lang) {

            function updateLanguage(schema, lang) {
                schema.options.mongooseIntl.defaultLanguage = lang.slice(0);

                // default language change for sub-documents schemas
                schema.eachPath(function (path, schemaType) {
                    if (schemaType.schema) {
                        updateLanguage(schemaType.schema, lang);
                    }
                });

            }
            if (lang && this.getLanguages().indexOf(lang) !== -1) {
                updateLanguage(this.schema, lang);
            }
        }
    });

    // Mongoose will emit 'init' event once the schema will be attached to the model
    schema.on('init', function (model) {
        // no actions are required in the global method is already defined
        if (model.db.setDefaultLanguage) {
            return;
        }

        // define a global method to change the language for all models (and their schemas)
        // created for the current mongo connection
        model.db.setDefaultLanguage = function (lang) {
            var model, modelName;
            for (modelName in this.models) {
                if (this.models.hasOwnProperty(modelName)) {
                    model = this.models[modelName];
                    model.setDefaultLanguage && model.setDefaultLanguage(lang);
                }
            }
        };

        // create an alias for the global change language method attached to the default connection
        if (!mongoose.setDefaultLanguage) {
            mongoose.setDefaultLanguage = mongoose.connection.setDefaultLanguage;
        }
    });
};
<|endoftext|>


<commit_before>#!/usr/bin/env ./nodeunit/bin/nodeunit

var crc = require('../lib/crc');

describe('crc8()', function(){
  it('should work with strings', function(){
    crc.crc8('hello world').should.equal(64);
  })
})<commit_msg>Added tests for other methods<commit_after>#!/usr/bin/env ./nodeunit/bin/nodeunit

var crc = require('../lib/crc');

describe('crc8()', function(){
  it('should work with strings', function(){
    crc.crc8('hello world').should.equal(64);
  })
})

describe('crc16()', function(){
  it('should work with strings', function(){
    crc.crc16('hello world').should.equal(15332);
  })
})

describe('crc32()', function(){
  it('should work with strings', function(){
    crc.crc32('hello world').should.equal(222957957);
  })
})

describe('crcArc()', function(){
  it('should work with strings', function(){
    crc.crcArc('hello world').should.equal(14785);
  })
})

describe('fcs16()', function(){
  it('should work with strings', function(){
    crc.fcs16('hello world').should.equal(44550);
  })
})

describe('hex8()', function(){
  it('should work with strings', function(){
    crc.hex8(64).should.equal('40');
  })
})

describe('hex16()', function(){
  it('should work with strings', function(){
    crc.hex16(15332).should.equal('3BE4');
  })
})

describe('hex32()', function(){
  it('should work with strings', function(){
    crc.hex32(222957957).should.equal('0D4A1185');
  })
})
<|endoftext|>


<commit_before>"use strict";

const gulp = require('gulp');
const notify = require('gulp-notify');
const env = require('gulp-environments');
const del = require('del');
const path = require('path');
const childProcess = require('child_process');
const webpack = require('webpack');
const gulpWebpack = require('gulp-webpack');
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackTemplate = require('html-webpack-template');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const CompressionPlugin = require("compression-webpack-plugin");

const config = require('config/gulp.js');

gulp.task('webpack', ['webpack:clean'], (done) => {

  console.log('Running webpack task');

  const gitCommit = childProcess.execSync('git rev-parse HEAD').toString().trim();

  const extractTextPlugin = new ExtractTextPlugin('[name].css');
  const occurenceOrderPlugin = new webpack.optimize.OccurrenceOrderPlugin()

  const commonsChunkPlugin = new CommonsChunkPlugin({
    names: ['fonts-loader'],
    minChunks: Infinity,
  });

  const bundleAnalyzerPlugin = new BundleAnalyzerPlugin({
    analyzerHost: process.env.BUNDLE_ANALYZER_HOST,
    analyzerPort: process.env.BUNDLE_ANALYZER_PORT,
  });

  const htmlWebpackPlugin = new HtmlWebpackPlugin({
    template: HtmlWebpackTemplate,
    title: 'Uprise app',
    appMountId: 'app', // Generate #app where to mount
    mobile: true, // Scale page on mobile
    inject: false // html-webpack-template requires this to work
  })

  const definePlugin = new webpack.DefinePlugin({
    'process.env': {
      'NODE_ENV': env.development() ? JSON.stringify('development') : JSON.stringify('production'),
      'SERVER_BASE_URL': '"' + process.env.SERVER_BASE_URL + '"',
      'CLIENT_BASE_URL': '"' + process.env.CLIENT_BASE_URL + '"',
      'AWS_S3_REGION': '"' + process.env.AWS_S3_REGION + '"',
      'AWS_S3_BUCKET_NAME': '"' + process.env.AWS_S3_BUCKET_NAME + '"',
      'AWS_S3_ENDPOINT': '"' + process.env.AWS_S3_ENDPOINT + '"',
      'SENTRY_PUBLIC_DSN': '"' + process.env.SENTRY_PUBLIC_DSN + '"',
      'GOOGLE_UA_ID': '"' + process.env.GOOGLE_UA_ID + '"',
      'GIT_COMMIT': '"' + gitCommit + '"',
    }
  });

  config.webpack = {
    entry: {
      'index': ['babel-polyfill', path.resolve(config.src, 'index')]
    },
    output: {
      filename: '[name]-[hash].js',
      publicPath: process.env.CLIENT_BASE_URL,
      path: path.resolve(config.dest)
    },
    devtool: env.development() ? "eval-cheap-module-source-map" : "",
    module: {
      rules: [
        {
          enforce: 'pre',
          test: /\.jsx?$/,
          loader: 'eslint-loader',
          options: {
            fix: false
          },
          include: [
            path.resolve(config.publicRoot),
          ]
        },
        {
          test: /\.jsx?$/,
          loader: 'babel-loader',
          include: [
            path.resolve(config.publicRoot),
            path.resolve(config.nodeModules, 'camelcase'),
          ],
          query: {
            cacheDirectory: true,
            presets: ['es2015', 'react', 'stage-3'],
            plugins: ['transform-runtime', 'transform-class-properties']
          }
        },
        {
          test: /\.(graphql|gql)$/,
          exclude: /node_modules/,
          loader: 'graphql-tag/loader'
        },
        {
          test: /\.scss$/,
          loader: 'style-loader!css-loader?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]__[hash:base64:5]!sass-loader?sourceMap'
            // loader: 'style-loader!css-loader?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]__[hash:base64:5]!sass-loader?sourceMap'
        },
        {
          test: /\.css$/,
          loader: 'style-loader!css-loader'
        },
        {
          test: /\.(png|jpg)$/,
          loader: 'url-loader?limit=8192' // inline base64 URLs for <=8k images, direct URLs for the rest
        },
        {
          test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
          loader: 'url-loader?limit=10000&mimetype=application/font-woff'
        },
        {
          test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
          loader: 'url-loader?limit=10000&mimetype=application/octet-stream'
        },
        {
          test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
          loader: 'file-loader'
        },
        {
          test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
          loader: 'url-loader?limit=10000&mimetype=image/svg+xml'
        },
        {
          test: /\.md$/,
          loader: path.resolve(config.publicRoot, 'src', 'lib', 'markdown-loader.js')
        },
      ]
    },
    externals: {
      'react/addons': true,
      'react/lib/ExecutionEnvironment': true,
      'react/lib/ReactContext': true
    },
    plugins: env.production() ? [
      htmlWebpackPlugin,
      extractTextPlugin,
      commonsChunkPlugin,

      new webpack.optimize.UglifyJsPlugin({
        mangle: true,
        compress: {
          // warnings: true,
          warnings: false, // Suppress uglification warnings
          dead_code: true,
          drop_debugger: true,
          conditionals: true,
          evaluate: true,
          drop_console: true,
          pure_getters: true,
          sequences: true,
          booleans: true,
          unsafe: true,
          unsafe_comps: true,
          screw_ie8: true
        },
        output: {
          comments: false
        }
      }),
      new CompressionPlugin({
        asset: "[path].gz[query]",
        algorithm: "gzip",
        test: /\.js$|\.css$|\.html$/,
        threshold: 10240,
        minRatio: 0
      }),
      definePlugin,
      occurenceOrderPlugin,
      new webpack.optimize.AggressiveMergingPlugin()
    ] : [
      // bundleAnalyzerPlugin,
      definePlugin,
      commonsChunkPlugin,
      extractTextPlugin,
      htmlWebpackPlugin,
      occurenceOrderPlugin,
      new webpack.optimize.AggressiveMergingPlugin()
    ],

    bail: env.production(),
    resolve: {
      extensions: ['*', '.js', '.jsx', '.json'],
    },
    node: {
      fs: 'empty'
    }
  };

  return gulp.src(config.src)
    .pipe(gulpWebpack(config.webpack, webpack))
    .on("error", notify.onError("Error: <%= error.message %>"))
    .pipe(gulp.dest(config.dest))
    .pipe(notify());
});

gulp.task('webpack:clean', (done) => {
  return del([
    config.dest + '/*.js',
    config.dest + '/*.map',
    config.dest + '/*.gz',
  ], done);
});
<commit_msg>Changing default title page.<commit_after>"use strict";

const gulp = require('gulp');
const notify = require('gulp-notify');
const env = require('gulp-environments');
const del = require('del');
const path = require('path');
const childProcess = require('child_process');
const webpack = require('webpack');
const gulpWebpack = require('gulp-webpack');
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackTemplate = require('html-webpack-template');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const CompressionPlugin = require("compression-webpack-plugin");

const config = require('config/gulp.js');

gulp.task('webpack', ['webpack:clean'], (done) => {

  console.log('Running webpack task');

  const gitCommit = childProcess.execSync('git rev-parse HEAD').toString().trim();

  const extractTextPlugin = new ExtractTextPlugin('[name].css');
  const occurenceOrderPlugin = new webpack.optimize.OccurrenceOrderPlugin()

  const commonsChunkPlugin = new CommonsChunkPlugin({
    names: ['fonts-loader'],
    minChunks: Infinity,
  });

  const bundleAnalyzerPlugin = new BundleAnalyzerPlugin({
    analyzerHost: process.env.BUNDLE_ANALYZER_HOST,
    analyzerPort: process.env.BUNDLE_ANALYZER_PORT,
  });

  const htmlWebpackPlugin = new HtmlWebpackPlugin({
    template: HtmlWebpackTemplate,
    title: 'UpRise App',
    appMountId: 'app', // Generate #app where to mount
    mobile: true, // Scale page on mobile
    inject: false // html-webpack-template requires this to work
  })

  const definePlugin = new webpack.DefinePlugin({
    'process.env': {
      'NODE_ENV': env.development() ? JSON.stringify('development') : JSON.stringify('production'),
      'SERVER_BASE_URL': '"' + process.env.SERVER_BASE_URL + '"',
      'CLIENT_BASE_URL': '"' + process.env.CLIENT_BASE_URL + '"',
      'AWS_S3_REGION': '"' + process.env.AWS_S3_REGION + '"',
      'AWS_S3_BUCKET_NAME': '"' + process.env.AWS_S3_BUCKET_NAME + '"',
      'AWS_S3_ENDPOINT': '"' + process.env.AWS_S3_ENDPOINT + '"',
      'SENTRY_PUBLIC_DSN': '"' + process.env.SENTRY_PUBLIC_DSN + '"',
      'GOOGLE_UA_ID': '"' + process.env.GOOGLE_UA_ID + '"',
      'GIT_COMMIT': '"' + gitCommit + '"',
    }
  });

  config.webpack = {
    entry: {
      'index': ['babel-polyfill', path.resolve(config.src, 'index')]
    },
    output: {
      filename: '[name]-[hash].js',
      publicPath: process.env.CLIENT_BASE_URL,
      path: path.resolve(config.dest)
    },
    devtool: env.development() ? "eval-cheap-module-source-map" : "",
    module: {
      rules: [
        {
          enforce: 'pre',
          test: /\.jsx?$/,
          loader: 'eslint-loader',
          options: {
            fix: false
          },
          include: [
            path.resolve(config.publicRoot),
          ]
        },
        {
          test: /\.jsx?$/,
          loader: 'babel-loader',
          include: [
            path.resolve(config.publicRoot),
            path.resolve(config.nodeModules, 'camelcase'),
          ],
          query: {
            cacheDirectory: true,
            presets: ['es2015', 'react', 'stage-3'],
            plugins: ['transform-runtime', 'transform-class-properties']
          }
        },
        {
          test: /\.(graphql|gql)$/,
          exclude: /node_modules/,
          loader: 'graphql-tag/loader'
        },
        {
          test: /\.scss$/,
          loader: 'style-loader!css-loader?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]__[hash:base64:5]!sass-loader?sourceMap'
            // loader: 'style-loader!css-loader?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]__[hash:base64:5]!sass-loader?sourceMap'
        },
        {
          test: /\.css$/,
          loader: 'style-loader!css-loader'
        },
        {
          test: /\.(png|jpg)$/,
          loader: 'url-loader?limit=8192' // inline base64 URLs for <=8k images, direct URLs for the rest
        },
        {
          test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
          loader: 'url-loader?limit=10000&mimetype=application/font-woff'
        },
        {
          test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
          loader: 'url-loader?limit=10000&mimetype=application/octet-stream'
        },
        {
          test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
          loader: 'file-loader'
        },
        {
          test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
          loader: 'url-loader?limit=10000&mimetype=image/svg+xml'
        },
        {
          test: /\.md$/,
          loader: path.resolve(config.publicRoot, 'src', 'lib', 'markdown-loader.js')
        },
      ]
    },
    externals: {
      'react/addons': true,
      'react/lib/ExecutionEnvironment': true,
      'react/lib/ReactContext': true
    },
    plugins: env.production() ? [
      htmlWebpackPlugin,
      extractTextPlugin,
      commonsChunkPlugin,

      new webpack.optimize.UglifyJsPlugin({
        mangle: true,
        compress: {
          // warnings: true,
          warnings: false, // Suppress uglification warnings
          dead_code: true,
          drop_debugger: true,
          conditionals: true,
          evaluate: true,
          drop_console: true,
          pure_getters: true,
          sequences: true,
          booleans: true,
          unsafe: true,
          unsafe_comps: true,
          screw_ie8: true
        },
        output: {
          comments: false
        }
      }),
      new CompressionPlugin({
        asset: "[path].gz[query]",
        algorithm: "gzip",
        test: /\.js$|\.css$|\.html$/,
        threshold: 10240,
        minRatio: 0
      }),
      definePlugin,
      occurenceOrderPlugin,
      new webpack.optimize.AggressiveMergingPlugin()
    ] : [
      // bundleAnalyzerPlugin,
      definePlugin,
      commonsChunkPlugin,
      extractTextPlugin,
      htmlWebpackPlugin,
      occurenceOrderPlugin,
      new webpack.optimize.AggressiveMergingPlugin()
    ],

    bail: env.production(),
    resolve: {
      extensions: ['*', '.js', '.jsx', '.json'],
    },
    node: {
      fs: 'empty'
    }
  };

  return gulp.src(config.src)
    .pipe(gulpWebpack(config.webpack, webpack))
    .on("error", notify.onError("Error: <%= error.message %>"))
    .pipe(gulp.dest(config.dest))
    .pipe(notify());
});

gulp.task('webpack:clean', (done) => {
  return del([
    config.dest + '/*.js',
    config.dest + '/*.map',
    config.dest + '/*.gz',
  ], done);
});
<|endoftext|>


<commit_before>var async = require('async');
var domain = require('domain');
var CallbackFiller = require('./callback_filler');

/**
 * Module that lets you specify a hierarchy of caches.
 */
var multiCaching = function(caches) {
    var self = {};
    if (!Array.isArray(caches)) {
        throw new Error('multiCaching requires an array of caches');
    }

    var callbackFiller = new CallbackFiller();

    function getFromHighestPriorityCache(key, options, cb) {
        if (typeof options === 'function') {
            cb = options;
            options = undefined;
        }

        var i = 0;
        async.eachSeries(caches, function(cache, next) {
            var callback = function(err, result) {
                if (err) {
                    return next(err);
                }
                if (result) {
                    // break out of async loop.
                    return cb(err, result, i);
                }

                i += 1;
                next();
            };

            if (typeof options === 'object') {
                cache.store.get(key, options, callback);
            } else {
                cache.store.get(key, callback);
            }
        }, cb);
    }

    function setInMultipleCaches(caches, opts, cb) {
        async.each(caches, function(cache, next) {
            if (typeof opts.options === 'object') {
                cache.store.set(opts.key, opts.value, opts.options, next);
            } else {
                cache.store.set(opts.key, opts.value, opts.ttl, next);
            }
        }, cb);
    }

    /**
     * Looks for an item in cache tiers.
     *
     * When a key is found in a lower cache, all higher levels are updated
     */
    self.getAndPassUp = function(key, cb) {
        getFromHighestPriorityCache(key, function(err, result, index) {
            if (err) {
                return cb(err);
            }

            cb(err, result);

            if (result !== undefined && index) {
                var cachesToUpdate = caches.slice(0, index);
                async.each(cachesToUpdate, function(cache, next) {
                    cache.set(key, result, result.ttl, next);
                });
            }
        });
    };

    /**
     * This is for backward-compatibility
     */
    //jscs:disable requireCamelCaseOrUpperCaseIdentifiers
    self.get_and_pass_up = self.getAndPassUp;
    //jscs:enable requireCamelCaseOrUpperCaseIdentifiers

    /**
     * Wraps a function in one or more caches.
     * Has same API as regular caching module.
     *
     * If a key doesn't exist in any cache, it gets set in all caches.
     * If a key exists in a high-priority (e.g., first) cache, it gets returned immediately
     * without getting set in other lower-priority caches.
     * If a key doesn't exist in a higher-priority cache but exists in a lower-priority
     * cache, it gets set in all higher-priority caches.
     */
    self.wrap = function(key, work, options, cb) {
        if (typeof options === 'function') {
            cb = options;
            options = undefined;
        }

        function getOptsForSet(result) {
            var opts = {
                key: key,
                value: result,
                options: options
            };

            if (typeof options !== 'object') {
                opts.ttl = options;
            }

            return opts;
        }

        var hasKey = callbackFiller.has(key);
        callbackFiller.add(key, {cb: cb, domain: process.domain});
        if (hasKey) { return; }

        getFromHighestPriorityCache(key, function(err, result, index) {
            if (err) {
                return callbackFiller.fill(key, err);
            } else if (result) {
                var cachesToUpdate = caches.slice(0, index);
                var opts = getOptsForSet(result);

                setInMultipleCaches(cachesToUpdate, opts, function(err) {
                    callbackFiller.fill(key, err, result);
                });
            } else {
                domain
                .create()
                .on('error', function(err) {
                    callbackFiller.fill(key, err);
                })
                .bind(work)(function(err, data) {
                    if (err) {
                        return callbackFiller.fill(key, err);
                    }

                    var opts = getOptsForSet(data);

                    setInMultipleCaches(caches, opts, function(err) {
                        callbackFiller.fill(key, err, data);
                    });
                });
            }
        });
    };

    self.set = function(key, value, options, cb) {
        var opts = {
            key: key,
            value: value,
            options: options
        };
        if (typeof options !== 'object') {
            opts.ttl = options;
        }
        setInMultipleCaches(caches, opts, cb);
    };

    self.get = function(key, options, cb) {
        if (typeof options === 'function') {
            cb = options;
            options = false;
        }
        getFromHighestPriorityCache(key, options, cb);
    };

    self.del = function(key, options, cb) {
        if (typeof options === 'function') {
            cb = options;
            options = false;
        }
        async.each(caches, function(cache, next) {
            if (typeof options === 'object') {
                cache.store.del(key, options, next);
            } else {
                cache.store.del(key, next);
            }
        }, cb);
    };

    return self;
};

module.exports = multiCaching;
<commit_msg>not trying to pass ttl into cache.set() in getAndPassUp()<commit_after>var async = require('async');
var domain = require('domain');
var CallbackFiller = require('./callback_filler');

/**
 * Module that lets you specify a hierarchy of caches.
 */
var multiCaching = function(caches) {
    var self = {};
    if (!Array.isArray(caches)) {
        throw new Error('multiCaching requires an array of caches');
    }

    var callbackFiller = new CallbackFiller();

    function getFromHighestPriorityCache(key, options, cb) {
        if (typeof options === 'function') {
            cb = options;
            options = undefined;
        }

        var i = 0;
        async.eachSeries(caches, function(cache, next) {
            var callback = function(err, result) {
                if (err) {
                    return next(err);
                }
                if (result) {
                    // break out of async loop.
                    return cb(err, result, i);
                }

                i += 1;
                next();
            };

            if (typeof options === 'object') {
                cache.store.get(key, options, callback);
            } else {
                cache.store.get(key, callback);
            }
        }, cb);
    }

    function setInMultipleCaches(caches, opts, cb) {
        async.each(caches, function(cache, next) {
            if (typeof opts.options === 'object') {
                cache.store.set(opts.key, opts.value, opts.options, next);
            } else {
                cache.store.set(opts.key, opts.value, opts.ttl, next);
            }
        }, cb);
    }

    /**
     * Looks for an item in cache tiers.
     *
     * When a key is found in a lower cache, all higher levels are updated
     */
    self.getAndPassUp = function(key, cb) {
        getFromHighestPriorityCache(key, function(err, result, index) {
            if (err) {
                return cb(err);
            }

            cb(err, result);

            if (result !== undefined && index) {
                var cachesToUpdate = caches.slice(0, index);
                async.each(cachesToUpdate, function(cache, next) {
                    // We rely on the cache module's default TTL
                    cache.set(key, result, next);
                });
            }
        });
    };

    /**
     * This is for backward-compatibility
     */
    //jscs:disable requireCamelCaseOrUpperCaseIdentifiers
    self.get_and_pass_up = self.getAndPassUp;
    //jscs:enable requireCamelCaseOrUpperCaseIdentifiers

    /**
     * Wraps a function in one or more caches.
     * Has same API as regular caching module.
     *
     * If a key doesn't exist in any cache, it gets set in all caches.
     * If a key exists in a high-priority (e.g., first) cache, it gets returned immediately
     * without getting set in other lower-priority caches.
     * If a key doesn't exist in a higher-priority cache but exists in a lower-priority
     * cache, it gets set in all higher-priority caches.
     */
    self.wrap = function(key, work, options, cb) {
        if (typeof options === 'function') {
            cb = options;
            options = undefined;
        }

        function getOptsForSet(result) {
            var opts = {
                key: key,
                value: result,
                options: options
            };

            if (typeof options !== 'object') {
                opts.ttl = options;
            }

            return opts;
        }

        var hasKey = callbackFiller.has(key);
        callbackFiller.add(key, {cb: cb, domain: process.domain});
        if (hasKey) { return; }

        getFromHighestPriorityCache(key, function(err, result, index) {
            if (err) {
                return callbackFiller.fill(key, err);
            } else if (result) {
                var cachesToUpdate = caches.slice(0, index);
                var opts = getOptsForSet(result);

                setInMultipleCaches(cachesToUpdate, opts, function(err) {
                    callbackFiller.fill(key, err, result);
                });
            } else {
                domain
                .create()
                .on('error', function(err) {
                    callbackFiller.fill(key, err);
                })
                .bind(work)(function(err, data) {
                    if (err) {
                        return callbackFiller.fill(key, err);
                    }

                    var opts = getOptsForSet(data);

                    setInMultipleCaches(caches, opts, function(err) {
                        callbackFiller.fill(key, err, data);
                    });
                });
            }
        });
    };

    self.set = function(key, value, options, cb) {
        var opts = {
            key: key,
            value: value,
            options: options
        };
        if (typeof options !== 'object') {
            opts.ttl = options;
        }
        setInMultipleCaches(caches, opts, cb);
    };

    self.get = function(key, options, cb) {
        if (typeof options === 'function') {
            cb = options;
            options = false;
        }
        getFromHighestPriorityCache(key, options, cb);
    };

    self.del = function(key, options, cb) {
        if (typeof options === 'function') {
            cb = options;
            options = false;
        }
        async.each(caches, function(cache, next) {
            if (typeof options === 'object') {
                cache.store.del(key, options, next);
            } else {
                cache.store.del(key, next);
            }
        }, cb);
    };

    return self;
};

module.exports = multiCaching;
<|endoftext|>