<commit_before>// import {FileSystemError} from 'core-error-predicates';

import {pick, defaults} from 'lodash';
import del from 'del';
import forge, {pki, md} from 'node-forge';
import fs from 'fs';
import moment from 'moment';
import path from 'path';
import Promise from 'bluebird';

Promise.promisifyAll(fs);
Promise.promisifyAll(pki.rsa);

const cwd = process.cwd();
const noop = () => {};

export default class EasyRSA {
  static defaults = {
    pkiDir: path.join(cwd, 'pki'),
    keysize: 2048,
    nopass: false,
    subca: false,
    days: 3650
  };
  constructor(argv) {
    this.config = defaults(pick(argv, ...Object.keys(EasyRSA.defaults)), EasyRSA.defaults);
    this.dir = this.config.pkiDir;
  }
  verifyPKI() {
    return fs.statAsync(this.dir).then((file) => {
      if (!file.isDirectory) {
        throw new Error('PKI not ready!');
      }
    });
  }
  loadCA() {
    return Promise.props({
      privateKey: fs.readFileAsync(path.join(this.dir, 'private', 'ca.key')).then(pki.privateKeyFromPem),
      cert: fs.readFileAsync(path.join(this.dir, 'ca.crt')).then(pki.certificateFromPem)
    }).tap(({privateKey, cert}) => {
      this.ca = {
        privateKey,
        cert
      };
    });
  }
  initPKI({force = false} = {}) {
    const setupFolders = dir => fs.mkdirAsync(dir)
      .then(() => Promise.all([
        fs.mkdirAsync(path.join(dir, 'private')),
        fs.mkdirAsync(path.join(dir, 'reqs'))
      ]));
    return setupFolders(this.dir)
    // @TODO fix catch
    .catch({code: 'EEXIST'}, (err) => {
      if (!force) {
        throw err;
      }
      return del(this.dir).then(() => setupFolders(this.dir));
    })
    // .catch(err => {
    //   d(err.name, err.code);
    // })
    .return(this.dir);
  }
  // https://github.com/OpenVPN/easy-rsa/blob/master/easyrsa3/easyrsa#L408
  // watch -n.75 'openssl x509 -in pki/ca.crt -text -noout'
  buildCA({commonName = 'Easy-RSA CA'} = {}) {
    const setupFolders = dir => Promise.all([
      fs.mkdirAsync(path.join(dir, 'issued')).catch((err) => {}),
      fs.mkdirAsync(path.join(dir, 'certs_by_serial'))
    ]);
    return this.verifyPKI()
      .then(() => setupFolders(this.dir).catch(noop))
      .then(() => generateFastKeyPair(this.config.keysize))
      .then(({privateKey, publicKey}) => {
        const cert = pki.createCertificate();
        cert.publicKey = publicKey;
        cert.serialNumber = 'cc3f3ee26d9a574e';
        const date = moment();
        cert.validity.notBefore = date.clone().toDate();
        cert.validity.notAfter = date.clone().add(this.config.days, 'days').toDate();
        const attrs = [{
          name: 'commonName',
          value: commonName
        }/* , {
          name: 'countryName',
          value: 'US'
        }, {
          shortName: 'ST',
          value: 'Virginia'
        }, {
          name: 'localityName',
          value: 'Blacksburg'
        }, {
          name: 'organizationName',
          value: 'Test'
        }, {
          shortName: 'OU',
          value: 'Test'
        }*/];
        cert.setSubject(attrs);
        cert.setIssuer(attrs);
        cert.setExtensions([{
          name: 'subjectKeyIdentifier'
        }, {
          name: 'authorityKeyIdentifier',
          keyIdentifier: true,
          authorityCertIssuer: true,
          serialNumber: true
        }, {
          name: 'basicConstraints',
          cA: true
        }, {
          name: 'keyUsage',
          keyCertSign: true,
          cRLSign: true
        }]);
        cert.sign(privateKey, md.sha256.create());
        return {privateKey, cert};
      })
      .tap(({privateKey, cert}) => Promise.all([
        fs.writeFileAsync(path.join(this.dir, 'ca.crt'), pki.certificateToPem(cert)),
        fs.writeFileAsync(path.join(this.dir, 'private', 'ca.key'), pki.privateKeyToPem(privateKey)),
        fs.writeFileAsync(path.join(this.dir, 'index.txt'), ''),
        fs.writeFileAsync(path.join(this.dir, 'serial'), '01')
      ]));
  }
  // watch -n.75 'openssl req -in pki/reqs/EntityName.req -text -noout'
  genReq({commonName}) {
    return this.verifyPKI()
      .then(() => generateFastKeyPair(this.config.keysize))
      .then(({privateKey, publicKey}) => {
        const csr = pki.createCertificationRequest();
        csr.publicKey = publicKey;
        csr.setSubject([{
          name: 'commonName',
          value: commonName
        }/* , {
          name: 'countryName',
          value: 'US'
        }, {
          shortName: 'ST',
          value: 'Virginia'
        }, {
          name: 'localityName',
          value: 'Blacksburg'
        }, {
          name: 'organizationName',
          value: 'Test'
        }, {
          shortName: 'OU',
          value: 'Test'
        }*/]);
        // csr.setAttributes([{
        //   name: 'challengePassword',
        //   value: 'password'
        // }, {
        //   name: 'unstructuredName',
        //   value: 'My Company, Inc.'
        // }, {
        //   name: 'extensionRequest',
        //   extensions: [{
        //     name: 'subjectAltName',
        //     altNames: [{
        //       // 2 is DNS type
        //       type: 2,
        //       value: 'test.domain.com'
        //     }, {
        //       type: 2,
        //       value: 'other.domain.com',
        //     }, {
        //       type: 2,
        //       value: 'www.domain.net'
        //     }]
        //   }]
        // }]);
        csr.sign(privateKey, md.sha256.create());
        return {privateKey, csr, commonName};
      }).tap(({privateKey, csr}) => Promise.all([
        fs.writeFileAsync(path.join(this.dir, 'reqs', `${commonName}.req`), pki.certificationRequestToPem(csr)),
        fs.writeFileAsync(path.join(this.dir, 'private', `${commonName}.key`), pki.privateKeyToPem(privateKey))
      ]));
  }
  signReq(type = 'client', commonName) {
    if (!commonName) {
      throw new Error('Missing commonName');
    }
    return this.verifyPKI()
      .then(::this.loadCA)
      .then(() => Promise.props({
        serial: fs.readFileAsync(path.join(this.dir, 'serial')).call('toString'),
        csr: fs.readFileAsync(path.join(this.dir, 'reqs', `${commonName}.req`))
               .then(forge.pki.certificationRequestFromPem)
      }))
      .then(({csr, serial}) => {
        if (!csr.verify()) {
          throw new Error('The certificate request file is not in a valid X509 request format.');
        }
        const cert = pki.createCertificate();
        cert.publicKey = csr.publicKey;
        cert.serialNumber = serial;
        const date = moment();
        cert.validity.notBefore = date.clone().toDate();
        cert.validity.notAfter = date.clone().add(this.config.days, 'days').toDate();
        //         Subject: CN=F567FC13-704D-47DE-9993-15C8EBB236AF, C=US, ST=CA, L=Cupertino, O=Apple Inc., OU=iPhone
        const attrs = [{
          name: 'commonName',
          value: 'Easy-RSA CA'
        }/* , {
          name: 'countryName',
          value: 'US'
        }, {
          shortName: 'ST',
          value: 'Virginia'
        }, {
          name: 'localityName',
          value: 'Blacksburg'
        }, {
          name: 'organizationName',
          value: 'Test'
        }, {
          shortName: 'OU',
          value: 'Test'
        }*/];
        cert.setSubject(attrs);
        cert.setIssuer(attrs);
        switch (type) {
          case 'client':
            cert.setExtensions([{
              name: 'basicConstraints',
              // critical: true, // iPad
              cA: true
            }, {
              name: 'subjectKeyIdentifier'
            }, {
              name: 'authorityKeyIdentifier',
              keyIdentifier: this.ca.cert.generateSubjectKeyIdentifier().getBytes()
              // authorityCertIssuer: this._ca.cert.issuer, // not-iPad
              // serialNumber: this._ca.cert.serialNumber // not-iPad
            }, {
              name: 'extKeyUsage',
              // critical: true, // iPad
              // serverAuth: true, // iPad
              clientAuth: true
            }, {
              name: 'keyUsage',
              cRLSign: true,
              keyCertSign: true
              // critical: true, // iPad
              // digitalSignature: true
              // keyEncipherment: true // iPad
            }]);
            break;
          default:
            throw new Error('Type not supported');
        }
        cert.sign(this.ca.privateKey, md.sha256.create());
        return {cert, serial, commonName};
      })
      .tap(({cert, serial}) => {
        const updatedSerial = (parseInt(serial, 16) + 1).toString(16);
        const certPem = pki.certificateToPem(cert);
        return Promise.all([
          fs.writeFileAsync(path.join(this.dir, 'certs_by_serial', `${serial}.pem`), certPem),
          fs.writeFileAsync(path.join(this.dir, 'issued', `${commonName}.crt`), certPem),
          fs.writeFileAsync(path.join(this.dir, 'serial'), updatedSerial.length % 2 ? `0${updatedSerial}` : updatedSerial)
        ]);
      });
  }
}

function generateFastKeyPair(bits = 2048, exponent = 65537) {
  try {
    const keyPair = require('ursa').generatePrivateKey(bits, exponent); // eslint-disable-line global-require
    return {
      privateKey: pki.privateKeyFromPem(keyPair.toPrivatePem().toString()),
      publicKey: pki.publicKeyFromPem(keyPair.toPublicPem().toString())
    };
  } catch (err) {
    return pki.rsa.generateKeyPairAsync({bits, workers: -1});
  }
}

// function prettyPrintCertificate(filePath) {
//   return fs.readFileAsync(filePath, 'utf8')
//     .then(contents => pem.decode(contents)[0])
//     .then(message => asn1.fromDer(message.body))
//     .then(object => asn1.prettyPrint(object))
//     .then(console.log.bind(console));
// }
<commit_msg>feat(update): minor changes<commit_after>// import {FileSystemError} from 'core-error-predicates';

import {pick, defaults} from 'lodash';
import del from 'del';
import forge, {pki, md} from 'node-forge';
import fs from 'fs';
import moment from 'moment';
import path from 'path';
import Promise from 'bluebird';

Promise.promisifyAll(fs);
Promise.promisifyAll(pki.rsa);

const cwd = process.cwd();
const noop = () => {};

export default class EasyRSA {
  static defaults = {
    pkiDir: path.join(cwd, 'pki'),
    keysize: 2048,
    nopass: false,
    subca: false,
    days: 3650
  };
  constructor(argv) {
    this.config = defaults(pick(argv, ...Object.keys(EasyRSA.defaults)), EasyRSA.defaults);
    this.dir = this.config.pkiDir;
  }
  verifyPKI() {
    return fs.statAsync(this.dir).then((file) => {
      if (!file.isDirectory) {
        throw new Error('PKI not ready!');
      }
    });
  }
  loadCA() {
    return Promise.props({
      privateKey: fs.readFileAsync(path.join(this.dir, 'private', 'ca.key')).then(pki.privateKeyFromPem),
      cert: fs.readFileAsync(path.join(this.dir, 'ca.crt')).then(pki.certificateFromPem)
    }).tap(({privateKey, cert}) => {
      this.ca = {
        privateKey,
        cert
      };
    });
  }
  initPKI({force = false} = {}) {
    const setupFolders = dir => fs.mkdirAsync(dir)
      .then(() => Promise.all([
        fs.mkdirAsync(path.join(dir, 'private')),
        fs.mkdirAsync(path.join(dir, 'reqs'))
      ]));
    return setupFolders(this.dir)
    // @TODO fix catch
    .catch({code: 'EEXIST'}, (err) => {
      if (!force) {
        throw err;
      }
      return del(this.dir).then(() => setupFolders(this.dir));
    })
    // .catch(err => {
    //   d(err.name, err.code);
    // })
    .return(this.dir);
  }
  // https://github.com/OpenVPN/easy-rsa/blob/master/easyrsa3/easyrsa#L408
  // watch -n.75 'openssl x509 -in pki/ca.crt -text -noout'
  buildCA({commonName = 'Easy-RSA CA'} = {}) {
    const setupFolders = dir => Promise.all([
      fs.mkdirAsync(path.join(dir, 'issued')).catch((err) => {}),
      fs.mkdirAsync(path.join(dir, 'certs_by_serial'))
    ]);
    return this.verifyPKI()
      .then(() => setupFolders(this.dir).catch(noop))
      .then(() => generateFastKeyPair(this.config.keysize))
      .then(({privateKey, publicKey}) => {
        const cert = pki.createCertificate();
        cert.publicKey = publicKey;
        cert.serialNumber = 'cc3f3ee26d9a574e';
        const date = moment();
        cert.validity.notBefore = date.clone().toDate();
        cert.validity.notAfter = date.clone().add(this.config.days, 'days').toDate();
        const attrs = [{
          name: 'commonName',
          value: commonName
        }/* , {
          name: 'countryName',
          value: 'US'
        }, {
          shortName: 'ST',
          value: 'Virginia'
        }, {
          name: 'localityName',
          value: 'Blacksburg'
        }, {
          name: 'organizationName',
          value: 'Test'
        }, {
          shortName: 'OU',
          value: 'Test'
        }*/];
        cert.setSubject(attrs);
        cert.setIssuer(attrs);
        cert.setExtensions([{
          name: 'subjectKeyIdentifier'
        }, {
          name: 'authorityKeyIdentifier',
          keyIdentifier: true,
          authorityCertIssuer: true,
          serialNumber: true
        }, {
          name: 'basicConstraints',
          cA: true
        }, {
          name: 'keyUsage',
          keyCertSign: true,
          cRLSign: true
        }]);
        cert.sign(privateKey, md.sha256.create());
        return {privateKey, cert};
      })
      .tap(({privateKey, cert}) => Promise.all([
        fs.writeFileAsync(path.join(this.dir, 'ca.crt'), pki.certificateToPem(cert)),
        fs.writeFileAsync(path.join(this.dir, 'private', 'ca.key'), pki.privateKeyToPem(privateKey)),
        fs.writeFileAsync(path.join(this.dir, 'index.txt'), ''),
        fs.writeFileAsync(path.join(this.dir, 'serial'), '01')
      ]));
  }
  // watch -n.75 'openssl req -in pki/reqs/EntityName.req -text -noout'
  genReq({commonName}) {
    return this.verifyPKI()
      .then(() => generateFastKeyPair(this.config.keysize))
      .then(({privateKey, publicKey}) => {
        const csr = pki.createCertificationRequest();
        csr.publicKey = publicKey;
        csr.setSubject([{
          name: 'commonName',
          value: commonName
        }/* , {
          name: 'countryName',
          value: 'US'
        }, {
          shortName: 'ST',
          value: 'Virginia'
        }, {
          name: 'localityName',
          value: 'Blacksburg'
        }, {
          name: 'organizationName',
          value: 'Test'
        }, {
          shortName: 'OU',
          value: 'Test'
        }*/]);
        // csr.setAttributes([{
        //   name: 'challengePassword',
        //   value: 'password'
        // }, {
        //   name: 'unstructuredName',
        //   value: 'My Company, Inc.'
        // }, {
        //   name: 'extensionRequest',
        //   extensions: [{
        //     name: 'subjectAltName',
        //     altNames: [{
        //       // 2 is DNS type
        //       type: 2,
        //       value: 'test.domain.com'
        //     }, {
        //       type: 2,
        //       value: 'other.domain.com',
        //     }, {
        //       type: 2,
        //       value: 'www.domain.net'
        //     }]
        //   }]
        // }]);
        csr.sign(privateKey, md.sha256.create());
        return {privateKey, csr, commonName};
      }).tap(({privateKey, csr}) => Promise.all([
        fs.writeFileAsync(path.join(this.dir, 'reqs', `${commonName}.req`), pki.certificationRequestToPem(csr)),
        fs.writeFileAsync(path.join(this.dir, 'private', `${commonName}.key`), pki.privateKeyToPem(privateKey))
      ]));
  }
  signReq(type = 'client', commonName) {
    if (!commonName) {
      throw new Error('Missing commonName');
    }
    return this.verifyPKI()
      .then(::this.loadCA)
      .then(() => Promise.props({
        serial: fs.readFileAsync(path.join(this.dir, 'serial')).call('toString'),
        csr: fs.readFileAsync(path.join(this.dir, 'reqs', `${commonName}.req`))
               .then(forge.pki.certificationRequestFromPem)
      }))
      .then(({csr, serial}) => {
        if (!csr.verify()) {
          throw new Error('The certificate request file is not in a valid X509 request format.');
        }
        const cert = pki.createCertificate();
        cert.publicKey = csr.publicKey;
        cert.serialNumber = serial;
        const date = moment();
        cert.validity.notBefore = date.clone().toDate();
        cert.validity.notAfter = date.clone().add(this.config.days, 'days').toDate();
        //         Subject: CN=F567FC13-704D-47DE-9993-15C8EBB236AF, C=US, ST=CA, L=Cupertino, O=Apple Inc., OU=iPhone
        const attrs = [{
          name: 'commonName',
          value: 'Easy-RSA CA'
        }/* , {
          name: 'countryName',
          value: 'US'
        }, {
          shortName: 'ST',
          value: 'Virginia'
        }, {
          name: 'localityName',
          value: 'Blacksburg'
        }, {
          name: 'organizationName',
          value: 'Test'
        }, {
          shortName: 'OU',
          value: 'Test'
        }*/];
        cert.setSubject(attrs);
        cert.setIssuer(attrs);
        switch (type) {
          case 'client':
            cert.setExtensions([{
              name: 'basicConstraints',
              // critical: true, // iPad
              cA: true
            }, {
              name: 'subjectKeyIdentifier'
            }, {
              name: 'authorityKeyIdentifier',
              keyIdentifier: this.ca.cert.generateSubjectKeyIdentifier().getBytes()
              // authorityCertIssuer: this._ca.cert.issuer, // not-iPad
              // serialNumber: this._ca.cert.serialNumber // not-iPad
            }, {
              name: 'extKeyUsage',
              // critical: true, // iPad
              // serverAuth: true, // iPad
              clientAuth: true
            }, {
              name: 'keyUsage',
              cRLSign: true,
              keyCertSign: true
              // critical: true, // iPad
              // digitalSignature: true
              // keyEncipherment: true // iPad
            }]);
            break;
          default:
            throw new Error('Type not supported');
        }
        cert.sign(this.ca.privateKey, md.sha256.create());
        return {cert, serial, commonName};
      })
      .tap(({cert, serial}) => {
        const updatedSerial = (parseInt(serial, 16) + 1).toString(16);
        const certPem = pki.certificateToPem(cert);
        return Promise.all([
          fs.writeFileAsync(path.join(this.dir, 'certs_by_serial', `${serial}.pem`), certPem),
          fs.writeFileAsync(path.join(this.dir, 'issued', `${commonName}.crt`), certPem),
          fs.writeFileAsync(path.join(this.dir, 'serial'), updatedSerial.length % 2 ? `0${updatedSerial}` : updatedSerial)
        ]);
      });
  }
}

function generateFastKeyPair(bits = 2048, exponent = 65537) {
  try {
    const keyPair = require('ursa').generatePrivateKey(bits, exponent); // eslint-disable-line global-require

    return {
      privateKey: pki.privateKeyFromPem(keyPair.toPrivatePem().toString()),
      publicKey: pki.publicKeyFromPem(keyPair.toPublicPem().toString())
    };
  } catch (err) {
    return pki.rsa.generateKeyPairAsync({bits, workers: -1});
  }
}

// function prettyPrintCertificate(filePath) {
//   return fs.readFileAsync(filePath, 'utf8')
//     .then(contents => pem.decode(contents)[0])
//     .then(message => asn1.fromDer(message.body))
//     .then(object => asn1.prettyPrint(object))
//     .then(console.log.bind(console));
// }
<|endoftext|>


<commit_before>let path = require('path');
let Mix = require('./Mix');

/**
 * We'll fetch some Webpack config plugins here for cleanliness.
 */
module.exports.plugins = {
    WebpackNotifierPlugin: require('webpack-notifier'),
    WebpackOnBuildPlugin: require('on-build-webpack'),
    ExtractTextPlugin: require('extract-text-webpack-plugin'),
    CopyWebpackPlugin: require('copy-webpack-plugin'),
    FriendlyErrorsWebpackPlugin: require('friendly-errors-webpack-plugin'),
    StatsWriterPlugin: require('webpack-stats-plugin').StatsWriterPlugin,
    WebpackMd5HashPlugin: require('webpack-md5-hash')
};

/**
 * Register the Webpack entry/output paths.
 *
 * @param {mixed}  entry
 * @param {string} output
 */
module.exports.js = (entry, output) => {
    entry = (Array.isArray(entry) ? entry : [entry]).map(file => {
        return new Mix.File(path.resolve(file)).parsePath();
    });

    output = new Mix.File(output).parsePath();

    if (output.isDir) {
        output = new Mix.File(
            path.posix.join(output.path, entry[0].file)
        ).parsePath();
    }

    Mix.js = (Mix.js || []).concat({ entry, output, vendor: false });

    return this;
};

/**
 * Register vendor libs that should be extracted.
 * This helps drastically with long-term caching.
 *
 * @param {array} libs
 */
module.exports.extract = (libs) => {
    Mix.js.vendor = libs;

    return this;
};

/**
 * Register libraries to automatically "autoload" when
 * the appropriate variable is references in js
 *
 * @param {object} libs
 */
module.exports.autoload = (libs) => {
    let aliases = {};

    Object.keys(libs).forEach(library => {
        libs[library].forEach(alias => {
            aliases[alias] = library;
        });
    });

    Mix.js.autoload = aliases;

    return this;
};

/**
 * Register Sass compilation.
 *
 * @param {string} src
 * @param {string} output
 */
module.exports.sass = (src, output) => {
    return module.exports.preprocess('sass', src, output);
};

/**
 * Register Less compilation.
 *
 * @param {string} src
 * @param {string} output
 */
module.exports.less = (src, output) => {
    return module.exports.preprocess('less', src, output);
};

/**
 * Register a generic CSS preprocessor.
 *
 * @param {string} type
 * @param {string} src
 * @param {string} output
 */
module.exports.preprocess = (type, src, output) => {
    src = new Mix.File(path.resolve(src)).parsePath();
    output = new Mix.File(output).parsePath();

    if (output.isDir) {
        output = new Mix.File(
            path.posix.join(output.path, src.name + '.css')
        ).parsePath();
    }

    Mix[type] = (Mix[type] || []).concat({ src, output });

    Mix.cssPreprocessor = type;

    return this;
};

/**
 * Combine a collection of files.
 *
 * @param {string|array} src
 * @param {string}       output
 */
module.exports.combine = (src, output) => {
    Mix.combine = (Mix.combine || []).concat({ src, output });

    return this;
};

/**
 * Copy one or more files to a new location.
 *
 * @param {string} from
 * @param {string} to
 */
module.exports.copy = (from, to) => {
    Mix.copy = (Mix.copy || []).concat({
        from,
        to: Mix.Paths.root(to),
        flatten: true
    });

    return this;
};

/**
 * Minify the provided file.
 *
 * @param {string|array} src
 */
module.exports.minify = (src) => {
    Mix.minify = (Mix.minify || []).concat(src);

    return this;
};

/**
 * Enable sourcemap support.
 */
module.exports.sourceMaps = () => {
    Mix.sourcemaps = (Mix.inProduction ? '#source-map' : '#inline-source-map');

    return this;
};

/**
 * Enable compiled file versioning.
 */
module.exports.version = () => {
    Mix.versioning = true;

    return this;
};

/**
 * Disable all OS notifications.
 */
module.exports.disableNotifications = () => {
    Mix.notifications = false;

    return this;
};

/**
 * Set the path to your public folder.
 *
 * @param {string} path
 */
module.exports.setPublicPath = (path) => {
    Mix.publicPath = path;

    return this;
};

/**
 * Set the temporary cache directory.
 *
 * @param {string} path
 */
module.exports.setCachePath = (path) => {
    Mix.cachePath = path;

    return this;
};

/**
 * Merge custom config with the provided webpack.config file.
 *
 * @param {object} config
 */
module.exports.webpackConfig = (config) => {
    Mix.webpackConfig = config;

    return this;
}

/**
 * Reset all configuration to their defaults.
 */
module.exports.reset = () => {
    [
        'js', 'cssPreprocessor', 'sass',
        'less', 'sourceMaps'
    ].forEach(prop => Mix[prop] = null);

    return this;
};

module.exports.config = Mix;
module.exports.mix = module.exports;
<commit_msg>adding extra param to copy<commit_after>let path = require('path');
let Mix = require('./Mix');

/**
 * We'll fetch some Webpack config plugins here for cleanliness.
 */
module.exports.plugins = {
    WebpackNotifierPlugin: require('webpack-notifier'),
    WebpackOnBuildPlugin: require('on-build-webpack'),
    ExtractTextPlugin: require('extract-text-webpack-plugin'),
    CopyWebpackPlugin: require('copy-webpack-plugin'),
    FriendlyErrorsWebpackPlugin: require('friendly-errors-webpack-plugin'),
    StatsWriterPlugin: require('webpack-stats-plugin').StatsWriterPlugin,
    WebpackMd5HashPlugin: require('webpack-md5-hash')
};

/**
 * Register the Webpack entry/output paths.
 *
 * @param {mixed}  entry
 * @param {string} output
 */
module.exports.js = (entry, output) => {
    entry = (Array.isArray(entry) ? entry : [entry]).map(file => {
        return new Mix.File(path.resolve(file)).parsePath();
    });

    output = new Mix.File(output).parsePath();

    if (output.isDir) {
        output = new Mix.File(
            path.posix.join(output.path, entry[0].file)
        ).parsePath();
    }

    Mix.js = (Mix.js || []).concat({ entry, output, vendor: false });

    return this;
};

/**
 * Register vendor libs that should be extracted.
 * This helps drastically with long-term caching.
 *
 * @param {array} libs
 */
module.exports.extract = (libs) => {
    Mix.js.vendor = libs;

    return this;
};

/**
 * Register libraries to automatically "autoload" when
 * the appropriate variable is references in js
 *
 * @param {object} libs
 */
module.exports.autoload = (libs) => {
    let aliases = {};

    Object.keys(libs).forEach(library => {
        libs[library].forEach(alias => {
            aliases[alias] = library;
        });
    });

    Mix.js.autoload = aliases;

    return this;
};

/**
 * Register Sass compilation.
 *
 * @param {string} src
 * @param {string} output
 */
module.exports.sass = (src, output) => {
    return module.exports.preprocess('sass', src, output);
};

/**
 * Register Less compilation.
 *
 * @param {string} src
 * @param {string} output
 */
module.exports.less = (src, output) => {
    return module.exports.preprocess('less', src, output);
};

/**
 * Register a generic CSS preprocessor.
 *
 * @param {string} type
 * @param {string} src
 * @param {string} output
 */
module.exports.preprocess = (type, src, output) => {
    src = new Mix.File(path.resolve(src)).parsePath();
    output = new Mix.File(output).parsePath();

    if (output.isDir) {
        output = new Mix.File(
            path.posix.join(output.path, src.name + '.css')
        ).parsePath();
    }

    Mix[type] = (Mix[type] || []).concat({ src, output });

    Mix.cssPreprocessor = type;

    return this;
};

/**
 * Combine a collection of files.
 *
 * @param {string|array} src
 * @param {string}       output
 */
module.exports.combine = (src, output) => {
    Mix.combine = (Mix.combine || []).concat({ src, output });

    return this;
};

/**
 * Copy one or more files to a new location.
 *
 * @param {string} from
 * @param {string} to
 * @param {boolean} flatten
 */
module.exports.copy = (from, to, flatten = true) => {
    Mix.copy = (Mix.copy || []).concat({
        from,
        to: Mix.Paths.root(to),
        flatten: flatten
    });

    return this;
};

/**
 * Minify the provided file.
 *
 * @param {string|array} src
 */
module.exports.minify = (src) => {
    Mix.minify = (Mix.minify || []).concat(src);

    return this;
};

/**
 * Enable sourcemap support.
 */
module.exports.sourceMaps = () => {
    Mix.sourcemaps = (Mix.inProduction ? '#source-map' : '#inline-source-map');

    return this;
};

/**
 * Enable compiled file versioning.
 */
module.exports.version = () => {
    Mix.versioning = true;

    return this;
};

/**
 * Disable all OS notifications.
 */
module.exports.disableNotifications = () => {
    Mix.notifications = false;

    return this;
};

/**
 * Set the path to your public folder.
 *
 * @param {string} path
 */
module.exports.setPublicPath = (path) => {
    Mix.publicPath = path;

    return this;
};

/**
 * Set the temporary cache directory.
 *
 * @param {string} path
 */
module.exports.setCachePath = (path) => {
    Mix.cachePath = path;

    return this;
};

/**
 * Merge custom config with the provided webpack.config file.
 *
 * @param {object} config
 */
module.exports.webpackConfig = (config) => {
    Mix.webpackConfig = config;

    return this;
}

/**
 * Reset all configuration to their defaults.
 */
module.exports.reset = () => {
    [
        'js', 'cssPreprocessor', 'sass',
        'less', 'sourceMaps'
    ].forEach(prop => Mix[prop] = null);

    return this;
};

module.exports.config = Mix;
module.exports.mix = module.exports;
<|endoftext|>


<commit_before>'use strict'

const slack = require('slack')
const _ = require('lodash')
const botkit = require('botkit')
const config = require('./config')
var os = require('os');

const commands = require('./commands')

var controller = botkit.slackbot({
    debug: false,
});

var bot = controller.spawn({
  token: config('SLACK_TOKEN')
}).startRTM(function(err) {
  if (err) {
    throw new Error(err);
  }
});

controller.hears(['hello'], 'direct_message,direct_mention,mention', function(bot, message) {
    bot.reply(message, 'Hello.');
});

controller.hears(['release the hounds'], 'direct_message,direct_mention,mention', function(bot, message) {
    bot.reply(message, 'Excelent!');
});

controller.hears(['help', 'halp'], 'direct_message,direct_mention,mention', function(bot, message) {
    var reply = {
        'attachments': commands.help()
    }
    bot.reply(message, reply);
});

controller.hears(['opportunities'], 'direct_message,direct_mention,mention', function(bot, message) {
    const callback = function(response) {
            bot.reply(message, response);
        }
    commands.opportunities.cmd(message.text, callback)
});

controller.hears(['uptime', 'identify yourself', 'who are you', 'what is your name'],
    'direct_message,direct_mention,mention', function(bot, message) {

        var hostname = os.hostname();
        var uptime = formatUptime(process.uptime());

        bot.reply(message,
            ':robot_face: I am a bot named <@' + bot.identity.name +
             '>. I have been running for ' + uptime + ' on ' + hostname + '.');

    });

function printResponse(response, status, message){
    bot.reply(message, " - " + response)
}

function formatUptime(uptime) {
    var unit = 'second';
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'minute';
    }
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'hour';
    }
    if (uptime != 1) {
        unit = unit + 's';
    }

    uptime = uptime + ' ' + unit;
    return uptime;
}
<commit_msg>cleaner code, double yay<commit_after>'use strict'

const botkit = require('botkit')
const config = require('./config')
var os = require('os');

const commands = require('./commands')
const seneca = require('seneca')

var controller = botkit.slackbot({
    debug: false,
});

var bot = controller.spawn({
  token: config('SLACK_TOKEN')
}).startRTM(function(err) {
  if (err) {
    throw new Error(err);
  }
});

controller.hears(['hello'], 'direct_message,direct_mention,mention', function(bot, message) {
    bot.reply(message, 'Hello.');
});

controller.hears(['help', 'halp'], 'direct_message,direct_mention,mention', function(bot, message) {
    var reply = {
        'attachments': commands.help()
    }
    bot.reply(message, reply);
});

controller.hears(['opportunities'], 'direct_message,direct_mention,mention', function(bot, message) {
    opportunities(message.text)
});

function printResponse(response, status, message){
    bot.reply(message, response)
}
<|endoftext|>


<commit_before>import pluginLodash from 'babel-plugin-lodash'
import pluginReactRequire from 'babel-plugin-react-require'
import presetLatest from 'babel-preset-latest'
import presetStage1 from 'babel-preset-stage-1'
import presetReact from 'babel-preset-react'

const defaultOptions = {
  lodash: true,
  modules: 'commonjs',
}

export default (context, userOptions) => {
  const options = {...defaultOptions, ...userOptions}
  return {
    plugins: [
      options.lodash && pluginLodash,
      pluginReactRequire,
    ].filter(Boolean),
    presets: [
      [presetLatest, {es2015: {modules: options.modules}}],
      presetStage1,
      presetReact,
    ],
  }
}
<commit_msg>Set modules option based on BABEL_ENV<commit_after>import pluginLodash from 'babel-plugin-lodash'
import pluginReactRequire from 'babel-plugin-react-require'
import presetLatest from 'babel-preset-latest'
import presetStage1 from 'babel-preset-stage-1'
import presetReact from 'babel-preset-react'

const defaultOptions = {
  lodash: true,
}

export default (context, userOptions) => {
  const options = {...defaultOptions, ...userOptions}
  if (options.modules === undefined) {
    if (process.env.BABEL_ENV === 'cjs') options.modules = 'commonjs'
    else if (process.env.BABEL_ENV === 'es') options.modules = false
  }
  return {
    plugins: [
      options.lodash && pluginLodash,
      pluginReactRequire,
    ].filter(Boolean),
    presets: [
      [presetLatest, {es2015: {modules: options.modules}}],
      presetStage1,
      presetReact,
    ],
  }
}
<|endoftext|>


<commit_before>/* @flow */

import * as React from 'react';
import { findDOMNode } from 'react-dom';
import Draggable from 'react-draggable';
import Resizable from 're-resizable';
import type { ResizeDirection, ResizeCallback, ResizeStartCallback } from 're-resizable';

export type Grid = [number, number];

export type Position = {
  x: number;
  y: number;
}

export type DraggableData = {
  node: HTMLElement;
  deltaX: number, deltaY: number;
  lastX: number, lastY: number;
} & Position;

export type RndDragCallback = (
  e: Event, data: DraggableData,
) => void | false;

export type RndResizeStartCallback = (
  e: SyntheticMouseEvent<HTMLDivElement> | SyntheticTouchEvent<HTMLDivElement>,
  dir: ResizeDirection,
  refToElement: React.ElementRef<'div'>,
) => void;

export type ResizableDelta = {
  width: number, height: number,
}

export type RndResizeCallback = (
  e: MouseEvent | TouchEvent,
  dir: ResizeDirection,
  refToElement: React.ElementRef<'div'>,
  delta: ResizableDelta,
  position: Position,
) => void;

type Size = {
  width: string | number;
  height: string | number;
}

type State = {
  disableDragging: boolean;
  z?: number;
  original: Position;
  bounds: {
    top: number;
    right: number;
    bottom: number;
    left: number;
  };
  maxWidth?: number;
  maxHeight?: number;
  isMounted: boolean;
}

export type ResizeEnable = {
  bottom?: boolean,
  bottomLeft?: boolean,
  bottomRight?: boolean,
  left?: boolean,
  right?: boolean,
  top?: boolean,
  topLeft?: boolean,
  topRight?: boolean
}

export type HandleClasses = {
  bottom?: string,
  bottomLeft?: string,
  bottomRight?: string,
  left?: string,
  right?: string,
  top?: string,
  topLeft?: string,
  topRight?: string
}

type Style = {
  [key: string]: string | number;
}

export type HandleStyles = {
  bottom?: Style,
  bottomLeft?: Style,
  bottomRight?: Style,
  left?: Style,
  right?: Style,
  top?: Style,
  topLeft?: Style,
  topRight?: Style
}

type Props = {
  z?: number;
  dragGrid?: Grid;
  default?: {
    x: number;
    y: number;
  } & Size;
  position?: {
    x: number;
    y: number;
  };
  size?: Size;
  resizeGrid?: Grid;
  bounds?: string;
  onResizeStart?: RndResizeStartCallback;
  onResize?: RndResizeCallback;
  onResizeStop?: RndResizeCallback;
  onDragStart?: RndDragCallback;
  onDrag?: RndDragCallback;
  onDragStop?: RndDragCallback;
  className?: string;
  style?: Style;
  children?: React.Node;
  enableResizing?: ResizeEnable;
  extendsProps?: { [key: string]: any };
  resizeHandleClasses?: HandleClasses;
  resizeHandleStyles?: HandleStyles;
  resizeHandleWrapperClass?: string;
  resizeHandleWrapperStyle?: Style;
  lockAspectRatio?: boolean;
  maxHeight?: number;
  maxWidth?: number;
  minHeight?: number;
  minWidth?: number;
  dragAxis?: 'x' | 'y' | 'both' | 'none';
  dragHandleClassName?: string;
  disableDragging?: boolean;
}

const resizableStyle = {
  width: 'auto',
  height: 'auto',
  display: 'inline-block',
  position: 'absolute',
  top: 0,
  left: 0,
};

export default class Rnd extends React.Component<Props, State> {
  static defaultProps = {
    maxWidth: Number.MAX_SAFE_INTEGER,
    maxHeight: Number.MAX_SAFE_INTEGER,
    onResizeStart: () => { },
    onResize: () => { },
    onResizeStop: () => { },
    onDragStart: () => { },
    onDrag: () => { },
    onDragStop: () => { },
  };
  resizable: (React$ElementRef<typeof Resizable> | null);
  draggable: Draggable;
  onResizeStart: ResizeStartCallback;
  onResize: ResizeCallback;
  onResizeStop: ResizeCallback;
  onDragStart: RndDragCallback;
  onDrag: RndDragCallback;
  onDragStop: RndDragCallback;
  wrapper: HTMLElement;
  parentId: string;

  constructor(props: Props) {
    super(props);
    this.state = {
      disableDragging: false,
      z: props.z,
      original: {
        x: 0,
        y: 0,
      },
      bounds: {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0,
      },
      maxWidth: props.maxWidth,
      maxHeight: props.maxHeight,
      isMounted: false,
    };
    this.onResizeStart = this.onResizeStart.bind(this);
    this.onResize = this.onResize.bind(this);
    this.onResizeStop = this.onResizeStop.bind(this);
    this.onDragStart = this.onDragStart.bind(this);
    this.onDrag = this.onDrag.bind(this);
    this.onDragStop = this.onDragStop.bind(this);
  }

  componentWillReceiveProps(nextProps: Props) {
    if (this.props.z !== nextProps.z) {
      this.setState({ z: nextProps.z });
    }
  }

  componentDidMount() {
    this.setParentPosition();
  }

  getSelfElement(): null | Element | Text {
    if (!this) return null;
    return findDOMNode(this);
  }

  setParentPosition() {
    const element = this.getSelfElement();
    if (element instanceof Element) {
      const parent = element.parentNode;
      if (!parent || typeof window === 'undefined') return;
      if (!(parent instanceof HTMLElement)) return;
      if (getComputedStyle(parent).position !== 'static') {
        this.setState({ isMounted: true });
        return;
      }
      parent.style.position = 'relative';
      this.setState({ isMounted: true });
    }
  }

  onDragStart(e: Event, data: DraggableData) {
    if (this.props.onDragStart) {
      this.props.onDragStart(e, data);
    }
    if (!this.props.bounds) return;
    const parent = this.resizable && this.resizable.parentNode;
    const target = this.props.bounds === 'parent'
      ? parent
      : document.querySelector(this.props.bounds);
    if (!(target instanceof HTMLElement) || !(parent instanceof HTMLElement)) return;
    const targetRect = target.getBoundingClientRect();
    const targetLeft = targetRect.left;
    const targetTop = targetRect.top;
    const parentRect = parent.getBoundingClientRect();
    const parentLeft = parentRect.left;
    const parentTop = parentRect.top;
    const left = targetLeft - parentLeft;
    const top = targetTop - parentTop;
    if (!this.resizable) return;
    this.setState({
      bounds: {
        top,
        right: left + (target.offsetWidth - this.resizable.size.width),
        bottom: top + (target.offsetHeight - this.resizable.size.height),
        left,
      },
    });
  }

  onDrag(e: Event, data: DraggableData) {
    if (this.props.onDrag) {
      this.props.onDrag(e, data);
    }
  }

  onDragStop(e: Event, data: DraggableData) {
    if (this.props.onDragStop) {
      this.props.onDragStop(e, data);
    }
  }

  onResizeStart(
    e: SyntheticMouseEvent<HTMLDivElement> | SyntheticTouchEvent<HTMLDivElement>,
    dir: ResizeDirection,
    refToElement: React.ElementRef<'div'>,
  ) {
    e.stopPropagation();
    this.setState({
      disableDragging: true,
      original: { x: this.draggable.state.x, y: this.draggable.state.y },
    });
    if (this.props.bounds) {
      const parent = this.resizable && this.resizable.parentNode;
      const target = this.props.bounds === 'parent'
        ? parent
        : document.querySelector(this.props.bounds);
      const self = this.getSelfElement();
      if (self instanceof Element && target instanceof HTMLElement && parent instanceof HTMLElement) {
        const maxWidth = typeof this.props.maxWidth === 'undefined' ? Number.MAX_SAFE_INTEGER : this.props.maxWidth;
        const maxHeight = typeof this.props.maxHeight === 'undefined' ? Number.MAX_SAFE_INTEGER : this.props.maxHeight;
        const selfRect = self.getBoundingClientRect();
        const selfLeft = selfRect.left;
        const selfTop = selfRect.top;
        const targetRect = target.getBoundingClientRect();
        const targetLeft = targetRect.left;
        const targetTop = targetRect.top;
        if (/left/i.test(dir) && this.resizable) {
          const max = (selfLeft - targetLeft) + this.resizable.size.width;
          this.setState({ maxWidth: max > maxWidth ? maxWidth : max });
        }
        if (/right/i.test(dir)) {
          const max = target.offsetWidth + (targetLeft - selfLeft);
          this.setState({ maxWidth: max > maxWidth ? maxWidth : max });
        }
        if (/top/i.test(dir) && this.resizable) {
          const max = (selfTop - targetTop) + this.resizable.size.height;
          this.setState({ maxHeight: max > maxHeight ? maxHeight : max });
        }
        if (/bottom/i.test(dir)) {
          const max = target.offsetHeight + (targetTop - selfTop);
          this.setState({ maxHeight: max > maxHeight ? maxHeight : max });
        }
      }
    } else {
      this.setState({ maxWidth: this.props.maxWidth, maxHeight: this.props.maxHeight });
    }
    if (this.props.onResizeStart) {
      this.props.onResizeStart(e, dir, refToElement);
    }
  }

  onResize(
    e: MouseEvent | TouchEvent,
    direction: ResizeDirection,
    refToResizableElement: React.ElementRef<'div'>,
    delta: { height: number, width: number },
  ) {
    let parentLeft = 0;
    let selfLeft = 0;
    let parentTop = 0;
    let selfTop = 0;
    let x;
    let y;
    if (this.props.bounds) {
      const parent = this.resizable && this.resizable.parentNode;
      const target = this.props.bounds === 'parent'
        ? parent
        : document.querySelector(this.props.bounds);
      const self = this.getSelfElement();
      if (self instanceof Element && target instanceof HTMLElement && parent instanceof HTMLElement) {
        const selfRect = self.getBoundingClientRect();
        const parentRect = parent.getBoundingClientRect();
        selfLeft = selfRect.left;
        selfTop = selfRect.top;
        parentLeft = parentRect.left;
        parentTop = parentRect.top;
      }
    }
    if (/left/i.test(direction)) {
      x = selfLeft >= parentLeft
        ? (this.state.original.x - delta.width)
        : (parentLeft - selfLeft);
      this.draggable.setState({ x });
    }
    if (/top/i.test(direction)) {
      y = selfTop >= parentTop
        ? (this.state.original.y - delta.height)
        : (parentTop - selfTop);
      this.draggable.setState({ y });
    }
    if (this.props.onResize) {
      this.props.onResize(
        e,
        direction,
        refToResizableElement,
        delta,
        {
          x: x || this.draggable.state.x,
          y: y || this.draggable.state.y,
        });
    }
  }

  onResizeStop(
    e: MouseEvent | TouchEvent,
    direction: ResizeDirection,
    refToResizableElement: HTMLDivElement,
    delta: { height: number, width: number },
  ) {
    this.setState({ disableDragging: false });
    if (this.props.onResizeStop) {
      const position: Position = {
        x: this.draggable.state.x,
        y: this.draggable.state.y,
      };
      this.props.onResizeStop(e, direction, refToResizableElement, delta, position);
    }
  }

  updateSize(size: { width: number | string, height: number | string }) {
    if (!this.resizable) return;
    this.resizable.updateSize({ width: size.width, height: size.height });
  }

  updatePosition(position: Position) {
    this.draggable.setState(position);
  }

  updateZIndex(z: number) {
    this.setState({ z });
  }

  render(): React.Node {
    const cursorStyle = this.props.disableDragging || this.props.dragHandleClassName
      ? { cursor: 'normal' }
      : { cursor: 'move' };
    const innerStyle = {
      ...resizableStyle,
      zIndex: this.state.z,
      ...cursorStyle,
      ...this.props.style,
    };
    // HACK: Wait for setting relative to parent element.
    if (!this.state.isMounted) return <div />;
    return (
      <Draggable
        ref={(c: Draggable) => { this.draggable = c; }}
        handle={this.props.dragHandleClassName}
        defaultPosition={this.props.default}
        onStart={this.onDragStart}
        onDrag={this.onDrag}
        onStop={this.onDragStop}
        axis={this.props.dragAxis}
        disabled={this.props.disableDragging}
        grid={this.props.dragGrid}
        bounds={this.props.bounds ? this.state.bounds : undefined}
        position={this.props.position}
      >
        <Resizable
          className={this.props.className}
          ref={(c: (React$ElementRef<typeof Resizable> | null)) => {
            this.resizable = c;
          }}
          defaultSize={this.props.default}
          size={this.props.size}
          enable={this.props.enableResizing}
          onResizeStart={this.onResizeStart}
          onResize={this.onResize}
          onResizeStop={this.onResizeStop}
          style={innerStyle}
          minWidth={this.props.minWidth}
          minHeight={this.props.minHeight}
          maxWidth={this.state.maxWidth}
          maxHeight={this.state.maxHeight}
          grid={this.props.resizeGrid}
          handleWrapperClass={this.props.resizeHandleWrapperClass}
          handleWrapperStyle={this.props.resizeHandleWrapperStyle}
          lockAspectRatio={this.props.lockAspectRatio}
          handleStyles={this.props.resizeHandleStyles}
          handleClasses={this.props.resizeHandleClasses}
        >
          {this.props.children}
        </Resizable>
      </Draggable >
    );
  }
}
<commit_msg>Add freeBottomBounds (#181)<commit_after>/* @flow */

import * as React from 'react';
import { findDOMNode } from 'react-dom';
import Draggable from 'react-draggable';
import Resizable from 're-resizable';
import type { ResizeDirection, ResizeCallback, ResizeStartCallback } from 're-resizable';

export type Grid = [number, number];

export type Position = {
  x: number;
  y: number;
}

export type DraggableData = {
  node: HTMLElement;
  deltaX: number, deltaY: number;
  lastX: number, lastY: number;
} & Position;

export type RndDragCallback = (
  e: Event, data: DraggableData,
) => void | false;

export type RndResizeStartCallback = (
  e: SyntheticMouseEvent<HTMLDivElement> | SyntheticTouchEvent<HTMLDivElement>,
  dir: ResizeDirection,
  refToElement: React.ElementRef<'div'>,
) => void;

export type ResizableDelta = {
  width: number, height: number,
}

export type RndResizeCallback = (
  e: MouseEvent | TouchEvent,
  dir: ResizeDirection,
  refToElement: React.ElementRef<'div'>,
  delta: ResizableDelta,
  position: Position,
) => void;

type Size = {
  width: string | number;
  height: string | number;
}

type State = {
  disableDragging: boolean;
  z?: number;
  original: Position;
  bounds: {
    top: number;
    right: number;
    bottom: number;
    left: number;
  };
  maxWidth?: number;
  maxHeight?: number;
  isMounted: boolean;
}

export type ResizeEnable = {
  bottom?: boolean,
  bottomLeft?: boolean,
  bottomRight?: boolean,
  left?: boolean,
  right?: boolean,
  top?: boolean,
  topLeft?: boolean,
  topRight?: boolean
}

export type HandleClasses = {
  bottom?: string,
  bottomLeft?: string,
  bottomRight?: string,
  left?: string,
  right?: string,
  top?: string,
  topLeft?: string,
  topRight?: string
}

type Style = {
  [key: string]: string | number;
}

export type HandleStyles = {
  bottom?: Style,
  bottomLeft?: Style,
  bottomRight?: Style,
  left?: Style,
  right?: Style,
  top?: Style,
  topLeft?: Style,
  topRight?: Style
}

type Props = {
  z?: number;
  dragGrid?: Grid;
  default?: {
    x: number;
    y: number;
  } & Size;
  position?: {
    x: number;
    y: number;
  };
  size?: Size;
  resizeGrid?: Grid;
  bounds?: string;
  onResizeStart?: RndResizeStartCallback;
  onResize?: RndResizeCallback;
  onResizeStop?: RndResizeCallback;
  onDragStart?: RndDragCallback;
  onDrag?: RndDragCallback;
  onDragStop?: RndDragCallback;
  className?: string;
  style?: Style;
  children?: React.Node;
  enableResizing?: ResizeEnable;
  extendsProps?: { [key: string]: any };
  resizeHandleClasses?: HandleClasses;
  resizeHandleStyles?: HandleStyles;
  resizeHandleWrapperClass?: string;
  resizeHandleWrapperStyle?: Style;
  lockAspectRatio?: boolean;
  maxHeight?: number;
  maxWidth?: number;
  minHeight?: number;
  minWidth?: number;
  dragAxis?: 'x' | 'y' | 'both' | 'none';
  dragHandleClassName?: string;
  disableDragging?: boolean;
  _freeBottomBounds?: boolean; // Back door for react-elastic-grid.
}

const resizableStyle = {
  width: 'auto',
  height: 'auto',
  display: 'inline-block',
  position: 'absolute',
  top: 0,
  left: 0,
};

export default class Rnd extends React.Component<Props, State> {
  static defaultProps = {
    maxWidth: Number.MAX_SAFE_INTEGER,
    maxHeight: Number.MAX_SAFE_INTEGER,
    onResizeStart: () => { },
    onResize: () => { },
    onResizeStop: () => { },
    onDragStart: () => { },
    onDrag: () => { },
    onDragStop: () => { },
  };
  resizable: (React$ElementRef<typeof Resizable> | null);
  draggable: Draggable;
  onResizeStart: ResizeStartCallback;
  onResize: ResizeCallback;
  onResizeStop: ResizeCallback;
  onDragStart: RndDragCallback;
  onDrag: RndDragCallback;
  onDragStop: RndDragCallback;
  wrapper: HTMLElement;
  parentId: string;

  constructor(props: Props) {
    super(props);
    this.state = {
      disableDragging: false,
      z: props.z,
      original: {
        x: 0,
        y: 0,
      },
      bounds: {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0,
      },
      maxWidth: props.maxWidth,
      maxHeight: props.maxHeight,
      isMounted: false,
    };
    this.onResizeStart = this.onResizeStart.bind(this);
    this.onResize = this.onResize.bind(this);
    this.onResizeStop = this.onResizeStop.bind(this);
    this.onDragStart = this.onDragStart.bind(this);
    this.onDrag = this.onDrag.bind(this);
    this.onDragStop = this.onDragStop.bind(this);
  }

  componentWillReceiveProps(nextProps: Props) {
    if (this.props.z !== nextProps.z) {
      this.setState({ z: nextProps.z });
    }
  }

  componentDidMount() {
    this.setParentPosition();
  }

  getSelfElement(): null | Element | Text {
    if (!this) return null;
    return findDOMNode(this);
  }

  setParentPosition() {
    const element = this.getSelfElement();
    if (element instanceof Element) {
      const parent = element.parentNode;
      if (!parent || typeof window === 'undefined') return;
      if (!(parent instanceof HTMLElement)) return;
      if (getComputedStyle(parent).position !== 'static') {
        this.setState({ isMounted: true });
        return;
      }
      parent.style.position = 'relative';
      this.setState({ isMounted: true });
    }
  }

  onDragStart(e: Event, data: DraggableData) {
    if (this.props.onDragStart) {
      this.props.onDragStart(e, data);
    }
    if (!this.props.bounds) return;
    const parent = this.resizable && this.resizable.parentNode;
    const target = this.props.bounds === 'parent'
      ? parent
      : document.querySelector(this.props.bounds);
    if (!(target instanceof HTMLElement) || !(parent instanceof HTMLElement)) return;
    const targetRect = target.getBoundingClientRect();
    const targetLeft = targetRect.left;
    const targetTop = targetRect.top;
    const parentRect = parent.getBoundingClientRect();
    const parentLeft = parentRect.left;
    const parentTop = parentRect.top;
    const left = targetLeft - parentLeft;
    const top = targetTop - parentTop;
    if (!this.resizable) return;
    this.setState({
      bounds: {
        top,
        right: left + (target.offsetWidth - this.resizable.size.width),
        bottom: this.props._freeBottomBounds // eslint-disable-line
          ? Number.MAX_SAFE_INTEGER
          : top + (target.offsetHeight - this.resizable.size.height),
        left,
      },
    });
  }

  onDrag(e: Event, data: DraggableData) {
    if (this.props.onDrag) {
      this.props.onDrag(e, data);
    }
  }

  onDragStop(e: Event, data: DraggableData) {
    if (this.props.onDragStop) {
      this.props.onDragStop(e, data);
    }
  }

  onResizeStart(
    e: SyntheticMouseEvent<HTMLDivElement> | SyntheticTouchEvent<HTMLDivElement>,
    dir: ResizeDirection,
    refToElement: React.ElementRef<'div'>,
  ) {
    e.stopPropagation();
    this.setState({
      disableDragging: true,
      original: { x: this.draggable.state.x, y: this.draggable.state.y },
    });
    if (this.props.bounds) {
      const parent = this.resizable && this.resizable.parentNode;
      const target = this.props.bounds === 'parent'
        ? parent
        : document.querySelector(this.props.bounds);
      const self = this.getSelfElement();
      if (self instanceof Element && target instanceof HTMLElement && parent instanceof HTMLElement) {
        const maxWidth = typeof this.props.maxWidth === 'undefined' ? Number.MAX_SAFE_INTEGER : this.props.maxWidth;
        const maxHeight = typeof this.props.maxHeight === 'undefined' ? Number.MAX_SAFE_INTEGER : this.props.maxHeight;
        const selfRect = self.getBoundingClientRect();
        const selfLeft = selfRect.left;
        const selfTop = selfRect.top;
        const targetRect = target.getBoundingClientRect();
        const targetLeft = targetRect.left;
        const targetTop = targetRect.top;
        if (/left/i.test(dir) && this.resizable) {
          const max = (selfLeft - targetLeft) + this.resizable.size.width;
          this.setState({ maxWidth: max > maxWidth ? maxWidth : max });
        }
        if (/right/i.test(dir)) {
          const max = target.offsetWidth + (targetLeft - selfLeft);
          this.setState({ maxWidth: max > maxWidth ? maxWidth : max });
        }
        if (/top/i.test(dir) && this.resizable) {
          const max = (selfTop - targetTop) + this.resizable.size.height;
          this.setState({ maxHeight: max > maxHeight ? maxHeight : max });
        }
        if (/bottom/i.test(dir)) {
          const max = target.offsetHeight + (targetTop - selfTop);
          this.setState({ maxHeight: max > maxHeight ? maxHeight : max });
        }
      }
    } else {
      this.setState({ maxWidth: this.props.maxWidth, maxHeight: this.props.maxHeight });
    }
    if (this.props.onResizeStart) {
      this.props.onResizeStart(e, dir, refToElement);
    }
  }

  onResize(
    e: MouseEvent | TouchEvent,
    direction: ResizeDirection,
    refToResizableElement: React.ElementRef<'div'>,
    delta: { height: number, width: number },
  ) {
    let parentLeft = 0;
    let selfLeft = 0;
    let parentTop = 0;
    let selfTop = 0;
    let x;
    let y;
    if (this.props.bounds) {
      const parent = this.resizable && this.resizable.parentNode;
      const target = this.props.bounds === 'parent'
        ? parent
        : document.querySelector(this.props.bounds);
      const self = this.getSelfElement();
      if (self instanceof Element && target instanceof HTMLElement && parent instanceof HTMLElement) {
        const selfRect = self.getBoundingClientRect();
        const parentRect = parent.getBoundingClientRect();
        selfLeft = selfRect.left;
        selfTop = selfRect.top;
        parentLeft = parentRect.left;
        parentTop = parentRect.top;
      }
    }
    if (/left/i.test(direction)) {
      x = selfLeft >= parentLeft
        ? (this.state.original.x - delta.width)
        : (parentLeft - selfLeft);
      this.draggable.setState({ x });
    }
    if (/top/i.test(direction)) {
      y = selfTop >= parentTop
        ? (this.state.original.y - delta.height)
        : (parentTop - selfTop);
      this.draggable.setState({ y });
    }
    if (this.props.onResize) {
      this.props.onResize(
        e,
        direction,
        refToResizableElement,
        delta,
        {
          x: x || this.draggable.state.x,
          y: y || this.draggable.state.y,
        });
    }
  }

  onResizeStop(
    e: MouseEvent | TouchEvent,
    direction: ResizeDirection,
    refToResizableElement: HTMLDivElement,
    delta: { height: number, width: number },
  ) {
    this.setState({ disableDragging: false });
    if (this.props.onResizeStop) {
      const position: Position = {
        x: this.draggable.state.x,
        y: this.draggable.state.y,
      };
      this.props.onResizeStop(e, direction, refToResizableElement, delta, position);
    }
  }

  updateSize(size: { width: number | string, height: number | string }) {
    if (!this.resizable) return;
    this.resizable.updateSize({ width: size.width, height: size.height });
  }

  updatePosition(position: Position) {
    this.draggable.setState(position);
  }

  updateZIndex(z: number) {
    this.setState({ z });
  }

  render(): React.Node {
    const cursorStyle = this.props.disableDragging || this.props.dragHandleClassName
      ? { cursor: 'normal' }
      : { cursor: 'move' };
    const innerStyle = {
      ...resizableStyle,
      zIndex: this.state.z,
      ...cursorStyle,
      ...this.props.style,
    };
    // HACK: Wait for setting relative to parent element.
    if (!this.state.isMounted) return <div />;
    return (
      <Draggable
        ref={(c: Draggable) => { this.draggable = c; }}
        handle={this.props.dragHandleClassName}
        defaultPosition={this.props.default}
        onStart={this.onDragStart}
        onDrag={this.onDrag}
        onStop={this.onDragStop}
        axis={this.props.dragAxis}
        disabled={this.props.disableDragging}
        grid={this.props.dragGrid}
        bounds={this.props.bounds ? this.state.bounds : undefined}
        position={this.props.position}
      >
        <Resizable
          className={this.props.className}
          ref={(c: (React$ElementRef<typeof Resizable> | null)) => {
            this.resizable = c;
          }}
          defaultSize={this.props.default}
          size={this.props.size}
          enable={this.props.enableResizing}
          onResizeStart={this.onResizeStart}
          onResize={this.onResize}
          onResizeStop={this.onResizeStop}
          style={innerStyle}
          minWidth={this.props.minWidth}
          minHeight={this.props.minHeight}
          maxWidth={this.state.maxWidth}
          maxHeight={this.state.maxHeight}
          grid={this.props.resizeGrid}
          handleWrapperClass={this.props.resizeHandleWrapperClass}
          handleWrapperStyle={this.props.resizeHandleWrapperStyle}
          lockAspectRatio={this.props.lockAspectRatio}
          handleStyles={this.props.resizeHandleStyles}
          handleClasses={this.props.resizeHandleClasses}
        >
          {this.props.children}
        </Resizable>
      </Draggable >
    );
  }
}
<|endoftext|>


<commit_before>import 'babel-polyfill';
import Koa from 'koa';
import dotenv from 'dotenv';

dotenv.config();

import {getEnv, debug} from './util';

const app = new Koa();
const PORT = getEnv('PORT') || 3000;

app.use(async (ctx) => {
  ctx.body = 'Hello World';
});

app.listen(PORT, () => debug(`Your awesome Koa API is running @ http://127.0.0.1:${PORT}`));
<commit_msg>Add simple route<commit_after>import 'babel-polyfill';
import Koa from 'koa';
import dotenv from 'dotenv';
import Router from 'koa-better-router';

dotenv.config();

import {getEnv, debug} from './util';

const routerOpts = {
  notFound: (ctx, next) => {
    ctx.status = 404;
    ctx.body = {msg: `Endpoint: ${ctx.originalUrl} not found!`};
    return next();
  },
};

const PORT = getEnv('PORT') || 3000;
const router = new Router(routerOpts).loadMethods();
const app = new Koa();

router.get('/', (ctx, next) => {
  ctx.body = `Wadaap from koa-api-boilerplate! ${ctx.route.prefix}`;
});

app.use(router.middleware());
app.listen(PORT, () => debug(`Your awesome Koa API is running @ http://127.0.0.1:${PORT}`));
<|endoftext|>


<commit_before>export function format(source) {
  if (source.constructor === Array) {
    let values = source
      .filter(value => typeof value === 'number')
      .filter(value => !isNaN(value))

    if (source.length === 6 && values.length === 6) {
      let matrix = identity()
      matrix[0] = values[0]
      matrix[1] = values[1]
      matrix[4] = values[2]
      matrix[5] = values[3]
      matrix[12] = values[4]
      matrix[13] = values[5]
      return matrix
    } else if (source.length === 16 && values.length === 16) {
      return source
    }
  }
  throw new TypeError('Expected a `number[]` with length 6 or 16.')
}

export function fromString(source) {
  if (typeof source === 'string') {
    let match = source.match(/matrix(3d)?\(([^)]+)\)/)
    if (match) {
      let raw = match[2].split(', ').map(parseFloat)
      return format(raw)
    }
  }
  throw new TypeError('Expected a string containing `matrix()` or `matrix3d()')
}

export function identity() {
  let matrix = []
  for (let i = 0; i < 16; i++) {
    i % 5 == 0 ? matrix.push(1) : matrix.push(0)
  }
  return matrix
}

export function inverse(source) {
  let m = format(source)

  let s0 = m[0] * m[5] - m[4] * m[1]
  let s1 = m[0] * m[6] - m[4] * m[2]
  let s2 = m[0] * m[7] - m[4] * m[3]
  let s3 = m[1] * m[6] - m[5] * m[2]
  let s4 = m[1] * m[7] - m[5] * m[3]
  let s5 = m[2] * m[7] - m[6] * m[3]

  let c5 = m[10] * m[15] - m[14] * m[11]
  let c4 = m[9] * m[15] - m[13] * m[11]
  let c3 = m[9] * m[14] - m[13] * m[10]
  let c2 = m[8] * m[15] - m[12] * m[11]
  let c1 = m[8] * m[14] - m[12] * m[10]
  let c0 = m[8] * m[13] - m[12] * m[9]

  let determinant = 1 / (s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0)

  if (isNaN(determinant) || determinant === Infinity) {
    throw new Error('Inverse determinant attempted to divide by zero.')
  }

  return [
    (m[5] * c5 - m[6] * c4 + m[7] * c3) * determinant,
    (-m[1] * c5 + m[2] * c4 - m[3] * c3) * determinant,
    (m[13] * s5 - m[14] * s4 + m[15] * s3) * determinant,
    (-m[9] * s5 + m[10] * s4 - m[11] * s3) * determinant,

    (-m[4] * c5 + m[6] * c2 - m[7] * c1) * determinant,
    (m[0] * c5 - m[2] * c2 + m[3] * c1) * determinant,
    (-m[12] * s5 + m[14] * s2 - m[15] * s1) * determinant,
    (m[8] * s5 - m[10] * s2 + m[11] * s1) * determinant,

    (m[4] * c4 - m[5] * c2 + m[7] * c0) * determinant,
    (-m[0] * c4 + m[1] * c2 - m[3] * c0) * determinant,
    (m[12] * s4 - m[13] * s2 + m[15] * s0) * determinant,
    (-m[8] * s4 + m[9] * s2 - m[11] * s0) * determinant,

    (-m[4] * c3 + m[5] * c1 - m[6] * c0) * determinant,
    (m[0] * c3 - m[1] * c1 + m[2] * c0) * determinant,
    (-m[12] * s3 + m[13] * s1 - m[14] * s0) * determinant,
    (m[8] * s3 - m[9] * s1 + m[10] * s0) * determinant,
  ]
}

export function multiply(matrixA, matrixB) {
  let fma = format(matrixA)
  let fmb = format(matrixB)
  let product = []

  for (let i = 0; i < 4; i++) {
    let row = [fma[i], fma[i + 4], fma[i + 8], fma[i + 12]]
    for (let j = 0; j < 4; j++) {
      let k = j * 4
      let col = [fmb[k], fmb[k + 1], fmb[k + 2], fmb[k + 3]]
      let result = row[0] * col[0] + row[1] * col[1] + row[2] * col[2] + row[3] * col[3]

      product[i + k] = result
    }
  }

  return product
}

export function perspective(distance) {
  let matrix = identity()
  matrix[11] = -1 / distance
  return matrix
}

export function rotate(angle) {
  return rotateZ(angle)
}

export function rotateX(angle) {
  let theta = (Math.PI / 180) * angle
  let matrix = identity()

  matrix[5] = matrix[10] = Math.cos(theta)
  matrix[6] = matrix[9] = Math.sin(theta)
  matrix[9] *= -1

  return matrix
}

export function rotateY(angle) {
  let theta = (Math.PI / 180) * angle
  let matrix = identity()

  matrix[0] = matrix[10] = Math.cos(theta)
  matrix[2] = matrix[8] = Math.sin(theta)
  matrix[2] *= -1

  return matrix
}

export function rotateZ(angle) {
  let theta = (Math.PI / 180) * angle
  let matrix = identity()

  matrix[0] = matrix[5] = Math.cos(theta)
  matrix[1] = matrix[4] = Math.sin(theta)
  matrix[4] *= -1

  return matrix
}

export function scale(scalar, scalarY) {
  let matrix = identity()

  matrix[0] = scalar
  matrix[5] = typeof scalarY === 'number' ? scalarY : scalar

  return matrix
}

export function scaleX(scalar) {
  let matrix = identity()
  matrix[0] = scalar
  return matrix
}

export function scaleY(scalar) {
  let matrix = identity()
  matrix[5] = scalar
  return matrix
}

export function scaleZ(scalar) {
  let matrix = identity()
  matrix[10] = scalar
  return matrix
}

export function skew(angleX, angleY) {
  let thetaX = (Math.PI / 180) * angleX
  let matrix = identity()

  matrix[4] = Math.tan(thetaX)

  if (angleY) {
    let thetaY = (Math.PI / 180) * angleY
    matrix[1] = Math.tan(thetaY)
  }

  return matrix
}

export function skewX(angle) {
  let theta = (Math.PI / 180) * angle
  let matrix = identity()

  matrix[4] = Math.tan(theta)

  return matrix
}

export function skewY(angle) {
  let theta = (Math.PI / 180) * angle
  let matrix = identity()

  matrix[1] = Math.tan(theta)

  return matrix
}

export function toString(source) {
  return `matrix3d(${format(source).join(', ')})`
}

export function translate(distanceX, distanceY) {
  let matrix = identity()
  matrix[12] = distanceX

  if (distanceY) {
    matrix[13] = distanceY
  }

  return matrix
}

export function translate3d(distanceX, distanceY, distanceZ) {
  let matrix = identity()
  if (distanceX !== undefined && distanceY !== undefined && distanceZ !== undefined) {
    matrix[12] = distanceX
    matrix[13] = distanceY
    matrix[14] = distanceZ
  }
  return matrix
}

export function translateX(distance) {
  let matrix = identity()
  matrix[12] = distance
  return matrix
}

export function translateY(distance) {
  let matrix = identity()
  matrix[13] = distance
  return matrix
}

export function translateZ(distance) {
  let matrix = identity()
  matrix[14] = distance
  return matrix
}
<commit_msg>Protect against undefined<commit_after>export function format(source) {
  if (source && source.constructor === Array) {
    let values = source
      .filter(value => typeof value === 'number')
      .filter(value => !isNaN(value))

    if (source.length === 6 && values.length === 6) {
      let matrix = identity()
      matrix[0] = values[0]
      matrix[1] = values[1]
      matrix[4] = values[2]
      matrix[5] = values[3]
      matrix[12] = values[4]
      matrix[13] = values[5]
      return matrix
    } else if (source.length === 16 && values.length === 16) {
      return source
    }
  }
  throw new TypeError('Expected a `number[]` with length 6 or 16.')
}

export function fromString(source) {
  if (typeof source === 'string') {
    let match = source.match(/matrix(3d)?\(([^)]+)\)/)
    if (match) {
      let raw = match[2].split(', ').map(parseFloat)
      return format(raw)
    }
  }
  throw new TypeError('Expected a string containing `matrix()` or `matrix3d()')
}

export function identity() {
  let matrix = []
  for (let i = 0; i < 16; i++) {
    i % 5 == 0 ? matrix.push(1) : matrix.push(0)
  }
  return matrix
}

export function inverse(source) {
  let m = format(source)

  let s0 = m[0] * m[5] - m[4] * m[1]
  let s1 = m[0] * m[6] - m[4] * m[2]
  let s2 = m[0] * m[7] - m[4] * m[3]
  let s3 = m[1] * m[6] - m[5] * m[2]
  let s4 = m[1] * m[7] - m[5] * m[3]
  let s5 = m[2] * m[7] - m[6] * m[3]

  let c5 = m[10] * m[15] - m[14] * m[11]
  let c4 = m[9] * m[15] - m[13] * m[11]
  let c3 = m[9] * m[14] - m[13] * m[10]
  let c2 = m[8] * m[15] - m[12] * m[11]
  let c1 = m[8] * m[14] - m[12] * m[10]
  let c0 = m[8] * m[13] - m[12] * m[9]

  let determinant = 1 / (s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0)

  if (isNaN(determinant) || determinant === Infinity) {
    throw new Error('Inverse determinant attempted to divide by zero.')
  }

  return [
    (m[5] * c5 - m[6] * c4 + m[7] * c3) * determinant,
    (-m[1] * c5 + m[2] * c4 - m[3] * c3) * determinant,
    (m[13] * s5 - m[14] * s4 + m[15] * s3) * determinant,
    (-m[9] * s5 + m[10] * s4 - m[11] * s3) * determinant,

    (-m[4] * c5 + m[6] * c2 - m[7] * c1) * determinant,
    (m[0] * c5 - m[2] * c2 + m[3] * c1) * determinant,
    (-m[12] * s5 + m[14] * s2 - m[15] * s1) * determinant,
    (m[8] * s5 - m[10] * s2 + m[11] * s1) * determinant,

    (m[4] * c4 - m[5] * c2 + m[7] * c0) * determinant,
    (-m[0] * c4 + m[1] * c2 - m[3] * c0) * determinant,
    (m[12] * s4 - m[13] * s2 + m[15] * s0) * determinant,
    (-m[8] * s4 + m[9] * s2 - m[11] * s0) * determinant,

    (-m[4] * c3 + m[5] * c1 - m[6] * c0) * determinant,
    (m[0] * c3 - m[1] * c1 + m[2] * c0) * determinant,
    (-m[12] * s3 + m[13] * s1 - m[14] * s0) * determinant,
    (m[8] * s3 - m[9] * s1 + m[10] * s0) * determinant,
  ]
}

export function multiply(matrixA, matrixB) {
  let fma = format(matrixA)
  let fmb = format(matrixB)
  let product = []

  for (let i = 0; i < 4; i++) {
    let row = [fma[i], fma[i + 4], fma[i + 8], fma[i + 12]]
    for (let j = 0; j < 4; j++) {
      let k = j * 4
      let col = [fmb[k], fmb[k + 1], fmb[k + 2], fmb[k + 3]]
      let result = row[0] * col[0] + row[1] * col[1] + row[2] * col[2] + row[3] * col[3]

      product[i + k] = result
    }
  }

  return product
}

export function perspective(distance) {
  let matrix = identity()
  matrix[11] = -1 / distance
  return matrix
}

export function rotate(angle) {
  return rotateZ(angle)
}

export function rotateX(angle) {
  let theta = (Math.PI / 180) * angle
  let matrix = identity()

  matrix[5] = matrix[10] = Math.cos(theta)
  matrix[6] = matrix[9] = Math.sin(theta)
  matrix[9] *= -1

  return matrix
}

export function rotateY(angle) {
  let theta = (Math.PI / 180) * angle
  let matrix = identity()

  matrix[0] = matrix[10] = Math.cos(theta)
  matrix[2] = matrix[8] = Math.sin(theta)
  matrix[2] *= -1

  return matrix
}

export function rotateZ(angle) {
  let theta = (Math.PI / 180) * angle
  let matrix = identity()

  matrix[0] = matrix[5] = Math.cos(theta)
  matrix[1] = matrix[4] = Math.sin(theta)
  matrix[4] *= -1

  return matrix
}

export function scale(scalar, scalarY) {
  let matrix = identity()

  matrix[0] = scalar
  matrix[5] = typeof scalarY === 'number' ? scalarY : scalar

  return matrix
}

export function scaleX(scalar) {
  let matrix = identity()
  matrix[0] = scalar
  return matrix
}

export function scaleY(scalar) {
  let matrix = identity()
  matrix[5] = scalar
  return matrix
}

export function scaleZ(scalar) {
  let matrix = identity()
  matrix[10] = scalar
  return matrix
}

export function skew(angleX, angleY) {
  let thetaX = (Math.PI / 180) * angleX
  let matrix = identity()

  matrix[4] = Math.tan(thetaX)

  if (angleY) {
    let thetaY = (Math.PI / 180) * angleY
    matrix[1] = Math.tan(thetaY)
  }

  return matrix
}

export function skewX(angle) {
  let theta = (Math.PI / 180) * angle
  let matrix = identity()

  matrix[4] = Math.tan(theta)

  return matrix
}

export function skewY(angle) {
  let theta = (Math.PI / 180) * angle
  let matrix = identity()

  matrix[1] = Math.tan(theta)

  return matrix
}

export function toString(source) {
  return `matrix3d(${format(source).join(', ')})`
}

export function translate(distanceX, distanceY) {
  let matrix = identity()
  matrix[12] = distanceX

  if (distanceY) {
    matrix[13] = distanceY
  }

  return matrix
}

export function translate3d(distanceX, distanceY, distanceZ) {
  let matrix = identity()
  if (distanceX !== undefined && distanceY !== undefined && distanceZ !== undefined) {
    matrix[12] = distanceX
    matrix[13] = distanceY
    matrix[14] = distanceZ
  }
  return matrix
}

export function translateX(distance) {
  let matrix = identity()
  matrix[12] = distance
  return matrix
}

export function translateY(distance) {
  let matrix = identity()
  matrix[13] = distance
  return matrix
}

export function translateZ(distance) {
  let matrix = identity()
  matrix[14] = distance
  return matrix
}
<|endoftext|>


<commit_before>var Protobuf = require('pbf'),
    VectorTile = require('vector-tile').VectorTile,
    L = require('leaflet'),
    corslite = require('corslite'),
    rbush = require('rbush');

module.exports = L.TileLayer.Underneath = L.TileLayer.extend({
    options: {
        layers: [],
        defaultTolerance: 100,
        isDuplicate: function(f, context) {
            var osmId = f.properties.osm_id,
                name = f.properties.name,
                isDupe = context[osmId] || context[name];

            if (!isDupe) {
                context[osmId] = true;
                context[name] = true;
            }

            return isDupe;
        },
        lazy: true
    },

    initialize: function(tileUrl, options) {
        L.TileLayer.prototype.initialize.call(this, tileUrl, options);
        this._bush = rbush(this.options.rbushMaxEntries);
    },

    query: function(latLng, tolerance, cb, context) {
        if (!this._map) return;

        tolerance = tolerance || this.options.defaultTolerance;

        var p = this._map.project(latLng),
            tilePoint = p.divideBy(this.options.tileSize)._floor(),
            query = function query() {
                var sqDistToP = function(s) {
                        var dx = s[0] - p.x,
                            dy = s[1] - p.y;
                        return dx * dx + dy * dy;
                    },
                    halfT = tolerance / 2,
                    search = this._bush.search([p.x - halfT, p.y - halfT, p.x + halfT, p.y + halfT]),
                    results = [],
                    context = {},
                    isDuplicate = this.options.isDuplicate,
                    i,
                    f;

                search.sort(function(a, b) { return sqDistToP(a) - sqDistToP(b); });

                for (i = 0; i < search.length; i++) {
                    f = search[i][4];
                    if (!isDuplicate(f, context)) {
                        results.push(f);
                    }
                }

                cb.call(context, null, results);
            }.bind(this);

        if (this.options.lazy && !this._tiles[this._tileKey(tilePoint)]) {
            this._addTile(tilePoint, null, true, query);
            return this;
        }

        query();
        return this;
    },

    _addTile: function(tilePoint, fragment, force, cb) {
        var key = this._tileKey(tilePoint),
            tile = { datum: null, processed: false };

        cb = cb || function() {};

        if (!this._tiles[key] && (!this.options.lazy || force)) {
            this._tiles[key] = tile;
            return this._loadTile(tile, tilePoint, cb);
        }

        return cb();
    },

    _tileKey: function(tilePoint) {
        return tilePoint.x + ':' + tilePoint.y;
    },

    _loadTile: function(tile, tilePoint, cb) {
        var url,
            request;

        cb = cb || function() {};

        this._adjustTilePoint(tilePoint);
        url = this.getTileUrl(tilePoint);
        request = corslite(url, L.bind(function(err, data) {
            if (err) {
                this.fire('tileerror', {
                    tilePoint: tilePoint,
                    url: url,
                    error: err
                });
                return cb(err);
            }

            this._tileLoaded(tile, tilePoint, new Uint8Array(data.response));
            return cb();
        }, this), true);
        request.responseType = 'arraybuffer';
    },

    _reset: function() {
        L.TileLayer.prototype._reset.call(this);
        this._bush.clear();
        this.fire('featurescleared');
    },

    _tileLoaded: function(tile, tilePoint, data) {
        var x = tilePoint.x,
            y = tilePoint.y,
            z = tilePoint.z,
            vectorTile = new VectorTile(new Protobuf(data)),
            filter = this.options.filter,
            i,
            j,
            layerName,
            layer,
            f,
            p,
            geojson;

        tile.processed = true;

        for (i = 0; i < this.options.layers.length; i++) {
            layerName = this.options.layers[i];
            layer = vectorTile.layers[layerName];
            if (layer) {
                for (j = 0; j < layer.length; j++) {
                    f = layer.feature(j);
                    if (!filter || filter(f)) {
                        geojson = f.toGeoJSON(x, y, z);
                        if (geojson.geometry.type !== 'Point') {
                            this.fire('featureerror', {
                                tilepoint: tilePoint,
                                error: 'Feature does not have a point geometry',
                                feature: f
                            });
                            continue;
                        }
                        p = this._map.project([geojson.geometry.coordinates[1], geojson.geometry.coordinates[0]]);
                        this._bush.insert([p.x, p.y, p.x, p.y, geojson]);
                        this.fire('featureadded', {
                            feature: geojson
                        });
                    }
                }
            }
        }
    }
});

<commit_msg>Minor refactor and fixed error handling<commit_after>var Protobuf = require('pbf'),
    VectorTile = require('vector-tile').VectorTile,
    L = require('leaflet'),
    corslite = require('corslite'),
    rbush = require('rbush');

module.exports = L.TileLayer.Underneath = L.TileLayer.extend({
    options: {
        layers: [],
        defaultTolerance: 100,
        isDuplicate: function(f, context) {
            var osmId = f.properties.osm_id,
                name = f.properties.name,
                isDupe = context[osmId] || context[name];

            if (!isDupe) {
                context[osmId] = true;
                context[name] = true;
            }

            return isDupe;
        },
        lazy: true
    },

    initialize: function(tileUrl, options) {
        L.TileLayer.prototype.initialize.call(this, tileUrl, options);
        this._bush = rbush(this.options.rbushMaxEntries);
    },

    query: function(latLng, tolerance, cb, context) {
        if (!this._map) return;

        tolerance = tolerance || this.options.defaultTolerance;

        var p = this._map.project(latLng),
            tilePoint = p.divideBy(this.options.tileSize)._floor();

        if (this.options.lazy && !this._tiles[this._tileKey(tilePoint)]) {
            this._addTile(tilePoint, null, true, L.bind(function(err) {
                if (err) {
                    return cb(err);
                }
                this._query(p, tolerance, cb, context);
            }, this));
            return this;
        }

        this._query(p, tolerance, cb, context);
        return this;
    },

    _query: function(p, tolerance, cb, context) {
        var sqDistToP = function(s) {
                var dx = s[0] - p.x,
                    dy = s[1] - p.y;
                return dx * dx + dy * dy;
            },
            halfT = tolerance / 2,
            search = this._bush.search([p.x - halfT, p.y - halfT, p.x + halfT, p.y + halfT]),
            results = [],
            context = {},
            isDuplicate = this.options.isDuplicate,
            i,
            f;

        search.sort(function(a, b) { return sqDistToP(a) - sqDistToP(b); });

        for (i = 0; i < search.length; i++) {
            f = search[i][4];
            if (!isDuplicate(f, context)) {
                results.push(f);
            }
        }

        cb.call(context, null, results);
    },

    _addTile: function(tilePoint, fragment, force, cb) {
        var key = this._tileKey(tilePoint),
            tile = { datum: null, processed: false };

        if (!this._tiles[key] && (!this.options.lazy || force)) {
            this._tiles[key] = tile;
            return this._loadTile(tile, tilePoint, cb);
        }

        return cb && cb();
    },

    _tileKey: function(tilePoint) {
        return tilePoint.x + ':' + tilePoint.y;
    },

    _loadTile: function(tile, tilePoint, cb) {
        var url,
            request;

        this._adjustTilePoint(tilePoint);
        url = this.getTileUrl(tilePoint);
        request = corslite(url, L.bind(function(err, data) {
            if (err) {
                this.fire('tileerror', {
                    tilePoint: tilePoint,
                    url: url,
                    error: err
                });
                return cb && cb(err);
            }

            this._tileLoaded(tile, tilePoint, new Uint8Array(data.response));
            return cb && cb();
        }, this), true);
        request.responseType = 'arraybuffer';
    },

    _reset: function() {
        L.TileLayer.prototype._reset.call(this);
        this._bush.clear();
        this.fire('featurescleared');
    },

    _tileLoaded: function(tile, tilePoint, data) {
        var x = tilePoint.x,
            y = tilePoint.y,
            z = tilePoint.z,
            vectorTile = new VectorTile(new Protobuf(data)),
            filter = this.options.filter,
            i,
            j,
            layerName,
            layer,
            f,
            p,
            geojson;

        tile.processed = true;

        for (i = 0; i < this.options.layers.length; i++) {
            layerName = this.options.layers[i];
            layer = vectorTile.layers[layerName];
            if (layer) {
                for (j = 0; j < layer.length; j++) {
                    f = layer.feature(j);
                    if (!filter || filter(f)) {
                        geojson = f.toGeoJSON(x, y, z);
                        if (geojson.geometry.type !== 'Point') {
                            this.fire('featureerror', {
                                tilepoint: tilePoint,
                                error: 'Feature does not have a point geometry',
                                feature: f
                            });
                            continue;
                        }
                        p = this._map.project([geojson.geometry.coordinates[1], geojson.geometry.coordinates[0]]);
                        this._bush.insert([p.x, p.y, p.x, p.y, geojson]);
                        this.fire('featureadded', {
                            feature: geojson
                        });
                    }
                }
            }
        }
    }
});

<|endoftext|>


<commit_before>var
	kind = require('enyo/kind');

var
	SampleList = require('./strawman/SampleList'),
	History = require('moonstone/History');

var
	samples = {
		Enyo: require('./enyo-samples'),
		Moonstone: require('./moonstone-samples'), //router blocking
		Layout: require('./layout-samples'),
		Spotlight: require('./spotlight-samples'),
	  	iLib: require('./enyo-ilib-samples'),
		Onyx: require('./onyx-samples'),
	  	Canvas: require('./canvas-samples'),
	  	Svg: require('./svg-samples')
	};

var
	List = kind({
		kind: SampleList,
		title: 'Enyo Strawman - Samples Gallery',
		classes: 'home',
		listType: 'grid',
		samples: samples
	});

History.set('enableBackHistoryAPI', false);

module.exports = {
	samples: samples,
	List: List
};
<commit_msg>Holy-Spacing Batman!<commit_after>var
	kind = require('enyo/kind');

var
	SampleList = require('./strawman/SampleList'),
	History = require('moonstone/History');

var
	samples = {
		Enyo: require('./enyo-samples'),
		Moonstone: require('./moonstone-samples'), //router blocking
		Layout: require('./layout-samples'),
		Spotlight: require('./spotlight-samples'),
		iLib: require('./enyo-ilib-samples'),
		Onyx: require('./onyx-samples'),
		Canvas: require('./canvas-samples'),
		Svg: require('./svg-samples')
	};

var
	List = kind({
		kind: SampleList,
		title: 'Enyo Strawman - Samples Gallery',
		classes: 'home',
		listType: 'grid',
		samples: samples
	});

History.set('enableBackHistoryAPI', false);

module.exports = {
	samples: samples,
	List: List
};
<|endoftext|>


<commit_before>var helpers = require('./helpers');
var attr = helpers.attr;

// api objects
var videoAnalytics = {}, priv = {};

// we want to keep context of our dom, so we can easily ref
// the nodes later on
priv.videos = {};

// each dom node will have events attached so we can easily
// interact with them, we'll do some data-binding to collect
// our nodes
priv.events = {};

// videos queue, because we load a 3rd party asset we want
// to mitigate race conditions of YT not being ready, so
// we keep all untracked videos in this queue and shift
// them out as we get to them
priv.queue = [];

// keep track of youtube calling our fn
priv.loaded = false;

// init fn that happens on DOMContentLoaded
priv.init = function() {
  priv.collectDom();
  if (priv.queue.length) priv.injectScripts();
};

// the way the iframe_api works is by replacing an element
// with an iframe, so we'll want to attach the video as
// needed
priv.attachVideos = function() {
  if (priv.loaded) {
    var video;
    while(video = priv.queue.shift()) {
      video.player = new YT.Player(video.el, video.opts);
      video.player._id = video.opts.videoId;
    }
  }
};

// this just allows us to open up .track publically and
// mitigate any race conditions with the youtube include
priv.attachVideosInternal = function() {
  priv.loaded = true;
  priv.attachVideos();
}

// we'll run this on init, or on demand for latent loaded
// html fragments
priv.collectDom = function() {
  var dom = document.querySelectorAll('[data-yt-analytics]');
  for(var i=0;i<dom.length;++i) {
    priv.referenceObject(dom[i]);
  }
};

// sets up our dom object, so we have a strict schema to
// adhere to later on in the api
priv.referenceObject = function(el) {
  var opts = {}, attrs = attr(el);
  opts.videoId = attrs('data-yt-analytics');
  if (attrs('data-yt-tracked') == null) {
    attrs('data-yt-tracked', true);

    // get opts from data attrs
    opts.width = attrs('data-yt-width') ? attrs('data-yt-width') : 640;
    opts.height = attrs('data-yt-height') ? attrs('data-yt-height') : 390;
    opts.playerVars = attrs('data-yt-vars') ? attrs('data-yt-vars') : null;
    opts.title = attrs('data-yt-title') ? attrs('data-yt-title') : opts.videoId;

    // setup base events
    opts.events = priv.setupEvents();

    // build video object to store
    priv.videos[opts.videoId] = { opts: opts, el: el, events: {} };
    priv.queue.push(priv.videos[opts.videoId]);
  }
};

// setup videos events, all are available publically, more info can be
// found at developers.google.com/youtube/iframe_api_reference#Events
priv.setupEvents = function() {
  var events = {};
  events.onReady = priv.events.ready;
  events.onStateChange = priv.events.stateChange;
  events.onError = priv.events.error;
  events.onPlaybackQualityChange = priv.events.playbackQualityChange;
  events.onPlaybackRateChange = priv.events.playbackRateChange;
  events.onApiChange = priv.events.apiChange;
  return events;
};

// we want to standardize how we handle events, this is the
// fn that handles such things
priv.processEvents = function(key, id, state, e) {
  // console.log('key %s id %s state %s', key, id, state);
  if (priv.videos[id].events[key]) {
    var events = priv.videos[id].events[key];
    var player = priv.videos[id].player;
    var processor = function(next) {
      return next(e, {
        currentTime: player.getCurrentTime(),
        duration: player.getDuration(),
        event: key,
        state: state,
        ms: new Date().getTime()
      });
    };
    events.forEach(processor);
  }
};

// the iframe_api allows us to attach dom style events to
// videos, we always fire these internally, but then we
// also allow you to attach events to a video, by its id
// --------------------------------------------------------
//

priv.events.apiChange = function(e) {
  priv.processEvents('apiChange', e.target._id, 'apiChange', e);
};

// according to youtube docs these status codes
// represent the state string that is indicative
// of the error
priv.events.error = function(e) {
  var state = 'invalid videoId';
  if (e.data == 2 || e.data == 100) {
    // basically nothing, as these are defaults
  } else if (e.data == 5) {
    state = 'html5 player error';
  } else if (e.data == 101 || e.data == 150) {
    state = 'embedding forbidden';
  }
  priv.processEvents('error', e.target._id, state, e);
};

priv.events.playbackRateChange = function(e) {
  priv.processEvents('playbackRateChange', e.target._id, 'playbackRateChange', e);
};

priv.events.playbackQualityChange = function(e) {
  priv.processEvents('playbackQualityChange', e.target._id, 'playbackQualityChange', e);
};

priv.events.ready = function(e) {
  priv.processEvents('ready', e.target._id, 'ready', e);
};

// we transform the current state `id` to a human readable
// string based on the youtube api docs
priv.events.stateChange = function(e) {
  var state = 'unstarted';
  if (e.data === YT.PlayerState.BUFFERING) {
    state = 'buffering';
  } else if (e.data === YT.PlayerState.CUED) {
    state = 'cued';
  } else if (e.data === YT.PlayerState.ENDED) {
    state = 'ended';
  } else if (e.data === YT.PlayerState.PAUSED) {
    state = 'paused';
  } else if (e.data === YT.PlayerState.PLAYING) {
    state = 'playing';
  }
  priv.processEvents('stateChange', e.target._id, state, e);
};

// we include youtubes js script async, and we'll need to
// keep track of the state of that include
priv.injectScripts = function(fn) {
  if (!priv.scriptInclude) {
    // we only want to do this once, and this is the best
    // time to do this once, this also keeps all of the
    // conditional stuff to a single entry, so it works
    window['onYouTubeIframeAPIReady'] = priv.attachVideosInternal;

    var placement = document.getElementsByTagName('script')[0];
    priv.scriptInclude = document.createElement('script');

    // if fn, lets treat async, otherwise we'll be blocking
    if (typeof fn == 'function') {
      priv.scriptInclude.setAttribute('async', true);
      priv.scriptInclude.addEventListener('load', fn, false);
    }

    priv.scriptInclude.setAttribute('src', '//www.youtube.com/iframe_api');
    placement.parentNode.insertBefore(priv.scriptInclude, placement);
  }
};

// public on event, so you can externally attach to videos
videoAnalytics.on = function(event, id, fn) {
  var processor = function(next) {
    if (priv.videos[next]) {
      if (!(priv.videos[next].events[event] instanceof Array)) priv.videos[next].events[event] = [];
      priv.videos[next].events[event].push(fn);
    }
  };
  // accepts `*` as an identifier of a "global"
  // event that should be attached to all videos
  if (id === '*') {
    Object.keys(priv.videos).forEach(processor);
  } else {
    processor(id);
  }
  return videoAnalytics;
};

// public tracking event, so you attach videos after dom
// load, or with some latent/async requests
videoAnalytics.track = function() {
  priv.collectDom();
  if (priv.queue.length) {
    priv.injectScripts();
    priv.attachVideos();
  }
  return videoAnalytics;
};

document.addEventListener('DOMContentLoaded', priv.init, false);

module.exports = videoAnalytics;<commit_msg>missing semi, lel<commit_after>var helpers = require('./helpers');
var attr = helpers.attr;

// api objects
var videoAnalytics = {}, priv = {};

// we want to keep context of our dom, so we can easily ref
// the nodes later on
priv.videos = {};

// each dom node will have events attached so we can easily
// interact with them, we'll do some data-binding to collect
// our nodes
priv.events = {};

// videos queue, because we load a 3rd party asset we want
// to mitigate race conditions of YT not being ready, so
// we keep all untracked videos in this queue and shift
// them out as we get to them
priv.queue = [];

// keep track of youtube calling our fn
priv.loaded = false;

// init fn that happens on DOMContentLoaded
priv.init = function() {
  priv.collectDom();
  if (priv.queue.length) priv.injectScripts();
};

// the way the iframe_api works is by replacing an element
// with an iframe, so we'll want to attach the video as
// needed
priv.attachVideos = function() {
  if (priv.loaded) {
    var video;
    while(video = priv.queue.shift()) {
      video.player = new YT.Player(video.el, video.opts);
      video.player._id = video.opts.videoId;
    }
  }
};

// this just allows us to open up .track publically and
// mitigate any race conditions with the youtube include
priv.attachVideosInternal = function() {
  priv.loaded = true;
  priv.attachVideos();
};

// we'll run this on init, or on demand for latent loaded
// html fragments
priv.collectDom = function() {
  var dom = document.querySelectorAll('[data-yt-analytics]');
  for(var i=0;i<dom.length;++i) {
    priv.referenceObject(dom[i]);
  }
};

// sets up our dom object, so we have a strict schema to
// adhere to later on in the api
priv.referenceObject = function(el) {
  var opts = {}, attrs = attr(el);
  opts.videoId = attrs('data-yt-analytics');
  if (attrs('data-yt-tracked') == null) {
    attrs('data-yt-tracked', true);

    // get opts from data attrs
    opts.width = attrs('data-yt-width') ? attrs('data-yt-width') : 640;
    opts.height = attrs('data-yt-height') ? attrs('data-yt-height') : 390;
    opts.playerVars = attrs('data-yt-vars') ? attrs('data-yt-vars') : null;
    opts.title = attrs('data-yt-title') ? attrs('data-yt-title') : opts.videoId;

    // setup base events
    opts.events = priv.setupEvents();

    // build video object to store
    priv.videos[opts.videoId] = { opts: opts, el: el, events: {} };
    priv.queue.push(priv.videos[opts.videoId]);
  }
};

// setup videos events, all are available publically, more info can be
// found at developers.google.com/youtube/iframe_api_reference#Events
priv.setupEvents = function() {
  var events = {};
  events.onReady = priv.events.ready;
  events.onStateChange = priv.events.stateChange;
  events.onError = priv.events.error;
  events.onPlaybackQualityChange = priv.events.playbackQualityChange;
  events.onPlaybackRateChange = priv.events.playbackRateChange;
  events.onApiChange = priv.events.apiChange;
  return events;
};

// we want to standardize how we handle events, this is the
// fn that handles such things
priv.processEvents = function(key, id, state, e) {
  // console.log('key %s id %s state %s', key, id, state);
  if (priv.videos[id].events[key]) {
    var events = priv.videos[id].events[key];
    var player = priv.videos[id].player;
    var processor = function(next) {
      return next(e, {
        currentTime: player.getCurrentTime(),
        duration: player.getDuration(),
        event: key,
        state: state,
        ms: new Date().getTime()
      });
    };
    events.forEach(processor);
  }
};

// the iframe_api allows us to attach dom style events to
// videos, we always fire these internally, but then we
// also allow you to attach events to a video, by its id
// --------------------------------------------------------
//

priv.events.apiChange = function(e) {
  priv.processEvents('apiChange', e.target._id, 'apiChange', e);
};

// according to youtube docs these status codes
// represent the state string that is indicative
// of the error
priv.events.error = function(e) {
  var state = 'invalid videoId';
  if (e.data == 2 || e.data == 100) {
    // basically nothing, as these are defaults
  } else if (e.data == 5) {
    state = 'html5 player error';
  } else if (e.data == 101 || e.data == 150) {
    state = 'embedding forbidden';
  }
  priv.processEvents('error', e.target._id, state, e);
};

priv.events.playbackRateChange = function(e) {
  priv.processEvents('playbackRateChange', e.target._id, 'playbackRateChange', e);
};

priv.events.playbackQualityChange = function(e) {
  priv.processEvents('playbackQualityChange', e.target._id, 'playbackQualityChange', e);
};

priv.events.ready = function(e) {
  priv.processEvents('ready', e.target._id, 'ready', e);
};

// we transform the current state `id` to a human readable
// string based on the youtube api docs
priv.events.stateChange = function(e) {
  var state = 'unstarted';
  if (e.data === YT.PlayerState.BUFFERING) {
    state = 'buffering';
  } else if (e.data === YT.PlayerState.CUED) {
    state = 'cued';
  } else if (e.data === YT.PlayerState.ENDED) {
    state = 'ended';
  } else if (e.data === YT.PlayerState.PAUSED) {
    state = 'paused';
  } else if (e.data === YT.PlayerState.PLAYING) {
    state = 'playing';
  }
  priv.processEvents('stateChange', e.target._id, state, e);
};

// we include youtubes js script async, and we'll need to
// keep track of the state of that include
priv.injectScripts = function(fn) {
  if (!priv.scriptInclude) {
    // we only want to do this once, and this is the best
    // time to do this once, this also keeps all of the
    // conditional stuff to a single entry, so it works
    window['onYouTubeIframeAPIReady'] = priv.attachVideosInternal;

    var placement = document.getElementsByTagName('script')[0];
    priv.scriptInclude = document.createElement('script');

    // if fn, lets treat async, otherwise we'll be blocking
    if (typeof fn == 'function') {
      priv.scriptInclude.setAttribute('async', true);
      priv.scriptInclude.addEventListener('load', fn, false);
    }

    priv.scriptInclude.setAttribute('src', '//www.youtube.com/iframe_api');
    placement.parentNode.insertBefore(priv.scriptInclude, placement);
  }
};

// public on event, so you can externally attach to videos
videoAnalytics.on = function(event, id, fn) {
  var processor = function(next) {
    if (priv.videos[next]) {
      if (!(priv.videos[next].events[event] instanceof Array)) priv.videos[next].events[event] = [];
      priv.videos[next].events[event].push(fn);
    }
  };
  // accepts `*` as an identifier of a "global"
  // event that should be attached to all videos
  if (id === '*') {
    Object.keys(priv.videos).forEach(processor);
  } else {
    processor(id);
  }
  return videoAnalytics;
};

// public tracking event, so you attach videos after dom
// load, or with some latent/async requests
videoAnalytics.track = function() {
  priv.collectDom();
  if (priv.queue.length) {
    priv.injectScripts();
    priv.attachVideos();
  }
  return videoAnalytics;
};

document.addEventListener('DOMContentLoaded', priv.init, false);

module.exports = videoAnalytics;<|endoftext|>