<commit_before>import request from 'supertest';
import app from '../js/app';
import {expect} from 'chai';

describe('root path', () => {
  it('should return 200 with correct title', done => {
    request(app)
      .get('/')
      .expect(200)
      .end((err, res) => {
        expect(res.text).to.contain('<title>不思議のダンジョン 値段識別表</title>');

        if (err) return done(err);
        done();
      });
  });
});

describe('shiren4 path', () => {
  it('should return 200 with correct title', done => {
    request(app)
      .get('/shiren4/')
      .expect(200)
      .end((err, res) => {
        expect(res.text).to.contain('<title>風来のシレン4 神の眼と悪魔のヘソ - 不思議のダンジョン 値段識別表</title>');

        if (err) return done(err);
        done();
      });
  });
});
<commit_msg>test/response.js: Add acceptable test for "/shiren5/" route<commit_after>import request from 'supertest';
import app from '../js/app';
import {expect} from 'chai';

describe('root path', () => {
  it('should return 200 with correct title', done => {
    request(app)
      .get('/')
      .expect(200)
      .end((err, res) => {
        expect(res.text).to.contain('<title>不思議のダンジョン 値段識別表</title>');

        if (err) return done(err);
        done();
      });
  });
});

describe('shiren4 path', () => {
  it('should return 200 with correct title', done => {
    request(app)
      .get('/shiren4/')
      .expect(200)
      .end((err, res) => {
        expect(res.text).to.contain('<title>風来のシレン4 神の眼と悪魔のヘソ - 不思議のダンジョン 値段識別表</title>');

        if (err) return done(err);
        done();
      });
  });
});

describe('shiren5 path', () => {
  it('should return 200 with correct title', done => {
    request(app)
      .get('/shiren5/')
      .expect(200)
      .end((err, res) => {
        expect(res.text).to.contain('<title>風来のシレン5 フォーチュンタワーと運命のダイス - 不思議のダンジョン 値段識別表</title>');

        if (err) return done(err);
        done();
      });
  });
});
<|endoftext|>


<commit_before>'use strict'

const assert = require('assert')
const childProcess = require('child_process')
const electronPackager = require('electron-packager')
const fs = require('fs-extra')
const includePathInPackagedApp = require('./include-path-in-packaged-app')
const getLicenseText = require('./get-license-text')
const path = require('path')
const spawnSync = require('./spawn-sync')

const CONFIG = require('../config')

module.exports = function () {
  const appName = getAppName()
  console.log(`Running electron-packager on ${CONFIG.intermediateAppPath} with app name "${appName}"`)
  return runPackager({
    'app-bundle-id': 'com.github.atom',
    'app-copyright': `Copyright © 2014-${(new Date()).getFullYear()} GitHub, Inc. All rights reserved.`,
    'app-version': CONFIG.appMetadata.version,
    'arch': process.platform === 'win32' ? 'ia32' : 'x64',
    'asar': {unpack: buildAsarUnpackGlobExpression()},
    'build-version': CONFIG.appMetadata.version,
    'download': {cache: CONFIG.electronDownloadPath},
    'dir': CONFIG.intermediateAppPath,
    'extend-info': path.join(CONFIG.repositoryRootPath, 'resources', 'mac', 'atom-Info.plist'),
    'helper-bundle-id': 'com.github.atom.helper',
    'icon': getIcon(),
    'name': appName,
    'out': CONFIG.buildOutputPath,
    'overwrite': true,
    'platform': process.platform,
    'version': CONFIG.appMetadata.electronVersion,
    'version-string': {
      'CompanyName': 'GitHub, Inc.',
      'FileDescription': 'Atom',
      'ProductName': 'Atom'
    }
  }).then((packagedAppPath) => {
    let bundledResourcesPath
    if (process.platform === 'darwin') {
      bundledResourcesPath = path.join(packagedAppPath, 'Contents', 'Resources')
      setAtomHelperVersion(packagedAppPath)
    } else if (process.platform === 'linux') {
      bundledResourcesPath = path.join(packagedAppPath, 'resources')
      chmodNodeFiles(packagedAppPath)
    } else {
      bundledResourcesPath = path.join(packagedAppPath, 'resources')
    }

    return copyNonASARResources(packagedAppPath, bundledResourcesPath).then(() => {
      console.log(`Application bundle created at ${packagedAppPath}`)
      return packagedAppPath
    })
  })
}

function copyNonASARResources (packagedAppPath, bundledResourcesPath) {
  console.log(`Copying non-ASAR resources to ${bundledResourcesPath}`)
  fs.copySync(
    path.join(CONFIG.repositoryRootPath, 'apm', 'node_modules', 'atom-package-manager'),
    path.join(bundledResourcesPath, 'app', 'apm'),
    {filter: includePathInPackagedApp}
  )
  if (process.platform !== 'win32') {
    // Existing symlinks on user systems point to an outdated path, so just symlink it to the real location of the apm binary.
    // TODO: Change command installer to point to appropriate path and remove this fallback after a few releases.
    fs.symlinkSync(path.join('..', '..', 'bin', 'apm'), path.join(bundledResourcesPath, 'app', 'apm', 'node_modules', '.bin', 'apm'))
    fs.copySync(path.join(CONFIG.repositoryRootPath, 'atom.sh'), path.join(bundledResourcesPath, 'app', 'atom.sh'))
  }
  if (process.platform === 'darwin') {
    fs.copySync(path.join(CONFIG.repositoryRootPath, 'resources', 'mac', 'file.icns'), path.join(bundledResourcesPath, 'file.icns'))
  } else if (process.platform === 'linux') {
    fs.copySync(path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'png', '1024.png'), path.join(packagedAppPath, 'atom.png'))
  } else if (process.platform === 'win32') {
    [ 'atom.cmd', 'atom.sh', 'atom.js', 'apm.cmd', 'apm.sh', 'file.ico' ]
      .forEach(file => fs.copySync(path.join('resources', 'win', file), path.join(bundledResourcesPath, 'cli', file)))
  }

  console.log(`Writing LICENSE.md to ${bundledResourcesPath}`)
  return getLicenseText().then((licenseText) => {
    fs.writeFileSync(path.join(bundledResourcesPath, 'LICENSE.md'), licenseText)
  })
}

function setAtomHelperVersion (packagedAppPath) {
  const frameworksPath = path.join(packagedAppPath, 'Contents', 'Frameworks')
  const helperPListPath = path.join(frameworksPath, 'Atom Helper.app', 'Contents', 'Info.plist')
  console.log(`Setting Atom Helper Version for ${helperPListPath}`)
  spawnSync('/usr/libexec/PlistBuddy', ['-c', `Add CFBundleVersion string ${CONFIG.appMetadata.version}`, helperPListPath])
  spawnSync('/usr/libexec/PlistBuddy', ['-c', `Add CFBundleShortVersionString string ${CONFIG.appMetadata.version}`, helperPListPath])
}

function chmodNodeFiles (packagedAppPath) {
  console.log(`Changing permissions for node files in ${packagedAppPath}`)
  childProcess.execSync(`find "${packagedAppPath}" -type f -name *.node -exec chmod a-x {} \\;`)
}

function buildAsarUnpackGlobExpression () {
  const unpack = [
    '*.node',
    'ctags-config',
    'ctags-darwin',
    'ctags-linux',
    'ctags-win32.exe',
    path.join('**', 'node_modules', 'spellchecker', '**'),
    path.join('**', 'resources', 'atom.png')
  ]

  return `{${unpack.join(',')}}`
}

function getAppName () {
  if (process.platform === 'darwin') {
    return CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom'
  } else {
    return 'atom'
  }
}

function getIcon () {
  switch (process.platform) {
    case 'darwin':
      return path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'atom.icns')
    case 'linux':
      // Don't pass an icon, as the dock/window list icon is set via the icon
      // option in the BrowserWindow constructor in atom-window.coffee.
      return null
    default:
      return path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'atom.ico')
  }
}

function runPackager (options) {
  return new Promise((resolve, reject) => {
    electronPackager(options, (err, packageOutputDirPaths) => {
      if (err) {
        reject(err)
        throw new Error(err)
      } else {
        assert(packageOutputDirPaths.length === 1, 'Generated more than one electron application!')
        const packagedAppPath = renamePackagedAppDir(packageOutputDirPaths[0])
        resolve(packagedAppPath)
      }
    })
  })
}

function renamePackagedAppDir (packageOutputDirPath) {
  let packagedAppPath
  if (process.platform === 'darwin') {
    const appBundleName = getAppName() + '.app'
    packagedAppPath = path.join(CONFIG.buildOutputPath, appBundleName)
    if (fs.existsSync(packagedAppPath)) fs.removeSync(packagedAppPath)
    fs.renameSync(path.join(packageOutputDirPath, appBundleName), packagedAppPath)
  } else if (process.platform === 'linux') {
    const appName = CONFIG.channel === 'beta' ? 'atom-beta' : 'atom'
    let architecture
    if (process.arch === 'ia32') {
      architecture = 'i386'
    } else if (process.arch === 'x64') {
      architecture = 'amd64'
    } else {
      architecture = process.arch
    }
    packagedAppPath = path.join(CONFIG.buildOutputPath, `${appName}-${CONFIG.appMetadata.version}-${architecture}`)
    if (fs.existsSync(packagedAppPath)) fs.removeSync(packagedAppPath)
    fs.renameSync(packageOutputDirPath, packagedAppPath)
  } else {
    const appName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom'
    packagedAppPath = path.join(CONFIG.buildOutputPath, appName)
    if (fs.existsSync(packagedAppPath)) fs.removeSync(packagedAppPath)
    fs.renameSync(packageOutputDirPath, packagedAppPath)
  }
  return packagedAppPath
}
<commit_msg>Pick the right architecture on linux #12972<commit_after>'use strict'

const assert = require('assert')
const childProcess = require('child_process')
const electronPackager = require('electron-packager')
const fs = require('fs-extra')
const includePathInPackagedApp = require('./include-path-in-packaged-app')
const getLicenseText = require('./get-license-text')
const path = require('path')
const spawnSync = require('./spawn-sync')

const CONFIG = require('../config')

module.exports = function () {
  const appName = getAppName()
  console.log(`Running electron-packager on ${CONFIG.intermediateAppPath} with app name "${appName}"`)
  return runPackager({
    'app-bundle-id': 'com.github.atom',
    'app-copyright': `Copyright © 2014-${(new Date()).getFullYear()} GitHub, Inc. All rights reserved.`,
    'app-version': CONFIG.appMetadata.version,
    'arch': (() => {
      if (process.platform === 'linux') {
        return process.arch
      } else {
        return process.platform === 'win32' ? 'ia32' : 'x64'
      }})(),
    'asar': {unpack: buildAsarUnpackGlobExpression()},
    'build-version': CONFIG.appMetadata.version,
    'download': {cache: CONFIG.electronDownloadPath},
    'dir': CONFIG.intermediateAppPath,
    'extend-info': path.join(CONFIG.repositoryRootPath, 'resources', 'mac', 'atom-Info.plist'),
    'helper-bundle-id': 'com.github.atom.helper',
    'icon': getIcon(),
    'name': appName,
    'out': CONFIG.buildOutputPath,
    'overwrite': true,
    'platform': process.platform,
    'version': CONFIG.appMetadata.electronVersion,
    'version-string': {
      'CompanyName': 'GitHub, Inc.',
      'FileDescription': 'Atom',
      'ProductName': 'Atom'
    }
  }).then((packagedAppPath) => {
    let bundledResourcesPath
    if (process.platform === 'darwin') {
      bundledResourcesPath = path.join(packagedAppPath, 'Contents', 'Resources')
      setAtomHelperVersion(packagedAppPath)
    } else if (process.platform === 'linux') {
      bundledResourcesPath = path.join(packagedAppPath, 'resources')
      chmodNodeFiles(packagedAppPath)
    } else {
      bundledResourcesPath = path.join(packagedAppPath, 'resources')
    }

    return copyNonASARResources(packagedAppPath, bundledResourcesPath).then(() => {
      console.log(`Application bundle created at ${packagedAppPath}`)
      return packagedAppPath
    })
  })
}

function copyNonASARResources (packagedAppPath, bundledResourcesPath) {
  console.log(`Copying non-ASAR resources to ${bundledResourcesPath}`)
  fs.copySync(
    path.join(CONFIG.repositoryRootPath, 'apm', 'node_modules', 'atom-package-manager'),
    path.join(bundledResourcesPath, 'app', 'apm'),
    {filter: includePathInPackagedApp}
  )
  if (process.platform !== 'win32') {
    // Existing symlinks on user systems point to an outdated path, so just symlink it to the real location of the apm binary.
    // TODO: Change command installer to point to appropriate path and remove this fallback after a few releases.
    fs.symlinkSync(path.join('..', '..', 'bin', 'apm'), path.join(bundledResourcesPath, 'app', 'apm', 'node_modules', '.bin', 'apm'))
    fs.copySync(path.join(CONFIG.repositoryRootPath, 'atom.sh'), path.join(bundledResourcesPath, 'app', 'atom.sh'))
  }
  if (process.platform === 'darwin') {
    fs.copySync(path.join(CONFIG.repositoryRootPath, 'resources', 'mac', 'file.icns'), path.join(bundledResourcesPath, 'file.icns'))
  } else if (process.platform === 'linux') {
    fs.copySync(path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'png', '1024.png'), path.join(packagedAppPath, 'atom.png'))
  } else if (process.platform === 'win32') {
    [ 'atom.cmd', 'atom.sh', 'atom.js', 'apm.cmd', 'apm.sh', 'file.ico' ]
      .forEach(file => fs.copySync(path.join('resources', 'win', file), path.join(bundledResourcesPath, 'cli', file)))
  }

  console.log(`Writing LICENSE.md to ${bundledResourcesPath}`)
  return getLicenseText().then((licenseText) => {
    fs.writeFileSync(path.join(bundledResourcesPath, 'LICENSE.md'), licenseText)
  })
}

function setAtomHelperVersion (packagedAppPath) {
  const frameworksPath = path.join(packagedAppPath, 'Contents', 'Frameworks')
  const helperPListPath = path.join(frameworksPath, 'Atom Helper.app', 'Contents', 'Info.plist')
  console.log(`Setting Atom Helper Version for ${helperPListPath}`)
  spawnSync('/usr/libexec/PlistBuddy', ['-c', `Add CFBundleVersion string ${CONFIG.appMetadata.version}`, helperPListPath])
  spawnSync('/usr/libexec/PlistBuddy', ['-c', `Add CFBundleShortVersionString string ${CONFIG.appMetadata.version}`, helperPListPath])
}

function chmodNodeFiles (packagedAppPath) {
  console.log(`Changing permissions for node files in ${packagedAppPath}`)
  childProcess.execSync(`find "${packagedAppPath}" -type f -name *.node -exec chmod a-x {} \\;`)
}

function buildAsarUnpackGlobExpression () {
  const unpack = [
    '*.node',
    'ctags-config',
    'ctags-darwin',
    'ctags-linux',
    'ctags-win32.exe',
    path.join('**', 'node_modules', 'spellchecker', '**'),
    path.join('**', 'resources', 'atom.png')
  ]

  return `{${unpack.join(',')}}`
}

function getAppName () {
  if (process.platform === 'darwin') {
    return CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom'
  } else {
    return 'atom'
  }
}

function getIcon () {
  switch (process.platform) {
    case 'darwin':
      return path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'atom.icns')
    case 'linux':
      // Don't pass an icon, as the dock/window list icon is set via the icon
      // option in the BrowserWindow constructor in atom-window.coffee.
      return null
    default:
      return path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'atom.ico')
  }
}

function runPackager (options) {
  return new Promise((resolve, reject) => {
    electronPackager(options, (err, packageOutputDirPaths) => {
      if (err) {
        reject(err)
        throw new Error(err)
      } else {
        assert(packageOutputDirPaths.length === 1, 'Generated more than one electron application!')
        const packagedAppPath = renamePackagedAppDir(packageOutputDirPaths[0])
        resolve(packagedAppPath)
      }
    })
  })
}

function renamePackagedAppDir (packageOutputDirPath) {
  let packagedAppPath
  if (process.platform === 'darwin') {
    const appBundleName = getAppName() + '.app'
    packagedAppPath = path.join(CONFIG.buildOutputPath, appBundleName)
    if (fs.existsSync(packagedAppPath)) fs.removeSync(packagedAppPath)
    fs.renameSync(path.join(packageOutputDirPath, appBundleName), packagedAppPath)
  } else if (process.platform === 'linux') {
    const appName = CONFIG.channel === 'beta' ? 'atom-beta' : 'atom'
    let architecture
    if (process.arch === 'ia32') {
      architecture = 'i386'
    } else if (process.arch === 'x64') {
      architecture = 'amd64'
    } else {
      architecture = process.arch
    }
    packagedAppPath = path.join(CONFIG.buildOutputPath, `${appName}-${CONFIG.appMetadata.version}-${architecture}`)
    if (fs.existsSync(packagedAppPath)) fs.removeSync(packagedAppPath)
    fs.renameSync(packageOutputDirPath, packagedAppPath)
  } else {
    const appName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom'
    packagedAppPath = path.join(CONFIG.buildOutputPath, appName)
    if (fs.existsSync(packagedAppPath)) fs.removeSync(packagedAppPath)
    fs.renameSync(packageOutputDirPath, packagedAppPath)
  }
  return packagedAppPath
}
<|endoftext|>


<commit_before>'use strict';

var debug = require('diagnostics')('bigpipe:primus')
  , async = require('async');

/**
 * Our real-time glue layer.
 *
 * @param {Spark} spark A new real-time connection has been made.
 * @api private
 */
module.exports = function connection(spark) {
  var pipe = this;

  debug('new real-time connection');

  //
  // The orchestrate "substream" is used to sync state back and forth between
  // a client and our BigPipe server. It allows us to know which pagelets are
  // available on a given page and even which page we're currently viewing.
  //
  var orchestrate = spark.substream('pipe:orchestrate')
    , pagelets = Object.create(null)
    , worker
    , page;

  worker = async.queue(function work(data, next) {
    switch (data.type) {
      //
      // The user has initiated a new Page so we need to get a new reference
      // to that page so we can get the correct pagelet instances.
      //
      case 'page':
        //
        // As part of setting a new Page instance, we need to release the
        // previously added pagelet
        //
        Object.keys(pagelets).forEach(function free(name) {
          delete pagelets[name];
        });

        spark.request.url = data.url || spark.request.url;
        pipe.router(spark.request, spark, data.id, function found(err, p) {
          if (err) return debug('Failed to initialise page %s: %j', spark.request.url, err), next();

          debug('initialised a new Page instance: %s', spark.request.url);

          //
          // Fake a HTTP response and request object.
          //
          p.req = spark.request;
          p.res = spark;

          spark.page = page = p;
          next();
        });
      break;

      //
      // The user has initialised a new pagelet for a given page.
      //
      case 'pagelet':
        if (data.name in pagelets) return debug('Pagelet %s is already initialised', data.name), next();
        if (!page) return debug('No initialised page, cannot initialise pagelet %j', data), next();
        if (!page.has(data.name)) return debug('Unknown pagelet, does not exist on page'), next();

        var pageletset = page.get(data.name);

        async.whilst(function canihas() {
          return !!pageletset.length;
        }, function work(next) {
          var Pagelet = pageletset.shift()
            , pagelet = new Pagelet({ temper: page.temper});

          pagelet.init({ page: page });
          pagelet.connect(spark, function connect(err) {
            if (err) {
              if (pagelet.destroy) pagelet.destroy();
              return next();
            }

            if (data.id && pagelet) pagelet.id = data.id;

            page.enabled.push(pagelet);
            pagelets[data.name] = pagelet;
            next();
          });
        }, function () {
          debug('Connected pagelet %s with the page', data.name);
          next();
        });

      break;
    }
  }, 1);

  orchestrate.on('data', function orchestration(data) {
    worker.push(data);
  });

  //
  // The current page id was sent with the connection string, so initialise
  // a new Page instantly using the given id.
  //
  if (spark.query._bp_pid) worker.push({ id: spark.query._bp_pid, type: 'page' });

  spark.once('end', function end() {
    debug('closed connection');

    spark.page = page = null;

    Object.keys(pagelets).forEach(function free(name) {
      delete pagelets[name];
    });
  });
};
<commit_msg>[fix] Create a new request.uri when a page is created.<commit_after>'use strict';

var debug = require('diagnostics')('bigpipe:primus')
  , async = require('async')
  , url = require('url');

/**
 * Our real-time glue layer.
 *
 * @param {Spark} spark A new real-time connection has been made.
 * @api private
 */
module.exports = function connection(spark) {
  var pipe = this;

  debug('new real-time connection');

  //
  // The orchestrate "substream" is used to sync state back and forth between
  // a client and our BigPipe server. It allows us to know which pagelets are
  // available on a given page and even which page we're currently viewing.
  //
  var orchestrate = spark.substream('pipe:orchestrate')
    , pagelets = Object.create(null)
    , worker
    , page;

  worker = async.queue(function work(data, next) {
    switch (data.type) {
      //
      // The user has initiated a new Page so we need to get a new reference
      // to that page so we can get the correct pagelet instances.
      //
      case 'page':
        //
        // As part of setting a new Page instance, we need to release the
        // previously added pagelet
        //
        Object.keys(pagelets).forEach(function free(name) {
          delete pagelets[name];
        });

        spark.request.url = data.url || spark.request.url;
        spark.request.uri = url.parse(spark.request.url, true);

        pipe.router(spark.request, spark, data.id, function found(err, p) {
          if (err) return debug('Failed to initialise page %s: %j', spark.request.url, err), next();

          debug('initialised a new Page instance: %s', spark.request.url);

          //
          // Fake a HTTP response and request object.
          //
          p.req = spark.request;
          p.res = spark;

          spark.page = page = p;
          next();
        });
      break;

      //
      // The user has initialised a new pagelet for a given page.
      //
      case 'pagelet':
        if (data.name in pagelets) return debug('Pagelet %s is already initialised', data.name), next();
        if (!page) return debug('No initialised page, cannot initialise pagelet %j', data), next();
        if (!page.has(data.name)) return debug('Unknown pagelet, does not exist on page'), next();

        var pageletset = page.get(data.name);

        async.whilst(function canihas() {
          return !!pageletset.length;
        }, function work(next) {
          var Pagelet = pageletset.shift()
            , pagelet = new Pagelet({ temper: page.temper});

          pagelet.init({ page: page });
          pagelet.connect(spark, function connect(err) {
            if (err) {
              if (pagelet.destroy) pagelet.destroy();
              return next();
            }

            if (data.id && pagelet) pagelet.id = data.id;

            page.enabled.push(pagelet);
            pagelets[data.name] = pagelet;
            next();
          });
        }, function () {
          debug('Connected pagelet %s with the page', data.name);
          next();
        });

      break;
    }
  }, 1);

  orchestrate.on('data', function orchestration(data) {
    worker.push(data);
  });

  //
  // The current page id was sent with the connection string, so initialise
  // a new Page instantly using the given id.
  //
  if (spark.query._bp_pid) worker.push({ id: spark.query._bp_pid, type: 'page' });

  spark.once('end', function end() {
    debug('closed connection');

    spark.page = page = null;

    Object.keys(pagelets).forEach(function free(name) {
      delete pagelets[name];
    });
  });
};
<|endoftext|>


<commit_before>'use strict';

var debug = require('debug')('bigpipe:primus');

module.exports = function connection(spark) {
  //
  // Setup the pipe substream which.
  //
  var orchestrate = spark.substream('pipe::orchestrate')
    , pipe = this
    , streams = {};

  /**
   * Configure a pagelet for substreaming.
   *
   * @param {Pagelet} pagelet The pagelet we need.
   * @api private
   */
  function substream(pagelet) {
    if (streams[pagelet.name]) return debug('already configured the Spark');

    debug('creating a new substream for pagelet::%s (%s)', pagelet.name, pagelet.id);
    var stream = streams[pagelet.name] = spark.substream('pagelet::'+ pagelet.name);

    //
    // Let the pagelet know that we've paired with a substream and spark.
    //
    if ('function' === typeof pagelet.pair) pagelet.pair(stream, spark);

    //
    // Incoming communication between the pagelet and it's substream.
    //
    stream.on('data', function substreamer(data) {
      if (!pagelet) return debug('substream data event called after pagelet was removed');

      switch (data.type) {
        case 'rpc':
          pagelet.trigger(data.method, data.args, data.id, stream);
        break;
      }
    });

    stream.on('end', function end() {
      debug('substream has ended: %s/%s', pagelet.name, pagelet.id);
      delete streams[pagelet.name];
    });
  }

  //
  // Incoming communication between our spark and the pagelet orchestration.
  //
  orchestrate.on('data', function orchestration(data) {
    switch (data.type) {
      case 'configure':
        return;
        var pagelet = pipe.expire.get(data.id);

        if (pagelet) {
          debug('registering Pagelet %s/%s as new substream', pagelet.name, data.id);
          substream(pipe.expire.get(data.id));
        }
      break;
    }
  });

  spark.on('end', function end() {
    //
    // Free all allocated pages and nuke all pagelets.
    //
    debug('connection has ended: %s were still active', Object.keys(streams).length);
  });
};
<commit_msg>[major] Complete rewrite of the primus bindings<commit_after>'use strict';

var debugs = require('debug')
  , debug = debugs('bigpipe::primus');

/**
 * Our real-time glue layer.
 *
 * @param {Spark} spark A new real-time connection has been made.
 * @api private
 */
module.exports = function connection(spark) {
  var pipe = this;

  //
  // The orchestrate "substream" is used to sync state back and forth between
  // a client and our BigPipe server. It allows us to know which pagelets are
  // available on a given page and even which page we're currently viewing.
  //
  var orchestrate = spark.substream('pipe::orchestrate')
    , pagelets = Object.create(null)
    , page;

  orchestrate.on('data', function orchestration(data) {
    switch (data.type) {
      //
      // The user has initiated a new Page so we need to get a new reference
      // to that page so we can get the correct pagelet instances.
      //
      case 'page':
        page.emit('free');

        //
        // As part of setting a new Page instance, we need to release the
        // previously added pagelet
        //
        Object.keys(pagelets).forEach(function free(name) {
          pagelets[name].emit('free');
          delete pagelets[name];
        });

        spark.request.url = data.url || spark.request.url;
        pipe.find(spark.request, data.id, function found(err, p) {
          if (err) debug('Failed to initialise a page %j', err);

          page = p;
        });
      break;

      //
      // The user has initialised a new pagelet for a given page.
      //
      case 'pagelet':
        if (data.name in pagelets) return debug('Pagelet %s is already initialised', data.name);
        if (!page) return debug('No initialised page, cannot initialise pagelet %j', data);
        if (!page.has(data.name)) return debug('Unknown pagelet, does not exist on page');

        page.get(data.name).connect(spark, function substream(err, pagelet) {
          pagelets[data.name] = pagelet;
        });
      break;
    }
  });

  //
  // The current page id was sent with the connection string, so initialise
  // a new Page instantly using the given id.
  //
  if (spark.query._bp_pid) orchestrate.emit('data', {
    id: spark.query._bp_pid
  });
};
<|endoftext|>


<commit_before>'use strict';

angular.module('mgcrea.ngStrap.helpers.dateParser', [])

.provider('$dateParser', function($localeProvider) {

  var proto = Date.prototype;

  function isNumeric(n) {
    return !isNaN(parseFloat(n)) && isFinite(n);
  }

  var defaults = this.defaults = {
    format: 'shortDate',
    strict: false
  };

  this.$get = function($locale) {

    var DateParserFactory = function(config) {

      var options = angular.extend({}, defaults, config);

      var $dateParser = {};

      var regExpMap = {
        'sss'   : '[0-9]{3}',
        'ss'    : '[0-5][0-9]',
        's'     : options.strict ? '[1-5]?[0-9]' : '[0-5][0-9]',
        'mm'    : '[0-5][0-9]',
        'm'     : options.strict ? '[1-5]?[0-9]' : '[0-5][0-9]',
        'HH'    : '[01][0-9]|2[0-3]',
        'H'     : options.strict ? '[0][1-9]|[1][012]' : '[01][0-9]|2[0-3]',
        'hh'    : '[0][1-9]|[1][012]',
        'h'     : options.strict ? '[1-9]|[1][012]' : '[0]?[1-9]|[1][012]',
        'a'     : 'AM|PM',
        'EEEE'  : $locale.DATETIME_FORMATS.DAY.join('|'),
        'EEE'   : $locale.DATETIME_FORMATS.SHORTDAY.join('|'),
        'dd'    : '[0-2][0-9]{1}|[3][01]{1}',
        'd'     : options.strict ? '[1-2]?[0-9]{1}|[3][01]{1}' : '[0-2][0-9]{1}|[3][01]{1}',
        'MMMM'  : $locale.DATETIME_FORMATS.MONTH.join('|'),
        'MMM'   : $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
        'MM'    : '[0][1-9]|[1][012]',
        'M'     : options.strict ? '[1-9]|[1][012]' : '[0][1-9]|[1][012]',
        'yyyy'  : '[1]{1}[0-9]{3}|[2]{1}[0-9]{3}',
        'yy'    : '[0-9]{2}',
        'y'     : options.strict ? '[1-9]{1}[0-9]{1}' : '[0-9]{2}',
      };

      var setFnMap = {
        'sss'   : proto.setMilliseconds,
        'ss'    : proto.setSeconds,
        's'     : proto.setSeconds,
        'mm'    : proto.setMinutes,
        'm'     : proto.setMinutes,
        'HH'    : proto.setHours,
        'H'     : proto.setHours,
        'hh'    : proto.setHours,
        'h'     : proto.setHours,
        'dd'    : proto.setDate,
        'd'     : proto.setDate,
        'a'     : function(value) { var hours = this.getHours(); return this.setHours(value.match(/pm/i) ? hours + 12 : hours); },
        'MMMM'  : function(value) { return this.setMonth($locale.DATETIME_FORMATS.MONTH.indexOf(value)); },
        'MMM'   : function(value) { return this.setMonth($locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value)); },
        'MM'    : function(value) { return this.setMonth(1 * value - 1); },
        'M'     : function(value) { return this.setMonth(1 * value - 1); },
        'yyyy'  : proto.setFullYear,
        'yy'    : function(value) { return this.setFullYear(2000 + 1 * value); },
        'y'    : proto.setFullYear
      };

      var regex, setMap;

      $dateParser.init = function() {
        $dateParser.$format = $locale.DATETIME_FORMATS[options.format] || options.format;
        regex = regExpForFormat($dateParser.$format);
        setMap = setMapForFormat($dateParser.$format);
      };

      $dateParser.isValid = function(date) {
        if(angular.isDate(date)) return !isNaN(date.getTime());
        return regex.test(date);
      };

      $dateParser.parse = function(value, baseDate) {
        if(angular.isDate(value)) return value;
        var matches = regex.exec(value);
        if(!matches) return false;
        var date = baseDate || new Date(0);
        for(var i = 0; i < matches.length - 1; i++) {
          setMap[i] && setMap[i].call(date, matches[i+1]);
        }
        return date;
      };

      // Private functions

      function setMapForFormat(format) {
        var keys = Object.keys(setFnMap), i;
        var map = [], sortedMap = [];
        // Map to setFn
        var clonedFormat = format;
        for(i = 0; i < keys.length; i++) {
          if(format.split(keys[i]).length > 1) {
            var index = clonedFormat.search(keys[i]);
            format = format.split(keys[i]).join('');
            if(setFnMap[keys[i]]) map[index] = setFnMap[keys[i]];
          }
        }
        // Sort result map
        angular.forEach(map, function(v) {
          sortedMap.push(v);
        });
        return sortedMap;
      }

      function escapeReservedSymbols(text) {
        return text.replace(/\//g, '[\\/]').replace('/-/g', '[-]').replace(/\./g, '[.]').replace(/\\s/g, '[\\s]');
      }

      function regExpForFormat(format) {
        var keys = Object.keys(regExpMap), i;

        var re = format;
        // Abstract replaces to avoid collisions
        for(i = 0; i < keys.length; i++) {
          re = re.split(keys[i]).join('${' + i + '}');
        }
        // Replace abstracted values
        for(i = 0; i < keys.length; i++) {
          re = re.split('${' + i + '}').join('(' + regExpMap[keys[i]] + ')');
        }
        format = escapeReservedSymbols(format);

        return new RegExp('^' + re + '$', ['i']);
      }

      $dateParser.init();
      return $dateParser;

    };

    return DateParserFactory;

  };

});
<commit_msg>fix(dateParser): fix several broken / flawed regular expressions<commit_after>'use strict';

angular.module('mgcrea.ngStrap.helpers.dateParser', [])

.provider('$dateParser', function($localeProvider) {

  var proto = Date.prototype;

  function isNumeric(n) {
    return !isNaN(parseFloat(n)) && isFinite(n);
  }

  var defaults = this.defaults = {
    format: 'shortDate',
    strict: false
  };

  this.$get = function($locale) {

    var DateParserFactory = function(config) {

      var options = angular.extend({}, defaults, config);

      var $dateParser = {};

      var regExpMap = {
        'sss'   : '[0-9]{3}',
        'ss'    : '[0-5][0-9]',
        's'     : options.strict ? '[1-5]?[0-9]' : '[0-9]|[0-5][0-9]',
        'mm'    : '[0-5][0-9]',
        'm'     : options.strict ? '[1-5]?[0-9]' : '[0-9]|[0-5][0-9]',
        'HH'    : '[01][0-9]|2[0-3]',
        'H'     : options.strict ? '1?[0-9]|2[0-3]' : '[01]?[0-9]|2[0-3]',
        'hh'    : '[0][1-9]|[1][012]',
        'h'     : options.strict ? '[1-9]|1[012]' : '0?[1-9]|1[012]',
        'a'     : 'AM|PM',
        'EEEE'  : $locale.DATETIME_FORMATS.DAY.join('|'),
        'EEE'   : $locale.DATETIME_FORMATS.SHORTDAY.join('|'),
        'dd'    : '0[1-9]|[12][0-9]|3[01]',
        'd'     : options.strict ? '[1-9]|[1-2][0-9]|3[01]' : '0?[1-9]|[1-2][0-9]|3[01]',
        'MMMM'  : $locale.DATETIME_FORMATS.MONTH.join('|'),
        'MMM'   : $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
        'MM'    : '0[1-9]|1[012]',
        'M'     : options.strict ? '[1-9]|1[012]' : '0?[1-9]|1[012]',
        'yyyy'  : '[1]{1}[0-9]{3}|[2]{1}[0-9]{3}',
        'yy'    : '[0-9]{2}',
        'y'     : options.strict ? '-?(0|[1-9][0-9]{0,3})' : '-?0*[0-9]{1,4}',
      };

      var setFnMap = {
        'sss'   : proto.setMilliseconds,
        'ss'    : proto.setSeconds,
        's'     : proto.setSeconds,
        'mm'    : proto.setMinutes,
        'm'     : proto.setMinutes,
        'HH'    : proto.setHours,
        'H'     : proto.setHours,
        'hh'    : proto.setHours,
        'h'     : proto.setHours,
        'dd'    : proto.setDate,
        'd'     : proto.setDate,
        'a'     : function(value) { var hours = this.getHours(); return this.setHours(value.match(/pm/i) ? hours + 12 : hours); },
        'MMMM'  : function(value) { return this.setMonth($locale.DATETIME_FORMATS.MONTH.indexOf(value)); },
        'MMM'   : function(value) { return this.setMonth($locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value)); },
        'MM'    : function(value) { return this.setMonth(1 * value - 1); },
        'M'     : function(value) { return this.setMonth(1 * value - 1); },
        'yyyy'  : proto.setFullYear,
        'yy'    : function(value) { return this.setFullYear(2000 + 1 * value); },
        'y'    : proto.setFullYear
      };

      var regex, setMap;

      $dateParser.init = function() {
        $dateParser.$format = $locale.DATETIME_FORMATS[options.format] || options.format;
        regex = regExpForFormat($dateParser.$format);
        setMap = setMapForFormat($dateParser.$format);
      };

      $dateParser.isValid = function(date) {
        if(angular.isDate(date)) return !isNaN(date.getTime());
        return regex.test(date);
      };

      $dateParser.parse = function(value, baseDate) {
        if(angular.isDate(value)) return value;
        var matches = regex.exec(value);
        if(!matches) return false;
        var date = baseDate || new Date(0);
        for(var i = 0; i < matches.length - 1; i++) {
          setMap[i] && setMap[i].call(date, matches[i+1]);
        }
        return date;
      };

      // Private functions

      function setMapForFormat(format) {
        var keys = Object.keys(setFnMap), i;
        var map = [], sortedMap = [];
        // Map to setFn
        var clonedFormat = format;
        for(i = 0; i < keys.length; i++) {
          if(format.split(keys[i]).length > 1) {
            var index = clonedFormat.search(keys[i]);
            format = format.split(keys[i]).join('');
            if(setFnMap[keys[i]]) map[index] = setFnMap[keys[i]];
          }
        }
        // Sort result map
        angular.forEach(map, function(v) {
          sortedMap.push(v);
        });
        return sortedMap;
      }

      function escapeReservedSymbols(text) {
        return text.replace(/\//g, '[\\/]').replace('/-/g', '[-]').replace(/\./g, '[.]').replace(/\\s/g, '[\\s]');
      }

      function regExpForFormat(format) {
        var keys = Object.keys(regExpMap), i;

        var re = format;
        // Abstract replaces to avoid collisions
        for(i = 0; i < keys.length; i++) {
          re = re.split(keys[i]).join('${' + i + '}');
        }
        // Replace abstracted values
        for(i = 0; i < keys.length; i++) {
          re = re.split('${' + i + '}').join('(' + regExpMap[keys[i]] + ')');
        }
        format = escapeReservedSymbols(format);

        return new RegExp('^' + re + '$', ['i']);
      }

      $dateParser.init();
      return $dateParser;

    };

    return DateParserFactory;

  };

});
<|endoftext|>


<commit_before>var Hapi = require('hapi');

// Create the Walmart Labs Hapi Server
var PORT = process.env.PORT || 8000
var server = new Hapi.Server(PORT);

server.route({
  method: 'GET',
  path: '/{path*}',
  handler: {
    directory: {
      path: './public',
      listing: false,
      index: true
    }
  }
});

// Start your Mullet Server
server.start(function () {
  console.log('The Mullet Stack is running on port:', PORT);
});
<commit_msg>missed semicolon<commit_after>var Hapi = require('hapi');

// Create the Walmart Labs Hapi Server
var PORT = process.env.PORT || 8000;
var server = new Hapi.Server(PORT);

server.route({
  method: 'GET',
  path: '/{path*}',
  handler: {
    directory: {
      path: './public',
      listing: false,
      index: true
    }
  }
});

// Start your Mullet Server
server.start(function () {
  console.log('The Mullet Stack is running on port:', PORT);
});
<|endoftext|>


<commit_before>sb.include('Element.prototype.cssTransition');

/**
@Name: sb.widget.notifier
@Description: provides growl like messages for user
@Version: 1.0 08-06-2009
@Example:
//add this css
#sb_notifications{
    position:absolute;
    right:10px;
    top:10px;
    z-index:999;
}

.sb_notify{
    width:500px;
    border:1px solid #ACACAC;
    color:#333;
    background-color:#DFDFDF;
    padding:10px;
    margin:5px;
    opacity:0.9;
    font-size:1.5em;
}

//then
sb.widget.notifier.init();
*/
sb.widget.notifier = {

    box : null,

    moveCounter : 0,

	timeout : 6000,

    clearMethod : function(){

        var el = this;
        if(this.clearing){
            window.clearTimeout(this.clearing);
        }

        el.style.zIndex = 999;
        var height = el.offsetHeight;
        height = height-(parseInt(el.getStyle('padding'), 10));
        height = height-(parseInt(el.getStyle('border'), 10));
        height = height-(parseInt(el.getStyle('margin'), 10));

        el.style.overflow='hidden';

        if(el.resizing){
            el.resizing.stop();
        }
        el.resizing = el.cssTransition([
            {
                prop : 'height',
                begin : height,
                change : -height,

                tween : el.effect.heightTween || 'inQuad'
            },
            {
                prop : 'opacity',
                begin : 1,
                change : -1,
                onEnd : function(){

                    el.style.padding='0';
                    el.style.border='0';
                    el.remove();
                    if(typeof el.effect.onEnd == 'function'){
                        el.effect.onEnd(el);
                        el.remove();
                        el = null;
                    }
                },
                tween : el.effect.opacityTween || 'inCubic'
            }
        ], el.effect.duration || 48).start();
    },

    createBox : function(){
        this.box = new sb.element({
           tag : 'div',
           id : 'sb_notifications'
        });

        this.box.appendToTop(document.body);
    },

    /**
	@Name: sb.widget.notifier.clearAll
	@Description: Used to clear any open notifcation messages
    @Example:
    sb.widget.notifier.clearAll();
	*/
    clearAll : function(){
        var t = this;
        this.box.$('.sb_notify').forEach(function(v){
            t.clearMethod.call(v);
		});
    },

    /**
	@Name: sb.widget.notifier.notify
	@Description: All user notifications feedback goes through this function.
    @Param: message string The message to be displayed, can be plain text or HTML
    @Param: className string Optional The CSS className to assign to the message
	@Param: stay boolean Optional Determines if box stays or fade by default
    @Param: effect object Optional Can override the properties of the effect used to move the boxes
        effect.heightTween string The height tween method
        effect.opacityTween string The opacity tween method
        effect.duration integer The duration of the fade out, roll up
        effect.onEnd function A function which fires when the message is done fading out
    @Example:
    sb.widget.notifier.notify('Hello World '+new Date());
	*/
    notify : function(message, className, stay, effect){

        className = ' '+className || '';
        var t = this;

        var el = new sb.element({
            tag : 'div',
            className : 'sb_notify'+className,
            innerHTML : message,
            effect : effect || {}
        });

        el.clear = function(){
            t.clearMethod.call(el);
        };

        el.appendToTop(this.box);
		if(!stay){
			el.clearing = window.setTimeout(el.clear, sb.widget.notifier.timeout);
		}
        return el;

    },

    /**
	@Name: sb.widget.notifier.followScrollBars
	@Description: Used Internally. All user notifications feedback goes through this function.
	*/
    followScrollBars : function(e){

        var t = this;
        if(t.moving){
            window.clearTimeout(t.moving);
        }

        t.moving = window.setTimeout(function(){
            var pos = sb.browser.getScrollPosition();

            if(t.sliding){
                t.sliding.stop();
            }
            var y = t.box.getY();
            var x = parseInt(t.box.style.right, 10);
            t.box.style.zIndex = 999;
            t.sliding = t.box.cssTransition([{
                prop : 'top',
                begin : y,
                change : pos[1]-y,
                onEnd : function(){
                    t.box.style.zIndex = 999;
                },
                tween : 'inQuad'
            },{
                prop : 'right',
                begin : x,
                change : -pos[0]-x,
                onEnd : function(){
                    t.box.style.zIndex = 999;
                },
                tween : 'inQuad'
            }], 24).start();

        }, 100);

    },

    /**
	@Name: sb.widget.notifier.init
	@Description: Looks for sb_notifications box, if not found, creates and appends to the top of body tag
	*/
    init : function(o){
		o = o || {};
        this.box = sb.$('#sb_notifications');

        this.events = o.events || {};
        if(!this.box){

            this.createBox();
        }

        this.box.style.zIndex = 999;
        var t = this;
        this.box.events(this.events);

        var t = this;
        sb.events.add(window, 'scroll', function(e){
            t.followScrollBars(e);
        });

    }
};<commit_msg>fixed bug which caused css error in notifier widget when style right was not set<commit_after>sb.include('Element.prototype.cssTransition');

/**
@Name: sb.widget.notifier
@Description: provides growl like messages for user
@Version: 1.0 08-06-2009
@Example:
//add this css
#sb_notifications{
    position:absolute;
    right:10px;
    top:10px;
    z-index:999;
}

.sb_notify{
    width:500px;
    border:1px solid #ACACAC;
    color:#333;
    background-color:#DFDFDF;
    padding:10px;
    margin:5px;
    opacity:0.9;
    font-size:1.5em;
}

//then
sb.widget.notifier.init();
*/
sb.widget.notifier = {

    box : null,

    moveCounter : 0,

	timeout : 6000,

    clearMethod : function(){

        var el = this;
        if(this.clearing){
            window.clearTimeout(this.clearing);
        }

        el.style.zIndex = 999;
        var height = el.offsetHeight;
        height = height-(parseInt(el.getStyle('padding'), 10));
        height = height-(parseInt(el.getStyle('border'), 10));
        height = height-(parseInt(el.getStyle('margin'), 10));

        el.style.overflow='hidden';

        if(el.resizing){
            el.resizing.stop();
        }
        el.resizing = el.cssTransition([
            {
                prop : 'height',
                begin : height,
                change : -height,

                tween : el.effect.heightTween || 'inQuad'
            },
            {
                prop : 'opacity',
                begin : 1,
                change : -1,
                onEnd : function(){

                    el.style.padding='0';
                    el.style.border='0';
                    el.remove();
                    if(typeof el.effect.onEnd == 'function'){
                        el.effect.onEnd(el);
                        el.remove();
                        el = null;
                    }
                },
                tween : el.effect.opacityTween || 'inCubic'
            }
        ], el.effect.duration || 48).start();
    },

    createBox : function(){
        this.box = new sb.element({
           tag : 'div',
           id : 'sb_notifications'
        });

        this.box.appendToTop(document.body);
    },

    /**
	@Name: sb.widget.notifier.clearAll
	@Description: Used to clear any open notifcation messages
    @Example:
    sb.widget.notifier.clearAll();
	*/
    clearAll : function(){
        var t = this;
        this.box.$('.sb_notify').forEach(function(v){
            t.clearMethod.call(v);
		});
    },

    /**
	@Name: sb.widget.notifier.notify
	@Description: All user notifications feedback goes through this function.
    @Param: message string The message to be displayed, can be plain text or HTML
    @Param: className string Optional The CSS className to assign to the message
	@Param: stay boolean Optional Determines if box stays or fade by default
    @Param: effect object Optional Can override the properties of the effect used to move the boxes
        effect.heightTween string The height tween method
        effect.opacityTween string The opacity tween method
        effect.duration integer The duration of the fade out, roll up
        effect.onEnd function A function which fires when the message is done fading out
    @Example:
    sb.widget.notifier.notify('Hello World '+new Date());
	*/
    notify : function(message, className, stay, effect){

        className = ' '+className || '';
        var t = this;

        var el = new sb.element({
            tag : 'div',
            className : 'sb_notify'+className,
            innerHTML : message,
            effect : effect || {}
        });

        el.clear = function(){
            t.clearMethod.call(el);
        };

        el.appendToTop(this.box);
		if(!stay){
			el.clearing = window.setTimeout(el.clear, sb.widget.notifier.timeout);
		}
        return el;

    },

    /**
	@Name: sb.widget.notifier.followScrollBars
	@Description: Used Internally. All user notifications feedback goes through this function.
	*/
    followScrollBars : function(e){

        var t = this;
        if(t.moving){
            window.clearTimeout(t.moving);
        }

        t.moving = window.setTimeout(function(){
            var pos = sb.browser.getScrollPosition();

            if(t.sliding){
                t.sliding.stop();
            }
            var y = t.box.getY();
            var x = parseInt(t.box.style.right, 10) || 0;

            t.box.style.zIndex = 999;
            t.sliding = t.box.cssTransition([{
                prop : 'top',
                begin : y,
                change : pos[1]-y,
                onEnd : function(){
                    t.box.style.zIndex = 999;
                },
                tween : 'inQuad'
            },{
                prop : 'right',
                begin : x,
                change : -pos[0]-x,
                onEnd : function(){
                    t.box.style.zIndex = 999;
                },
                tween : 'inQuad'
            }], 24).start();

        }, 100);

    },

    /**
	@Name: sb.widget.notifier.init
	@Description: Looks for sb_notifications box, if not found, creates and appends to the top of body tag
	*/
    init : function(o){
		o = o || {};
        this.box = sb.$('#sb_notifications');

        this.events = o.events || {};
        if(!this.box){

            this.createBox();
        }

        this.box.style.zIndex = 999;
        var t = this;
        this.box.events(this.events);

        var t = this;
        sb.events.add(window, 'scroll', function(e){
            t.followScrollBars(e);
        });

    }
};<|endoftext|>


<commit_before>/**
 * TODO:
 * Should it prompt multiple times?
 * Node?
 * Add options
 * Call with new Prompt('mousemove', 5000, cb)
 */

function Prompt(listenFor, interval, cb) {

	this.listenFor = listenFor || 'mousemove';
	this.interval  = interval  || 6000;
	this.now       = new Date();
	this.cb        = cb || undefined;

	this.listener.call(this);

	if (document.addEventListener) {
		document.addEventListener(this.listenFor, this.listener.call(this), false);
	} else if (document.attachEvent) {
		document.attachEvent(this.listenFor, this.listener.call(this));
	}

	// Run check every n seconds
	setInterval(this.checkForInteraction.call(this), this.interval);
}

Prompt.prototype.listener = function() {
	// console.log("listener", this);
	this.now = new Date();
	this.checkForInteraction.call(this);
};

// Checks that are done every n seconds
Prompt.prototype.checkForInteraction = function() {
	var timeCheck       = new Date();
	var timeCheckString = timeCheck.getMinutes() + ':' + timeCheck.getSeconds();
	var nowString       = this.now.getMinutes() + ':' + this.now.getSeconds();
	console.log("checkForInteraction", this);

	if ( timeCheckString !== nowString ) {
		this.promptUser.call(this);
	} else {
		// Run this function or do nothing
		this.dontPromptUser.call(this);
	}
};

//
Prompt.prototype.promptUser = function() {
	console.log("promptUser", this);
	this.prompt = true;
	console.log('Do something!');
	this.cb();
};

//
Prompt.prototype.dontPromptUser = function() {
	this.prompt = false;
	console.log('Do nothing!');
	// this.cb();
};<commit_msg>Update to v0.2.0 - prompt works again (YAY!) after code refactor - added AMD and node/Browserify support<commit_after>/**
 * TODO:
 * Should it prompt multiple times?
 * Disable prompt after n times?
 */

function Prompt(event, interval, cb) {

	this.event     = event || 'mousemove';
	this.interval  = interval  || 6000;
	this.now       = new Date();
	this.cb        = cb || undefined;

	if (document.addEventListener) {
		document.addEventListener(this.event, this.listener.bind(this));
	} else if (document.attachEvent) {
		document.attachEvent(this.event, this.listener);
	}

	// Run check every n seconds
	setInterval(this.checkForInteraction.bind(this), this.interval);
}

Prompt.prototype.listener = function() {
	this.now = new Date();
	this.checkForInteraction.call(this);
};

// Checks that are done every n seconds
Prompt.prototype.checkForInteraction = function() {
	var timeCheck       = new Date();
	var timeCheckString = timeCheck.getMinutes() + ':' + timeCheck.getSeconds();
	var nowString       = this.now.getMinutes() + ':' + this.now.getSeconds();

	if ( timeCheckString !== nowString ) {
		this.promptUser.call(this);
	}
};

Prompt.prototype.promptUser = function() {
	this.prompt = true;
	this.cb();
};

// Check for AMD/Module support, otherwise define trak as a global variable.
if (typeof define !== 'undefined' && define.amd) {
	// AMD. Register as an anonymous module.
	define (function() {
		'use strict';
		return Prompt;
	});
} else if (typeof module !== 'undefined' && module.exports) {
	module.exports = Prompt;
} else {
	window.Prompt = Prompt;
}
<|endoftext|>


<commit_before>/*********************************************************************
 * AnitomyJs - Node.js wrapper for Anitomy
 *
 * MIT License <https://github.com/nevermnd/anitomy-js/blob/master/LICENSE>
 ********************************************************************/

var chai = require('chai')
var expect = chai.expect
var anitomy = require('../index.js')

chai.use(require('chai-as-promised'))

describe('anitomy-js', function () {
  var fixture = require('./data.json')
  var fixtureValues = []
  var fixtureKeys = Object.keys(fixture)

  for (var key in fixture) {
    fixtureValues.push(fixture[ key ])
  }

  describe('parseSync', function () {
    it('should parse all anime filenames, one by one', function () {
      fixtureKeys.forEach(function (key) {
        expect(anitomy.parseSync(key)).to.deep.equal(fixture[ key ])
      })
    })

    it('should parse all anime filenames, all at once', function () {
      expect(anitomy.parseSync(fixtureKeys)).to.deep.equal(fixtureValues)
    })

    it('should parse anime filenames, ignoring episode number, title, file extension and release group',
      function () {
        var parsed = anitomy.parseSync(fixtureKeys, {
          'parse_episode_title': false,
          'parse_episode_number': false,
          'parse_file_extension': false,
          'parse_release_group': false
        })
        parsed.forEach(function (anime) {
          expect(anime).to.not.have.property('episode_title')
          // check episode_number_alt because anitomy still parsing
          // some file's episode number even with parse_episode_number set to
          // false
          expect(anime).to.not.have.property('episode_number_alt')
          expect(anime).to.not.have.property('file_extension')
          expect(anime).to.not.have.property('release_group')
        })
      })

    it('should ignore string', function () {
      var parsed = anitomy.parseSync('[ANBU]_Princess_Lover!_-_01_[2048A39A].mkv', { 'ignored_strings': [ '!' ] })
      expect(parsed.file_name).to.eq('[ANBU]_Princess_Lover_-_01_[2048A39A]')
    })

    it('should override default delimiters', function () {
      let parsed = anitomy.parseSync(
        '[chibi-Doki] Seikon no Qwaser - 13v0 (Uncensored Director\'s Cut) [988DB090].mkv',
        { 'allowed_delimiters': '_.&+,|' }
      )

      expect(parsed).to.deep.equal({
        anime_title: 'Seikon no Qwaser - 13v0 (Uncensored Director\'s Cut)',
        file_checksum: '988DB090',
        file_extension: 'mkv',
        file_name:
          '[chibi-Doki] Seikon no Qwaser - 13v0 (Uncensored Director\'s Cut) [988DB090]',
        release_group: 'chibi-Doki'
      })
    })

    it('should throw an exception for wrong datatypes (files)', function () {
      expect(function () {
        anitomy.parseSync(1)
      }).to.throw('Wrong data type')
    })

    it('should use empty object as default options', function () {
      expect(anitomy.parseSync('', null)).to.be.deep.equals({})
    })

    it('should throw an exception for wrong datatypes (options/ignored_strings)', function () {
      expect(function () {
        anitomy.parseSync('', { 'ignored_strings': null })
      }).to.throw('ignored_strings must be an array')
    })

    it('should throw wrong number of arguments', function () {
      expect(function () {
        anitomy.parseSync()
      }).to.throw('Wrong data type')
    })
  })

  describe('parseAsync', function () {
    it('should parse all anime filenames, one by one', function () {
      var length = fixtureKeys.length
      var promises = fixtureKeys.map(function (key) {
        return anitomy.parseAsync(key).then(function (data) {
          expect(data).to.deep.equal(fixture[ key ])
          --length
        })
      })
      expect(promises).to.length(fixtureKeys.length)
      return Promise.all(promises).then(function () {
        expect(length).to.be.equals(0)
      })
    })

    it('should parse all anime filenames, all at once', function () {
      return expect(anitomy.parseAsync(fixtureKeys)).eventually.deep.equals(fixtureValues)
    })

    it('should parse anime filenames, ignoring episode number, title, file extension and release group', function () {
      var options = {
        'parse_episode_title': false,
        'parse_episode_number': false,
        'parse_file_extension': false,
        'parse_release_group': false
      }
      return anitomy.parseAsync(fixtureKeys, options).then(function (data) {
        data.forEach(function (anime) {
          // check episode_number_alt because anitomy still parsing
          // some file's episode number even with parse_episode_number set to
          // false
          expect(anime, anime.file_name).to.not.have.property('episode_title')
          expect(anime, anime.file_name).to.not.have.property('episode_number_alt')
          expect(anime, anime.file_name).to.not.have.property('file_extension')
          expect(anime, anime.file_name).to.not.have.property('release_group')
        })
      })
    })

    it('should ignore string', function () {
      var options = { 'ignored_strings': [ '!' ] }
      var input = '[ANBU]_Princess_Lover!_-_01_[2048A39A].mkv'
      return expect(anitomy.parseAsync(input, options)).eventually.include({
        file_name: '[ANBU]_Princess_Lover_-_01_[2048A39A]'
      })
    })

    it('should override default delimiters', function () {
      var input = '[chibi-Doki] Seikon no Qwaser - 13v0 (Uncensored Director\'s Cut) [988DB090].mkv'
      var options = { 'allowed_delimiters': '_.&+,|' }
      return expect(anitomy.parseAsync(input, options)).eventually.deep.equals({
        anime_title: 'Seikon no Qwaser - 13v0 (Uncensored Director\'s Cut)',
        file_checksum: '988DB090',
        file_extension: 'mkv',
        file_name: '[chibi-Doki] Seikon no Qwaser - 13v0 (Uncensored Director\'s Cut) [988DB090]',
        release_group: 'chibi-Doki'
      })
    })

    it('should throw an exception for wrong datatypes (files)', function () {
      return expect(anitomy.parseAsync(1)).be.rejectedWith('Wrong data type')
    })

    it('should not throw and return empty object', function () {
      return expect(anitomy.parseAsync('', null, {})).to.eventually.be.deep.equals({})
    })

    it('should throw an exception for wrong datatypes (options)', function () {
      return expect(anitomy.parseAsync('', null)).to.eventually.be.rejectedWith('Options must be an object')
    })

    it('should throw an exception for wrong datatypes (options.ignored_strings)', function () {
      var options = { 'ignored_strings': null }
      return expect(anitomy.parseAsync('', options)).to.eventually.be.rejectedWith('ignored_strings must be an array')
    })

    it('should throw an exception for wrong datatypes (input)', function () {
      return expect(anitomy.parseAsync()).to.eventually.be.rejectedWith('Wrong data type')
    })

    it('should emit deprecation warning on single argument callback', function (done) {
      var originalErr = console.error
      console.error = function () {
        // prevent output pollution
      }
      process.on('warning', function (dep) {
        console.error = originalErr
        expect(dep.code).to.be.equals('anitomy-js#parseAsync')
        expect(dep.name).to.be.equals('DeprecationWarning')
        expect(dep.message).to.be.equal('Using a callback with only one argument is deprecated. Switch to using a node style callback (err, data) or use the promise api.')
        done()
      })
      anitomy.parseAsync('', function (data) {
        //
      })
    })

    it('should accept callback with two arguments with data as 2nd argument', function (done) {
      process.on('warning', function (dep) {
        expect(dep.code).to.not.be.equals('anitomy-js#parseAsync')
      })
      anitomy.parseAsync('', function (err, data) {
        expect(err).to.be.undefined
        expect(data).to.be.deep.equals({})
        done()
      })
    })

    it('should accept callback with two arguments with error as 1st argument', function (done) {
      anitomy.parseAsync(undefined, function (err, data) {
        expect(err).to.be.an.instanceOf(Error)
        expect(data).to.be.undefined
        done()
      })
    })
  })
})
<commit_msg>Fix tests on node < 6<commit_after>/*********************************************************************
 * AnitomyJs - Node.js wrapper for Anitomy
 *
 * MIT License <https://github.com/nevermnd/anitomy-js/blob/master/LICENSE>
 ********************************************************************/

'use strict'

var chai = require('chai')
var expect = chai.expect
var anitomy = require('../index.js')

chai.use(require('chai-as-promised'))

describe('anitomy-js', function () {
  var fixture = require('./data.json')
  var fixtureValues = []
  var fixtureKeys = Object.keys(fixture)

  for (var key in fixture) {
    fixtureValues.push(fixture[ key ])
  }

  describe('parseSync', function () {
    it('should parse all anime filenames, one by one', function () {
      fixtureKeys.forEach(function (key) {
        expect(anitomy.parseSync(key)).to.deep.equal(fixture[ key ])
      })
    })

    it('should parse all anime filenames, all at once', function () {
      expect(anitomy.parseSync(fixtureKeys)).to.deep.equal(fixtureValues)
    })

    it('should parse anime filenames, ignoring episode number, title, file extension and release group',
      function () {
        var parsed = anitomy.parseSync(fixtureKeys, {
          'parse_episode_title': false,
          'parse_episode_number': false,
          'parse_file_extension': false,
          'parse_release_group': false
        })
        parsed.forEach(function (anime) {
          expect(anime).to.not.have.property('episode_title')
          // check episode_number_alt because anitomy still parsing
          // some file's episode number even with parse_episode_number set to
          // false
          expect(anime).to.not.have.property('episode_number_alt')
          expect(anime).to.not.have.property('file_extension')
          expect(anime).to.not.have.property('release_group')
        })
      })

    it('should ignore string', function () {
      var parsed = anitomy.parseSync('[ANBU]_Princess_Lover!_-_01_[2048A39A].mkv', { 'ignored_strings': [ '!' ] })
      expect(parsed.file_name).to.eq('[ANBU]_Princess_Lover_-_01_[2048A39A]')
    })

    it('should override default delimiters', function () {
      var parsed = anitomy.parseSync(
        '[chibi-Doki] Seikon no Qwaser - 13v0 (Uncensored Director\'s Cut) [988DB090].mkv',
        { 'allowed_delimiters': '_.&+,|' }
      )

      expect(parsed).to.deep.equal({
        anime_title: 'Seikon no Qwaser - 13v0 (Uncensored Director\'s Cut)',
        file_checksum: '988DB090',
        file_extension: 'mkv',
        file_name:
          '[chibi-Doki] Seikon no Qwaser - 13v0 (Uncensored Director\'s Cut) [988DB090]',
        release_group: 'chibi-Doki'
      })
    })

    it('should throw an exception for wrong datatypes (files)', function () {
      expect(function () {
        anitomy.parseSync(1)
      }).to.throw('Wrong data type')
    })

    it('should use empty object as default options', function () {
      expect(anitomy.parseSync('', null)).to.be.deep.equals({})
    })

    it('should throw an exception for wrong datatypes (options/ignored_strings)', function () {
      expect(function () {
        anitomy.parseSync('', { 'ignored_strings': null })
      }).to.throw('ignored_strings must be an array')
    })

    it('should throw wrong number of arguments', function () {
      expect(function () {
        anitomy.parseSync()
      }).to.throw('Wrong data type')
    })
  })

  describe('parseAsync', function () {
    it('should parse all anime filenames, one by one', function () {
      var length = fixtureKeys.length
      var promises = fixtureKeys.map(function (key) {
        return anitomy.parseAsync(key).then(function (data) {
          expect(data).to.deep.equal(fixture[ key ])
          --length
        })
      })
      expect(promises).to.length(fixtureKeys.length)
      return Promise.all(promises).then(function () {
        expect(length).to.be.equals(0)
      })
    })

    it('should parse all anime filenames, all at once', function () {
      return expect(anitomy.parseAsync(fixtureKeys)).eventually.deep.equals(fixtureValues)
    })

    it('should parse anime filenames, ignoring episode number, title, file extension and release group', function () {
      var options = {
        'parse_episode_title': false,
        'parse_episode_number': false,
        'parse_file_extension': false,
        'parse_release_group': false
      }
      return anitomy.parseAsync(fixtureKeys, options).then(function (data) {
        data.forEach(function (anime) {
          // check episode_number_alt because anitomy still parsing
          // some file's episode number even with parse_episode_number set to
          // false
          expect(anime, anime.file_name).to.not.have.property('episode_title')
          expect(anime, anime.file_name).to.not.have.property('episode_number_alt')
          expect(anime, anime.file_name).to.not.have.property('file_extension')
          expect(anime, anime.file_name).to.not.have.property('release_group')
        })
      })
    })

    it('should ignore string', function () {
      var options = { 'ignored_strings': [ '!' ] }
      var input = '[ANBU]_Princess_Lover!_-_01_[2048A39A].mkv'
      return expect(anitomy.parseAsync(input, options)).eventually.include({
        file_name: '[ANBU]_Princess_Lover_-_01_[2048A39A]'
      })
    })

    it('should override default delimiters', function () {
      var input = '[chibi-Doki] Seikon no Qwaser - 13v0 (Uncensored Director\'s Cut) [988DB090].mkv'
      var options = { 'allowed_delimiters': '_.&+,|' }
      return expect(anitomy.parseAsync(input, options)).eventually.deep.equals({
        anime_title: 'Seikon no Qwaser - 13v0 (Uncensored Director\'s Cut)',
        file_checksum: '988DB090',
        file_extension: 'mkv',
        file_name: '[chibi-Doki] Seikon no Qwaser - 13v0 (Uncensored Director\'s Cut) [988DB090]',
        release_group: 'chibi-Doki'
      })
    })

    it('should throw an exception for wrong datatypes (files)', function () {
      return expect(anitomy.parseAsync(1)).be.rejectedWith('Wrong data type')
    })

    it('should not throw and return empty object', function () {
      return expect(anitomy.parseAsync('', null, {})).to.eventually.be.deep.equals({})
    })

    it('should throw an exception for wrong datatypes (options)', function () {
      return expect(anitomy.parseAsync('', null)).to.eventually.be.rejectedWith('Options must be an object')
    })

    it('should throw an exception for wrong datatypes (options.ignored_strings)', function () {
      var options = { 'ignored_strings': null }
      return expect(anitomy.parseAsync('', options)).to.eventually.be.rejectedWith('ignored_strings must be an array')
    })

    it('should throw an exception for wrong datatypes (input)', function () {
      return expect(anitomy.parseAsync()).to.eventually.be.rejectedWith('Wrong data type')
    })

    it('should emit deprecation warning on single argument callback', function (done) {
      // warning event was only implemented on node > 6
      if (Number(process.version.match(/^v(\d+\.\d+)/)[ 1 ]) < 6) {
        this.skip()
      } else {
        var originalErr = console.error
        console.error = function () {
          // prevent output pollution
        }
        process.on('warning', function (dep) {
          console.error = originalErr
          expect(dep.code).to.be.equals('anitomy-js#parseAsync')
          expect(dep.name).to.be.equals('DeprecationWarning')
          expect(dep.message).to.be.equal('Using a callback with only one argument is deprecated. Switch to using a node style callback (err, data) or use the promise api.')
          done()
        })
        anitomy.parseAsync('', function (data) {
          //
        })
      }
    })

    it('should accept callback with two arguments with data as 2nd argument', function (done) {
      process.on('warning', function (dep) {
        expect(dep.code).to.not.be.equals('anitomy-js#parseAsync')
      })
      anitomy.parseAsync('', function (err, data) {
        expect(err).to.be.undefined
        expect(data).to.be.deep.equals({})
        done()
      })
    })

    it('should accept callback with two arguments with error as 1st argument', function (done) {
      anitomy.parseAsync(undefined, function (err, data) {
        expect(err).to.be.an.instanceOf(Error)
        expect(data).to.be.undefined
        done()
      })
    })
  })
})
<|endoftext|>


<commit_before>var vumigo = require('vumigo_v02');
var App = vumigo.App;
var AppTester = vumigo.AppTester;
var EndState = vumigo.states.EndState;
var FreeText = vumigo.states.FreeText;
var MetricsHelper = require('../lib');
var assert = require('assert');

describe('MetricsHelper', function() {
    var app;
    var tester;
    var metricsH;

    beforeEach(function() {
        app = new App('states:test');
        tester = new AppTester(app);

        app.states.add('states:test', function(name) {
            return new FreeText(name, {
                question: 'This is the first state.',
                next: 'states:test2'
            });
        });

        app.states.add('states:test2', function(name) {
            return new EndState(name, {
                text: 'This is the end state.',
                next: 'states:test'
            });
        });

        tester
            .setup.config.app({
                name: 'metricsHelper-tester'
            });
    });

    describe('When a new user accesses the service', function() {

        beforeEach(function() {
            app.init = function() {
                metricsH = new MetricsHelper(app.im);
                metricsH
                    .add.total_unique_users('uniqueUsers')
                    .add.total_unique_users();
            };
        });

        it('should fire the new user metrics', function() {
            return tester
                .start()
                .check(function(api, im , app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].uniqueUsers;
                    metrics_trans = api.metrics.stores['metricsHelper-tester']
                        ['uniqueUsers.transient'];
                    assert.deepEqual(metrics, {agg: 'last', values: [ 1 ]});
                    assert.deepEqual(metrics_trans, {agg: 'sum', values: [1]});
                })
                .run();
        });

        it('should fire both new user metrics', function() {
            return tester
                .start()
                .check(function(api, im , app) {
                    metrics1 = api.metrics
                        .stores['metricsHelper-tester'].uniqueUsers;
                    metrics2 = api.metrics
                        .stores['metricsHelper-tester'].unique_users;
                    assert.deepEqual(metrics1, {agg: 'last', values: [ 1 ]});
                    assert.deepEqual(metrics2, {agg: 'last', values: [ 1 ]});
                })
                .run();
        });

        it('should fire the metric for each new user', function() {
            return tester
                .inputs(
                    {from_addr: '+271234', content: null},
                    {from_addr: '+274321', content: null})
                .check(function(api, im , app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].uniqueUsers;
                    assert.deepEqual(metrics, {agg: 'last', values: [ 1, 2 ]});
                })
                .run();
        });

        it('should not fire the metrics for existing users', function() {
            return tester
                .setup.user({addr: '+271234', state:'states:test'})
                .start()
                .check(function(api, im , app) {
                    metrics = api.metrics.stores['metricsHelper-tester'];
                    assert.equal(metrics, undefined);
                })
                .run();
        });

    });

    describe('when a new session is started', function() {

        beforeEach(function() {
            app.init = function() {
                metricsH = new MetricsHelper(app.im);
                metricsH
                    .add.total_sessions('sessions')
                    .add.total_sessions();
            };
        });

        it('should fire the sessions metric', function() {
            return tester
                .start()
                .check(function(api, im , app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].sessions;
                    metrics_trans = api.metrics.stores['metricsHelper-tester']
                        ['sessions.transient'];
                    assert.deepEqual(metrics, {agg: 'last', values: [ 1 ]});
                    assert.deepEqual(metrics_trans, {agg: 'sum', values: [1]});
                })
                .run();
        });

        it('should fire both sessions metrics', function() {
            return tester
                .start()
                .check(function(api, im, app) {
                    metric1 = api.metrics
                        .stores['metricsHelper-tester'].sessions;
                    metric2 = api.metrics
                        .stores['metricsHelper-tester'].total_sessions;
                    assert.deepEqual(metric1, {agg: 'last', values: [ 1 ]});
                    assert.deepEqual(metric2, {agg: 'last', values: [ 1 ]});
                })
                .run();
        });

        it('should fire the metric for each new session', function() {
            return tester
                .inputs(null, null)
                .check(function(api, im, app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].sessions;
                    assert.deepEqual(metrics, {agg: 'last', values: [ 1, 2 ]});
                })
                .run();
        });

        it('should not fire when resuming a session', function() {
            return tester
                .input('resume')
                .check(function(api, im, app) {
                    metrics = api.metrics.stores['metricsHelper-tester'];
                    assert.equal(metrics, undefined);
                })
                .run();
        });

    });

    describe('when the state action is triggered', function() {

        beforeEach(function() {
            app.init = function() {
                metricsH = new MetricsHelper(app.im);
                metricsH
                    .add.total_state_actions(
                        {state:'states:test', action:'exit'}, 'exits')
                    .add.total_state_actions({state:'states:test'});
            };
        });

        it('should trigger the state exit metric', function() {
            return tester
                .inputs(null, 'test')
                .check(function(api, im, app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].exits;
                    metrics_trans = api.metrics.stores['metricsHelper-tester']
                        ['exits.transient'];
                    assert.deepEqual(metrics, {agg: 'last', values: [ 1 ]});
                    assert.deepEqual(metrics_trans, {agg: 'sum', values: [1]});
                })
                .run();
        });

        it('should trigger the state enter metric', function() {
            return tester
                .start()
                .check(function(api, im, app) {
                    metric1 = api.metrics.stores['metricsHelper-tester']
                        .total_action_enter_states_test;
                    assert.deepEqual(metric1, {agg: 'last', values: [ 1 ]});
                })
                .run();
        });

        it('should trigger each time the state is entered', function() {
            return tester
                .inputs(null, 'test', null)
                .check(function(api, im, app) {
                    metrics = api.metrics.stores['metricsHelper-tester']
                        .total_action_enter_states_test;
                    assert.deepEqual(metrics, {agg: 'last', values: [ 1, 2 ]});
                })
                .run();
        });

        it('should throw an exception on bad state actions', function() {
            assert.throws( function() {
                metricsH.add.total_state_actions(
                    {state:'states:test', action:'foo'}, 'bad');},
                /^(Invalid state action foo)$/
                );
        });

    });

     describe('when there is a sessions_until_state metric', function() {

        beforeEach(function() {
            app.init = function() {
                metricsH = new MetricsHelper(app.im);
                metricsH
                    .add.sessions_until_state(
                        {state: 'states:test2', action: 'enter'},
                        'sessions_until')
                    .add.sessions_until_state({state: 'states:test2'});
            };
        });

        it('should trigger the metric on entering the state', function() {
            return tester
                .inputs(null, 'test')
                .check(function(api, im, app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].sessions_until;
                    assert.deepEqual(metrics, {agg: 'avg', values: [ 1 ]});
                })
                .run();
        });

        it('should trigger both metrics', function() {
            return tester
                .inputs(null, 'test')
                .check(function(api, im, app) {
                    metric1 = api.metrics
                        .stores['metricsHelper-tester'].sessions_until;
                    metric2 = api.metrics.stores['metricsHelper-tester']
                        .sessions_until_enter_states_test2;
                    assert.deepEqual(metric1, {agg: 'avg', values: [ 1 ]});
                    assert.deepEqual(metric2, {agg: 'avg', values: [ 1 ]});
                })
                .run();
        });

        it('should trigger each time the state is reached', function() {
            return tester
                .inputs(null, 'test', null, 'test')
                .check(function(api, im, app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].sessions_until;
                    assert.deepEqual(metrics, {agg: 'avg', values: [ 1, 1 ]});
                })
                .run();
        });

        it('should count multiple sessions', function() {
            return tester
                .inputs(null, null, null, 'test', null, 'test')
                .check(function(api, im, app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].sessions_until;
                    assert.deepEqual(metrics, {agg: 'avg', values: [ 3, 1 ]});
                })
                .run();
        });

    });

    describe('when there is a time_between_states metric', function() {
        beforeEach(function() {
            app.init = function() {
                metricsH = new MetricsHelper(app.im);
                metricsH
                    .add.time_between_states(
                        {state: 'states:test', action: 'enter'},
                        {state: 'states:test2', action: 'exit'},
                        'time_between')
                    .add.time_between_states(
                        {state: 'states:test'},
                        {state: 'states:test2'});
            };
        });

        it('should fire the metric when the to state is reached', function() {
            return tester
                .inputs(null, 'test', null)
                .check(function(api) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].time_between;
                    assert.equal(metrics.agg, 'avg');
                    assert.equal(metrics.values.length, 1);
                    assert.equal(typeof metrics.values[0], 'number');
                })
                .run();
        });

        it('should trigger both metrics', function() {
            return tester
                .inputs(null, 'test')
                .check(function(api) {
                    metrics = api.metrics.stores['metricsHelper-tester']
                        .time_between_enter_states_test_enter_states_test2;
                    assert.equal(metrics.agg, 'avg');
                    assert.equal(metrics.values.length, 1);
                    assert.equal(typeof metrics.values[0], 'number');
                })
                .run();
        });

        it('should fire the metric for every event', function() {
            return tester
                .inputs(null, 'test', null, 'test', null)
                .check(function(api) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].time_between;
                    assert.equal(metrics.agg, 'avg');
                    assert.equal(metrics.values.length, 2);
                    assert.equal(typeof metrics.values[0], 'number');
                    assert.equal(typeof metrics.values[1], 'number');
                })
                .run();
        });

    });

});
<commit_msg>Added tests for tracker function<commit_after>var vumigo = require('vumigo_v02');
var App = vumigo.App;
var AppTester = vumigo.AppTester;
var EndState = vumigo.states.EndState;
var FreeText = vumigo.states.FreeText;
var MetricsHelper = require('../lib');
var assert = require('assert');

describe('MetricsHelper', function() {
    var app;
    var tester;
    var metricsH;

    beforeEach(function() {
        app = new App('states:test');
        tester = new AppTester(app);

        app.states.add('states:test', function(name) {
            return new FreeText(name, {
                question: 'This is the first state.',
                next: 'states:test2'
            });
        });

        app.states.add('states:test2', function(name) {
            return new EndState(name, {
                text: 'This is the end state.',
                next: 'states:test'
            });
        });

        tester
            .setup.config.app({
                name: 'metricsHelper-tester'
            });
    });

    describe('When a new user accesses the service', function() {

        beforeEach(function() {
            app.init = function() {
                metricsH = new MetricsHelper(app.im);
                metricsH
                    .add.total_unique_users('uniqueUsers')
                    .add.total_unique_users();
            };
        });

        it('should fire the new user metrics', function() {
            return tester
                .start()
                .check(function(api, im , app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].uniqueUsers;
                    metrics_trans = api.metrics.stores['metricsHelper-tester']
                        ['uniqueUsers.transient'];
                    assert.deepEqual(metrics, {agg: 'last', values: [ 1 ]});
                    assert.deepEqual(metrics_trans, {agg: 'sum', values: [1]});
                })
                .run();
        });

        it('should fire both new user metrics', function() {
            return tester
                .start()
                .check(function(api, im , app) {
                    metrics1 = api.metrics
                        .stores['metricsHelper-tester'].uniqueUsers;
                    metrics2 = api.metrics
                        .stores['metricsHelper-tester'].unique_users;
                    assert.deepEqual(metrics1, {agg: 'last', values: [ 1 ]});
                    assert.deepEqual(metrics2, {agg: 'last', values: [ 1 ]});
                })
                .run();
        });

        it('should fire the metric for each new user', function() {
            return tester
                .inputs(
                    {from_addr: '+271234', content: null},
                    {from_addr: '+274321', content: null})
                .check(function(api, im , app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].uniqueUsers;
                    assert.deepEqual(metrics, {agg: 'last', values: [ 1, 2 ]});
                })
                .run();
        });

        it('should not fire the metrics for existing users', function() {
            return tester
                .setup.user({addr: '+271234', state:'states:test'})
                .start()
                .check(function(api, im , app) {
                    metrics = api.metrics.stores['metricsHelper-tester'];
                    assert.equal(metrics, undefined);
                })
                .run();
        });

    });

    describe('when a new session is started', function() {

        beforeEach(function() {
            app.init = function() {
                metricsH = new MetricsHelper(app.im);
                metricsH
                    .add.total_sessions('sessions')
                    .add.total_sessions();
            };
        });

        it('should fire the sessions metric', function() {
            return tester
                .start()
                .check(function(api, im , app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].sessions;
                    metrics_trans = api.metrics.stores['metricsHelper-tester']
                        ['sessions.transient'];
                    assert.deepEqual(metrics, {agg: 'last', values: [ 1 ]});
                    assert.deepEqual(metrics_trans, {agg: 'sum', values: [1]});
                })
                .run();
        });

        it('should fire both sessions metrics', function() {
            return tester
                .start()
                .check(function(api, im, app) {
                    metric1 = api.metrics
                        .stores['metricsHelper-tester'].sessions;
                    metric2 = api.metrics
                        .stores['metricsHelper-tester'].total_sessions;
                    assert.deepEqual(metric1, {agg: 'last', values: [ 1 ]});
                    assert.deepEqual(metric2, {agg: 'last', values: [ 1 ]});
                })
                .run();
        });

        it('should fire the metric for each new session', function() {
            return tester
                .inputs(null, null)
                .check(function(api, im, app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].sessions;
                    assert.deepEqual(metrics, {agg: 'last', values: [ 1, 2 ]});
                })
                .run();
        });

        it('should not fire when resuming a session', function() {
            return tester
                .input('resume')
                .check(function(api, im, app) {
                    metrics = api.metrics.stores['metricsHelper-tester'];
                    assert.equal(metrics, undefined);
                })
                .run();
        });

    });

    describe('when the state action is triggered', function() {

        beforeEach(function() {
            app.init = function() {
                metricsH = new MetricsHelper(app.im);
                metricsH
                    .add.total_state_actions(
                        {state:'states:test', action:'exit'}, 'exits')
                    .add.total_state_actions({state:'states:test'});
            };
        });

        it('should trigger the state exit metric', function() {
            return tester
                .inputs(null, 'test')
                .check(function(api, im, app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].exits;
                    metrics_trans = api.metrics.stores['metricsHelper-tester']
                        ['exits.transient'];
                    assert.deepEqual(metrics, {agg: 'last', values: [ 1 ]});
                    assert.deepEqual(metrics_trans, {agg: 'sum', values: [1]});
                })
                .run();
        });

        it('should trigger the state enter metric', function() {
            return tester
                .start()
                .check(function(api, im, app) {
                    metric1 = api.metrics.stores['metricsHelper-tester']
                        .total_action_enter_states_test;
                    assert.deepEqual(metric1, {agg: 'last', values: [ 1 ]});
                })
                .run();
        });

        it('should trigger each time the state is entered', function() {
            return tester
                .inputs(null, 'test', null)
                .check(function(api, im, app) {
                    metrics = api.metrics.stores['metricsHelper-tester']
                        .total_action_enter_states_test;
                    assert.deepEqual(metrics, {agg: 'last', values: [ 1, 2 ]});
                })
                .run();
        });

        it('should throw an exception on bad state actions', function() {
            assert.throws( function() {
                metricsH.add.total_state_actions(
                    {state:'states:test', action:'foo'}, 'bad');},
                /^(Invalid state action foo)$/
                );
        });

    });

     describe('when there is a sessions_until_state metric', function() {

        beforeEach(function() {
            app.init = function() {
                metricsH = new MetricsHelper(app.im);
                metricsH
                    .add.sessions_until_state(
                        {state: 'states:test2', action: 'enter'},
                        'sessions_until')
                    .add.sessions_until_state({state: 'states:test2'});
            };
        });

        it('should trigger the metric on entering the state', function() {
            return tester
                .inputs(null, 'test')
                .check(function(api, im, app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].sessions_until;
                    assert.deepEqual(metrics, {agg: 'avg', values: [ 1 ]});
                })
                .run();
        });

        it('should trigger both metrics', function() {
            return tester
                .inputs(null, 'test')
                .check(function(api, im, app) {
                    metric1 = api.metrics
                        .stores['metricsHelper-tester'].sessions_until;
                    metric2 = api.metrics.stores['metricsHelper-tester']
                        .sessions_until_enter_states_test2;
                    assert.deepEqual(metric1, {agg: 'avg', values: [ 1 ]});
                    assert.deepEqual(metric2, {agg: 'avg', values: [ 1 ]});
                })
                .run();
        });

        it('should trigger each time the state is reached', function() {
            return tester
                .inputs(null, 'test', null, 'test')
                .check(function(api, im, app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].sessions_until;
                    assert.deepEqual(metrics, {agg: 'avg', values: [ 1, 1 ]});
                })
                .run();
        });

        it('should count multiple sessions', function() {
            return tester
                .inputs(null, null, null, 'test', null, 'test')
                .check(function(api, im, app) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].sessions_until;
                    assert.deepEqual(metrics, {agg: 'avg', values: [ 3, 1 ]});
                })
                .run();
        });

    });

    describe('when there is a time_between_states metric', function() {
        beforeEach(function() {
            app.init = function() {
                metricsH = new MetricsHelper(app.im);
                metricsH
                    .add.time_between_states(
                        {state: 'states:test', action: 'enter'},
                        {state: 'states:test2', action: 'exit'},
                        'time_between')
                    .add.time_between_states(
                        {state: 'states:test'},
                        {state: 'states:test2'});
            };
        });

        it('should fire the metric when the to state is reached', function() {
            return tester
                .inputs(null, 'test', null)
                .check(function(api) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].time_between;
                    assert.equal(metrics.agg, 'avg');
                    assert.equal(metrics.values.length, 1);
                    assert.equal(typeof metrics.values[0], 'number');
                })
                .run();
        });

        it('should trigger both metrics', function() {
            return tester
                .inputs(null, 'test')
                .check(function(api) {
                    metrics = api.metrics.stores['metricsHelper-tester']
                        .time_between_enter_states_test_enter_states_test2;
                    assert.equal(metrics.agg, 'avg');
                    assert.equal(metrics.values.length, 1);
                    assert.equal(typeof metrics.values[0], 'number');
                })
                .run();
        });

        it('should fire the metric for every event', function() {
            return tester
                .inputs(null, 'test', null, 'test', null)
                .check(function(api) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].time_between;
                    assert.equal(metrics.agg, 'avg');
                    assert.equal(metrics.values.length, 2);
                    assert.equal(typeof metrics.values[0], 'number');
                    assert.equal(typeof metrics.values[1], 'number');
                })
                .run();
        });

    });

    describe('the tracker function', function() {
        beforeEach(function() {
            app.init = function() {
                metricsH = new MetricsHelper(app.im);
                metricsH
                    .add.tracker(
                        { enter: 'states:test'}, { exit: 'states:test2'},
                        { time_between_states: 'time_between' });
            };
        });

        it('should add a time_between_states metric', function() {
            return tester
                .inputs(null, 'test', null)
                .check(function(api) {
                    metrics = api.metrics
                        .stores['metricsHelper-tester'].time_between;
                    assert.equal(metrics.agg, 'avg');
                    assert.equal(metrics.values.length, 1);
                    assert.equal(typeof metrics.values[0], 'number');
                })
                .run();
        });
    });

});
<|endoftext|>