<commit_before>'use strict';

var _        = require('lodash'),
    jsonPath = require('jsonpath'),
    fs       = require('fs'),
    Chance   = require('chance')
    // TODO - assert

var hazy = {}

//    ___             __ _
//   / __\___  _ __  / _(_) __ _
//  / /  / _ \| '_ \| |_| |/ _` |
// / /__| (_) | | | |  _| | (_| |
// \____/\___/|_| |_|_| |_|\__, |
//                         |___/

hazy.config = {
  seed: null,
  lazy: true,
  debug: false,
  matcher: {
    use: true
  }
}

hazy.meta = {
  random: {
    types: {
      basic  : ['bool', 'character', 'integer', 'natural', 'string'],
      text   : ['paragraph', 'sentence', 'syllable', 'word'],
      person : ['age', 'birthday', 'cpf', 'first', 'gender', 'last', 'name', 'prefix', 'ssn', 'suffix'],
      mobile : ['android_id', 'apple_token', 'bb_pin', 'wp7_anid', 'wp8_anid2'],
      web    : ['color', 'domain', 'email', 'fbid', 'google_analytics', 'hashtag', 'ip', 'ipv6', 'klout', 'tld', 'twitter', 'url'],
      geo    : ['address', 'altitude', 'areacode', 'city', 'coordinates', 'country', 'depth', 'geohash', 'latitude', 'longitude', 'phone', 'postal', 'province', 'state', 'street', 'zip'],
      time   : ['ampm', 'date', 'hammertime', 'hour', 'millisecond', 'minute', 'month', 'second', 'timestamp', 'year'],
      misc   : ['guid', 'hash', 'hidden', 'n', 'normal', 'radio', 'rpg', 'tv', 'unique', 'weighted']
    }
  }
}

//    __
//   / /  __ _ _ __   __ _
//  / /  / _` | '_ \ / _` |
// / /__| (_| | | | | (_| |
// \____/\__,_|_| |_|\__, |
//                   |___/

hazy.lang = {
  expression: {
    first : /\|(:|~|@)(.*?)?\|/,
    all   : /\|(:|~|@)(.*?)?\|/g
  },

  tokens: {
    "|": function(prev, next) { // expression start/end
      var isPrevToken    = this.validate(prev),
          isNextTokenEnd = /\|/.test(next)

      if (isPrevToken || isNextTokenEnd) {
        throw hazy.lang.exception('Cannot define an empty expression')
      }

      if (!next) {
        // TODO - return a special value saying that this is the end of the token expression sequence. currenlty not technically needed
      }
    },

    ":": function(prev, next) { // property accessor
      if (!prev) {
        throw 'Syntax error, : requires a left operand'
      }

      return prev[next]
    },

    "~": function(prev, next, rest) { // random data
      var randProp   = next.split(':')[0],
          randVal    = next.split(':')[1],
          canUseProp = hazy.random.hasOwnProperty(randProp)

      if (canUseProp) {
        var randObjByProp  = hazy.random[randProp],
            randObjSubType = randVal // get property of random operator following ":"

        if (!randObjByProp || !randObjByProp[randObjSubType]) {
          throw hazy.lang.exception('Invalid random data type "' + randObjSubType + '". Supported:', hazy.meta.random.types[randProp])
        }

        return randObjByProp[randObjSubType]()
      } else {
        throw hazy.lang.exception('Invalid random data category "' + randProp + '". Supported', hazy.meta.random.types)
      }
    },

    "@": function(prev, next) { // link to fixture (TODO - make syntax support accessing properties of linked fixture)
      return hazy.fixture.get(next)
    },

    // TODO - / escape character

    validate: function(token) {
      return this.hasOwnProperty(token)
    },

    process: function(token, prev, next, rest) {
      return _.bindKey(hazy.lang.tokens, token, prev, next, rest)()
    }
  },

  // extracts tokens from strs and evaluates them. interpolates strings, ignores and simply returns other data types
  process: function(str) {
    var matches = str.split(hazy.lang.expression.all),
        tokens  = []

    _.forEach(matches, function(match, i) {
      var isToken = hazy.lang.tokens.validate(match)

      if (isToken) {
        var prevMatch   = matches[i - 1],
            nextMatch   = matches[i + 1],
            restMatches = _.drop(matches, i + 1),
            tokenResult = this.tokens.process(match, prevMatch, nextMatch, restMatches)

        if (tokenResult) {
          // if processed token result is a string, substitute original string as we iterate
          if (_.isString(tokenResult)) {
            str = str.replace(hazy.lang.expression.first, tokenResult)
          } else {
            tokens.push(tokenResult)
          }
        }
      }
    }, this)

    if (!_.isEmpty(tokens)) {
      return _.reduce(tokens) || str
    }

    return str
  },

  exception: function(msg) {
    return '[Hazy syntax error] ' + msg
  }
}

//    __                 _
//   /__\ __ _ _ __   __| | ___  _ __ ___
//  / \/// _` | '_ \ / _` |/ _ \| '_ ` _ \
// / _  \ (_| | | | | (_| | (_) | | | | | |
// \/ \_/\__,_|_| |_|\__,_|\___/|_| |_| |_|
//

hazy.random = _.mapValues(hazy.meta.random.types, function(value, key) {
  var hazyRandObj = {}

  _.forEach(value, function(v) {
     hazyRandObj[v] = function() { return new Chance()[v]() }
  })

  return hazyRandObj
})

//    ___ _      _
//   / __(_)_  _| |_ _   _ _ __ ___  ___
//  / _\ | \ \/ / __| | | | '__/ _ \/ __|
// / /   | |>  <| |_| |_| | | |  __/\__ \
// \/    |_/_/\_\\__|\__,_|_|  \___||___/
//

hazy.fixture = {
  pool: {},

  // fetches a fixture from the pool and processes it if necessary
  get: function(key) {
    var fixture     = this.pool[key],
        hazyFixture = hazy.config.lazy && _.isFunction(fixture) ? fixture() : fixture

    if (hazy.config.matcher.use) {
      return hazy.matcher.processDeep(hazyFixture)
    }

    return hazyFixture
  },

  lazyGet: _.memoize(function(key) {
    return hazy.fixture.get(key)
  }),

  // registers a processable fixture into the fixture pool
  register: function(name, fixture, lazy) {
    this.pool[name] = lazy || hazy.config.lazy ? function() { return hazy.fixture.process(fixture) } : this.process(fixture)
  },

   // dynamically process fixture values by type (object, string, array, or function)
  process: function(fixture) {
    var processedFixture = fixture

    if (_.isPlainObject(fixture)) {
      _.forEach(fixture, function(value, key) {
        var processedKey = hazy.lang.process(key),
            nextFixture  = value

        processedFixture[processedKey] = hazy.fixture.process(nextFixture)
      })
    }

    if (_.isString(fixture)) {
      return hazy.lang.process(fixture)
    }

    if (_.isArray(fixture)) {
      return _.map(fixture, hazy.fixture.process)
    }

    return processedFixture
  },

  // load and register a fixture from file
  load: function(file) {
    if (_.isArray(file)) {
      _.forEach(file, function() {
         // TODO - load from FS, call register
      })
    } else {

    }
  },

  write: function(path) {
    // TODO - write to FS
  }
}

//               _       _
//   /\/\   __ _| |_ ___| |__   ___ _ __ ___
//  /    \ / _` | __/ __| '_ \ / _ \ '__/ __|
// / /\/\ \ (_| | || (__| | | |  __/ |  \__ \
// \/    \/\__,_|\__\___|_| |_|\___|_|  |___/
//

hazy.matcher = {
  pool: {},

  register: function(path, value) {
    this.pool[path] = value
  },

  config: function(config) {
    // if (!_.isEmpty(hazy.fixture.pool))
    //   throw 'Matches can only be added before fixtures are in the fixture pool'

    var matcherPath    = config.path,
        matcherHandler = config.handler

    if (this.pool[matcherPath]) {
      delete this.pool[matcherPath]
    }

    this.pool[matcherPath] = {path: matcherPath, handler: matcherHandler}
  },

  // provides a map of all matched patterns in a fixture (pattern as key)
  matches: function(fixture) {
    var matches = {}

    if (hazy.config.matcher.use) {
      _.mapKeys(hazy.matcher.pool, function(v, pattern) {
        if (_.isObject(fixture)) {
          var jpMatches = jsonPath.query(fixture, pattern)

          if (!_.isEmpty(jpMatches)) {
            matches[pattern] = jpMatches
          }
        }
      })
    } else {
      // WARN - matching disabled
    }

    return matches
  },

  // determines if any matches in the pool apply to the fixture
  hasMatch: function(fixture) {
    return !_.isEmpty(
      hazy.matcher.matches(fixture)
    )
  },

  // executes a single pattern matcher handler on a fixture
  process: function(pattern, fixture) {
    var matcher = hazy.matcher.pool[pattern]

    if (_.isObject(matcher) && _.isFunction(matcher.handler)) {
      var matches = jsonPath.query(fixture, pattern),
          handler = matcher.handler

      return handler(fixture, matches, pattern)
    } else {
      throw 'Match pattern does not apply to fixture or handler is not a function'
    }
  },

  // executes handlers for all pattern matches on a fixture
  processDeep: function(fixture) {
    var patternMatches   = this.matches(fixture)
    var processedFixture = fixture

    _.mapKeys(patternMatches, function(match, pattern) {
      processedFixture = hazy.matcher.process(pattern, fixture) || processedFixture
    })

    return processedFixture
  }
}

//    ___       _ _     _
//   / __\_   _(_) | __| |
//  /__\// | | | | |/ _` |
// / \/  \ |_| | | | (_| |
// \_____/\__,_|_|_|\__,_|
//

// creates a clone of the current hazy object (shallow, new memory references)
hazy.fork = function() {
  return _.clone(hazy, false)
}

module.exports = hazy
<commit_msg>:yum: derp<commit_after>'use strict';

var _        = require('lodash'),
    jsonPath = require('jsonpath'),
    fs       = require('fs'),
    Chance   = require('chance')
    // TODO - assert

var hazy = {}

//    ___             __ _
//   / __\___  _ __  / _(_) __ _
//  / /  / _ \| '_ \| |_| |/ _` |
// / /__| (_) | | | |  _| | (_| |
// \____/\___/|_| |_|_| |_|\__, |
//                         |___/

hazy.config = {
  seed: null,
  lazy: true,
  debug: false,
  matcher: {
    use: true
  }
}

hazy.meta = {
  random: {
    types: {
      basic  : ['bool', 'character', 'integer', 'natural', 'string'],
      text   : ['paragraph', 'sentence', 'syllable', 'word'],
      person : ['age', 'birthday', 'cpf', 'first', 'gender', 'last', 'name', 'prefix', 'ssn', 'suffix'],
      mobile : ['android_id', 'apple_token', 'bb_pin', 'wp7_anid', 'wp8_anid2'],
      web    : ['color', 'domain', 'email', 'fbid', 'google_analytics', 'hashtag', 'ip', 'ipv6', 'klout', 'tld', 'twitter', 'url'],
      geo    : ['address', 'altitude', 'areacode', 'city', 'coordinates', 'country', 'depth', 'geohash', 'latitude', 'longitude', 'phone', 'postal', 'province', 'state', 'street', 'zip'],
      time   : ['ampm', 'date', 'hammertime', 'hour', 'millisecond', 'minute', 'month', 'second', 'timestamp', 'year'],
      misc   : ['guid', 'hash', 'hidden', 'n', 'normal', 'radio', 'rpg', 'tv', 'unique', 'weighted']
    }
  }
}

//    __
//   / /  __ _ _ __   __ _
//  / /  / _` | '_ \ / _` |
// / /__| (_| | | | | (_| |
// \____/\__,_|_| |_|\__, |
//                   |___/

hazy.lang = {
  expression: {
    first : /\|(:|~|@)(.*?)?\|/,
    all   : /\|(:|~|@)(.*?)?\|/g
  },

  tokens: {
    "|": function(prev, next) { // expression start/end
      var isPrevToken    = this.validate(prev),
          isNextTokenEnd = /\|/.test(next)

      if (isPrevToken || isNextTokenEnd) {
        throw hazy.lang.exception('Cannot define an empty expression')
      }

      if (!next) {
        // TODO - return a special value saying that this is the end of the token expression sequence. currenlty not technically needed
      }
    },

    ":": function(prev, next) { // property accessor
      if (!prev) {
        throw 'Syntax error, : requires a left operand'
      }

      return prev[next]
    },

    "~": function(prev, next, rest) { // random data
      var randProp   = next.split(':')[0],
          randVal    = next.split(':')[1],
          canUseProp = hazy.random.hasOwnProperty(randProp)

      if (canUseProp) {
        var randObjByProp  = hazy.random[randProp],
            randObjSubType = randVal // get property of random operator following ":"

        if (!randObjByProp || !randObjByProp[randObjSubType]) {
          throw hazy.lang.exception('Invalid random data type "' + randObjSubType + '". Supported:', hazy.meta.random.types[randProp])
        }

        return randObjByProp[randObjSubType]()
      } else {
        throw hazy.lang.exception('Invalid random data category "' + randProp + '". Supported', hazy.meta.random.types)
      }
    },

    "@": function(prev, next) { // link to fixture (TODO - make syntax support accessing properties of linked fixture)
      return hazy.fixture.get(next)
    },

    // TODO - / escape character

    validate: function(token) {
      return this.hasOwnProperty(token)
    },

    process: function(token, prev, next, rest) {
      return _.bindKey(hazy.lang.tokens, token, prev, next, rest)()
    }
  },

  // extracts tokens from strs and evaluates them. interpolates strings, ignores and simply returns other data types
  process: function(str) {
    var matches = str.split(hazy.lang.expression.all),
        tokens  = []

    _.forEach(matches, function(match, i) {
      var isToken = hazy.lang.tokens.validate(match)

      if (isToken) {
        var prevMatch   = matches[i - 1],
            nextMatch   = matches[i + 1],
            restMatches = _.drop(matches, i + 1),
            tokenResult = this.tokens.process(match, prevMatch, nextMatch, restMatches)

        if (tokenResult) {
          // if processed token result is a string, substitute original string as we iterate
          if (_.isString(tokenResult)) {
            str = str.replace(hazy.lang.expression.first, tokenResult)
          } else {
            tokens.push(tokenResult)
          }
        }
      }
    }, this)

    if (!_.isEmpty(tokens)) {
      return _.reduce(tokens) || str
    }

    return str
  },

  exception: function(msg) {
    return '[Hazy syntax error] ' + msg
  }
}

//    __                 _
//   /__\ __ _ _ __   __| | ___  _ __ ___
//  / \/// _` | '_ \ / _` |/ _ \| '_ ` _ \
// / _  \ (_| | | | | (_| | (_) | | | | | |
// \/ \_/\__,_|_| |_|\__,_|\___/|_| |_| |_|
//

hazy.random = _.mapValues(hazy.meta.random.types, function(value, key) {
  var hazyRandObj = {}

  _.forEach(value, function(v) {
     hazyRandObj[v] = function() { return new Chance()[v]() }
  })

  return hazyRandObj
})

//    ___ _      _
//   / __(_)_  _| |_ _   _ _ __ ___  ___
//  / _\ | \ \/ / __| | | | '__/ _ \/ __|
// / /   | |>  <| |_| |_| | | |  __/\__ \
// \/    |_/_/\_\\__|\__,_|_|  \___||___/
//

hazy.fixture = {
  pool: {},

  // fetches a fixture from the pool and processes it if necessary
  get: function(key) {
    var fixture     = this.pool[key],
        hazyFixture = hazy.config.lazy && _.isFunction(fixture) ? fixture() : fixture

    if (hazy.config.matcher.use) {
      return hazy.matcher.processDeep(hazyFixture)
    }

    return hazyFixture
  },

  lazyGet: _.memoize(function(key) {
    return hazy.fixture.get(key)
  }),

  // registers a processable fixture into the fixture pool
  register: function(name, fixture, lazy) {
    this.pool[name] = lazy || hazy.config.lazy ? function() { return hazy.fixture.process(fixture) } : this.process(fixture)
  },

   // dynamically process fixture values by type (object, string, array, or function)
  process: function(fixture) {
    var processedFixture = fixture

    if (_.isPlainObject(fixture)) {
      _.forEach(fixture, function(value, key) {
        var processedKey = hazy.lang.process(key),
            nextFixture  = value

        processedFixture[processedKey] = hazy.fixture.process(nextFixture)
      })
    }

    if (_.isString(fixture)) {
      return hazy.lang.process(fixture)
    }

    if (_.isArray(fixture)) {
      return _.map(fixture, hazy.fixture.process)
    }

    return processedFixture
  },

  // load and register a fixture from file
  load: function(file) {
    if (_.isArray(file)) {
      _.forEach(file, function() {
         // TODO - load from FS, call register
      })
    } else {

    }
  },

  write: function(path) {
    // TODO - write to FS
  }
}

//               _       _
//   /\/\   __ _| |_ ___| |__   ___ _ __ ___
//  /    \ / _` | __/ __| '_ \ / _ \ '__/ __|
// / /\/\ \ (_| | || (__| | | |  __/ |  \__ \
// \/    \/\__,_|\__\___|_| |_|\___|_|  |___/
//

hazy.matcher = {
  pool: {},

  register: function(path, value) {
    this.pool[path] = value
  },

  config: function(config) {
    // if (!_.isEmpty(hazy.fixture.pool))
    //   throw 'Matches can only be added before fixtures are in the fixture pool'

    var matcherPath    = config.path,
        matcherHandler = config.handler

    if (this.pool[matcherPath]) {
      delete this.pool[matcherPath]
    }

    this.pool[matcherPath] = {path: matcherPath, handler: matcherHandler}
  },

  // provides a map of all matched patterns in a fixture (pattern as key)
  matches: function(fixture) {
    var matches = {}

    if (hazy.config.matcher.use) {
      _.mapKeys(hazy.matcher.pool, function(v, pattern) {
        if (_.isObject(fixture)) {
          var jpMatches = jsonPath.query(fixture, pattern)

          if (!_.isEmpty(jpMatches)) {
            matches[pattern] = jpMatches
          }
        }
      })
    } else {
      // WARN - matching disabled
    }

    return matches
  },

  // determines if any matches in the pool apply to the fixture
  hasMatch: function(fixture) {
    return !_.isEmpty(
      hazy.matcher.matches(fixture)
    )
  },

  // executes a single pattern matcher handler on a fixture
  process: function(pattern, fixture) {
    var matcher = hazy.matcher.pool[pattern]

    if (_.isObject(matcher) && _.isFunction(matcher.handler)) {
      var matches = jsonPath.query(fixture, pattern),
          handler = matcher.handler

      return handler(fixture, matches, pattern)
    } else {
      throw 'Match pattern does not apply to fixture or handler is not a function'
    }
  },

  // executes handlers for all pattern matches on a fixture
  processDeep: function(fixture) {
    var patternMatches   = this.matches(fixture)
    var processedFixture = fixture

    _.forEach(patternMatches, function(match, pattern) {
      processedFixture = hazy.matcher.process(pattern, fixture) || processedFixture
    })

    return processedFixture
  }
}

//    ___       _ _     _
//   / __\_   _(_) | __| |
//  /__\// | | | | |/ _` |
// / \/  \ |_| | | | (_| |
// \_____/\__,_|_|_|\__,_|
//

// creates a clone of the current hazy object (shallow, new memory references)
hazy.fork = function() {
  return _.clone(hazy, false)
}

module.exports = hazy
<|endoftext|>


<commit_before>import Axios from 'axios';
import {
  BAD_REQUEST,
  FORBIDDEN,
  NOT_FOUND,
  TOO_MANY_REQUESTS,
} from './responses';

/**
 * An interface to the haveibeenpwned.com API (version 2).
 */
const hibp = {
  /**
   * The Axios instance used for API queries. Not meant for general use.
   *
   * @private
   */
  _axios: Axios.create({
    baseURL: 'https://haveibeenpwned.com/api',
    headers: {
      Accept: 'application/vnd.haveibeenpwned.v2+json',
    },
  }),

  /**
   * Fetches data from the supplied API endpoint.
   *
   * HTTP status code 200 returns an Object (data found).
   * HTTP status code 404 returns null (no data found).
   * HTTP status code 400 throws an Error (bad request).
   * HTTP status code 403 throws an Error (forbidden).
   * HTTP status code 429 throws an Error (too many requests).
   *
   * @private
   * @param {string} endpoint the API endpoint to query
   * @returns {Promise} a Promise which resolves to the data resulting from the
   * query (or null for 404 Not Found responses), or rejects with an Error
   */
  _fetchFromApi: endpoint => Promise
    .resolve(hibp._axios.get(endpoint))
    .then(res => res.data)
    .catch((err) => {
      if (err.response) {
        switch (err.response.status) {
          case BAD_REQUEST.status:
            throw new Error(BAD_REQUEST.statusText);
          case FORBIDDEN.status:
            throw new Error(FORBIDDEN.statusText);
          case NOT_FOUND.status:
            return null;
          case TOO_MANY_REQUESTS.status:
            throw new Error(err.response.data);
          default:
            throw new Error(err.response.statusText);
        }
      } else {
        throw err;
      }
    }),

  /**
   * Fetches breach data for the specified account.
   *
   * @param {string} account a username or email address
   * @param {Object} [options] a configuration object
   * @param {string} [options.domain] a domain by which to filter the results
   * (default: all domains)
   * @param {boolean} [options.truncate] truncate the results to only include
   * the name of each breach (default: false)
   * @returns {Promise} a Promise which resolves to an array of breach objects
   * (or null if no breaches were found), or rejects with an Error
   * @example
   * hibp.breachedAccount('foo')
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   * @example
   * hibp.breachedAccount('bar', { truncate: true })
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   * @example
   * hibp.breachedAccount('baz', { domain: 'adobe.com', truncate: true })
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   */
  breachedAccount: (account, options = {}) => {
    const endpoint = `/breachedaccount/${encodeURIComponent(account)}?`;
    const params = [];
    if (options.domain) {
      params.push(`domain=${encodeURIComponent(options.domain)}`);
    }
    if (options.truncate) {
      params.push('truncateResponse=true');
    }
    return hibp._fetchFromApi(`${endpoint}${params.join('&')}`);
  },

  /**
   * Fetches all breached sites in the system.
   *
   * @param {Object} [options] a configuration object
   * @param {string} [options.domain] a domain by which to filter the results
   * (default: all domains)
   * @returns {Promise} a Promise which resolves to an array of breach objects
   * (an empty array if no breaches were found), or rejects with an Error
   * @example
   * hibp.breaches()
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   * @example
   * hibp.breaches({ domain: 'adobe.com' })
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   */
  breaches: (options = {}) => {
    const endpoint = '/breaches?';
    const params = [];
    if (options.domain) {
      params.push(`domain=${encodeURIComponent(options.domain)}`);
    }
    return hibp._fetchFromApi(`${endpoint}${params.join('&')}`);
  },

  /**
   * Fetches breach data for a single site by breach name.
   *
   * @param {string} breachName the name of a breach in the system
   * @returns {Promise} a Promise which resolves to an object representing a
   * breach (or null if no breach was found), or rejects with an Error
   * @example
   * hibp.breach('Adobe')
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   */
  breach: breachName => (
    hibp._fetchFromApi(`/breach/${encodeURIComponent(breachName)}`)
  ),

  /**
   * Fetches all data classes in the system.
   *
   * @returns {Promise} a Promise which resolves to an array of strings (or
   * null if no data classes were found), or rejects with an Error
   * @example
   * hibp.dataClasses()
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   */
  dataClasses: () => hibp._fetchFromApi('/dataclasses'),

  /**
   * Fetches all pastes for an account (email address).
   *
   * @param {string} email the email address to query
   * @returns {Promise} a Promise which resolves to an array of paste objects
   * (or null if no pastes were found), or rejects with an Error
   * @example
   * hibp.pasteAccount('foo@bar.com')
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   */
  pasteAccount: email => (
    hibp._fetchFromApi(`/pasteaccount/${encodeURIComponent(email)}`)
  ),

  /**
   * Fetches all breaches and all pastes associated with the provided account
   * (an email address or username). Note that the remote API does not support
   * querying pastes by username (only email addresses), so in the event the
   * provided account is not a valid email address, only breach data is queried
   * and the "pastes" field of the resulting object will always be null. This is
   * exactly how searching via the current web interface behaves, which this
   * convenience method is designed to mimic.
   *
   * @param {string} account an email address or username
   * @param {Object} [breachOptions] a configuration object pertaining to
   * breach queries
   * @param {string} [breachOptions.domain] a domain by which to filter the
   * results (default: all domains)
   * @param {boolean} [breachOptions.truncate] truncate the results to only
   * include the name of each breach (default: false)
   * @returns {Promise} a Promise which resolves to an object containing a
   * "breaches" key (which can be null or an array of breach objects) and a
   * "pastes" key (which can be null or an array of paste objects), or rejects
   * with an Error
   * @example
   * hibp.search('foo')
   *   .then((data) => {
   *     if (data.breaches || data.pastes) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   * @example
   * hibp.search('nobody@nowhere.com', { truncate: true })
   *   .then((data) => {
   *     if (data.breaches || data.pastes) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   *
   * @see https://haveibeenpwned.com/
   */
  search: (account, breachOptions = {}) => Promise
    .all([
      hibp.breachedAccount(account, breachOptions),
      // This email regex is garbage but it seems to be what the API uses:
      /^.+@.+$/.test(account) ? hibp.pasteAccount(account) : null,
    ])
    .then(breachesAndPastes => ({
      breaches: breachesAndPastes[0],
      pastes: breachesAndPastes[1],
    })),
};

export default hibp;
<commit_msg>Tidy up Promise function calls and parentheses<commit_after>import Axios from 'axios';
import {
  BAD_REQUEST,
  FORBIDDEN,
  NOT_FOUND,
  TOO_MANY_REQUESTS,
} from './responses';

/**
 * An interface to the haveibeenpwned.com API (version 2).
 */
const hibp = {
  /**
   * The Axios instance used for API queries. Not meant for general use.
   *
   * @private
   */
  _axios: Axios.create({
    baseURL: 'https://haveibeenpwned.com/api',
    headers: {
      Accept: 'application/vnd.haveibeenpwned.v2+json',
    },
  }),

  /**
   * Fetches data from the supplied API endpoint.
   *
   * HTTP status code 200 returns an Object (data found).
   * HTTP status code 404 returns null (no data found).
   * HTTP status code 400 throws an Error (bad request).
   * HTTP status code 403 throws an Error (forbidden).
   * HTTP status code 429 throws an Error (too many requests).
   *
   * @private
   * @param {string} endpoint the API endpoint to query
   * @returns {Promise} a Promise which resolves to the data resulting from the
   * query (or null for 404 Not Found responses), or rejects with an Error
   */
  _fetchFromApi: endpoint =>
    Promise.resolve(hibp._axios.get(endpoint))
      .then(res => res.data)
      .catch((err) => {
        if (err.response) {
          switch (err.response.status) {
            case BAD_REQUEST.status:
              throw new Error(BAD_REQUEST.statusText);
            case FORBIDDEN.status:
              throw new Error(FORBIDDEN.statusText);
            case NOT_FOUND.status:
              return null;
            case TOO_MANY_REQUESTS.status:
              throw new Error(err.response.data);
            default:
              throw new Error(err.response.statusText);
          }
        } else {
          throw err;
        }
      }),

  /**
   * Fetches breach data for the specified account.
   *
   * @param {string} account a username or email address
   * @param {Object} [options] a configuration object
   * @param {string} [options.domain] a domain by which to filter the results
   * (default: all domains)
   * @param {boolean} [options.truncate] truncate the results to only include
   * the name of each breach (default: false)
   * @returns {Promise} a Promise which resolves to an array of breach objects
   * (or null if no breaches were found), or rejects with an Error
   * @example
   * hibp.breachedAccount('foo')
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   * @example
   * hibp.breachedAccount('bar', { truncate: true })
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   * @example
   * hibp.breachedAccount('baz', { domain: 'adobe.com', truncate: true })
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   */
  breachedAccount: (account, options = {}) => {
    const endpoint = `/breachedaccount/${encodeURIComponent(account)}?`;
    const params = [];
    if (options.domain) {
      params.push(`domain=${encodeURIComponent(options.domain)}`);
    }
    if (options.truncate) {
      params.push('truncateResponse=true');
    }
    return hibp._fetchFromApi(`${endpoint}${params.join('&')}`);
  },

  /**
   * Fetches all breached sites in the system.
   *
   * @param {Object} [options] a configuration object
   * @param {string} [options.domain] a domain by which to filter the results
   * (default: all domains)
   * @returns {Promise} a Promise which resolves to an array of breach objects
   * (an empty array if no breaches were found), or rejects with an Error
   * @example
   * hibp.breaches()
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   * @example
   * hibp.breaches({ domain: 'adobe.com' })
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   */
  breaches: (options = {}) => {
    const endpoint = '/breaches?';
    const params = [];
    if (options.domain) {
      params.push(`domain=${encodeURIComponent(options.domain)}`);
    }
    return hibp._fetchFromApi(`${endpoint}${params.join('&')}`);
  },

  /**
   * Fetches breach data for a single site by breach name.
   *
   * @param {string} breachName the name of a breach in the system
   * @returns {Promise} a Promise which resolves to an object representing a
   * breach (or null if no breach was found), or rejects with an Error
   * @example
   * hibp.breach('Adobe')
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   */
  breach: breachName =>
    hibp._fetchFromApi(`/breach/${encodeURIComponent(breachName)}`),

  /**
   * Fetches all data classes in the system.
   *
   * @returns {Promise} a Promise which resolves to an array of strings (or
   * null if no data classes were found), or rejects with an Error
   * @example
   * hibp.dataClasses()
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   */
  dataClasses: () => hibp._fetchFromApi('/dataclasses'),

  /**
   * Fetches all pastes for an account (email address).
   *
   * @param {string} email the email address to query
   * @returns {Promise} a Promise which resolves to an array of paste objects
   * (or null if no pastes were found), or rejects with an Error
   * @example
   * hibp.pasteAccount('foo@bar.com')
   *   .then((data) => {
   *     if (data) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   */
  pasteAccount: email =>
    hibp._fetchFromApi(`/pasteaccount/${encodeURIComponent(email)}`),

  /**
   * Fetches all breaches and all pastes associated with the provided account
   * (an email address or username). Note that the remote API does not support
   * querying pastes by username (only email addresses), so in the event the
   * provided account is not a valid email address, only breach data is queried
   * and the "pastes" field of the resulting object will always be null. This is
   * exactly how searching via the current web interface behaves, which this
   * convenience method is designed to mimic.
   *
   * @param {string} account an email address or username
   * @param {Object} [breachOptions] a configuration object pertaining to
   * breach queries
   * @param {string} [breachOptions.domain] a domain by which to filter the
   * results (default: all domains)
   * @param {boolean} [breachOptions.truncate] truncate the results to only
   * include the name of each breach (default: false)
   * @returns {Promise} a Promise which resolves to an object containing a
   * "breaches" key (which can be null or an array of breach objects) and a
   * "pastes" key (which can be null or an array of paste objects), or rejects
   * with an Error
   * @example
   * hibp.search('foo')
   *   .then((data) => {
   *     if (data.breaches || data.pastes) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   * @example
   * hibp.search('nobody@nowhere.com', { truncate: true })
   *   .then((data) => {
   *     if (data.breaches || data.pastes) {
   *       // ...
   *     } else {
   *       // ...
   *     }
   *   })
   *   .catch((err) => {
   *     // ...
   *   });
   *
   * @see https://haveibeenpwned.com/
   */
  search: (account, breachOptions = {}) =>
    Promise.all([
      hibp.breachedAccount(account, breachOptions),
      // This email regex is garbage but it seems to be what the API uses:
      /^.+@.+$/.test(account) ? hibp.pasteAccount(account) : null,
    ]).then(breachesAndPastes => ({
      breaches: breachesAndPastes[0],
      pastes: breachesAndPastes[1],
    })),
};

export default hibp;
<|endoftext|>


<commit_before>#!/usr/bin/env node
require("proof")(2, function (equal, tz) {
    var tz = require('timezone')
  var detroit = tz(require("timezone/America/Detroit"), "America/Detroit");
  equal(detroit(Date.UTC(2000, 0, 1), "%m/%d/%Y %H:%M"), "12/31/1999 19:00", "timezone");
  var detroit = detroit("%m/%d/%Y %H:%M");
  equal(detroit(Date.UTC(2000, 0, 1)), "12/31/1999 19:00", "format and timezone");
});
<commit_msg>Tidy specialization test.<commit_after>#!/usr/bin/env node

require('proof')(2, prove)

function prove (assert) {
    var tz = require('timezone')
    var detroit = tz(require('timezone/America/Detroit'), 'America/Detroit')
    assert(detroit(Date.UTC(2000, 0, 1), '%m/%d/%Y %H:%M'), '12/31/1999 19:00', 'timezone')
    var detroit = detroit('%m/%d/%Y %H:%M')
    assert(detroit(Date.UTC(2000, 0, 1)), '12/31/1999 19:00', 'format and timezone')
}
<|endoftext|>


<commit_before>
    var th = {};
    var Thread = Concurrent.Thread;
    var moveStep = 2;
    var sleepTime = 10;
    var rotateStep = 5;

    var Turtle = (function () {

        var Turtle = function () {
            this.x = 100.0;
            this.y = 100.0;

            //  dx = sin(angle), dy = -cos(angle)
            this.dx = 0.0;
            this.dy = 0.0;

            // Turtle animation rubber line
            this.rx = this.x;
            this.ry = this.y;
            this.rubber = false; // 何に使ってる…？

            this.angle = -90.0;

            this.show = true;

            this.penDown = true;
            this.penColor = "black";

            this.kameColor = "green";

            this.size = 10;
        };

        var p = Turtle.prototype;
        p.fd = function (d) {	// 使ってない、関数実装サンプル
            th = Thread.create(function (d) {

            }, d)
        };
        return Turtle;
    })();

    var ttl = new Turtle();

    function fd(d) {
        if (d < 0) bk(-d);
        else th = Thread.create(function (d) {
            var xx = ttl.x, yy = ttl.y;
            ttl.rubber = ttl.penDown;
            ttl.dx = Math.cos(deg2rad(ttl.angle));
            ttl.dy = Math.sin(deg2rad(ttl.angle));
            setRxRy(ttl);
            for (var i = 0; i < d; i += moveStep) {
                ttl.x = xx + ttl.dx * i;
                ttl.y = yy + ttl.dy * i;
                draw(ttl);
                setRxRy(ttl);
                sleep(sleepTime);
            }
            ttl.x = xx + ttl.dx * d;
            ttl.y = yy + ttl.dy * d;
            draw(ttl);
        }, d);
        // TODO if penDown then save Line
    }

    function bk(d) {
        if (d < 0) fd(-d);
        else th = Thread.create(function (d) {
            var xx = ttl.x, yy = ttl.y;
            ttl.rubber = ttl.penDown;
            ttl.dx = Math.cos(deg2rad(ttl.angle));
            ttl.dy = Math.sin(deg2rad(ttl.angle));
            setRxRy(ttl);
            for (var i = 0; i < d; i += moveStep) {
                ttl.x = xx - ttl.dx * i;
                ttl.y = yy - ttl.dy * i;
                draw(ttl);
                setRxRy(ttl);
                sleep(sleepTime);
            }
            ttl.x = xx - ttl.dx * d;
            ttl.y = yy - ttl.dy * d;
            draw(ttl);
        }, d);
        // TODO if penDown then save Line
    }

    function rt(deg) {
        if (deg < 0) lt(-deg);
        else th = Thread.create(function (deg) {
            var tmpAngle = ttl.angle;
            var tmpPendown = ttl.penDown;
            up();
            for (var i = 0; i < deg; i += rotateStep) {
                draw(ttl);
                ttl.angle += rotateStep;
                sleep(sleepTime);
            }
            ttl.angle = tmpAngle + deg;
            ttl.penDown = tmpPendown;
            drawTurtle(ttl);
        }, deg);
    }

    function lt(deg) {
        if (deg < 0) rt(-deg);
        else th = Thread.create(function (deg) {
            var tmpAngle = ttl.angle;
            var tmpPendown = ttl.penDown;
            up();
            for (var i = 0; i < deg; i += rotateStep) {
                draw(ttl);
                ttl.angle -= rotateStep;
                sleep(sleepTime);
            }
            ttl.angle = tmpAngle - deg;
            ttl.penDown = tmpPendown;
            drawTurtle(ttl);
        }, deg);
    }

    function up(){
        ttl.penDown = false;
    }

    function down(){
        ttl.penDown = true;
    }

    function color(c){
        ttl.penColor = c;
    }

    /* 描画関連 */

    function draw(t) {
        if (t.show)drawTurtle(t);
        if (t.penDown) drawLine(t);
    }

    function drawTurtle(t) {
        var canvas = document.getElementById('turtleCanvas');
        if (!canvas.getContext)return;
        var w = canvas.width;
        var h = canvas.height;
        var ctx = canvas.getContext('2d');
        ctx.clearRect(0, 0, w, h);

        // 胴体
        ctx.beginPath();
        ctx.arc(t.x, t.y, 10, 0, Math.PI * 2, true);
        ctx.fillStyle = t.kameColor;
        ctx.fill();
        ctx.closePath();

        // 頭部分
        ctx.beginPath();
        ctx.arc(t.x + 5 * Math.cos(deg2rad(t.angle)),
                t.y + 5 * Math.sin(deg2rad(t.angle)), 3, 0, Math.PI * 2, true);
        ctx.fillStyle = "red";
        ctx.fill();
        ctx.closePath();

    }

    function drawLine(t) {
        var canvas = document.getElementById('locusCanvas');
        if (!canvas.getContext)return;
        var ctx = canvas.getContext('2d');

        ctx.beginPath();
        ctx.moveTo(t.rx, t.ry);
        ctx.lineTo(t.x, t.y);
        ctx.closePath();
        ctx.strokeStyle = t.penColor;
        ctx.stroke();
    }

    /* Turtle補助メソッド */
    function setRxRy(t) {
        t.rx = t.x;
        t.ry = t.y;
    }

    /* 一般補助メソッド */

    function deg2rad(deg) {
        return deg * Math.PI / 180;
    }

    function sleep(t) {
        Thread.sleep(t)
    }

    // /* 実行部分 */
    // function start() {
    //     main();
    // }

<commit_msg>リファクタリング<commit_after>'use strict';
var th = {};
var Thread = Concurrent.Thread;
var moveStep = 2;
var sleepTime = 10;
var rotateStep = 5;

var Turtle = (function () {

    var Turtle = function () {
        this.x = 100.0;
        this.y = 100.0;

        //  dx = sin(angle), dy = -cos(angle)
        this.dx = 0.0;
        this.dy = 0.0;

        // Turtle animation rubber line
        this.rx = this.x;
        this.ry = this.y;
        this.rubber = false; // 何に使ってる…？

        this.angle = -90.0;

        this.show = true;

        this.penDown = true;
        this.penColor = "black";

        this.kameColor = "green";

        this.size = 10;
    };

    var p = Turtle.prototype;

    return Turtle;
})();

var ttl = new Turtle();

function fd(d) {
    if (d < 0){
        bk(-d);
    } else {
        th = Thread.create(function (d) {
            var xx = ttl.x, yy = ttl.y;
            ttl.rubber = ttl.penDown;
            ttl.dx = Math.cos(deg2rad(ttl.angle));
            ttl.dy = Math.sin(deg2rad(ttl.angle));
            setRxRy(ttl);
            for (var i = 0; i < d; i += moveStep) {
                ttl.x = xx + ttl.dx * i;
                ttl.y = yy + ttl.dy * i;
                draw(ttl);
                setRxRy(ttl);
                sleep(sleepTime);
            }
            ttl.x = xx + ttl.dx * d;
            ttl.y = yy + ttl.dy * d;
            draw(ttl);
        }, d);
    }
    // TODO if penDown then save Line
}

function bk(d) {
    if (d < 0){
        fd(-d);
    } else {
        th = Thread.create(function (d) {
            var xx = ttl.x, yy = ttl.y;
            ttl.rubber = ttl.penDown;
            ttl.dx = Math.cos(deg2rad(ttl.angle));
            ttl.dy = Math.sin(deg2rad(ttl.angle));
            setRxRy(ttl);
            for (var i = 0; i < d; i += moveStep) {
                ttl.x = xx - ttl.dx * i;
                ttl.y = yy - ttl.dy * i;
                draw(ttl);
                setRxRy(ttl);
                sleep(sleepTime);
            }
            ttl.x = xx - ttl.dx * d;
            ttl.y = yy - ttl.dy * d;
            draw(ttl);
        }, d);
    // TODO if penDown then save Line
}
}

function rt(deg) {
    if (deg < 0) {
        lt(-deg);
    } else {
       th = Thread.create(function (deg) {
        var tmpAngle = ttl.angle;
        var tmpPendown = ttl.penDown;
        up();
        for (var i = 0; i < deg; i += rotateStep) {
            draw(ttl);
            ttl.angle += rotateStep;
            sleep(sleepTime);
        }
        ttl.angle = tmpAngle + deg;
        ttl.penDown = tmpPendown;
        drawTurtle(ttl);
    }, deg);
   }
}

function lt(deg) {
    if (deg < 0) {
        rt(-deg);
    } else {
        th = Thread.create(function (deg) {
            var tmpAngle = ttl.angle;
            var tmpPendown = ttl.penDown;
            up();
            for (var i = 0; i < deg; i += rotateStep) {
                draw(ttl);
                ttl.angle -= rotateStep;
                sleep(sleepTime);
            }
            ttl.angle = tmpAngle - deg;
            ttl.penDown = tmpPendown;
            drawTurtle(ttl);
        }, deg);
    }
}

function up(){
    ttl.penDown = false;
}

function down(){
    ttl.penDown = true;
}

function color(c){
    ttl.penColor = c;
}

/* 描画関連 */

function draw(t) {
    if (t.show) {
        drawTurtle(t);
    }
    if (t.penDown) {
        drawLine(t);
    }
}

function drawTurtle(t) {
    var canvas = document.getElementById('turtleCanvas');
    if (!canvas.getContext) {
        return;
    }
    var w = canvas.width;
    var h = canvas.height;
    var ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, w, h);

    // 胴体
    ctx.beginPath();
    ctx.arc(t.x, t.y, 10, 0, Math.PI * 2, true);
    ctx.fillStyle = t.kameColor;
    ctx.fill();
    ctx.closePath();

    // 頭部分
    ctx.beginPath();
    ctx.arc(t.x + 5 * Math.cos(deg2rad(t.angle)),
        t.y + 5 * Math.sin(deg2rad(t.angle)), 3, 0, Math.PI * 2, true);
    ctx.fillStyle = "red";
    ctx.fill();
    ctx.closePath();

}

function drawLine(t) {
    var canvas = document.getElementById('locusCanvas');
    if (!canvas.getContext) {
        return;
    }
    var ctx = canvas.getContext('2d');

    ctx.beginPath();
    ctx.moveTo(t.rx, t.ry);
    ctx.lineTo(t.x, t.y);
    ctx.closePath();
    ctx.strokeStyle = t.penColor;
    ctx.stroke();
}

/* Turtle補助メソッド */
function setRxRy(t) {
    t.rx = t.x;
    t.ry = t.y;
}

/* 一般補助メソッド */

function deg2rad(deg) {
    return deg * Math.PI / 180;
}

function sleep(t) {
    Thread.sleep(t)
}

// /* 実行部分 */
// function start() {
//     main();
// }

<|endoftext|>


<commit_before>import React from "react"
import PropTypes from "prop-types"

export default function HTML(props) {
  return (
    <html {...props.htmlAttributes}>
      <head>
        <meta charSet="utf-8" />
        <meta httpEquiv="x-ua-compatible" content="ie=edge" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1, shrink-to-fit=no"
        />
        <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato"></link>
        <link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' />
        {props.headComponents}
      </head>
      <body {...props.bodyAttributes}>
        {props.preBodyComponents}
        <noscript key="noscript" id="gatsby-noscript">
          This app works best with JavaScript enabled.
        </noscript>
        <div
          key={`body`}
          id="___gatsby"
          dangerouslySetInnerHTML={{ __html: props.body }}
          className="gatsby__body"
        />
        {props.postBodyComponents}
      </body>
    </html>
  )
}

HTML.propTypes = {
  htmlAttributes: PropTypes.object,
  headComponents: PropTypes.array,
  bodyAttributes: PropTypes.object,
  preBodyComponents: PropTypes.array,
  body: PropTypes.string,
  postBodyComponents: PropTypes.array,
}
<commit_msg>add title<commit_after>import React from "react"
import PropTypes from "prop-types"

export default function HTML(props) {
  return (
    <html {...props.htmlAttributes}>
      <head>
        <title>colin tinney</title>
        <meta charSet="utf-8" />
        <meta httpEquiv="x-ua-compatible" content="ie=edge" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1, shrink-to-fit=no"
        />
        <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato"></link>
        <link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' />
        {props.headComponents}
      </head>
      <body {...props.bodyAttributes}>
        {props.preBodyComponents}
        <noscript key="noscript" id="gatsby-noscript">
          This app works best with JavaScript enabled.
        </noscript>
        <div
          key={`body`}
          id="___gatsby"
          dangerouslySetInnerHTML={{ __html: props.body }}
          className="gatsby__body"
        />
        {props.postBodyComponents}
      </body>
    </html>
  )
}

HTML.propTypes = {
  htmlAttributes: PropTypes.object,
  headComponents: PropTypes.array,
  bodyAttributes: PropTypes.object,
  preBodyComponents: PropTypes.array,
  body: PropTypes.string,
  postBodyComponents: PropTypes.array,
}
<|endoftext|>


<commit_before>var cookiesMap = {};
//               //  1   //////// 2 /////// 3 ////    4  //
var domainReg = /^(https?)\:\/\/(.*?)(?:\/(.*?))?([\?#].*)?$/i;
var { efrontURI, cross_host = efrontURI } = this;
var location_host = location.href.replace(domainReg, '$1://$2/');
if (cross_host) {
    if (!domainReg.test(cross_host)) {
        console.error("cross_host格式不正确", cross_host);
        cross_host = "/";
    } else {
        cross_host = (/^https/.test(cross_host) ? "https://" : "http://") + cross_host.replace(domainReg, '$2/');
    }
}
var base = domainReg.test(location.href) ? cross_host || '/' : "http://efront.cc/";
var HeadersKeys = ["Content-Type"];
var cookieItemsInSessionStorageKey = "--zimoli-coms-cross";
var cookiesData = sessionStorage.getItem(cookieItemsInSessionStorageKey);
if (cookiesData) {
    try {
        extend(cookiesMap, JSON.parse(cookiesData));
    } catch (e) {
        console.warn("加载cookie出错！");
    }
}
function getDomainPath(url) {
    return url.replace(domainReg, "$2/$3");
}
function getRequestProtocol(url) {
    if (/^https:/i.test(url)) {
        return "https";
    }
    return "http";
}
function getCookies(domainPath) {
    var cookieObject = {};
    var splited = domainPath.split("/");
    var domain = splited[0];
    do {
        var copy = splited.slice(0);
        do {
            domainPath = copy.join("/");
            var cookie = cookiesMap[domainPath];
            if (cookie) {
                for (var k in cookie) {
                    if (!cookieObject[k]) {
                        cookieObject[k] = cookie[k];
                    }
                }
            }
            copy.pop();
        } while (copy.length);
        domain = domain.replace(/^.*?(\.|$)/, "");
        splited[0] = domain;
    } while (domain.length);
    return serialize(cookieObject, ";");
}
function addCookie(cookie, originDomain = "") {
    if (cookie) {
        clearTimeout(addCookie.save_timer);
        addCookie.save_timer = setTimeout(function () {
            sessionStorage.setItem(cookieItemsInSessionStorageKey, JSON.stringify(cookiesMap));
        }, 20);
        cookie.replace(/(^|;|,)\s*(expires)=(\w*),([^=]*)(;|$)/ig, "$1$2=$3.$4$5")
            .split(/,\s*/).map(function (cookie) {
                var cookieObject = {};
                var result = cookie.split(/;\s*/);
                result.slice(1).map(function (kev) {
                    var kvs = /^(.+?)\=(.+?)$/.exec(kev);
                    if (kvs) {
                        var [, k, v] = kvs;
                        cookieObject[k.toLowerCase()] = v;
                    }
                });
                var { path, domain } = cookieObject;
                if (!domain) {
                    domain = originDomain.replace(/[^\/]+$/, "");
                }
                if (/^\./.test(domain)) {
                    domain = domain.replace(/^\.+/, "");
                }
                var destPath;
                if (/^\//.test(path)) {
                    destPath = domain.replace(/\/.*$/, "") + path;
                } else {
                    destPath = domain;
                }
                destPath = destPath.replace(/\/+$/, "");
                if (originDomain.indexOf(destPath) >= 0) {
                    var cookieMap = parseKV(result[0], ";");
                    if (!cookiesMap[destPath]) {
                        cookiesMap[destPath] = cookieMap;
                    } else {
                        extend(cookiesMap[destPath], cookieMap);
                    }
                }
            });
    }
}
function isChildPath(relative, path) {
    return relative.replace(/^(.*\/)[^\/]*$/, path);
}
var digest = function () {
    if (cross.digest instanceof Function) cross.digest();
};

var getCrossUrl = function (domain, headers) {
    if (location_host === domain.slice(0, location_host.length)) return domain;
    var originDomain = getDomainPath(domain);
    var _cookies = getCookies(originDomain);
    var _headers = {};
    if (_cookies) {
        _headers.Cookie = _cookies;
    }
    extend(_headers, headers);
    var spliter = encodeURIComponent("/");
    return domain
        .replace(/^s?\/\//i, "http$&")
        .replace(domainReg, base + `%7B${/^(https\:|s\/\/)/i.test(domain) ? "s" : ""}${spliter + spliter}$2${spliter}${serialize(_headers)}%7D@$3$4`);
};
function cross(method, url, headers) {
    var originDomain = getDomainPath(url);
    if (!originDomain) throw new Error("Unsupposed url format!");
    var _cookies = getCookies(originDomain);
    var _headers = {};
    if (_cookies) {
        _headers.Cookie = _cookies;
    }
    extend(_headers, headers);
    if (/^[mc]/i.test(method)) {
        _headers["User-Agent"] = /^m/i.test(method)
            ? "Efront/1.9 (iPhone) Safari/602.1"
            : "Efront/1.9 (Windows) Chrome/77.0.3865.90";
        method = method.slice(1);
    }
    var xhr = new XHR;
    var abort = xhr.abort;
    xhr.abort = function () {
        xhr.onreadystatechange = null;
        abort.call(this);
    };
    xhr.open(method, getCrossUrl(url, _headers));
    HeadersKeys.map(function (k) {
        if (_headers[k]) xhr.setRequestHeader(k, _headers[k]);
        delete _headers[k];
    });

    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.getResponseHeader) {
                var cookie = xhr.getResponseHeader("efront-cookie");
                addCookie(cookie, originDomain);
            }
            switch (xhr.status) {
                case 0:
                case 200:
                case 201:
                case 304:
                    onload(xhr);
                    break;
                case 302:
                case 301:
                    if (xhr.isRedirected > 2) break;
                    var location = xhr.getResponseHeader("efront-location");
                    if (!domainReg.test(location)) {
                        if (/^\//.test(location)) {
                            location = originDomain.replace(/\/.*$/, location);
                        } else {
                            location = originDomain.replace(/[^\/+]$/, location);
                        }
                        location = getRequestProtocol(url) + "://" + location;
                    }
                    var crs = cross("get", location, headers);
                    crs.isRedirected = (xhr.isRedirected || 0) + 1;
                    onload && crs.done(onload, false);
                    onerror && crs.error(onerror, false);
                    break;
                default:
                    onerror(xhr);
            }
            dispatch(window, 'render');
        }
    };
    setTimeout(function () {
        if (!isEmpty(jsondata) && isEmpty(datas)) {
            datas = JSON.stringify(jsondata);
            if (datas === "{}" || datas === "[]") {
                datas = '';
            } else {
                xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
            }
        }
        send.call(xhr, datas);
    }, 0);
    var onload = function (xhr) {
        if (xhr.decoder) {
            xhr = xhr.decoder(xhr);
        }
        onloads.map(e => e instanceof Function && e(xhr));
        digest();
    };
    var onerror = function (xhr) {
        onerrors.map(e => e instanceof Function && e(xhr));
        digest();
    };
    var onloads = [], onerrors = [];
    xhr.done = xhr.success = function (on, asqueue = true) {
        if (asqueue) {
            onloads.push(on);
        } else {
            onload = on;
        }
        return xhr;
    };
    var send = xhr.send;
    var datas = "";
    var jsondata = null;
    xhr.json = xhr.data = xhr.send = function (data, value) {
        if (!jsondata && !(isEmpty(data) && isEmpty(value))) jsondata = {};
        if (data instanceof Object) {
            extend(jsondata, data);
        } else if (value) {
            extend(jsondata, { [data]: value })
        } else if (isString(data) && value !== false) {
            var data = /^\{/.test(data) ? JSON.parse(data) : parseKV(data, "&", "=");
            extend(jsondata, data);
        } else {
            datas = data;
        }
        return xhr;
    };
    xhr.form = function (data) {
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xhr.data(data);
        datas = serialize(jsondata, "&", "=");
    };
    xhr.fail = xhr.error = function (on, asqueue = true) {
        if (asqueue) {
            onerrors.push(on);
        } else {
            onerror = on;
        }
        onerror = on;
        return xhr;
    };
    return xhr;
}
extend(cross, {
    getCookies,
    addCookie,
    getCrossUrl
});<commit_msg>get请求时把参数附加到url<commit_after>var cookiesMap = {};
//               //  1   //////// 2 /////// 3 ////    4  //
var domainReg = /^(https?)\:\/\/(.*?)(?:\/(.*?))?([\?#].*)?$/i;
var { efrontURI, cross_host = efrontURI } = this;
var location_host = location.href.replace(domainReg, '$1://$2/');
if (cross_host) {
    if (!domainReg.test(cross_host)) {
        console.error("cross_host格式不正确", cross_host);
        cross_host = "/";
    } else {
        cross_host = (/^https/.test(cross_host) ? "https://" : "http://") + cross_host.replace(domainReg, '$2/');
    }
}
var base = domainReg.test(location.href) ? cross_host || '/' : "http://efront.cc/";
var HeadersKeys = ["Content-Type"];
var cookieItemsInSessionStorageKey = "--zimoli-coms-cross";
var cookiesData = sessionStorage.getItem(cookieItemsInSessionStorageKey);
if (cookiesData) {
    try {
        extend(cookiesMap, JSON.parse(cookiesData));
    } catch (e) {
        console.warn("加载cookie出错！");
    }
}
function getDomainPath(url) {
    return url.replace(domainReg, "$2/$3");
}
function getRequestProtocol(url) {
    if (/^https:/i.test(url)) {
        return "https";
    }
    return "http";
}
function getCookies(domainPath) {
    var cookieObject = {};
    var splited = domainPath.split("/");
    var domain = splited[0];
    do {
        var copy = splited.slice(0);
        do {
            domainPath = copy.join("/");
            var cookie = cookiesMap[domainPath];
            if (cookie) {
                for (var k in cookie) {
                    if (!cookieObject[k]) {
                        cookieObject[k] = cookie[k];
                    }
                }
            }
            copy.pop();
        } while (copy.length);
        domain = domain.replace(/^.*?(\.|$)/, "");
        splited[0] = domain;
    } while (domain.length);
    return serialize(cookieObject, ";");
}
function addCookie(cookie, originDomain = "") {
    if (cookie) {
        clearTimeout(addCookie.save_timer);
        addCookie.save_timer = setTimeout(function () {
            sessionStorage.setItem(cookieItemsInSessionStorageKey, JSON.stringify(cookiesMap));
        }, 20);
        cookie.replace(/(^|;|,)\s*(expires)=(\w*),([^=]*)(;|$)/ig, "$1$2=$3.$4$5")
            .split(/,\s*/).map(function (cookie) {
                var cookieObject = {};
                var result = cookie.split(/;\s*/);
                result.slice(1).map(function (kev) {
                    var kvs = /^(.+?)\=(.+?)$/.exec(kev);
                    if (kvs) {
                        var [, k, v] = kvs;
                        cookieObject[k.toLowerCase()] = v;
                    }
                });
                var { path, domain } = cookieObject;
                if (!domain) {
                    domain = originDomain.replace(/[^\/]+$/, "");
                }
                if (/^\./.test(domain)) {
                    domain = domain.replace(/^\.+/, "");
                }
                var destPath;
                if (/^\//.test(path)) {
                    destPath = domain.replace(/\/.*$/, "") + path;
                } else {
                    destPath = domain;
                }
                destPath = destPath.replace(/\/+$/, "");
                if (originDomain.indexOf(destPath) >= 0) {
                    var cookieMap = parseKV(result[0], ";");
                    if (!cookiesMap[destPath]) {
                        cookiesMap[destPath] = cookieMap;
                    } else {
                        extend(cookiesMap[destPath], cookieMap);
                    }
                }
            });
    }
}
function isChildPath(relative, path) {
    return relative.replace(/^(.*\/)[^\/]*$/, path);
}
var digest = function () {
    if (cross.digest instanceof Function) cross.digest();
};

var getCrossUrl = function (domain, headers) {
    if (location_host === domain.slice(0, location_host.length)) return domain;
    var originDomain = getDomainPath(domain);
    var _cookies = getCookies(originDomain);
    var _headers = {};
    if (_cookies) {
        _headers.Cookie = _cookies;
    }
    extend(_headers, headers);
    var spliter = encodeURIComponent("/");
    return domain
        .replace(/^s?\/\//i, "http$&")
        .replace(domainReg, base + `%7B${/^(https\:|s\/\/)/i.test(domain) ? "s" : ""}${spliter + spliter}$2${spliter}${serialize(_headers)}%7D@$3$4`);
};
function cross(method, url, headers) {
    var originDomain = getDomainPath(url);
    if (!originDomain) throw new Error("Unsupposed url format!");
    var _cookies = getCookies(originDomain);
    var _headers = {};
    if (_cookies) {
        _headers.Cookie = _cookies;
    }
    extend(_headers, headers);
    if (/^[mc]/i.test(method)) {
        _headers["User-Agent"] = /^m/i.test(method)
            ? "Efront/1.9 (iPhone) Safari/602.1"
            : "Efront/1.9 (Windows) Chrome/77.0.3865.90";
        method = method.slice(1);
    }
    var xhr = new XHR;
    var abort = xhr.abort;
    xhr.abort = function () {
        xhr.onreadystatechange = null;
        abort.call(this);
    };
    HeadersKeys.map(function (k) {
        if (_headers[k]) xhr.setRequestHeader(k, _headers[k]);
        delete _headers[k];
    });

    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.getResponseHeader) {
                var cookie = xhr.getResponseHeader("efront-cookie");
                addCookie(cookie, originDomain);
            }
            switch (xhr.status) {
                case 0:
                case 200:
                case 201:
                case 304:
                    onload(xhr);
                    break;
                case 302:
                case 301:
                    if (xhr.isRedirected > 2) break;
                    var location = xhr.getResponseHeader("efront-location");
                    if (!domainReg.test(location)) {
                        if (/^\//.test(location)) {
                            location = originDomain.replace(/\/.*$/, location);
                        } else {
                            location = originDomain.replace(/[^\/+]$/, location);
                        }
                        location = getRequestProtocol(url) + "://" + location;
                    }
                    var crs = cross("get", location, headers);
                    crs.isRedirected = (xhr.isRedirected || 0) + 1;
                    onload && crs.done(onload, false);
                    onerror && crs.error(onerror, false);
                    break;
                default:
                    onerror(xhr);
            }
            dispatch(window, 'render');
        }
    };
    var setRequestHeader = xhr.setRequestHeader;
    var realHeaders = Object.create(null);
    xhr.setRequestHeader = function (key, value) {
        realHeaders[key] = value;
    };
    setTimeout(function () {
        if (!isEmpty(jsondata) && isEmpty(datas)) {
            if (/^(get|head|trace)$/i.test(method)) {
                url = url.replace(/#[\s\S]*/, '');
                datas = serialize(jsondata);
                url += (/\?/.test(url) ? "&" : "?") + datas;
            } else {
                datas = JSON.stringify(jsondata);
                if (datas === "{}" || datas === "[]") {
                    datas = '';
                } else {
                    realHeaders["Content-Type"] = "application/json;charset=UTF-8";
                }
            }
        }
        xhr.open(method, getCrossUrl(url, _headers));
        Object.keys(realHeaders).forEach(key => setRequestHeader.call(xhr, key, realHeaders[key]));
        send.call(xhr, datas);
    }, 0);
    var onload = function (xhr) {
        if (xhr.decoder) {
            xhr = xhr.decoder(xhr);
        }
        onloads.map(e => e instanceof Function && e(xhr));
        digest();
    };
    var onerror = function (xhr) {
        onerrors.map(e => e instanceof Function && e(xhr));
        digest();
    };
    var onloads = [], onerrors = [];
    xhr.done = xhr.success = function (on, asqueue = true) {
        if (asqueue) {
            onloads.push(on);
        } else {
            onload = on;
        }
        return xhr;
    };
    var send = xhr.send;
    var datas = "";
    var jsondata = null;
    xhr.json = xhr.data = xhr.send = function (data, value) {
        if (!jsondata && !(isEmpty(data) && isEmpty(value))) jsondata = {};
        if (data instanceof Object) {
            extend(jsondata, data);
        } else if (value) {
            extend(jsondata, { [data]: value });
        } else if (isString(data) && value !== false) {
            var data = /^\{/.test(data) ? JSON.parse(data) : parseKV(data, "&", "=");
            extend(jsondata, data);
        } else {
            datas = data;
        }
        return xhr;
    };
    xhr.form = function (data) {
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xhr.data(data);
        datas = serialize(jsondata, "&", "=");
    };
    xhr.fail = xhr.error = function (on, asqueue = true) {
        if (asqueue) {
            onerrors.push(on);
        } else {
            onerror = on;
        }
        onerror = on;
        return xhr;
    };
    return xhr;
}
extend(cross, {
    getCookies,
    addCookie,
    getCrossUrl
});<|endoftext|>


<commit_before>/*
Get movie data for a particular movie title.
Currently accessing:
- OMDB (https://www.omdbapi.com/)
- The Movie DB (https://www.themoviedb.org/)
- Rotten Tomatoes (https://www.rottentomatoes.com/)
*/

/* eslint-disable max-len */

const rpn = require('request-promise-native');
const cheerio = require('cheerio');
const Fuse = require('fuse.js'); // https://github.com/krisk/fuse

class MovieDatabases {

  /*
  Fetch movie data from The Movie DB.
  */
  static theMovieDB(movieTitle) {
    const theMovieDbBaseUrl = 'http://api.themoviedb.org/3';

    return new Promise((resolve, reject) => {
      const theMovieDbOptions = {
        uri: `${theMovieDbBaseUrl}/search/movie?query=${movieTitle}&api_key=${process.env.THE_MOVIE_DB_API_KEY}`,
        json: true,
      };

      const theMovieDbData = {
        overview: '',
        posterImage: '',
        trailer: '',
        title: movieTitle,
      };

      rpn(theMovieDbOptions)
      .then((result) => {
        if (result.results.length > 0) {
          // Get the movie title with the closest fuzzy string match
          const fuseOptions = {
            tokenize: true,
            matchAllTokens: true,
            keys: ['title'],
          };

          const fuse = new Fuse(result.results, fuseOptions);
          const bestMatchingMovie = fuse.search(movieTitle)[0];

          theMovieDbData.overview = bestMatchingMovie.overview;
          theMovieDbData.posterImage = `https://image.tmdb.org/t/p/w500${bestMatchingMovie.poster_path}`;
          theMovieDbData.title = bestMatchingMovie.title;

          // Get the movie trailer for it
          theMovieDbOptions.uri = `${theMovieDbBaseUrl}/movie/${bestMatchingMovie.id}/videos?api_key=${process.env.THE_MOVIE_DB_API_KEY}&language=en-US`;
          rpn(theMovieDbOptions)
          .then((videoData) => {
            if (videoData.results.length > 0) {
              /*
              Loop through the results to find the object that is tagged with type 'Trailer'.
              note: this returns the final trailer in the video array.
              */
              videoData.results.forEach((video) => {
                if (video.type === 'Trailer') {
                  theMovieDbData.trailer = `https://www.youtube.com/watch?v=${video.key}`;
                }
              });
              resolve(theMovieDbData);
            } else {
              resolve(theMovieDbData);
            }
          })
          .catch((trailerError) => {
            reject(`Error grabbing trailer: ${trailerError}`);
          });
        } else {
          resolve(theMovieDbData);
        }
      })

      .catch((error) => {
        reject(`Error getting data from The Movie DB: ${error}`);
      });
    });
  }

  /*
  Fetch a range of movie data from OMDB
  */
  static omdb(movieTitle) {
    const checkForValue = value => ((value === 'N/A') ? '' : value);
    return new Promise((resolve, reject) => {
      const movieDbOptions = {
        uri: `http://www.omdbapi.com/?t=${movieTitle}&y=&plot=short&r=json&tomatoes=true`,
        json: true,
      };

      const movieMetaData = {
        actors: '',
        runtime: '',
        imdbRating: '',
        imdbUrl: '',
        rottenTomatoesUrl: '',
      };

      rpn(movieDbOptions)
      .then((result) => {
        if (!result.Error) {
          movieMetaData.actors = checkForValue(result.Actors);
          movieMetaData.runtime = checkForValue(result.Runtime);
          movieMetaData.imdbRating = checkForValue(result.imdbRating);
          movieMetaData.imdbUrl = (checkForValue(result.imdbID) !== '') ? `http://www.imdb.com/title/${result.imdbID}` : '';
          movieMetaData.rottenTomatoesUrl = checkForValue(result.tomatoURL);
        }
        resolve(movieMetaData);
      })
      .catch((err) => {
        reject(`Error grabbing data from omdb: ${err}`);
      });
    });
  }

  /*
  Fetch data from Rotten Tomatoes itself.
  */
  static rottenTomatoes(rottenTomatoesUrl) {
    const rottenTomatoesMetaData = {
      tomatoMeter: '',
      tomatoConsensus: '',
    };

    if (!rottenTomatoesUrl) {
      return Promise.resolve(rottenTomatoesMetaData);
    }

    return new Promise((resolve, reject) => {
      const options = {
        uri: rottenTomatoesUrl,
        transform: body => cheerio.load(body),
      };

      rpn(options)
      .then(($) => {
        rottenTomatoesMetaData.tomatoMeter = $('#all-critics-numbers .meter-value span').text();
        rottenTomatoesMetaData.tomatoConsensus = $('#all-critics-numbers .critic_consensus').text().replace('Critics Consensus:', '').trim();
        resolve(rottenTomatoesMetaData);
      })
      .catch((err) => {
        reject(`Error grabbing data from Rotten Tomatoes: ${err}`);
      });
    });
  }
}

module.exports = MovieDatabases;
<commit_msg>pull movies from this year only<commit_after>/*
Get movie data for a particular movie title.
Currently accessing:
- OMDB (https://www.omdbapi.com/)
- The Movie DB (https://www.themoviedb.org/)
- Rotten Tomatoes (https://www.rottentomatoes.com/)
*/

/* eslint-disable max-len */

const rpn = require('request-promise-native');
const cheerio = require('cheerio');
const Fuse = require('fuse.js'); // https://github.com/krisk/fuse

class MovieDatabases {

  /*
  Fetch movie data from The Movie DB.
  */
  static theMovieDB(movieTitle) {
    const theMovieDbBaseUrl = 'http://api.themoviedb.org/3';

    return new Promise((resolve, reject) => {
      const theMovieDbOptions = {
        uri: `${theMovieDbBaseUrl}/search/movie?query=${movieTitle}&api_key=${process.env.THE_MOVIE_DB_API_KEY}`,
        json: true,
      };

      const theMovieDbData = {
        overview: '',
        posterImage: '',
        trailer: '',
        title: movieTitle,
      };

      rpn(theMovieDbOptions)
      .then((result) => {
        if (result.results.length > 0) {
          // Get the movie title with the closest fuzzy string match
          const fuseOptions = {
            tokenize: true,
            matchAllTokens: true,
            keys: ['title'],
          };

          const fuse = new Fuse(result.results, fuseOptions);
          const bestMatchingMovie = fuse.search(movieTitle)[0];

          theMovieDbData.overview = bestMatchingMovie.overview;
          theMovieDbData.posterImage = `https://image.tmdb.org/t/p/w500${bestMatchingMovie.poster_path}`;
          theMovieDbData.title = bestMatchingMovie.title;

          // Get the movie trailer for it
          theMovieDbOptions.uri = `${theMovieDbBaseUrl}/movie/${bestMatchingMovie.id}/videos?api_key=${process.env.THE_MOVIE_DB_API_KEY}&language=en-US`;
          rpn(theMovieDbOptions)
          .then((videoData) => {
            if (videoData.results.length > 0) {
              /*
              Loop through the results to find the object that is tagged with type 'Trailer'.
              note: this returns the final trailer in the video array.
              */
              videoData.results.forEach((video) => {
                if (video.type === 'Trailer') {
                  theMovieDbData.trailer = `https://www.youtube.com/watch?v=${video.key}`;
                }
              });
              resolve(theMovieDbData);
            } else {
              resolve(theMovieDbData);
            }
          })
          .catch((trailerError) => {
            reject(`Error grabbing trailer: ${trailerError}`);
          });
        } else {
          resolve(theMovieDbData);
        }
      })

      .catch((error) => {
        reject(`Error getting data from The Movie DB: ${error}`);
      });
    });
  }

  /*
  Fetch a range of movie data from OMDB
  */
  static omdb(movieTitle) {
    const checkForValue = value => ((value === 'N/A') ? '' : value);
    return new Promise((resolve, reject) => {
      const movieDbOptions = {
        uri: `http://www.omdbapi.com/?t=${movieTitle}&plot=short&r=json&tomatoes=true&y=${new Date().getFullYear()}`,
        json: true,
      };

      const movieMetaData = {
        actors: '',
        runtime: '',
        imdbRating: '',
        imdbUrl: '',
        rottenTomatoesUrl: '',
      };

      rpn(movieDbOptions)
      .then((result) => {
        if (!result.Error) {
          movieMetaData.actors = checkForValue(result.Actors);
          movieMetaData.runtime = checkForValue(result.Runtime);
          movieMetaData.imdbRating = checkForValue(result.imdbRating);
          movieMetaData.imdbUrl = (checkForValue(result.imdbID) !== '') ? `http://www.imdb.com/title/${result.imdbID}` : '';
          movieMetaData.rottenTomatoesUrl = checkForValue(result.tomatoURL);
        }
        resolve(movieMetaData);
      })
      .catch((err) => {
        reject(`Error grabbing data from omdb: ${err}`);
      });
    });
  }

  /*
  Fetch data from Rotten Tomatoes itself.
  */
  static rottenTomatoes(rottenTomatoesUrl) {
    const rottenTomatoesMetaData = {
      tomatoMeter: '',
      tomatoConsensus: '',
    };

    if (!rottenTomatoesUrl) {
      return Promise.resolve(rottenTomatoesMetaData);
    }

    return new Promise((resolve, reject) => {
      const options = {
        uri: rottenTomatoesUrl,
        transform: body => cheerio.load(body),
      };

      rpn(options)
      .then(($) => {
        rottenTomatoesMetaData.tomatoMeter = $('#all-critics-numbers .meter-value span').text();
        rottenTomatoesMetaData.tomatoConsensus = $('#all-critics-numbers .critic_consensus').text().replace('Critics Consensus:', '').trim();
        resolve(rottenTomatoesMetaData);
      })
      .catch((err) => {
        reject(`Error grabbing data from Rotten Tomatoes: ${err}`);
      });
    });
  }
}

module.exports = MovieDatabases;
<|endoftext|>


<commit_before>import dispatcher from "./dispatcher";
import api from "./api";
import m from "./model";
let model = m;

let id = 0;

function sortItems(a, b) {
    return (a.sort  b.sort ? -1 : 0);
}

export default React.createClass({
    showMessage(m, t) {
        const notification = {
            content: m,
            type: t || "info",
            id: id++
        };
        const model = this.state.model;
        model.notifications.push(notification);
        this.setState({model});
    },
    appError(e) {
        console.log(e);
        dispatcher.trigger("show:message", "Ooops, something went wrong...", "warning");
    },
    loadItems() {
        api.tasks.all()
            .then(res => {
                if (res.status !== 200) {
                    throw new Error("res.status is not OK:200 but " + res.status);
                }
                return res;
            })
            .then(res => res.text())
            .then(body => JSON.parse(body))
            .then(json => {
                const items = json.map(i => {
                    i.id = i._id;
                    return i;
                })
                .sort(sortItems);
                return items;
            })
            .then(items => this.setModel({items}))
            .catch(e => dispatcher.trigger("app:error", e));
    },
    selectItem(activeItemId) {
        const activeItem = this.state.model.items.filter(i => i.id === activeItemId)[0];
        // todo: multiple model sets
        this.setModel({editing: false});
        this.setModel({activeItem});
    },
    saveItem(item, sort) {
        const newItems = this.state.model.items.filter(i => i.id !== item.id);

        newItems.unshift(item);
        const items = newItems
            .sort(sortItems)
            .map((item, index) => {
                if (!sort) {
                    return item;
                }
                item.sort = newItems.length - index - 1;
                return item;
            });
        this.setModel({items});
        if (!sort) {
            dispatcher.trigger("select:item", item.id);
            api.tasks.update(item)
            .then(res => {
                if (res.status !== 200) {
                    throw new Error("res.status is not OK:200 but " + res.status);
                }
                return res;
            })
            .catch(e => dispatcher.trigger("app:error", e));
        } else {
            items.forEach(i => api.tasks.update(i));
        }
    },
    editItem() {
        this.setModel({editing: true});
    },
    cancelItem() {
        this.setModel({editing: false});
    },
    newItem() {
        const itemId = id++;
        const {items} = this.state.model;
        const sort = (items.length ? Math.max(...items.map(i => i.sort)) : 0) + 1; // todo ?= items.length

        const item = {title: "New Item", description: "", sort};
        api.tasks.create(item)
            .then(res => {
                if (res.status !== 201) {
                    throw new Error("res.status is not OK:201 but " + res.status);
                }
                return res;
            })
            .then(res => res.text())
            .then(body => JSON.parse(body))
            .then(json => {
                json.id = json._id;
                dispatcher.trigger("save:item-detail", json);
                dispatcher.trigger("select:item", json.id);
                this.setModel({editing: true});
            })
            .catch(e => dispatcher.trigger("dispatcher:error", e));
    },
    deleteItem(id) {
        const items = this.state.model.items.filter(i => i.id !== id);
        this.setModel({items});
        this.setModel({activeItem: null});
        api.tasks.remove(id)
            .then(res => {
                if (res.status !== 204) {
                    throw new Error("res.status is not OK:204 but " + res.status);
                }
                return res;
            })
            .catch(e => dispatcher.trigger("dispatcher:error", e));
    },
    dismissNotification(id) {
        const notifications = this.state.model.notifications.filter(n => n.id !== id);
        this.setModel({notifications});
    },
    setModel(o) {
        const model = this.state.model;
        const key = Object.keys(o)[0];
        model[key] = o[key];
        this.setState({model});
    },
    getInitialState() {
        dispatcher.on("app:error", this.appError);
        dispatcher.on("cancel:item-detail", this.cancelItem);
        dispatcher.on("delete:item-detail", this.deleteItem);
        dispatcher.on("dismiss:notification", this.dismissNotification);
        dispatcher.on("edit:item-detail", this.editItem);
        dispatcher.on("load:items", this.loadItems);
        dispatcher.on("new:item", this.newItem);
        dispatcher.on("save:item-detail", this.saveItem);
        dispatcher.on("select:item", this.selectItem)
        dispatcher.on("show:message", this.showMessage);

        return {
            model,
            trigger: dispatcher.trigger,
            on: e => {console.log(e + " still used")}
        };
    },
    componentDidMount() {
        dispatcher.trigger("load:items");
        dispatcher.trigger("show:message", "Hello, World!");
    },
    render() {
        return

            {this.props.render(this.state)}

;
    }
});
<commit_msg>Update app.js<commit_after>import dispatcher from "./dispatcher";
import api from "./api";
import m from "./model";
let model = m;

let id = 0;

function sortItems(a, b) {
    return (a.sort  b.sort ? -1 : 0);
}

export default React.createClass({
    showMessage(m, t) {
        const notification = {
            content: m,
            type: t || "info",
            id: id++
        };
        const model = this.state.model;
        model.notifications.push(notification);
        this.setState({model});
    },
    appError(e) {
        console.log(e);
        dispatcher.trigger("show:message", "Ooops, something went wrong...", "warning");
    },
    loadItems() {
        api.tasks.all()
            .then(res => {
                if (res.status !== 200) {
                    throw new Error("res.status is not OK:200 but " + res.status);
                }
                return res;
            })
            .then(res => res.text())
            .then(body => JSON.parse(body))
            .then(json => {
                const items = json.map(i => {
                    i.id = i._id;
                    return i;
                })
                .sort(sortItems);
                return items;
            })
            .then(items => this.setModel({items}))
            .catch(e => dispatcher.trigger("app:error", e));
    },
    selectItem(activeItemId) {
        const activeItem = this.state.model.items.filter(i => i.id === activeItemId)[0];
        // todo: multiple model sets
        this.setModel({editing: false});
        this.setModel({activeItem});
    },
    saveItem(item, sort) {
        const newItems = this.state.model.items.filter(i => i.id !== item.id);

        newItems.unshift(item);
        const items = newItems
            .sort(sortItems)
            .map((item, index) => {
                if (!sort) {
                    return item;
                }
                item.sort = newItems.length - index - 1;
                return item;
            });
        this.setModel({items});
        if (!sort) {
            dispatcher.trigger("select:item", item.id);
            api.tasks.update(item)
            .then(res => {
                if (res.status !== 200) {
                    throw new Error("res.status is not OK:200 but " + res.status);
                }
                return res;
            })
            .catch(e => dispatcher.trigger("app:error", e));
        } else {
            items.forEach(i => api.tasks.update(i));
        }
    },
    editItem() {
        this.setModel({editing: true});
    },
    cancelItem() {
        this.setModel({editing: false});
    },
    newItem() {
        const itemId = id++;
        const {items} = this.state.model;
        const sort = (items.length ? Math.max(...items.map(i => i.sort)) : 0) + 1; // todo ?= items.length

        const item = {title: "New Item", description: "", sort};
        api.tasks.create(item)
            .then(res => {
                if (res.status !== 201) {
                    throw new Error("res.status is not OK:201 but " + res.status);
                }
                return res;
            })
            .then(res => res.text())
            .then(body => JSON.parse(body))
            .then(json => {
                json.id = json._id;
                dispatcher.trigger("save:item-detail", json);
                dispatcher.trigger("select:item", json.id);
                this.setModel({editing: true});
            })
            .catch(e => dispatcher.trigger("dispatcher:error", e));
    },
    deleteItem(id) {
        const items = this.state.model.items.filter(i => i.id !== id);
        this.setModel({items});
        this.setModel({activeItem: null});
        api.tasks.remove(id)
            .then(res => {
                if (res.status !== 204) {
                    throw new Error("res.status is not OK:204 but " + res.status);
                }
                return res;
            })
            .catch(e => dispatcher.trigger("dispatcher:error", e));
    },
    dismissNotification(id) {
        const notifications = this.state.model.notifications.filter(n => n.id !== id);
        this.setModel({notifications});
    },
    setModel(o) {
        const model = this.state.model;
        const key = Object.keys(o)[0];
        model[key] = o[key];
        this.setState({model});
    },
    getInitialState() {
        dispatcher.on("app:error", this.appError);
        dispatcher.on("cancel:item-detail", this.cancelItem);
        dispatcher.on("delete:item-detail", this.deleteItem);
        dispatcher.on("dismiss:notification", this.dismissNotification);
        dispatcher.on("edit:item-detail", this.editItem);
        dispatcher.on("load:items", this.loadItems);
        dispatcher.on("new:item", this.newItem);
        dispatcher.on("save:item-detail", this.saveItem);
        dispatcher.on("select:item", this.selectItem)
        dispatcher.on("show:message", this.showMessage);

        return {
            model,
            trigger: dispatcher.trigger,
            on: e => {console.log(e + " still used")}
        };
    },
    componentDidMount() {
        dispatcher.trigger("load:items");
        dispatcher.trigger("show:message", "Hello, World!");
    },
    render() {
        // todo: render this.props.children instead?
        return

            {this.props.render(this.state)}

;
    }
});
<|endoftext|>


<commit_before>import Vue from 'vue'
import VueI18n from 'vue-i18n'

Vue.use(VueI18n)

export const messages = {
  en: require('@/locales/locale-en.json'),
  de: require('@/locales/locale-de.json'),
  eo: require('@/locales/locale-eo.json'),
  es: require('@/locales/locale-es.json'),
  fr: require('@/locales/locale-fr.json'),
  it: require('@/locales/locale-it.json'),
  ru: require('@/locales/locale-ru.json'),
  sv: require('@/locales/locale-sv.json'),
  zh: require('@/locales/locale-zh.json')
}

export const locales = [
  { locale: 'de', name: 'Deutsch' },
  { locale: 'en', name: 'English' },
  { locale: 'fr', name: 'Français' },
  { locale: 'sv', name: 'Svenska' },
  { locale: 'es', name: 'Español' },
  { locale: 'it', name: 'Italiano' },
  { locale: 'eo', name: 'Esperanto' },
  { locale: 'ru', name: 'Русский' },
  { locale: 'zh', name: '中文' }
]

const i18n = new VueI18n({
  locale: 'en',
  messages
})

export default i18n

if (module.hot) {
  /*

    Looks a bit verbose right?

    Hot module reloading seems to *really* want these to all be explicit strings.
    I did try making them variables, etc... and the hot reload would work for one change
    then start doing full page reloads again after that :/

    Would be happy if you can investigate further and get it to work, but I am happy
    with this approach right now :)

  */

  module.hot.accept([
    '@/locales/locale-en.json',
    '@/locales/locale-de.json',
    '@/locales/locale-eo.json',
    '@/locales/locale-es.json',
    '@/locales/locale-fr.json',
    '@/locales/locale-it.json',
    '@/locales/locale-ru.json',
    '@/locales/locale-sv.json',
    '@/locales/locale-zh.json'
  ], () => {
    i18n.setLocaleMessage('en', require('@/locales/locale-en.json'))
    i18n.setLocaleMessage('de', require('@/locales/locale-de.json'))
    i18n.setLocaleMessage('eo', require('@/locales/locale-eo.json'))
    i18n.setLocaleMessage('es', require('@/locales/locale-es.json'))
    i18n.setLocaleMessage('fr', require('@/locales/locale-fr.json'))
    i18n.setLocaleMessage('it', require('@/locales/locale-it.json'))
    i18n.setLocaleMessage('ru', require('@/locales/locale-ru.json'))
    i18n.setLocaleMessage('sv', require('@/locales/locale-sv.json'))
    i18n.setLocaleMessage('zh', require('@/locales/locale-zh.json'))
  })
}
<commit_msg>Detect locale from browser<commit_after>import Vue from 'vue'
import VueI18n from 'vue-i18n'

Vue.use(VueI18n)

export const messages = {
  en: require('@/locales/locale-en.json'),
  de: require('@/locales/locale-de.json'),
  eo: require('@/locales/locale-eo.json'),
  es: require('@/locales/locale-es.json'),
  fr: require('@/locales/locale-fr.json'),
  it: require('@/locales/locale-it.json'),
  ru: require('@/locales/locale-ru.json'),
  sv: require('@/locales/locale-sv.json'),
  zh: require('@/locales/locale-zh.json')
}

export const DEFAULT_LOCALE = 'en'

export function detectLocale () {
  // Based on https://angular-translate.github.io/docs/#/guide/07_multi-language#multi-language_determining-preferred-language-automatically
  let val =
    navigator.languages[0] ||
    navigator.language ||
    navigator.browserLanguage ||
    navigator.systemLanguage ||
    navigator.userLanguage
  if (val) {
    return val.replace(/-.*$/, '')
  }
}

export const locales = [
  { locale: 'de', name: 'Deutsch' },
  { locale: 'en', name: 'English' },
  { locale: 'fr', name: 'Français' },
  { locale: 'sv', name: 'Svenska' },
  { locale: 'es', name: 'Español' },
  { locale: 'it', name: 'Italiano' },
  { locale: 'eo', name: 'Esperanto' },
  { locale: 'ru', name: 'Русский' },
  { locale: 'zh', name: '中文' }
]

const i18n = new VueI18n({
  locale: detectLocale() || DEFAULT_LOCALE,
  messages
})

export default i18n

if (module.hot) {
  /*

    Looks a bit verbose right?

    Hot module reloading seems to *really* want these to all be explicit strings.
    I did try making them variables, etc... and the hot reload would work for one change
    then start doing full page reloads again after that :/

    Would be happy if you can investigate further and get it to work, but I am happy
    with this approach right now :)

  */

  module.hot.accept([
    '@/locales/locale-en.json',
    '@/locales/locale-de.json',
    '@/locales/locale-eo.json',
    '@/locales/locale-es.json',
    '@/locales/locale-fr.json',
    '@/locales/locale-it.json',
    '@/locales/locale-ru.json',
    '@/locales/locale-sv.json',
    '@/locales/locale-zh.json'
  ], () => {
    i18n.setLocaleMessage('en', require('@/locales/locale-en.json'))
    i18n.setLocaleMessage('de', require('@/locales/locale-de.json'))
    i18n.setLocaleMessage('eo', require('@/locales/locale-eo.json'))
    i18n.setLocaleMessage('es', require('@/locales/locale-es.json'))
    i18n.setLocaleMessage('fr', require('@/locales/locale-fr.json'))
    i18n.setLocaleMessage('it', require('@/locales/locale-it.json'))
    i18n.setLocaleMessage('ru', require('@/locales/locale-ru.json'))
    i18n.setLocaleMessage('sv', require('@/locales/locale-sv.json'))
    i18n.setLocaleMessage('zh', require('@/locales/locale-zh.json'))
  })
}
<|endoftext|>


<commit_before>const i18next = require('i18next');
const { join } = require('path');
const { readdirSync, lstatSync } = require('fs');
const fsBackend = require('i18next-fs-backend');

const i18nPath = join(__dirname, '../i18n');
const SUPPORTED_NAMESPACES = [
  'emails', // default
];

module.exports = (function initI18n() {
  const i18n = i18next.createInstance();

  i18n
    .use(fsBackend)
    .init({
      initImmediate: false,
      fallbackLng: 'en',
      nsSeparator: ':|',
      ns: SUPPORTED_NAMESPACES,
      defaultNS: 'emails',
      preload: readdirSync(i18nPath).filter((fileName) => lstatSync(join(i18nPath, fileName)).isDirectory()),
      backend: {
        loadPath: join(__dirname, '../i18n/{{lng}}/{{ns}}.json'),
      },
    });

  return i18n;
}());
<commit_msg>fix(i18n): fix i18n key related config<commit_after>const i18next = require('i18next');
const { join } = require('path');
const { readdirSync, lstatSync } = require('fs');
const fsBackend = require('i18next-fs-backend');

const i18nPath = join(__dirname, '../i18n');
const SUPPORTED_NAMESPACES = [
  'emails', // default
];

module.exports = (function initI18n() {
  const i18n = i18next.createInstance();

  i18n
    .use(fsBackend)
    .init({
      initImmediate: false,
      fallbackLng: 'en',
      nsSeparator: ':|',
      keySeparator: false,
      ns: SUPPORTED_NAMESPACES,
      defaultNS: 'emails',
      preload: readdirSync(i18nPath).filter((fileName) => lstatSync(join(i18nPath, fileName)).isDirectory()),
      backend: {
        loadPath: join(__dirname, '../i18n/{{lng}}/{{ns}}.json'),
      },
    });

  return i18n;
}());
<|endoftext|>


<commit_before>import { select } from 'd3-selection';

export default class Item {
  constructor() {
    this._id = null;
    this._first = null;
    this._icon = null;

    this._root = select('body')
      .append('div')
      .remove()
      .classed('scola item', true)
      .styles({
        'background': '#FFF',
        'display': 'flex',
        'flex-direction': 'row',
        'line-height': '3em'
      });

    this._padding = this._root
      .append('div')
      .classed('scola padding', true)
      .styles({
        'border-top': '1px solid transparent',
        'order': 1,
        'width': '1em'
      });

    this.first(false);
  }

  destroy() {
    this._root.dispatch('destroy');
    this._root.remove();
    this._root = null;
  }

  root() {
    return this._root;
  }

  id(id) {
    if (typeof id === 'undefined') {
      return this._id;
    }

    this._id = id;
    return this;
  }

  icon(name, size = '2em') {
    if (typeof name === 'undefined') {
      return this._icon;
    }

    if (name === false) {
      this._icon.remove();
      this._icon = null;

      return this;
    }

    this._icon = this._root
      .append('div')
      .classed('scola icon', true)
      .styles({
        'align-items': 'center',
        'border-top': '1px solid transparent',
        'display': 'flex',
        'order': 2,
        'width': '2.25em'
      });

    this._icon
      .append('div')
      .classed(name, true)
      .style('font-size', size);

    return this;
  }

  first(first) {
    if (first === this._first) {
      return this;
    }

    this._first = first;
    this._root.style('border-color',
      first === true ? 'transparent' : '#CCC');

    return this;
  }
}
<commit_msg>Add index to item<commit_after>import { select } from 'd3-selection';

export default class Item {
  constructor() {
    this._id = null;
    this._index = null;

    this._first = null;
    this._icon = null;

    this._root = select('body')
      .append('div')
      .remove()
      .classed('scola item', true)
      .styles({
        'background': '#FFF',
        'display': 'flex',
        'flex-direction': 'row',
        'line-height': '3em'
      });

    this._padding = this._root
      .append('div')
      .classed('scola padding', true)
      .styles({
        'border-top': '1px solid transparent',
        'order': 1,
        'width': '1em'
      });

    this.first(false);
  }

  destroy() {
    this._root.dispatch('destroy');
    this._root.remove();
    this._root = null;
  }

  root() {
    return this._root;
  }

  id(id) {
    if (typeof id === 'undefined') {
      return this._id;
    }

    this._id = id;
    return this;
  }

  index(index) {
    if (typeof index === 'undefined') {
      return this._index;
    }

    this._index = index;
    return this;
  }

  icon(name, size = '2em') {
    if (typeof name === 'undefined') {
      return this._icon;
    }

    if (name === false) {
      this._icon.remove();
      this._icon = null;

      return this;
    }

    this._icon = this._root
      .append('div')
      .classed('scola icon', true)
      .styles({
        'align-items': 'center',
        'border-top': '1px solid transparent',
        'display': 'flex',
        'order': 2,
        'width': '2.25em'
      });

    this._icon
      .append('div')
      .classed(name, true)
      .style('font-size', size);

    return this;
  }

  first(first) {
    if (first === this._first) {
      return this;
    }

    this._first = first;
    this._root.style('border-color',
      first === true ? 'transparent' : '#CCC');

    return this;
  }
}
<|endoftext|>