<commit_before>'use strict';

const fs = require('fs');
const request = require('request');
const Cheerio = require('cheerio');
const SteamTotp = require('steam-totp');

const Confirmation = require('/lib/Confirmation.js');

class SteamCommunityMobileConfirmations {
  this.STEAM_BASE = 'http://steamcommunity.com'
  this.steamId = '';
  this.identitySecret = '';
  this.deviceId = '';
  this.offest = 0;
  this.has429Error = false;
  this.timeBetweenCalls = 0;
  this._requestJar = request.jar();
  this._request = request.defaults({ jar: this._requestJar });

  constructor(data) {
    this.steamId = data.steamId;
    this.identitySecret = data.identitySecret;
    this.deviceId = data.deviceId || SteamTotp.getDeviceID(this.steamId);
    this.offset = data.steamOffset || 0;
    this.timeBetweenCalls = data.waitTime || 10000;

    // Set each cookie NOM NOM
    for (let cookie of data.webCookie) {
      this._requestJar.setCookie(request.cookie(cookie), 'https://steamcommunity.com');
    }
  }

  /**
   * Grabs all confirmations
   * @param {Function} callback The function callback'd.
   * @return {void}
   */
  fetchConfirmations(callback) {
    this._request({
      url:
    }, (err, response, body) => {
      let error = checkResponse(err, response, body);

      if (error) {
        callback(error, null);
        return;
      }

      // Parse our confirmations
      let confirmations = [];
      let $ = Cheerio.load(body);
      fs.writeFileSync(`output.html`, body);

      // Go through each confirmation, and generate a new Confirmation object
      $('[data-confid]').each((index, element) => {
        let $confirmation = $(element);
        let description = $confirmation.find('.mobileconf_list_entry_description > div').map(() => $(this).text());
        console.log(description);

        confirmations.push(new Confirmation({
          id: $confirmation.data('confid'),
          key: $confirmation.data('key'),
          description: description,
          cancel: $confirmation.data('cancel'),
          accept: $confirmation.data('accept')
        }));
      });

      callback(null, confirmations);
    });
  }

  /**
   * Sends the response to accept the confirmation
   * @param  {Confirmation} confirmation The Confirmation we are responding too
   * @param  {Function}     callback
   * @return {void}
   */
  acceptConfirmation(confirmation, callback) {
    if (!Array.isArray(confirmation)) {
      this._sendConfirmationResponse(confirmation, 'allow', (error, result) => {

      });
    } else {
      this._sendMultiConfirmationAjax(confirmation, 'allow', (error, result) => {

      });
    }
  }

  getConfirmationTradeId(confirmation, callback) {

  }

  /**
   * Our custom request call, applies headers where needed / etc
   * @param  {String}   url      The URL to be requested
   * @param  {String}   method   The HTTP method (GET, POST, PUT, DELETE)
   * @param  {Object}   form     (Optional) A form to be sent, only works if method is POST
   * @param  {Function} callback
   * @return {void}
   */
  function request(url, method, form, callback) {
    if (typeof form === 'function') {
      callback = form;
      form = {};
    }

    // Generate some headers to simulate a real application
    let headers = {
      'accept': 'text/javascript, text/html, application/xml, text/xml, */*',
      'user-agent': 'Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'
    };

    this._request({
      method: method,
      url: url,
      headers: headers,
      form: form
    }, callback);
  }

  /**
   * Sends a confirmation response
   * @param  {Confirmation} confirmation The Confirmation we are responding too
   * @param  {String}       operation    allow / cancel
   * @param  {Function}     callback     The function callback'd
   * @return {void}
   */
  _sendConfirmationResponse(confirmation, operation, callback) {
    let url = `${this.STEAM_BASE}/mobileconf/ajaxop?op=${operation}&${this._generateQueryString(operation)}&cid=${confirmation.id}&ck=${confirmation.key}`;

    this.request(url, 'GET', (err, response, body) => {
      let error = checkResponse(err, response, body);

      if (error) {
        callback(error, null);
        return;
      }

      try {
        let result = JSON.parse(body);
        callback(null, result.success);
      } catch (e) {
        callback(e, null);
      }
    });
  }

  _sendMultiConfirmationAjax(confirmations, operation, callback) {
    let url = `${this.STEAM_BASE}/mobileconf/multiajaxop`;
    let queryVariables = this._generateQueryVariables(operation);
    let form = {
      op: operation,
      cid: [],
      ck: []
    };

    for (let confirmation of confirmations) {
      url += `&cid[]=${confirmation.id}&ck[]=${confirmation.key}`;
    }

    this.request(url, 'POST', form, (err, response, body) => {
      let error = checkResponse(err, response, body);

      if (error) {
        callback(error, null);
        return;
      }

    });
  }

  /**
   * Gets the confirmation details
   * @param  {Confirmation} confirmation The Confirmation we care about
   * @param {Function} callback
   * @return {void}
   */
  _getConfirmationDetails(confirmation, callback) {
    let url = `https://steamcommunity.com/mobileconf/details/${confirmation.id}?${this._generateQueryString('details')}`;

    this.request(url, 'GET', (err, response, body) => {
      let error = checkResponse(err, response, body);

      if (error) {
        callback(error, null);
        return;
      }

      try {
        let result = JSON.parse(body);
        callback(null, result, confirmation);
      } catch (e) {
        callback(e, null);
      }
    });
  }

  /**
   * Generates the HTTP query string, to be prepended to all GET requests
   * @param  {String} tag The action. "conf" to load the confirmations page, "details" to load details about a trade, "allow" to confirm a trade, "cancel" to cancel it.
   * @return {String}     The generated string
   */
  _generateQueryString(tag) {
    let queryVariables = this._generateQueryVariables(tag);
    let queryString = '';

    // Generate our string
    for (let name in queryVariables) {
      queryString += `${name}=${queryVariables[name]}&`;
    }

    // Remove trailing &
    queryString = queryString.slice(0, -1);

    return queryString;
  }

  /**
   * Actually generate the needed variables
   * @param  {String} tag Action tag
   * @return {Object}     The variables, zipped together a nice & neat object!
   */
  _generateQueryVariables(tag) {
    let time = Math.floor(Date.now() / 1000) + this.steamOffset;

    let result = {
      p: this.deviceId,
      a: this.steamId,
      k: SteamTotp.generateConfirmationKey(this.identitySecret, time, tag),
      t: time,
      m: 'android',
      tag: tag
    };

    return result;
  }
}

/**
 * Checks the HTTP response
 * @param  {Error} error    An HTTP error
 * @param  {Object} response The HTTP response object
 * @param  {Object} body     The HTTP body
 * @return {Error}          An error, if one exists (null otherwise)
 */
function checkResponse(error, response, body) {
  if (error || response.statusCode != 200) {
    return error || new Error(response.statusCode);
  }

  if (!body) {
    return new Error('Invalid response body');
  }

  return null;
}

module.exports = SteamCommunityMobileConfirmations;
<commit_msg>Added underscore req<commit_after>'use strict';

const fs = require('fs');
const request = require('request');
const Cheerio = require('cheerio');
const SteamTotp = require('steam-totp');
const _ = require('underscore');

const Confirmation = require('/lib/Confirmation.js');

class SteamCommunityMobileConfirmations {
  this.STEAM_BASE = 'http://steamcommunity.com'
  this.steamId = '';
  this.identitySecret = '';
  this.deviceId = '';
  this.offest = 0;
  this.has429Error = false;
  this.timeBetweenCalls = 0;
  this._requestJar = request.jar();
  this._request = request.defaults({ jar: this._requestJar });

  constructor(data) {
    this.steamId = data.steamId;
    this.identitySecret = data.identitySecret;
    this.deviceId = data.deviceId || SteamTotp.getDeviceID(this.steamId);
    this.offset = data.steamOffset || 0;
    this.timeBetweenCalls = data.waitTime || 10000;

    // Set each cookie NOM NOM
    for (let cookie of data.webCookie) {
      this._requestJar.setCookie(request.cookie(cookie), 'https://steamcommunity.com');
    }
  }

  /**
   * Grabs all confirmations
   * @param {Function} callback The function callback'd.
   * @return {void}
   */
  fetchConfirmations(callback) {
    this._request({
      url:
    }, (err, response, body) => {
      let error = checkResponse(err, response, body);

      if (error) {
        callback(error, null);
        return;
      }

      // Parse our confirmations
      let confirmations = [];
      let $ = Cheerio.load(body);
      fs.writeFileSync(`output.html`, body);

      // Go through each confirmation, and generate a new Confirmation object
      $('[data-confid]').each((index, element) => {
        let $confirmation = $(element);
        let description = $confirmation.find('.mobileconf_list_entry_description > div').map(() => $(this).text());
        console.log(description);

        confirmations.push(new Confirmation({
          id: $confirmation.data('confid'),
          key: $confirmation.data('key'),
          description: description,
          cancel: $confirmation.data('cancel'),
          accept: $confirmation.data('accept')
        }));
      });

      callback(null, confirmations);
    });
  }

  /**
   * Sends the response to accept the confirmation
   * @param  {Confirmation} confirmation The Confirmation we are responding too
   * @param  {Function}     callback
   * @return {void}
   */
  acceptConfirmation(confirmation, callback) {
    if (!Array.isArray(confirmation)) {
      this._sendConfirmationResponse(confirmation, 'allow', (error, result) => {

      });
    } else {
      this._sendMultiConfirmationAjax(confirmation, 'allow', (error, result) => {

      });
    }
  }

  getConfirmationTradeId(confirmation, callback) {

  }

  /**
   * Our custom request call, applies headers where needed / etc
   * @param  {String}   url      The URL to be requested
   * @param  {String}   method   The HTTP method (GET, POST, PUT, DELETE)
   * @param  {Object}   form     (Optional) A form to be sent, only works if method is POST
   * @param  {Function} callback
   * @return {void}
   */
  function request(url, method, form, callback) {
    if (typeof form === 'function') {
      callback = form;
      form = {};
    }

    // Generate some headers to simulate a real application
    let headers = {
      'accept': 'text/javascript, text/html, application/xml, text/xml, */*',
      'user-agent': 'Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'
    };

    this._request({
      method: method,
      url: url,
      headers: headers,
      form: form
    }, callback);
  }

  /**
   * Sends a confirmation response
   * @param  {Confirmation} confirmation The Confirmation we are responding too
   * @param  {String}       operation    allow / cancel
   * @param  {Function}     callback     The function callback'd
   * @return {void}
   */
  _sendConfirmationResponse(confirmation, operation, callback) {
    let url = `${this.STEAM_BASE}/mobileconf/ajaxop?op=${operation}&${this._generateQueryString(operation)}&cid=${confirmation.id}&ck=${confirmation.key}`;

    this.request(url, 'GET', (err, response, body) => {
      let error = checkResponse(err, response, body);

      if (error) {
        callback(error, null);
        return;
      }

      try {
        let result = JSON.parse(body);
        callback(null, result.success);
      } catch (e) {
        callback(e, null);
      }
    });
  }

  _sendMultiConfirmationAjax(confirmations, operation, callback) {
    let url = `${this.STEAM_BASE}/mobileconf/multiajaxop`;
    let queryVariables = this._generateQueryVariables(operation);
    let form = {
      op: operation,
      cid: [],
      ck: []
    };

    for (let confirmation of confirmations) {
      url += `&cid[]=${confirmation.id}&ck[]=${confirmation.key}`;
    }

    this.request(url, 'POST', form, (err, response, body) => {
      let error = checkResponse(err, response, body);

      if (error) {
        callback(error, null);
        return;
      }

    });
  }

  /**
   * Gets the confirmation details
   * @param  {Confirmation} confirmation The Confirmation we care about
   * @param {Function} callback
   * @return {void}
   */
  _getConfirmationDetails(confirmation, callback) {
    let url = `https://steamcommunity.com/mobileconf/details/${confirmation.id}?${this._generateQueryString('details')}`;

    this.request(url, 'GET', (err, response, body) => {
      let error = checkResponse(err, response, body);

      if (error) {
        callback(error, null);
        return;
      }

      try {
        let result = JSON.parse(body);
        callback(null, result, confirmation);
      } catch (e) {
        callback(e, null);
      }
    });
  }

  /**
   * Generates the HTTP query string, to be prepended to all GET requests
   * @param  {String} tag The action. "conf" to load the confirmations page, "details" to load details about a trade, "allow" to confirm a trade, "cancel" to cancel it.
   * @return {String}     The generated string
   */
  _generateQueryString(tag) {
    let queryVariables = this._generateQueryVariables(tag);
    let queryString = '';

    // Generate our string
    for (let name in queryVariables) {
      queryString += `${name}=${queryVariables[name]}&`;
    }

    // Remove trailing &
    queryString = queryString.slice(0, -1);

    return queryString;
  }

  /**
   * Actually generate the needed variables
   * @param  {String} tag Action tag
   * @return {Object}     The variables, zipped together a nice & neat object!
   */
  _generateQueryVariables(tag) {
    let time = Math.floor(Date.now() / 1000) + this.steamOffset;

    let result = {
      p: this.deviceId,
      a: this.steamId,
      k: SteamTotp.generateConfirmationKey(this.identitySecret, time, tag),
      t: time,
      m: 'android',
      tag: tag
    };

    return result;
  }
}

/**
 * Checks the HTTP response
 * @param  {Error} error    An HTTP error
 * @param  {Object} response The HTTP response object
 * @param  {Object} body     The HTTP body
 * @return {Error}          An error, if one exists (null otherwise)
 */
function checkResponse(error, response, body) {
  if (error || response.statusCode != 200) {
    return error || new Error(response.statusCode);
  }

  if (!body) {
    return new Error('Invalid response body');
  }

  return null;
}

module.exports = SteamCommunityMobileConfirmations;
<|endoftext|>


<commit_before>module.exports.lintText = lintText
module.exports.lintFiles = lintFiles

var dezalgo = require('dezalgo')
var eslint = require('eslint')
var findRoot = require('find-root')
var glob = require('glob')
var parallel = require('run-parallel')
var path = require('path')
var uniq = require('uniq')

var DEFAULT_PATTERNS = [
  '**/*.js',
  '**/*.jsx'
]

var DEFAULT_IGNORE_PATTERNS = [
  '**/node_modules/**',
  '.git/**',
  'coverage/**',
  '**/*.min.js',
  '**/bundle.js'
]

var ESLINT_CONFIG = { configFile: path.join(__dirname, 'rc', '.eslintrc') }

/**
 * Lint text to enforce JavaScript Standard Style.
 *
 * @param {string} text                 file text to lint
 * @param {Object} opts                 options object
 * @param {Array.<String>} opts.ignore  files to ignore
 * @param {string} opts.cwd             current working directory
 * @param {function(Error, Object)} cb  callback
 */
function lintText (text, opts, cb) {
  if (typeof opts === 'function') {
    cb = opts
    opts = {}
  }
  opts = parseOpts(opts)
  cb = dezalgo(cb)

  var result
  try {
    result = new eslint.CLIEngine(ESLINT_CONFIG).executeOnText(text)
  } catch (err) {
    return cb(err)
  }
  return cb(null, result)
}

/**
 * Lint files to enforce JavaScript Standard Style.
 *
 * @param {Array.<string>} files        file globs to lint
 * @param {Object} opts                 options object
 * @param {Array.<String>} opts.ignore  files to ignore
 * @param {string} opts.cwd             current working directory
 * @param {function(Error, Object)} cb  callback
 */
function lintFiles (files, opts, cb) {
  if (typeof opts === 'function') {
    cb = opts
    opts = {}
  }
  opts = parseOpts(opts)
  cb = dezalgo(cb)

  if (typeof files === 'string') files = [ files ]
  if (files.length === 0) files = DEFAULT_PATTERNS

  // traverse filesystem
  parallel(files.map(function (pattern) {
    return function (cb) {
      glob(pattern, {
        cwd: opts.cwd,
        ignore: opts.ignore,
        nodir: true
      }, cb)
    }
  }), function (err, results) {
    if (err) return cb(err)

    // flatten nested arrays
    var files = results.reduce(function (files, result) {
      result.forEach(function (file) {
        files.push(file)
      })
      return files
    }, [])

    // de-dupe
    files = uniq(files)

    // undocumented – do not use (used by bin/cmd.js)
    if (opts._onFiles) opts._onFiles(files)

    var result
    try {
      result = new eslint.CLIEngine(ESLINT_CONFIG).executeOnFiles(files)
    } catch (err) {
      return cb(err)
    }
    return cb(null, result)
  })
}

function parseOpts (opts) {
  if (!opts) opts = {}

  if (!opts.cwd) opts.cwd = process.cwd()

  // Add user ignore patterns to default ignore patterns
  opts.ignore = (opts.ignore || []).concat(DEFAULT_IGNORE_PATTERNS)

  // Add additional ignore patterns from the closest `package.json`
  try {
    var root = findRoot(opts.cwd)
    var packageOpts = require(path.join(root, 'package.json')).standard
    if (packageOpts) opts.ignore = opts.ignore.concat(packageOpts.ignore)
  } catch (e) {}

  return opts
}
<commit_msg>don't use the user's ~/.eslintrc<commit_after>module.exports.lintText = lintText
module.exports.lintFiles = lintFiles

var dezalgo = require('dezalgo')
var eslint = require('eslint')
var findRoot = require('find-root')
var glob = require('glob')
var parallel = require('run-parallel')
var path = require('path')
var uniq = require('uniq')

var DEFAULT_PATTERNS = [
  '**/*.js',
  '**/*.jsx'
]

var DEFAULT_IGNORE_PATTERNS = [
  '**/node_modules/**',
  '.git/**',
  'coverage/**',
  '**/*.min.js',
  '**/bundle.js'
]

var ESLINT_CONFIG = {
  configFile: path.join(__dirname, 'rc', '.eslintrc'),
  useEslintrc: false
}

/**
 * Lint text to enforce JavaScript Standard Style.
 *
 * @param {string} text                 file text to lint
 * @param {Object} opts                 options object
 * @param {Array.<String>} opts.ignore  files to ignore
 * @param {string} opts.cwd             current working directory
 * @param {function(Error, Object)} cb  callback
 */
function lintText (text, opts, cb) {
  if (typeof opts === 'function') {
    cb = opts
    opts = {}
  }
  opts = parseOpts(opts)
  cb = dezalgo(cb)

  var result
  try {
    result = new eslint.CLIEngine(ESLINT_CONFIG).executeOnText(text)
  } catch (err) {
    return cb(err)
  }
  return cb(null, result)
}

/**
 * Lint files to enforce JavaScript Standard Style.
 *
 * @param {Array.<string>} files        file globs to lint
 * @param {Object} opts                 options object
 * @param {Array.<String>} opts.ignore  files to ignore
 * @param {string} opts.cwd             current working directory
 * @param {function(Error, Object)} cb  callback
 */
function lintFiles (files, opts, cb) {
  if (typeof opts === 'function') {
    cb = opts
    opts = {}
  }
  opts = parseOpts(opts)
  cb = dezalgo(cb)

  if (typeof files === 'string') files = [ files ]
  if (files.length === 0) files = DEFAULT_PATTERNS

  // traverse filesystem
  parallel(files.map(function (pattern) {
    return function (cb) {
      glob(pattern, {
        cwd: opts.cwd,
        ignore: opts.ignore,
        nodir: true
      }, cb)
    }
  }), function (err, results) {
    if (err) return cb(err)

    // flatten nested arrays
    var files = results.reduce(function (files, result) {
      result.forEach(function (file) {
        files.push(file)
      })
      return files
    }, [])

    // de-dupe
    files = uniq(files)

    // undocumented – do not use (used by bin/cmd.js)
    if (opts._onFiles) opts._onFiles(files)

    var result
    try {
      result = new eslint.CLIEngine(ESLINT_CONFIG).executeOnFiles(files)
    } catch (err) {
      return cb(err)
    }
    return cb(null, result)
  })
}

function parseOpts (opts) {
  if (!opts) opts = {}

  if (!opts.cwd) opts.cwd = process.cwd()

  // Add user ignore patterns to default ignore patterns
  opts.ignore = (opts.ignore || []).concat(DEFAULT_IGNORE_PATTERNS)

  // Add additional ignore patterns from the closest `package.json`
  try {
    var root = findRoot(opts.cwd)
    var packageOpts = require(path.join(root, 'package.json')).standard
    if (packageOpts) opts.ignore = opts.ignore.concat(packageOpts.ignore)
  } catch (e) {}

  return opts
}
<|endoftext|>


<commit_before>module.exports = require("autodafe").config({
    http: true
}).run();<commit_msg>http component started<commit_after>module.exports = require("autodafe").config({
    baseUrl: __dirname,

    components: {
        http: true
    }
});<|endoftext|>


<commit_before>const CopyWebpackPlugin = require('copy-webpack-plugin')
const path = require('path')

function CopyWebpackExternalsManifest(options) {
    this.externalsAssets = [];
    this.externals = {};
    this.chunksOrder = options.chunksOrder || ["manifest", "vendor"]
    this.fileName = options.fileName || 'manifest.json'

    options.externals.forEach(item => {
        // externals
        this.externals[item.module] = item.export;

        // get version
        let version = require(`${item.module}/package.json`).version;

        this.externalsAssets.push(Object.assign(item, {version: version}));
    })
};

CopyWebpackExternalsManifest.prototype.apply = function (compiler) {
    let _this = this;

    // assign webpack config externals
    compiler.options.externals = (typeof compiler.options.externals === 'object')
        ? Object.assign(compiler.options.externals, _this.externals)
        : _this.externals;

    let copyAssets = [];
    let externalsManifest = {};
    _this.externalsAssets.forEach(item => {
        let fromDir = `node_modules/${item.module}/`;
        let toDir = `${compiler.options.output.path}/${item.module}/${item.version}/`;
        let externalsDir = `${compiler.options.output.publicPath}${item.module}/${item.version}/`

        // copy entry
        if (typeof item.entry === "string") {
            copyAssets.push({
                from: fromDir + item.entry,
                to: toDir + item.entry
            });
            externalsManifest[`${item.module}/${item.entry}`] = `${externalsDir}${item.entry}`;
        } else if (Array.isArray(item.entry)) {
            item.entry.forEach(entry => {
                copyAssets.push({
                    from: fromDir + entry,
                    to: toDir + entry
                });
                externalsManifest[`${item.module}/${entry}`] = `${externalsDir}${entry}`;
            })
        }

        // copy assets
        if (Array.isArray(item.assets)) {
            let subjectAssets = item.assets.map(entry => ({
                from: fromDir + entry,
                to: toDir + entry
            }));
            copyAssets.push(...subjectAssets);
        }
    });

    new CopyWebpackPlugin({patterns : copyAssets}).apply(compiler);

    compiler.plugin("done", function (stats) {
        let chunks = stats.toJson().assetsByChunkName;

        require('fs').writeFileSync(
            path.join(compiler.options.output.path, _this.fileName),
            JSON.stringify(
                {
                    externals: externalsManifest,
                    chunks: sortByList(chunks, _this.chunksOrder, compiler.options.output.publicPath)
                }
            )
        );
    });
};

function sortByList(chunks, orderList, publicPath) {
    let chunksManifest = {};
    let orderChunks = {};
    let palceholder = "Meaningless";

    if (Array.isArray(orderList)) {
        orderList.forEach(i => {
            orderChunks[i] = palceholder;
        })
    }

    Object.assign(orderChunks, chunks);

    Object.keys(orderChunks).forEach(chunk => {
        let chunkFile = orderChunks[chunk]

        if (chunkFile === palceholder) {
            return
        }

        if (typeof chunkFile === 'string') {
            chunksManifest[chunk + path.extname(chunkFile)] = publicPath + chunkFile
        } else if (Array.isArray(chunkFile)) {
            chunkFile.forEach(item => {
                chunksManifest[chunk + path.extname(item)] = publicPath + item
            })
        }
    })

    return chunksManifest
}

module.exports = CopyWebpackExternalsManifest;
<commit_msg>compiler hooks function signature updated<commit_after>const CopyWebpackPlugin = require('copy-webpack-plugin')
const path = require('path')

function CopyWebpackExternalsManifest(options) {
    this.externalsAssets = [];
    this.externals = {};
    this.chunksOrder = options.chunksOrder || ["manifest", "vendor"]
    this.fileName = options.fileName || 'manifest.json'

    options.externals.forEach(item => {
        // externals
        this.externals[item.module] = item.export;

        // get version
        let version = require(`${item.module}/package.json`).version;

        this.externalsAssets.push(Object.assign(item, {version: version}));
    })
};

CopyWebpackExternalsManifest.prototype.apply = function (compiler) {
    let _this = this;

    // assign webpack config externals
    compiler.options.externals = (typeof compiler.options.externals === 'object')
        ? Object.assign(compiler.options.externals, _this.externals)
        : _this.externals;

    let copyAssets = [];
    let externalsManifest = {};
    _this.externalsAssets.forEach(item => {
        let fromDir = `node_modules/${item.module}/`;
        let toDir = `${compiler.options.output.path}/${item.module}/${item.version}/`;
        let externalsDir = `${compiler.options.output.publicPath}${item.module}/${item.version}/`

        // copy entry
        if (typeof item.entry === "string") {
            copyAssets.push({
                from: fromDir + item.entry,
                to: toDir + item.entry
            });
            externalsManifest[`${item.module}/${item.entry}`] = `${externalsDir}${item.entry}`;
        } else if (Array.isArray(item.entry)) {
            item.entry.forEach(entry => {
                copyAssets.push({
                    from: fromDir + entry,
                    to: toDir + entry
                });
                externalsManifest[`${item.module}/${entry}`] = `${externalsDir}${entry}`;
            })
        }

        // copy assets
        if (Array.isArray(item.assets)) {
            let subjectAssets = item.assets.map(entry => ({
                from: fromDir + entry,
                to: toDir + entry
            }));
            copyAssets.push(...subjectAssets);
        }
    });

    new CopyWebpackPlugin({patterns : copyAssets}).apply(compiler);

    compiler.hooks.done.tap("onComplete", function (stats) {
        let chunks = stats.toJson().assetsByChunkName;

        require('fs').writeFileSync(
            path.join(compiler.options.output.path, _this.fileName),
            JSON.stringify(
                {
                    externals: externalsManifest,
                    chunks: sortByList(chunks, _this.chunksOrder, compiler.options.output.publicPath)
                }
            )
        );
    });
};

function sortByList(chunks, orderList, publicPath) {
    let chunksManifest = {};
    let orderChunks = {};
    let palceholder = "Meaningless";

    if (Array.isArray(orderList)) {
        orderList.forEach(i => {
            orderChunks[i] = palceholder;
        })
    }

    Object.assign(orderChunks, chunks);

    Object.keys(orderChunks).forEach(chunk => {
        let chunkFile = orderChunks[chunk]

        if (chunkFile === palceholder) {
            return
        }

        if (typeof chunkFile === 'string') {
            chunksManifest[chunk + path.extname(chunkFile)] = publicPath + chunkFile
        } else if (Array.isArray(chunkFile)) {
            chunkFile.forEach(item => {
                chunksManifest[chunk + path.extname(item)] = publicPath + item
            })
        }
    })

    return chunksManifest
}

module.exports = CopyWebpackExternalsManifest;
<|endoftext|>


<commit_before>var h = require('hyperscript')
var u = require('./lib/util'),
  each = u.each,
  isVisible = u.isVisible,
  setVisible = u.setVisible,
  setInvisible = u.setInvisible

var Tabs = require('./tabs')

module.exports = function (opts) {
  if (!opts) opts = {}

  var content = h('section.content')
  var tabs = Tabs(content, function () { getSelection() }, opts.onClose)
  var d = h('div.Hypertabs', [
    h('nav', [
      opts.prepend,
      tabs,
      opts.append
    ]),
    content
  ])

  function getSelection () {
    var sel = []
    each(content.children, function (page, i) {
      if(isVisible(page))
        sel.push(i)
    })
    if(''+sel === ''+selection) return
    d.selected = selection = sel
    if(opts.onSelect) opts.onSelect(selection)
    return sel
  }

  var selection = d.selected = []

  d.add = function (el, change, split) {
    var page = h('div.page', el)
    page.content = el

    if(!split) setInvisible(page)
    var index = content.children.length
    content.appendChild(page)
    if(change !== false && !split) d.select(index)
    getSelection()

    return page
  }

  function find (name) {
    if(Number.isInteger(name)) return name

    for(var i = 0; i < content.children.length; i++)
      if(page(i).content.id == name) return i

    return -1
  }

  function page (index) {
    return content.children[index]
  }

  d.has = function (name) {
    return ~find(name)
  }

  // getPage
  d.get = function (name) {
    return page(find(name))
  }

  d.select = function (index, change, split) {
    if('string' === typeof index) index = find(index)

    var max = content.children.length - 1
    if(index > max) index = 0
    if(index < 0) index = max

    if(split)
      page(index).style.display = 'flex'
    else
      [].forEach.call(content.children, function (page, i) {
        i == index ? setVisible(page) : setInvisible(page)
      })
    getSelection()
  }

  d.selectRelative = function (n) {
    getSelection()
    d.select(selection[0] + n)
  }

  d.remove = function (i) {
    if(Array.isArray(i)) return i.reverse().forEach(d.remove)
    var el = d.get(i)
    opts.onClose && opts.onClose(el.firstChild)
    if(el) content.removeChild(el)
  }

  var _display
  d.fullscreen = function (full) {
    tabs.style.display = full ? 'none' : null
    return full
  }

  d.isFullscreen = function () {
    return tabs.style.display === 'none'
  }

  return d
}

<commit_msg>Call onclose after we remove the tabs in the interface in the same way we do on clicking close<commit_after>var h = require('hyperscript')
var u = require('./lib/util'),
  each = u.each,
  isVisible = u.isVisible,
  setVisible = u.setVisible,
  setInvisible = u.setInvisible

var Tabs = require('./tabs')

module.exports = function (opts) {
  if (!opts) opts = {}

  var content = h('section.content')
  var tabs = Tabs(content, function () { getSelection() }, opts.onClose)
  var d = h('div.Hypertabs', [
    h('nav', [
      opts.prepend,
      tabs,
      opts.append
    ]),
    content
  ])

  function getSelection () {
    var sel = []
    each(content.children, function (page, i) {
      if(isVisible(page))
        sel.push(i)
    })
    if(''+sel === ''+selection) return
    d.selected = selection = sel
    if(opts.onSelect) opts.onSelect(selection)
    return sel
  }

  var selection = d.selected = []

  d.add = function (el, change, split) {
    var page = h('div.page', el)
    page.content = el

    if(!split) setInvisible(page)
    var index = content.children.length
    content.appendChild(page)
    if(change !== false && !split) d.select(index)
    getSelection()

    return page
  }

  function find (name) {
    if(Number.isInteger(name)) return name

    for(var i = 0; i < content.children.length; i++)
      if(page(i).content.id == name) return i

    return -1
  }

  function page (index) {
    return content.children[index]
  }

  d.has = function (name) {
    return ~find(name)
  }

  // getPage
  d.get = function (name) {
    return page(find(name))
  }

  d.select = function (index, change, split) {
    if('string' === typeof index) index = find(index)

    var max = content.children.length - 1
    if(index > max) index = 0
    if(index < 0) index = max

    if(split)
      page(index).style.display = 'flex'
    else
      [].forEach.call(content.children, function (page, i) {
        i == index ? setVisible(page) : setInvisible(page)
      })
    getSelection()
  }

  d.selectRelative = function (n) {
    getSelection()
    d.select(selection[0] + n)
  }

  d.remove = function (i) {
    if(Array.isArray(i)) return i.reverse().forEach(d.remove)
    var el = d.get(i)
    if(el) {
      content.removeChild(el)
      opts.onClose && opts.onClose(el.firstChild)
    }
  }

  var _display
  d.fullscreen = function (full) {
    tabs.style.display = full ? 'none' : null
    return full
  }

  d.isFullscreen = function () {
    return tabs.style.display === 'none'
  }

  return d
}

<|endoftext|>


<commit_before>const history = require('sheet-router/history')
const sheetRouter = require('sheet-router')
const document = require('global/document')
const href = require('sheet-router/href')
const hash = require('sheet-router/hash')
const hashMatch = require('hash-match')
const sendAction = require('send-action')
const mutate = require('xtend/mutable')
const assert = require('assert')
const xtend = require('xtend')
const yo = require('yo-yo')

choo.view = yo
module.exports = choo

// framework for creating sturdy web applications
// null -> fn
function choo () {
  const _models = []
  var _router = null

  start.toString = toString
  start.router = router
  start.model = model
  start.start = start

  return start

  // render the application to a string
  // (str, obj) -> str
  function toString (route, serverState) {
    const initialState = {}
    const nsState = {}

    _models.forEach(function (model) {
      const ns = model.namespace
      if (ns) {
        if (!nsState[ns]) nsState[ns] = {}
        apply(ns, model.state, nsState)
        nsState[ns] = xtend(nsState[ns], serverState[ns])
      } else {
        apply(model.namespace, model.state, initialState)
      }
    })

    const state = xtend(initialState, xtend(serverState, nsState))
    const tree = _router(route, state, function () {
      throw new Error('send() cannot be called on the server')
    })

    return tree.toString()
  }

  // start the application
  // (str?, obj?) -> DOMNode
  function start (rootId, opts) {
    if (!opts) {
      opts = rootId
      rootId = null
    }
    opts = opts || {}
    const name = opts.name || 'choo'
    const initialState = {}
    const reducers = {}
    const effects = {}

    _models.push(appInit(opts))
    _models.forEach(function (model) {
      if (model.state) apply(model.namespace, model.state, initialState)
      if (model.reducers) apply(model.namespace, model.reducers, reducers)
      if (model.effects) apply(model.namespace, model.effects, effects)
    })

    // send() is used to trigger actions inside
    // views, effects and subscriptions
    const send = sendAction({
      onaction: handleAction,
      onchange: onchange,
      state: initialState
    })

    // subscriptions are loaded after sendAction() is called
    // because they both need access to send() and can't
    // react to actions (read-only)
    _models.forEach(function (model) {
      if (model.subscriptions) {
        assert.ok(Array.isArray(model.subscriptions, 'subs must be an array'))
        model.subscriptions.forEach(function (sub) {
          sub(send)
        })
      }
    })

    // If an id is provided, the application will rehydrate
    // on the node. If no id is provided it will return
    // a tree that's ready to be appended to the DOM.
    //
    // The rootId is determined to find the application root
    // on update. Since the DOM nodes change between updates,
    // we must call document.querySelector() to find the root.
    // Use different names when loading multiple choo applications
    // on the same page
    if (rootId) {
      document.addEventListener('DOMContentLoaded', function (event) {
        rootId = rootId.replace(/^#/, '')

        const oldTree = document.querySelector('#' + rootId)
        assert.ok(oldTree, 'could not find node #' + rootId)

        const newTree = _router(send.state().app.location, send.state(), send)

        yo.update(oldTree, newTree)
      })
    } else {
      rootId = name + '-root'
      const tree = _router(send.state().app.location, send.state(), send)
      tree.setAttribute('id', rootId)
      return tree
    }

    // handle an action by either reducers, effects
    // or both - return the new state when done
    // (obj, obj, fn) -> obj
    function handleAction (action, state, send) {
      var reducersCalled = false
      var effectsCalled = false
      const newState = xtend(state)

      // validate if a namespace exists. Namespaces
      // are delimited by the first ':'. Perhaps
      // we'll allow recursive namespaces in the
      // future - who knows
      if (/:/.test(action.type)) {
        const arr = action.type.split(':')
        var ns = arr.shift()
        action.type = arr.join(':')
      }

      const _reducers = ns ? reducers[ns] : reducers
      if (_reducers && _reducers[action.type]) {
        if (ns) {
          const reducedState = _reducers[action.type](action, state[ns])
          if (!newState[ns]) newState[ns] = {}
          mutate(newState[ns], xtend(state[ns], reducedState))
        } else {
          mutate(newState, reducers[action.type](action, state))
        }
        reducersCalled = true
      }

      const _effects = ns ? effects[ns] : effects
      if (_effects && _effects[action.type]) {
        if (ns) _effects[action.type](action, state[ns], send)
        else _effects[action.type](action, state, send)
        effectsCalled = true
      }

      if (!reducersCalled && !effectsCalled) {
        throw new Error('Could not find action ' + action.type)
      }

      // allows (newState === oldState) checks
      return (reducersCalled) ? newState : state
    }

    // update the DOM after every state mutation
    // (obj, obj) -> null
    function onchange (action, newState, oldState) {
      if (newState === oldState) return
      const oldTree = document.querySelector('#' + rootId)
      assert.ok(oldTree, "Could not find DOM node '#" + rootId + "' to update")
      const newTree = _router(newState.app.location, newState, send, oldState)
      newTree.setAttribute('id', rootId)
      yo.update(oldTree, newTree)
    }
  }

  // register all routes on the router
  // [obj|fn] -> null
  function router (cb) {
    _router = sheetRouter(cb)
    return _router
  }

  // create a new model
  // (str?, obj) -> null
  function model (model) {
    _models.push(model)
  }
}

// initial application state model
// obj -> obj
function appInit (opts) {
  const initialLocation = (opts.hash === true)
    ? hashMatch(document.location.hash)
    : document.location.href

  const model = {
    namespace: 'app',
    state: { location: initialLocation },
    subscriptions: [],
    reducers: {
      // handle href links
      location: function setLocation (action, state) {
        return {
          location: action.location.replace(/#.*/, '')
        }
      }
    }
  }

  // if hash routing explicitly enabled, subscribe to it
  if (opts.hash === true) {
    pushLocationSub(function (navigate) {
      hash(function (fragment) {
        navigate(hashMatch(fragment))
      })
    })
  // otherwise, subscribe to HTML5 history API
  } else {
    if (opts.history !== false) pushLocationSub(history)
    // enable catching  links
    if (opts.href !== false) pushLocationSub(href)
  }

  return model

  // create a new subscription that modifies
  // 'app:location' and push it to be loaded
  // fn -> null
  function pushLocationSub (cb) {
    model.subscriptions.push(function (send) {
      cb(function (href) {
        send('app:location', { location: href })
      })
    })
  }
}

// compose an object conditionally
// optionally contains a namespace
// which is used to nest properties.
// (str, obj, obj) -> null
function apply (ns, source, target) {
  Object.keys(source).forEach(function (key) {
    if (ns) {
      if (!target[ns]) target[ns] = {}
      target[ns][key] = source[key]
    } else target[key] = source[key]
  })
}
<commit_msg>Allow for naming the rootId with no options<commit_after>const history = require('sheet-router/history')
const sheetRouter = require('sheet-router')
const document = require('global/document')
const href = require('sheet-router/href')
const hash = require('sheet-router/hash')
const hashMatch = require('hash-match')
const sendAction = require('send-action')
const mutate = require('xtend/mutable')
const assert = require('assert')
const xtend = require('xtend')
const yo = require('yo-yo')

choo.view = yo
module.exports = choo

// framework for creating sturdy web applications
// null -> fn
function choo () {
  const _models = []
  var _router = null

  start.toString = toString
  start.router = router
  start.model = model
  start.start = start

  return start

  // render the application to a string
  // (str, obj) -> str
  function toString (route, serverState) {
    const initialState = {}
    const nsState = {}

    _models.forEach(function (model) {
      const ns = model.namespace
      if (ns) {
        if (!nsState[ns]) nsState[ns] = {}
        apply(ns, model.state, nsState)
        nsState[ns] = xtend(nsState[ns], serverState[ns])
      } else {
        apply(model.namespace, model.state, initialState)
      }
    })

    const state = xtend(initialState, xtend(serverState, nsState))
    const tree = _router(route, state, function () {
      throw new Error('send() cannot be called on the server')
    })

    return tree.toString()
  }

  // start the application
  // (str?, obj?) -> DOMNode
  function start (rootId, opts) {
    if (!opts && typeof rootId !== 'string') {
      opts = rootId
      rootId = null
    }
    opts = opts || {}
    const name = opts.name || 'choo'
    const initialState = {}
    const reducers = {}
    const effects = {}

    _models.push(appInit(opts))
    _models.forEach(function (model) {
      if (model.state) apply(model.namespace, model.state, initialState)
      if (model.reducers) apply(model.namespace, model.reducers, reducers)
      if (model.effects) apply(model.namespace, model.effects, effects)
    })

    // send() is used to trigger actions inside
    // views, effects and subscriptions
    const send = sendAction({
      onaction: handleAction,
      onchange: onchange,
      state: initialState
    })

    // subscriptions are loaded after sendAction() is called
    // because they both need access to send() and can't
    // react to actions (read-only)
    _models.forEach(function (model) {
      if (model.subscriptions) {
        assert.ok(Array.isArray(model.subscriptions, 'subs must be an array'))
        model.subscriptions.forEach(function (sub) {
          sub(send)
        })
      }
    })

    // If an id is provided, the application will rehydrate
    // on the node. If no id is provided it will return
    // a tree that's ready to be appended to the DOM.
    //
    // The rootId is determined to find the application root
    // on update. Since the DOM nodes change between updates,
    // we must call document.querySelector() to find the root.
    // Use different names when loading multiple choo applications
    // on the same page
    if (rootId) {
      document.addEventListener('DOMContentLoaded', function (event) {
        rootId = rootId.replace(/^#/, '')

        const oldTree = document.querySelector('#' + rootId)
        assert.ok(oldTree, 'could not find node #' + rootId)

        const newTree = _router(send.state().app.location, send.state(), send)

        yo.update(oldTree, newTree)
      })
    } else {
      rootId = name + '-root'
      const tree = _router(send.state().app.location, send.state(), send)
      tree.setAttribute('id', rootId)
      return tree
    }

    // handle an action by either reducers, effects
    // or both - return the new state when done
    // (obj, obj, fn) -> obj
    function handleAction (action, state, send) {
      var reducersCalled = false
      var effectsCalled = false
      const newState = xtend(state)

      // validate if a namespace exists. Namespaces
      // are delimited by the first ':'. Perhaps
      // we'll allow recursive namespaces in the
      // future - who knows
      if (/:/.test(action.type)) {
        const arr = action.type.split(':')
        var ns = arr.shift()
        action.type = arr.join(':')
      }

      const _reducers = ns ? reducers[ns] : reducers
      if (_reducers && _reducers[action.type]) {
        if (ns) {
          const reducedState = _reducers[action.type](action, state[ns])
          if (!newState[ns]) newState[ns] = {}
          mutate(newState[ns], xtend(state[ns], reducedState))
        } else {
          mutate(newState, reducers[action.type](action, state))
        }
        reducersCalled = true
      }

      const _effects = ns ? effects[ns] : effects
      if (_effects && _effects[action.type]) {
        if (ns) _effects[action.type](action, state[ns], send)
        else _effects[action.type](action, state, send)
        effectsCalled = true
      }

      if (!reducersCalled && !effectsCalled) {
        throw new Error('Could not find action ' + action.type)
      }

      // allows (newState === oldState) checks
      return (reducersCalled) ? newState : state
    }

    // update the DOM after every state mutation
    // (obj, obj) -> null
    function onchange (action, newState, oldState) {
      if (newState === oldState) return
      const oldTree = document.querySelector('#' + rootId)
      assert.ok(oldTree, "Could not find DOM node '#" + rootId + "' to update")
      const newTree = _router(newState.app.location, newState, send, oldState)
      newTree.setAttribute('id', rootId)
      yo.update(oldTree, newTree)
    }
  }

  // register all routes on the router
  // [obj|fn] -> null
  function router (cb) {
    _router = sheetRouter(cb)
    return _router
  }

  // create a new model
  // (str?, obj) -> null
  function model (model) {
    _models.push(model)
  }
}

// initial application state model
// obj -> obj
function appInit (opts) {
  const initialLocation = (opts.hash === true)
    ? hashMatch(document.location.hash)
    : document.location.href

  const model = {
    namespace: 'app',
    state: { location: initialLocation },
    subscriptions: [],
    reducers: {
      // handle href links
      location: function setLocation (action, state) {
        return {
          location: action.location.replace(/#.*/, '')
        }
      }
    }
  }

  // if hash routing explicitly enabled, subscribe to it
  if (opts.hash === true) {
    pushLocationSub(function (navigate) {
      hash(function (fragment) {
        navigate(hashMatch(fragment))
      })
    })
  // otherwise, subscribe to HTML5 history API
  } else {
    if (opts.history !== false) pushLocationSub(history)
    // enable catching  links
    if (opts.href !== false) pushLocationSub(href)
  }

  return model

  // create a new subscription that modifies
  // 'app:location' and push it to be loaded
  // fn -> null
  function pushLocationSub (cb) {
    model.subscriptions.push(function (send) {
      cb(function (href) {
        send('app:location', { location: href })
      })
    })
  }
}

// compose an object conditionally
// optionally contains a namespace
// which is used to nest properties.
// (str, obj, obj) -> null
function apply (ns, source, target) {
  Object.keys(source).forEach(function (key) {
    if (ns) {
      if (!target[ns]) target[ns] = {}
      target[ns][key] = source[key]
    } else target[key] = source[key]
  })
}
<|endoftext|>


<commit_before>/*!
 * async-helpers <https://github.com/doowb/async-helpers>
 *
 * Copyright (c) 2015, Brian Woodward.
 * Licensed under the MIT License.
 */

'use strict';

var typeOf = require('kind-of');
var stringify = require('safe-json-stringify');
var define = require('define-property');
var extend = require('extend-shallow');
var co = require('co');

/**
 * Caches
 */

var cache = {};
var stash = {};

/**
 * Create a new instance of AsyncHelpers
 *
 * ```js
 * var asyncHelpers = new AsyncHelpers();
 * ```
 *
 * @param {Object} `options` options to pass to instance
 * @return {Object} new AsyncHelpers instance
 * @api public
 */

function AsyncHelpers(options) {
  if (!(this instanceof AsyncHelpers)) {
    return new AsyncHelpers(options);
  }
  this.options = extend({}, options);
  this.prefix = this.options.prefix || '{$ASYNCID$';
  this.globalCounter = AsyncHelpers.globalCounter++;
  this.helpers = {};
  this.counter = 0;
  this.prefixRegex = toRegex(this.prefix);
}

/**
 * Keep track of instances created for generating globally
 * unique ids
 * @type {Number}
 */

AsyncHelpers.globalCounter = 0;
AsyncHelpers.cache = cache;
AsyncHelpers.stash = stash;

/**
 * Add a helper to the cache.
 *
 * ```js
 * asyncHelpers.set('upper', function(str, cb) {
 *   cb(null, str.toUpperCase());
 * });
 * ```
 *
 * @param {String} `name` Name of the helper
 * @param {Function} `fn` Helper function
 * @return {Object} Returns `this` for chaining
 * @api public
 */

AsyncHelpers.prototype.set = function(name, fn) {
  if (isObject(name)) {
    for (var key in name) {
      if (name.hasOwnProperty(key)) {
        this.set(key, name[key]);
      }
    }
    return this;
  }

  if (typeof name !== 'string') {
    throw new TypeError('AsyncHelpers#set: expected `name` to be a string');
  }
  if (typeof fn !== 'function' && !isObject(fn)) {
    throw new TypeError('AsyncHelpers#set: expected `fn` to be a function or object');
  }

  this.helpers[name] = fn;
  return this;
};

/**
 * Get all helpers or a helper with the given name.
 *
 * ```js
 * var helpers = asyncHelpers.get();
 * var wrappedHelpers = asyncHelpers.get({wrap: true});
 * ```
 *
 * @param  {String} `name` Optionally pass in a name of a helper to get.
 * @param  {Object} `options` Additional options to use.
 *   @option {Boolean} `wrap` Wrap the helper(s) with async processing capibilities
 * @return {Function|Object} Single helper function when `name` is provided, otherwise object of all helpers
 * @api public
 */

AsyncHelpers.prototype.get = function(helper, options) {
  if (typeof helper === 'string') {
    return this.wrapHelper(helper, options);
  }
  return this.wrapHelpers(this.helpers, helper);
};

/**
 * Wrap a helper with async handling capibilities.
 *
 * ```js
 * var wrappedHelper = asyncHelpers.wrap('upper');
 * var wrappedHelpers = asyncHelpers.wrap();
 * ```
 *
 * @param  {String} `helper` Optionally pass the name of the helper to wrap
 * @return {Function|Object} Single wrapped helper function when `name` is provided, otherwise object of all wrapped helpers.
 * @api public
 */

AsyncHelpers.prototype.wrapHelper = function(helper, options) {
  if (isObject(helper) && typeof options === 'undefined') {
    options = helper;
    helper = this.helpers;
  }

  options = options || {};
  helper = helper || this.helpers;

  var type = typeOf(helper);
  switch (type) {
    case 'string':
      return this.wrapHelper(this.helpers[helper], options);
    case 'object':
      return this.wrapHelpers(helper, options);
    case 'function':
      if (isHelperGroup(helper)) {
        return this.wrapHelpers(helper, options);
      }

      if (options.wrap && helper.wrapped !== true) {
        return this.wrapper(helper.name || helper.displayName, helper, this);
      }

      return helper;
    default: {
      throw new TypeError('AsyncHelpers.wrapHelper: unsupported type: ' + type);
    }
  }
};

/**
 * Wrap an object of helpers to enable async handling
 * @param  {Object} `helpers`
 * @param  {Object} `options`
 */

AsyncHelpers.prototype.wrapHelpers = function(helpers, options) {
  if (!isObject(helpers) && isHelperGroup(helpers)) {
    throw new TypeError('expected helpers to be an object');
  }

  var res = {};
  for (var key in helpers) {
    if (helpers.hasOwnProperty(key)) {
      var helper = helpers[key];
      if (isObject(helper)) {
        res[key] = this.wrapHelpers(helper, options);
      } else {
        if (helper.wrapped !== true) {
          if (typeOf(helper) === 'function' && !helper.name && !helper.displayName) {
            helper.displayName = key;
          }
          res[key] = this.wrapHelper(helper, options);
        } else {
          res[key] = helper;
        }
      }
    }
  }
  return res;
};

/**
 * Returns a wrapper function for a single helper.
 * @param  {String} `name` The name of the helper
 * @param  {Function} `fn` The actual helper function
 * @param  {Object} `context` Context
 * @return {String} Returns an async ID to use for resolving the value. ex: `{$ASYNCID$!$8$}`
 */

AsyncHelpers.prototype.wrapper = function(name, fn) {
  var prefix = appendPrefix(this.prefix, this.globalCounter);
  var self = this;

  // wrap the helper and generate a unique ID for resolving it
  function wrapper() {
    var num = self.counter++;
    var id = createId(prefix, num);

    var token = {
      name: name,
      async: !!fn.async,
      prefix: prefix,
      num: num,
      id: id,
      fn: fn,
      args: [].slice.call(arguments)
    };

    define(token, 'context', this);
    stash[id] = token;
    return id;
  }

  define(wrapper, 'wrapped', true);
  define(wrapper, 'helperName', name);
  return wrapper;
};

/**
 * Reset all the stashed helpers.
 *
 * ```js
 * asyncHelpers.reset();
 * ```
 * @return {Object} Returns `this` to enable chaining
 * @api public
 */

AsyncHelpers.prototype.reset = function() {
  stash = {};
  this.counter = 0;
  return this;
};

/**
 * Get all matching ids from the given `str`
 * @return {Array} Returns an array of matching ids
 */

AsyncHelpers.prototype.matches = function(str) {
  if (typeof str !== 'string') {
    throw new TypeError('AsyncHelpers#matches expects a string');
  }

  var matches = [];
  var match;
  var input = str;

  while ((match = this.prefixRegex.exec(input))) {
    matches.push(match[0]);
    input = input.slice(match.index + match[0].length);
  }

  return matches;
};

/**
 * Returns true if the given string has an async helper id
 * @return {Boolean}
 */

AsyncHelpers.prototype.hasAsyncId = function(str) {
  if (typeof str !== 'string') {
    throw new TypeError('AsyncHelpers#hasAsyncId expects a string');
  }
  return str.indexOf(this.prefix) !== -1;
};

/**
 * Resolve a stashed helper by the generated id.
 * This is a generator function and should be used with [co][]
 *
 * ```js
 * var upper = asyncHelpers.get('upper', {wrap: true});
 * var id = upper('doowb');
 *
 * co(asyncHelpers.resolveId(id))
 *   .then(console.log)
 *   .catch(console.error);
 *
 * //=> DOOWB
 * ```
 *
 * @param  {String} `key` ID generated when from executing a wrapped helper.
 * @api public
 */

AsyncHelpers.prototype.resolveId = function * (key) {
  if (typeof key !== 'string') {
    throw new Error('AsyncHelpers#resolveId: expects `key` to be a string.');
  }

  var helper = stash[key];
  if (!helper) {
    throw new Error('AsyncHelpers#resolveId: cannot resolve helper: "' + key + '"');
  }

  var args = yield this.resolveArgs(helper);
  var self = this;
  var str;

  return yield function(cb) {
    if (typeof helper.fn !== 'function') {
      cb(null, helper.fn);
      return;
    }

    var next = function(err, val) {
      if (typeof val !== 'undefined') {
        helper.fn = val;
        cb(err, helper.fn);
        return;
      }
      cb(err, '');
      return;
    };

    if (helper.fn.async) {
      var callback = function(err, result) {
        if (err) {
          next(formatError(err, helper, args));
          return;
        }

        if (typeof result === 'string' && self.hasAsyncId(result)) {
          self.resolveIds(result, next);
          return;
        }

        next(null, result);
        return;
      };

      args.push(callback);
    }

    try {
      str = helper.fn.apply(helper.context, args);
      if (typeof str === 'string' && self.hasAsyncId(str)) {
        self.resolveIds(str, next);
        return;
      }
    } catch (err) {
      next(formatError(err, helper, args));
      return;
    }

    if (!helper.fn.async) {
      next(null, str);
      return;
    }

    // do nothing
  };
};

/**
 * Generator function for resolving helper arguments
 * that contain async ids. This function should be used
 * with [co][].
 *
 * This is used inside `resolveId`:
 *
 * ```js
 * var args = yield co(asyncHelpers.resolveArgs(helper));
 * ```
 * @param {Object} `helper` helper object with an `argRefs` array.
 */

AsyncHelpers.prototype.resolveArgs = function * (helper) {
  for (var i = 0; i < helper.args.length; i++) {
    var arg = helper.args[i];
    if (!arg) continue;

    if (typeof arg === 'string' && this.hasAsyncId(arg)) {
      helper.args[i] = yield this.resolveId(arg);

    } else if (isObject(arg) && isObject(arg.hash)) {
      arg.hash = yield this.resolveObject(arg.hash);
    }
  }
  return helper.args;
};

/**
 * Generator function for resolving values on an object
 * that contain async ids. This function should be used
 * with [co][].
 *
 * This is used inside `resolveArgs`:
 *
 * ```js
 * var args = yield co(asyncHelpers.resolveObject(options.hash));
 * ```
 * @param {Object} `obj` object with with values that may be async ids.
 * @returns {Object} Object with resolved values.
 */

AsyncHelpers.prototype.resolveObject = function * (obj) {
  var keys = Object.keys(obj);
  var self = this;

  return yield keys.reduce(function(acc, key) {
    return co(function * () {
      var val = acc[key];
      if (typeof val === 'string' && self.hasAsyncId(val)) {
        acc[key] = yield self.resolveId(val);
      }
      return acc;
    });
  }, obj);
};

/**
 * After rendering a string using wrapped async helpers,
 * use `resolveIds` to invoke the original async helpers and replace
 * the async ids with results from the async helpers.
 *
 * ```js
 * asyncHelpers.resolveIds(renderedString, function(err, content) {
 *   if (err) return console.error(err);
 *   console.log(content);
 * });
 * ```
 * @param  {String} `str` String containing async ids
 * @param  {Function} `cb` Callback function accepting an `err` and `content` parameters.
 * @api public
 */

AsyncHelpers.prototype.resolveIds = function(str, cb) {
  if (typeof cb !== 'function') {
    throw new TypeError('AsyncHelpers#resolveIds() expects a callback function.');
  }
  if (typeof str !== 'string') {
    return cb(new TypeError('AsyncHelpers#resolveIds() expects a string.'));
  }

  var matches = this.matches(str);

  var self = this;
  co(function * () {
    if (!matches) {
      return str;
    };

    for (var i = 0; i < matches.length; i++) {
      var key = matches[i];
      var val = yield self.resolveId(key);
      str = str.split(key).join(val);
    }
    return str;
  })
  .then(function(res) {
    cb(null, res);
  })
  .catch(cb);
};

/**
 * Format an error message to provide better information about the
 * helper and the arguments passed to the helper when the error occurred.
 *
 * @param  {Object} `err` Error object
 * @param  {Object} `helper` helper object to provide more information
 * @param  {Array} `args` Array of arguments passed to the helper.
 * @return {Object} Formatted Error object
 */

function formatError(err, helper, args) {
  args = args.filter(function(arg) {
    if (!arg || typeof arg === 'function') {
      return false;
    }
    return true;
  }).map(function(arg) {
    return stringify(arg);
  });

  err.reason = '"' + helper.name
    + '" helper cannot resolve: `'
    + args.join(', ') + '`';

  err.helper = helper;
  err.args = args;
  return err;
}

/**
 * Create a prefix to use when generating an async id.
 *
 * @param  {String} `prefix` prefix string to start with.
 * @param  {String} `counter` string to append.
 * @return {String} new prefix
 */

function appendPrefix(prefix, counter) {
  return prefix + counter + '$';
}

/**
 * Create an async id from the provided prefix and counter.
 *
 * @param  {String} `prefix` prefix string to start with
 * @param  {String} `counter` string to append.
 * @return {String} async id
 */

function createId(prefix, counter) {
  return appendPrefix(prefix, counter) + '}';
}

/**
 * Create a regular expression based on the given `prefix`.
 * @param  {String} `prefix`
 * @return {RegExp}
 */

function toRegex(prefix) {
  var key = appendPrefix(prefix, '(\\d+)');
  if (cache.hasOwnProperty(key)) {
    return cache[key];
  }
  var regex = new RegExp(createRegexString(key));
  cache[key] = regex;
  return regex;
}

/**
 * Create a string to pass into `RegExp` for checking for and finding async ids.
 * @param  {String} `prefix` prefix to use for the first part of the regex
 * @return {String} string to pass into `RegExp`
 */

function createRegexString(prefix) {
  var key = 'createRegexString:' + prefix;
  if (cache.hasOwnProperty(key)) {
    return cache[key];
  }
  var str = (prefix + '(\\d+)$}').replace(/\\?([${}])/g, '\\$1');
  cache[key] = str;
  return str;
}

/**
 * Return true if the given value is a helper "group"
 */

function isHelperGroup(helpers) {
  if (!helpers) return false;
  if (helpers.isGroup) {
    return true;
  }
  if (typeof helpers === 'function' || isObject(helpers)) {
    var len = Object.keys(helpers).length;
    var min = ['async', 'sync', 'displayName'].reduce(function(acc, name) {
      acc += (helpers[name] ? 1 : 0);
      return acc;
    }, 0);
    return len > min;
  }
  return false;
}

/**
 * Return true if the given value is an object
 */

function isObject(val) {
  return typeOf(val) === 'object';
}

/**
 * Expose `AsyncHelpers`
 */

module.exports = AsyncHelpers;

<commit_msg>update count logic<commit_after>/*!
 * async-helpers <https://github.com/doowb/async-helpers>
 *
 * Copyright (c) 2015, Brian Woodward.
 * Licensed under the MIT License.
 */

'use strict';

var typeOf = require('kind-of');
var stringify = require('safe-json-stringify');
var define = require('define-property');
var extend = require('extend-shallow');
var co = require('co');

/**
 * Caches
 */

var cache = {};
var stash = {};

/**
 * Create a new instance of AsyncHelpers
 *
 * ```js
 * var asyncHelpers = new AsyncHelpers();
 * ```
 *
 * @param {Object} `options` options to pass to instance
 * @return {Object} new AsyncHelpers instance
 * @api public
 */

function AsyncHelpers(options) {
  if (!(this instanceof AsyncHelpers)) {
    return new AsyncHelpers(options);
  }
  this.options = extend({}, options);
  this.prefix = this.options.prefix || '{$ASYNCID$';
  this.globalCounter = AsyncHelpers.globalCounter++;
  this.helpers = {};
  this.counter = 0;
  this.prefixRegex = toRegex(this.prefix);
}

/**
 * Keep track of instances created for generating globally
 * unique ids
 * @type {Number}
 */

AsyncHelpers.globalCounter = 0;
AsyncHelpers.cache = cache;
AsyncHelpers.stash = stash;

/**
 * Add a helper to the cache.
 *
 * ```js
 * asyncHelpers.set('upper', function(str, cb) {
 *   cb(null, str.toUpperCase());
 * });
 * ```
 *
 * @param {String} `name` Name of the helper
 * @param {Function} `fn` Helper function
 * @return {Object} Returns `this` for chaining
 * @api public
 */

AsyncHelpers.prototype.set = function(name, fn) {
  if (isObject(name)) {
    for (var key in name) {
      if (name.hasOwnProperty(key)) {
        this.set(key, name[key]);
      }
    }
    return this;
  }

  if (typeof name !== 'string') {
    throw new TypeError('AsyncHelpers#set: expected `name` to be a string');
  }
  if (typeof fn !== 'function' && !isObject(fn)) {
    throw new TypeError('AsyncHelpers#set: expected `fn` to be a function or object');
  }

  this.helpers[name] = fn;
  return this;
};

/**
 * Get all helpers or a helper with the given name.
 *
 * ```js
 * var helpers = asyncHelpers.get();
 * var wrappedHelpers = asyncHelpers.get({wrap: true});
 * ```
 *
 * @param  {String} `name` Optionally pass in a name of a helper to get.
 * @param  {Object} `options` Additional options to use.
 *   @option {Boolean} `wrap` Wrap the helper(s) with async processing capibilities
 * @return {Function|Object} Single helper function when `name` is provided, otherwise object of all helpers
 * @api public
 */

AsyncHelpers.prototype.get = function(helper, options) {
  if (typeof helper === 'string') {
    return this.wrapHelper(helper, options);
  }
  return this.wrapHelpers(this.helpers, helper);
};

/**
 * Wrap a helper with async handling capibilities.
 *
 * ```js
 * var wrappedHelper = asyncHelpers.wrap('upper');
 * var wrappedHelpers = asyncHelpers.wrap();
 * ```
 *
 * @param  {String} `helper` Optionally pass the name of the helper to wrap
 * @return {Function|Object} Single wrapped helper function when `name` is provided, otherwise object of all wrapped helpers.
 * @api public
 */

AsyncHelpers.prototype.wrapHelper = function(helper, options) {
  if (isObject(helper) && typeof options === 'undefined') {
    options = helper;
    helper = this.helpers;
  }

  options = options || {};
  helper = helper || this.helpers;

  var type = typeOf(helper);
  switch (type) {
    case 'string':
      return this.wrapHelper(this.helpers[helper], options);
    case 'object':
      return this.wrapHelpers(helper, options);
    case 'function':
      if (isHelperGroup(helper)) {
        return this.wrapHelpers(helper, options);
      }

      if (options.wrap && helper.wrapped !== true) {
        return this.wrapper(helper.name || helper.displayName, helper, this);
      }

      return helper;
    default: {
      throw new TypeError('AsyncHelpers.wrapHelper: unsupported type: ' + type);
    }
  }
};

/**
 * Wrap an object of helpers to enable async handling
 * @param  {Object} `helpers`
 * @param  {Object} `options`
 */

AsyncHelpers.prototype.wrapHelpers = function(helpers, options) {
  if (!isObject(helpers) && isHelperGroup(helpers)) {
    throw new TypeError('expected helpers to be an object');
  }

  var res = {};
  for (var key in helpers) {
    if (helpers.hasOwnProperty(key)) {
      var helper = helpers[key];
      if (isObject(helper)) {
        res[key] = this.wrapHelpers(helper, options);
      } else {
        if (helper.wrapped !== true) {
          if (typeOf(helper) === 'function' && !helper.name && !helper.displayName) {
            helper.displayName = key;
          }
          res[key] = this.wrapHelper(helper, options);
        } else {
          res[key] = helper;
        }
      }
    }
  }
  return res;
};

/**
 * Returns a wrapper function for a single helper.
 * @param  {String} `name` The name of the helper
 * @param  {Function} `fn` The actual helper function
 * @param  {Object} `context` Context
 * @return {String} Returns an async ID to use for resolving the value. ex: `{$ASYNCID$!$8$}`
 */

AsyncHelpers.prototype.wrapper = function(name, fn) {
  var prefix = appendPrefix(this.prefix, this.globalCounter);
  var self = this;

  // wrap the helper and generate a unique ID for resolving it
  function wrapper() {
    var num = self.counter++;
    var id = createId(prefix, num);

    var token = {
      name: name,
      async: !!fn.async,
      prefix: prefix,
      num: num,
      id: id,
      fn: fn,
      args: [].slice.call(arguments)
    };

    define(token, 'context', this);
    stash[id] = token;
    return id;
  }

  define(wrapper, 'wrapped', true);
  define(wrapper, 'helperName', name);
  return wrapper;
};

/**
 * Reset all the stashed helpers.
 *
 * ```js
 * asyncHelpers.reset();
 * ```
 * @return {Object} Returns `this` to enable chaining
 * @api public
 */

AsyncHelpers.prototype.reset = function() {
  stash = {};
  this.counter = 0;
  return this;
};

/**
 * Get all matching ids from the given `str`
 * @return {Array} Returns an array of matching ids
 */

AsyncHelpers.prototype.matches = function(str) {
  if (typeof str !== 'string') {
    throw new TypeError('AsyncHelpers#matches expects a string');
  }

  var matches = [];
  var match;
  var input = str;

  while ((match = this.prefixRegex.exec(input))) {
    matches.push(match[0]);
    input = input.slice(match.index + match[0].length);
  }

  return matches;
};

/**
 * Returns true if the given string has an async helper id
 * @return {Boolean}
 */

AsyncHelpers.prototype.hasAsyncId = function(str) {
  if (typeof str !== 'string') {
    throw new TypeError('AsyncHelpers#hasAsyncId expects a string');
  }
  return str.indexOf(this.prefix) !== -1;
};

/**
 * Resolve a stashed helper by the generated id.
 * This is a generator function and should be used with [co][]
 *
 * ```js
 * var upper = asyncHelpers.get('upper', {wrap: true});
 * var id = upper('doowb');
 *
 * co(asyncHelpers.resolveId(id))
 *   .then(console.log)
 *   .catch(console.error);
 *
 * //=> DOOWB
 * ```
 *
 * @param  {String} `key` ID generated when from executing a wrapped helper.
 * @api public
 */

AsyncHelpers.prototype.resolveId = function * (key) {
  if (typeof key !== 'string') {
    throw new Error('AsyncHelpers#resolveId: expects `key` to be a string.');
  }

  var helper = stash[key];
  if (!helper) {
    throw new Error('AsyncHelpers#resolveId: cannot resolve helper: "' + key + '"');
  }

  var args = yield this.resolveArgs(helper);
  var self = this;
  var str;

  return yield function(cb) {
    if (typeof helper.fn !== 'function') {
      cb(null, helper.fn);
      return;
    }

    var next = function(err, val) {
      if (typeof val !== 'undefined') {
        helper.fn = val;
        cb(err, helper.fn);
        return;
      }
      cb(err, '');
      return;
    };

    if (helper.fn.async) {
      var callback = function(err, result) {
        if (err) {
          next(formatError(err, helper, args));
          return;
        }

        if (typeof result === 'string' && self.hasAsyncId(result)) {
          self.resolveIds(result, next);
          return;
        }

        next(null, result);
        return;
      };

      args.push(callback);
    }

    try {
      str = helper.fn.apply(helper.context, args);
      if (typeof str === 'string' && self.hasAsyncId(str)) {
        self.resolveIds(str, next);
        return;
      }
    } catch (err) {
      next(formatError(err, helper, args));
      return;
    }

    if (!helper.fn.async) {
      next(null, str);
      return;
    }

    // do nothing
  };
};

/**
 * Generator function for resolving helper arguments
 * that contain async ids. This function should be used
 * with [co][].
 *
 * This is used inside `resolveId`:
 *
 * ```js
 * var args = yield co(asyncHelpers.resolveArgs(helper));
 * ```
 * @param {Object} `helper` helper object with an `argRefs` array.
 */

AsyncHelpers.prototype.resolveArgs = function * (helper) {
  for (var i = 0; i < helper.args.length; i++) {
    var arg = helper.args[i];
    if (!arg) continue;

    if (typeof arg === 'string' && this.hasAsyncId(arg)) {
      helper.args[i] = yield this.resolveId(arg);

    } else if (isObject(arg) && isObject(arg.hash)) {
      arg.hash = yield this.resolveObject(arg.hash);
    }
  }
  return helper.args;
};

/**
 * Generator function for resolving values on an object
 * that contain async ids. This function should be used
 * with [co][].
 *
 * This is used inside `resolveArgs`:
 *
 * ```js
 * var args = yield co(asyncHelpers.resolveObject(options.hash));
 * ```
 * @param {Object} `obj` object with with values that may be async ids.
 * @returns {Object} Object with resolved values.
 */

AsyncHelpers.prototype.resolveObject = function * (obj) {
  var keys = Object.keys(obj);
  var self = this;

  return yield keys.reduce(function(acc, key) {
    return co(function * () {
      var val = acc[key];
      if (typeof val === 'string' && self.hasAsyncId(val)) {
        acc[key] = yield self.resolveId(val);
      }
      return acc;
    });
  }, obj);
};

/**
 * After rendering a string using wrapped async helpers,
 * use `resolveIds` to invoke the original async helpers and replace
 * the async ids with results from the async helpers.
 *
 * ```js
 * asyncHelpers.resolveIds(renderedString, function(err, content) {
 *   if (err) return console.error(err);
 *   console.log(content);
 * });
 * ```
 * @param  {String} `str` String containing async ids
 * @param  {Function} `cb` Callback function accepting an `err` and `content` parameters.
 * @api public
 */

AsyncHelpers.prototype.resolveIds = function(str, cb) {
  if (typeof cb !== 'function') {
    throw new TypeError('AsyncHelpers#resolveIds() expects a callback function.');
  }
  if (typeof str !== 'string') {
    return cb(new TypeError('AsyncHelpers#resolveIds() expects a string.'));
  }

  var matches = this.matches(str);

  var self = this;
  co(function * () {
    if (!matches) {
      return str;
    };

    for (var i = 0; i < matches.length; i++) {
      var key = matches[i];
      var val = yield self.resolveId(key);
      str = str.split(key).join(val);
    }
    return str;
  })
  .then(function(res) {
    cb(null, res);
  })
  .catch(cb);
};

/**
 * Format an error message to provide better information about the
 * helper and the arguments passed to the helper when the error occurred.
 *
 * @param  {Object} `err` Error object
 * @param  {Object} `helper` helper object to provide more information
 * @param  {Array} `args` Array of arguments passed to the helper.
 * @return {Object} Formatted Error object
 */

function formatError(err, helper, args) {
  args = args.filter(function(arg) {
    if (!arg || typeof arg === 'function') {
      return false;
    }
    return true;
  }).map(function(arg) {
    return stringify(arg);
  });

  err.reason = '"' + helper.name
    + '" helper cannot resolve: `'
    + args.join(', ') + '`';

  err.helper = helper;
  err.args = args;
  return err;
}

/**
 * Create a prefix to use when generating an async id.
 *
 * @param  {String} `prefix` prefix string to start with.
 * @param  {String} `counter` string to append.
 * @return {String} new prefix
 */

function appendPrefix(prefix, counter) {
  return prefix + counter + '$';
}

/**
 * Create an async id from the provided prefix and counter.
 *
 * @param  {String} `prefix` prefix string to start with
 * @param  {String} `counter` string to append.
 * @return {String} async id
 */

function createId(prefix, counter) {
  return appendPrefix(prefix, counter) + '}';
}

/**
 * Create a regular expression based on the given `prefix`.
 * @param  {String} `prefix`
 * @return {RegExp}
 */

function toRegex(prefix) {
  var key = appendPrefix(prefix, '(\\d+)');
  if (cache.hasOwnProperty(key)) {
    return cache[key];
  }
  var regex = new RegExp(createRegexString(key));
  cache[key] = regex;
  return regex;
}

/**
 * Create a string to pass into `RegExp` for checking for and finding async ids.
 * @param  {String} `prefix` prefix to use for the first part of the regex
 * @return {String} string to pass into `RegExp`
 */

function createRegexString(prefix) {
  var key = 'createRegexString:' + prefix;
  if (cache.hasOwnProperty(key)) {
    return cache[key];
  }
  var str = (prefix + '(\\d+)$}').replace(/\\?([${}])/g, '\\$1');
  cache[key] = str;
  return str;
}

/**
 * Return true if the given value is a helper "group"
 */

function isHelperGroup(helpers) {
  if (!helpers) return false;
  if (helpers.isGroup) {
    return true;
  }
  if (typeof helpers === 'function' || isObject(helpers)) {
    var keys = Object.keys(helpers).filter(function(name) {
      return ['async', 'sync', 'displayName'].indexOf(name) === -1;
    });
    return keys.length > 1;
  }
  return false;
}

/**
 * Return true if the given value is an object
 */

function isObject(val) {
  return typeOf(val) === 'object';
}

/**
 * Expose `AsyncHelpers`
 */

module.exports = AsyncHelpers;

<|endoftext|>


<commit_before>/* jshint node: true */

'use strict';

/**
# rtc/detect

A browser detection helper for accessing prefix-free versions of the various
WebRTC types.

## Example Usage

If you wanted to get the native `RTCPeerConnection` prototype in any browser
you could do the following:

```js
var detect = require('rtc/detect');
var RTCPeerConnection = detect('RTCPeerConnection');
```

This would provide whatever the browser prefixed version of the
RTCPeerConnection is available (`webkitRTCPeerConnection`,
`mozRTCPeerConnection`, etc).
**/
module.exports = function(target, prefixes) {
  var prefixIdx, prefix, testName;

  // initialise to default prefixes
  // (reverse order as we use a decrementing for loop)
  prefixes = (prefixes || ['ms', 'o', 'moz', 'webkit']).concat('');

  // iterate through the prefixes and return the class if found in global
  for (prefixIdx = prefixes.length; prefixIdx--; ) {
    prefix = prefixes[prefixIdx];

    // construct the test class name
    // if we have a prefix ensure the target has an uppercase first character
    // such that a test for getUserMedia would result in a
    // search for webkitGetUserMedia
    testName = prefix + (prefix ?
                            target.charAt(0).toUpperCase() + target.slice(1) :
                            target);

    if (typeof this[testName] == 'function') {
      return this[testName];
    }
  }
};<commit_msg>Fixed detect script<commit_after>/* jshint node: true */

'use strict';

/**
# rtc/detect

A browser detection helper for accessing prefix-free versions of the various
WebRTC types.

## Example Usage

If you wanted to get the native `RTCPeerConnection` prototype in any browser
you could do the following:

```js
var detect = require('rtc/detect');
var RTCPeerConnection = detect('RTCPeerConnection');
```

This would provide whatever the browser prefixed version of the
RTCPeerConnection is available (`webkitRTCPeerConnection`,
`mozRTCPeerConnection`, etc).
**/
module.exports = function(target, prefixes) {
  var prefixIdx;
  var prefix;
  var testName;
  var hostObject = this || window;

  // initialise to default prefixes
  // (reverse order as we use a decrementing for loop)
  prefixes = (prefixes || ['ms', 'o', 'moz', 'webkit']).concat('');

  // iterate through the prefixes and return the class if found in global
  for (prefixIdx = prefixes.length; prefixIdx--; ) {
    prefix = prefixes[prefixIdx];

    // construct the test class name
    // if we have a prefix ensure the target has an uppercase first character
    // such that a test for getUserMedia would result in a
    // search for webkitGetUserMedia
    testName = prefix + (prefix ?
                            target.charAt(0).toUpperCase() + target.slice(1) :
                            target);

    if (typeof hostObject[testName] == 'function') {
      return hostObject[testName];
    }
  }
};<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var walkSync = require('walk-sync');
var Minimatch = require('minimatch').Minimatch;
var arrayEqual = require('array-equal');
var Plugin = require('broccoli-plugin');
var symlinkOrCopy = require('symlink-or-copy');
var debug = require('debug');

function makeDictionary() {
  var cache = Object.create(null);

  cache['_dict'] = null;
  delete cache['_dict'];
  return cache;
}
// copied mostly from node-glob cc @isaacs
function isNotAPattern(pattern) {
  var set = new Minimatch(pattern).set;
  if (set.length > 1) {
    return false;
  }

  for (var j = 0; j < set[0].length; j++) {
    if (typeof set[0][j] !== 'string') {
      return false;
    }
  }

  return true;
}

Funnel.prototype = Object.create(Plugin.prototype);
Funnel.prototype.constructor = Funnel;
function Funnel(inputNode, options) {
  if (!(this instanceof Funnel)) { return new Funnel(inputNode, options); }

  Plugin.call(this, [inputNode]);

  this._includeFileCache = makeDictionary();
  this._destinationPathCache = makeDictionary();

  var keys = Object.keys(options || {});
  for (var i = 0, l = keys.length; i < l; i++) {
    var key = keys[i];
    this[key] = options[key];
  }

  this.destDir = this.destDir || '/';

  if (this.files && typeof this.files === 'function') {
    // Save dynamic files func as a different variable and let the rest of the code
    // still assume that this.files is always an array.
    this._dynamicFilesFunc = this.files;
    delete this.files;
  } else if (this.files && !Array.isArray(this.files)) {
    throw new Error('Invalid files option, it must be an array or function (that returns an array).');
  }

  if ((this.files || this._dynamicFilesFunc) && (this.include || this.exclude)) {
    throw new Error('Cannot pass files option (array or function) and a include/exlude filter. You can only have one or the other');
  }

  if (this.files) {
    if (this.files.filter(isNotAPattern).length !== this.files.length) {
      console.warn('broccoli-funnel does not support `files:` option with globs, please use `include:` instead');
      this.include = this.files;
      this.files = undefined;
    }
  }

  this._setupFilter('include');
  this._setupFilter('exclude');

  this._matchedWalk = this.include && this.include.filter(function(a) {
    return a instanceof Minimatch;
  }).length === this.include.length;

  this._instantiatedStack = (new Error()).stack;
  this._buildStart = undefined;
}

Funnel.prototype._debug = function(message) {
  debug('broccoli-funnel:' + (this.description || this.name || this.constructor.name)).apply(null, arguments);
};

Funnel.prototype._setupFilter = function(type) {
  if (!this[type]) {
    return;
  }

  if (!Array.isArray(this[type])) {
    throw new Error('Invalid ' + type + ' option, it must be an array. You specified `' + typeof this[type] + '`.');
  }

  // Clone the filter array so we are not mutating an external variable
  var filters = this[type] = this[type].slice(0);

  for (var i = 0, l = filters.length; i < l; i++) {
    filters[i] = this._processPattern(filters[i]);
  }
};

Funnel.prototype._processPattern = function(pattern) {
  if (pattern instanceof RegExp) {
    return pattern;
  }

  var type = typeof pattern;

  if (type === 'string') {
    return new Minimatch(pattern);
  }

  if (type === 'function') {
    return pattern;
  }

  throw new Error('include/exclude patterns can be a RegExp, glob string, or function. You supplied `' + typeof pattern +'`.');
};

Funnel.prototype.shouldLinkRoots = function() {
  return !this.files && !this.include && !this.exclude && !this.getDestinationPath;
};

Funnel.prototype.build = function() {
  this._buildStart = new Date();
  this.destPath = path.join(this.outputPath, this.destDir);
  if (this.destPath[this.destPath.length -1] === '/') {
    this.destPath = this.destPath.slice(0, -1);
  }

  var inputPath = this.inputPaths[0];
  if (this.srcDir) {
    inputPath = path.join(inputPath, this.srcDir);
  }

  if (this._dynamicFilesFunc) {
    this.lastFiles = this.files;
    this.files = this._dynamicFilesFunc() || [];

    // Blow away the include cache if the list of files is new
    if (this.lastFiles !== undefined && !arrayEqual(this.lastFiles, this.files)) {
      this._includeFileCache = makeDictionary();
    }
  }

  var linkedRoots = false;
  if (this.shouldLinkRoots()) {
    linkedRoots = true;
    if (fs.existsSync(inputPath)) {
      fs.rmdirSync(this.outputPath);
      this._copy(inputPath, this.destPath);
    } else if (this.allowEmpty) {
      mkdirp.sync(this.destPath);
    }
  } else {
    this.processFilters(inputPath);
  }

  this._debug('build, %o', {
    in: new Date() - this._buildStart + 'ms',
    linkedRoots: linkedRoots,
    inputPath: inputPath,
    destPath: this.destPath
  });
};

Funnel.prototype.processFilters = function(inputPath) {
  var files;

  if (this.files && !this.exclude && !this.include) {
    files = this.files.slice(0); //clone to be compatible with walkSync
  } else {
    if (this._matchedWalk) {
      files = walkSync(inputPath, undefined, this.include);
    } else {
      files = walkSync(inputPath);
    }
  }

  var relativePath, destRelativePath, fullInputPath, fullOutputPath;

  var count = 0;
  for (var i = 0, l = files.length; i < l; i++) {
    relativePath = files[i];

    if (this.includeFile(relativePath)) {
      count++;
      fullInputPath    = path.join(inputPath, relativePath);
      destRelativePath = this.lookupDestinationPath(relativePath);
      fullOutputPath   = path.join(this.destPath, destRelativePath);

      this.processFile(fullInputPath, fullOutputPath, relativePath);
    }
  }

  this._debug('processFilters %o', {
    in: new Date() - this._buildStart + 'ms',
    filesFound: files.length,
    filesProcessed: count,
    inputPath: inputPath,
    destPath: this.destPath
  });
};

Funnel.prototype.lookupDestinationPath = function(relativePath) {
  if (this._destinationPathCache[relativePath] !== undefined) {
    return this._destinationPathCache[relativePath];
  }

  if (this.getDestinationPath) {
    return this._destinationPathCache[relativePath] = this.getDestinationPath(relativePath);
  }

  return this._destinationPathCache[relativePath] = relativePath;
};

Funnel.prototype.includeFile = function(relativePath) {
  var includeFileCache = this._includeFileCache;

  if (includeFileCache[relativePath] !== undefined) {
    return includeFileCache[relativePath];
  }

  // do not include directories, only files
  if (relativePath[relativePath.length - 1] === '/') {
    return includeFileCache[relativePath] = false;
  }

  var i, l, pattern;

  // Check for specific files listing
  if (this.files) {
    return includeFileCache[relativePath] = this.files.indexOf(relativePath) > -1;
  }

  // Check exclude patterns
  if (this.exclude) {
    for (i = 0, l = this.exclude.length; i < l; i++) {
      // An exclude pattern that returns true should be ignored
      pattern = this.exclude[i];

      if (this._matchesPattern(pattern, relativePath)) {
        return includeFileCache[relativePath] = false;
      }
    }
  }

  // Check include patterns
  if (this.include && this.include.length > 0) {
    for (i = 0, l = this.include.length; i < l; i++) {
      // An include pattern that returns true (and wasn't excluded at all)
      // should _not_ be ignored
      pattern = this.include[i];

      if (this._matchesPattern(pattern, relativePath)) {
        return includeFileCache[relativePath] = true;
      }
    }

    // If no include patterns were matched, ignore this file.
    return includeFileCache[relativePath] = false;
  }

  // Otherwise, don't ignore this file
  return includeFileCache[relativePath] = true;
};

Funnel.prototype._matchesPattern = function(pattern, relativePath) {
  if (pattern instanceof RegExp) {
    return pattern.test(relativePath);
  } else if (pattern instanceof Minimatch) {
    return pattern.match(relativePath);
  } else if (typeof pattern === 'function') {
    return pattern(relativePath);
  }

  throw new Error('Pattern `' + pattern + '` was not a RegExp, Glob, or Function.');
};

Funnel.prototype.processFile = function(sourcePath, destPath /*, relativePath */) {
  this._copy(sourcePath, destPath);
};

Funnel.prototype._copy = function(sourcePath, destPath) {
  var destDir  = path.dirname(destPath);
  if (!fs.existsSync(destDir)) {
    mkdirp.sync(destDir);
  }

  symlinkOrCopy.sync(sourcePath, destPath);
};

module.exports = Funnel;
<commit_msg>walk-sync has a slightly different API<commit_after>'use strict';

var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var walkSync = require('walk-sync');
var Minimatch = require('minimatch').Minimatch;
var arrayEqual = require('array-equal');
var Plugin = require('broccoli-plugin');
var symlinkOrCopy = require('symlink-or-copy');
var debug = require('debug');

function makeDictionary() {
  var cache = Object.create(null);

  cache['_dict'] = null;
  delete cache['_dict'];
  return cache;
}
// copied mostly from node-glob cc @isaacs
function isNotAPattern(pattern) {
  var set = new Minimatch(pattern).set;
  if (set.length > 1) {
    return false;
  }

  for (var j = 0; j < set[0].length; j++) {
    if (typeof set[0][j] !== 'string') {
      return false;
    }
  }

  return true;
}

Funnel.prototype = Object.create(Plugin.prototype);
Funnel.prototype.constructor = Funnel;
function Funnel(inputNode, options) {
  if (!(this instanceof Funnel)) { return new Funnel(inputNode, options); }

  Plugin.call(this, [inputNode]);

  this._includeFileCache = makeDictionary();
  this._destinationPathCache = makeDictionary();

  var keys = Object.keys(options || {});
  for (var i = 0, l = keys.length; i < l; i++) {
    var key = keys[i];
    this[key] = options[key];
  }

  this.destDir = this.destDir || '/';

  if (this.files && typeof this.files === 'function') {
    // Save dynamic files func as a different variable and let the rest of the code
    // still assume that this.files is always an array.
    this._dynamicFilesFunc = this.files;
    delete this.files;
  } else if (this.files && !Array.isArray(this.files)) {
    throw new Error('Invalid files option, it must be an array or function (that returns an array).');
  }

  if ((this.files || this._dynamicFilesFunc) && (this.include || this.exclude)) {
    throw new Error('Cannot pass files option (array or function) and a include/exlude filter. You can only have one or the other');
  }

  if (this.files) {
    if (this.files.filter(isNotAPattern).length !== this.files.length) {
      console.warn('broccoli-funnel does not support `files:` option with globs, please use `include:` instead');
      this.include = this.files;
      this.files = undefined;
    }
  }

  this._setupFilter('include');
  this._setupFilter('exclude');

  this._matchedWalk = this.include && this.include.filter(function(a) {
    return a instanceof Minimatch;
  }).length === this.include.length;

  this._instantiatedStack = (new Error()).stack;
  this._buildStart = undefined;
}

Funnel.prototype._debug = function(message) {
  debug('broccoli-funnel:' + (this.description || this.name || this.constructor.name)).apply(null, arguments);
};

Funnel.prototype._setupFilter = function(type) {
  if (!this[type]) {
    return;
  }

  if (!Array.isArray(this[type])) {
    throw new Error('Invalid ' + type + ' option, it must be an array. You specified `' + typeof this[type] + '`.');
  }

  // Clone the filter array so we are not mutating an external variable
  var filters = this[type] = this[type].slice(0);

  for (var i = 0, l = filters.length; i < l; i++) {
    filters[i] = this._processPattern(filters[i]);
  }
};

Funnel.prototype._processPattern = function(pattern) {
  if (pattern instanceof RegExp) {
    return pattern;
  }

  var type = typeof pattern;

  if (type === 'string') {
    return new Minimatch(pattern);
  }

  if (type === 'function') {
    return pattern;
  }

  throw new Error('include/exclude patterns can be a RegExp, glob string, or function. You supplied `' + typeof pattern +'`.');
};

Funnel.prototype.shouldLinkRoots = function() {
  return !this.files && !this.include && !this.exclude && !this.getDestinationPath;
};

Funnel.prototype.build = function() {
  this._buildStart = new Date();
  this.destPath = path.join(this.outputPath, this.destDir);
  if (this.destPath[this.destPath.length -1] === '/') {
    this.destPath = this.destPath.slice(0, -1);
  }

  var inputPath = this.inputPaths[0];
  if (this.srcDir) {
    inputPath = path.join(inputPath, this.srcDir);
  }

  if (this._dynamicFilesFunc) {
    this.lastFiles = this.files;
    this.files = this._dynamicFilesFunc() || [];

    // Blow away the include cache if the list of files is new
    if (this.lastFiles !== undefined && !arrayEqual(this.lastFiles, this.files)) {
      this._includeFileCache = makeDictionary();
    }
  }

  var linkedRoots = false;
  if (this.shouldLinkRoots()) {
    linkedRoots = true;
    if (fs.existsSync(inputPath)) {
      fs.rmdirSync(this.outputPath);
      this._copy(inputPath, this.destPath);
    } else if (this.allowEmpty) {
      mkdirp.sync(this.destPath);
    }
  } else {
    this.processFilters(inputPath);
  }

  this._debug('build, %o', {
    in: new Date() - this._buildStart + 'ms',
    linkedRoots: linkedRoots,
    inputPath: inputPath,
    destPath: this.destPath
  });
};

Funnel.prototype.processFilters = function(inputPath) {
  var files;

  if (this.files && !this.exclude && !this.include) {
    files = this.files.slice(0); //clone to be compatible with walkSync
  } else {
    if (this._matchedWalk) {
      files = walkSync(inputPath, this.include);
    } else {
      files = walkSync(inputPath);
    }
  }

  var relativePath, destRelativePath, fullInputPath, fullOutputPath;

  var count = 0;
  for (var i = 0, l = files.length; i < l; i++) {
    relativePath = files[i];

    if (this.includeFile(relativePath)) {
      count++;
      fullInputPath    = path.join(inputPath, relativePath);
      destRelativePath = this.lookupDestinationPath(relativePath);
      fullOutputPath   = path.join(this.destPath, destRelativePath);

      this.processFile(fullInputPath, fullOutputPath, relativePath);
    }
  }

  this._debug('processFilters %o', {
    in: new Date() - this._buildStart + 'ms',
    filesFound: files.length,
    filesProcessed: count,
    inputPath: inputPath,
    destPath: this.destPath
  });
};

Funnel.prototype.lookupDestinationPath = function(relativePath) {
  if (this._destinationPathCache[relativePath] !== undefined) {
    return this._destinationPathCache[relativePath];
  }

  if (this.getDestinationPath) {
    return this._destinationPathCache[relativePath] = this.getDestinationPath(relativePath);
  }

  return this._destinationPathCache[relativePath] = relativePath;
};

Funnel.prototype.includeFile = function(relativePath) {
  var includeFileCache = this._includeFileCache;

  if (includeFileCache[relativePath] !== undefined) {
    return includeFileCache[relativePath];
  }

  // do not include directories, only files
  if (relativePath[relativePath.length - 1] === '/') {
    return includeFileCache[relativePath] = false;
  }

  var i, l, pattern;

  // Check for specific files listing
  if (this.files) {
    return includeFileCache[relativePath] = this.files.indexOf(relativePath) > -1;
  }

  // Check exclude patterns
  if (this.exclude) {
    for (i = 0, l = this.exclude.length; i < l; i++) {
      // An exclude pattern that returns true should be ignored
      pattern = this.exclude[i];

      if (this._matchesPattern(pattern, relativePath)) {
        return includeFileCache[relativePath] = false;
      }
    }
  }

  // Check include patterns
  if (this.include && this.include.length > 0) {
    for (i = 0, l = this.include.length; i < l; i++) {
      // An include pattern that returns true (and wasn't excluded at all)
      // should _not_ be ignored
      pattern = this.include[i];

      if (this._matchesPattern(pattern, relativePath)) {
        return includeFileCache[relativePath] = true;
      }
    }

    // If no include patterns were matched, ignore this file.
    return includeFileCache[relativePath] = false;
  }

  // Otherwise, don't ignore this file
  return includeFileCache[relativePath] = true;
};

Funnel.prototype._matchesPattern = function(pattern, relativePath) {
  if (pattern instanceof RegExp) {
    return pattern.test(relativePath);
  } else if (pattern instanceof Minimatch) {
    return pattern.match(relativePath);
  } else if (typeof pattern === 'function') {
    return pattern(relativePath);
  }

  throw new Error('Pattern `' + pattern + '` was not a RegExp, Glob, or Function.');
};

Funnel.prototype.processFile = function(sourcePath, destPath /*, relativePath */) {
  this._copy(sourcePath, destPath);
};

Funnel.prototype._copy = function(sourcePath, destPath) {
  var destDir  = path.dirname(destPath);
  if (!fs.existsSync(destDir)) {
    mkdirp.sync(destDir);
  }

  symlinkOrCopy.sync(sourcePath, destPath);
};

module.exports = Funnel;
<|endoftext|>


<commit_before>/**
 * Jaiminho library features.
 *
 * @author João Eduardo Montandon
 */

const dns = require('dns');

class Jaimito {

    /**
     * check e-mail validity
     *
     * @param {String} email email that will be checked
     * @param {Function} callback a callback that can be used instead of Promise
     */
    static validate(email, callback) {

        const regex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

        callback = callback || function() { };

        return new Promise((resolve, reject) => {
            let emailDomain = email.split('@')[1];
            const options = {
                family: 4,
                hints: dns.ADDRCONFIG | dns.V4MAPPED,
                all: true
              };

            if(!regex.test(email)) {
                reject();
                return callback(false);
            }
            else {
                dns.resolve(emailDomain, 'MX', (err, records) => {
                    if(!err && records.length > 0) {
                        resolve();
                        return callback(true);
                    }
                    else {
                        reject();
                        return callback(false);
                    }
                });
            }
        });
    }
}

module.exports = Jaimito;<commit_msg>Added strict mode for retro compatibility.<commit_after>"use strict";

/**
 * Jaiminho library features.
 *
 * @author João Eduardo Montandon
 */

const dns = require('dns');

class Jaimito {

    /**
     * check e-mail validity
     *
     * @param {String} email email that will be checked
     * @param {Function} callback a callback that can be used instead of Promise
     */
    static validate(email, callback) {

        const regex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

        callback = callback || function() { };

        return new Promise((resolve, reject) => {
            let emailDomain = email.split('@')[1];
            const options = {
                family: 4,
                hints: dns.ADDRCONFIG | dns.V4MAPPED,
                all: true
              };

            if(!regex.test(email)) {
                reject();
                return callback(false);
            }
            else {
                dns.resolve(emailDomain, 'MX', (err, records) => {
                    if(!err && records.length > 0) {
                        resolve();
                        return callback(true);
                    }
                    else {
                        reject();
                        return callback(false);
                    }
                });
            }
        });
    }
}

module.exports = Jaimito;<|endoftext|>