<commit_before>'use strict'

var EventLite = require('event-lite')

// Local modules.
var memoryAdapter = require('./adapter/adapters/memory')
var AdapterSingleton = require('./adapter/singleton')
var assign = require('./common/assign')
var validate = require('./record_type/validate')
var ensureTypes = require('./record_type/ensure_types')
var dispatch = require('./dispatch')
var promise = require('./common/promise')
var middlewares = dispatch.middlewares

// Static re-exports.
var Adapter = require('./adapter')
var errors = require('./common/errors')
var methods = require('./common/methods')
var events = require('./common/events')
var message = require('./common/message')

/**
 * This is the default export of the `fortune` package. It implements a
 * subset of `EventEmitter`, and it has a few static properties attached to it
 * that may be useful to access:
 *
 * - `Adapter`: abstract base class for the Adapter.
 * - `adapters`: included adapters, defaults to memory adapter. Note that the
 * browser build also includes `indexedDB` and `webStorage` adapters.
 * - `net`: network protocol helpers, varies based on client or server build.
 * - `errors`: custom typed errors, useful for throwing errors in transform
 * functions.
 * - `methods`: a hash that maps to string constants. Available are: `find`,
 * `create`, `update`, and `delete`.
 * - `events`: names for events on the Fortune instance.
 * - `message`: a function which accepts the arguments (`id`, `language`,
 * `data`). It has properties keyed by two-letter language codes, which by
 * default includes only `en`.
 * - `Promise`: by default, the native Promise implementation is used in the
 * browser, or [Bluebird](https://github.com/petkaantonov/bluebird/) in
 * Node.js. If an alternative is desired, simply assign this property with the
 * new Promise class. This will affect all instances of Fortune.
 */
function Fortune (options) {
  this.constructor(options)
}

// Inherit from EventLite class.
Fortune.prototype = Object.create(EventLite.prototype)

/**
 * Create a new instance, the only required input is record type definitions.
 * The first argument must be an object keyed by name, valued by definition
 * objects. Here are some example field definitions:
 *
 * ```js
 * {
 *   // A singular value.
 *   name: { type: String },
 *
 *   // An array containing values of a single type.
 *   luckyNumbers: { type: Number, isArray: true },
 *
 *   // Creates a to-many link to `animal` record type. If the field `owner`
 *   // on the `animal` record type is not an array, this is a many-to-one
 *   // relationship, otherwise it is many-to-many.
 *   pets: { link: 'animal', isArray: true, inverse: 'owner' },
 *
 *   // The `min` and `max` keys are open to interpretation by the specific
 *   // adapter, which may introspect the field definition.
 *   thing: { type: Number, min: 0, max: 100 },
 *
 *   // Nested field definitions are invalid. Use `Object` type instead.
 *   nested: { thing: { ... } } // Will throw an error.
 * }
 * ```
 *
 * The allowed native types are `String`, `Number`, `Boolean`, `Date`,
 * `Object`, and `Buffer`. Note that the `Object` type should be a JSON
 * serializable object that may be persisted. The only other allowed type is
 * a `Function`, which may be used to define custom types.
 *
 * A type function should accept one argument, the value, and return a
 * boolean based on whether the value is valid for the type or not. It may
 * optionally have properties `sort` and `equal`, which should be valued as
 * functions.
 *
 * - `compare`: same signature as comparing with `Array.prototype.sort`.
 * - `equal`: return a boolean value if the two arguments are equal.
 *
 * These optional functions are used for the memory adapter and web browser
 * adapters, but may not be run by other adapters.
 *
 * The options object may contain the following keys:
 *
 * - `adapter`: configuration array for the adapter. The default type is the
 *   memory adapter. If the value is not an array, its settings will be
 *   considered omitted.
 *
 *   ```js
 *   [
 *     // Must be a class that extends `Fortune.Adapter`, or a function
 *     // that accepts the Adapter class and returns a subclass. Required.
 *     Adapter => { ... },
 *
 *     // An options object that is specific to the adapter. Optional.
 *     { ... }
 *   ]
 *   ```
 *
 * - `transforms`: keyed by type name, valued by an array containing an `input`
 *   and/or `output` function at indices `0` and `1` respectively.
 *
 *   A transform function takes at least two arguments, the internal `context`
 *   object and a single `record`. A special case is the `update` argument for
 *   the `update` method.
 *
 *   There are two kinds of transforms, before a record is written to transform
 *   input, and after it is read to transform output, both are optional. If an
 *   error occurs within an transform function, it will be forwarded to the
 *   response. Use typed errors to provide the appropriate feedback. It is
 *   varant to note that `output` transforms are run every time a record is
 *   shown in a response, so it should be idempotent.
 *
 *   For a create request, the input transform must return the second argument
 *   `record` either synchronously, or asynchronously as a Promise. The return
 *   value of a delete request is inconsequential, but it may return a value or
 *   a Promise. There is a special case of the `update` method accepting a
 *   `update` object as a third parameter, which must be returned synchronously
 *   or as a Promise.
 *
 *   An example transform to apply a timestamp on a record before creation,
 *   and displaying the timestamp in the server's locale:
 *
 *   ```js
 *   [
 *     (context, record, update) => {
 *       const method = context.request.method
 *
 *       if (method === 'create') {
 *         record.timestamp = new Date()
 *         return record
 *       }
 *
 *       if (update) return update
 *
 *       // If we get here, return value of the delete method doesn't matter.
 *       return null
 *     },
 *     (context, record) => {
 *       record.timestamp = record.timestamp.toLocaleString()
 *       return record
 *     }
 *   ]
 *   ```
 *
 *   Requests to update a record will **NOT** have the updates already applied
 *   to the record.
 *
 *   Another feature of the input transform is that it will have access to a
 *   temporary field `context.transaction`. This is useful for ensuring that
 *   bulk write operations are all or nothing. Each request is treated as a
 *   single transaction.
 *
 * - `settings`: internal settings to configure.
 *
 *   ```js
 *   {
 *     // Whether or not to enforce referential integrity. Default: `true` for
 *     // server, `false` for browser.
 *     enforceLinks: true
 *   }
 *   ```
 *
 * The return value of the constructor is the instance itself.
 *
 * @param {Object} recordTypes
 * @param {Object} [options]
 * @return {Fortune}
 */
Fortune.prototype.constructor = function (recordTypes, options) {
  var self = this
  var adapter, method, stack, flows, type, transforms, i, j

  if (typeof recordTypes !== 'object')
    throw new TypeError('First argument must be an object.')

  if (!Object.keys(recordTypes).length)
    throw new Error('At least one type must be specified.')

  if (!('adapter' in options)) options.adapter = [ memoryAdapter ]
  if (!('settings' in options)) options.settings = {}
  if (!('transforms' in options)) options.transforms = {}
  if (!('enforceLinks' in options.settings))
    options.settings.enforceLinks = true

  // Bind middleware methods to instance.
  flows = {}
  for (method in methods) {
    stack = [ middlewares[method], middlewares.include, middlewares.end ]

    for (i = 0, j = stack.length; i < j; i++)
      stack[i] = bindMiddleware(self, stack[i])

    flows[methods[method]] = stack
  }

  transforms = options.transforms

  // Validate transforms.
  for (type in transforms) {
    if (!(type in recordTypes)) throw new Error(
      'Attempted to define transform on "' + type + '" type ' +
      'which does not exist.')
    if (!Array.isArray(transforms[type]))
      throw new TypeError('Transform value for "' + type + '" type ' +
        'must be an array.')
  }

  // Validate record types.
  for (type in recordTypes) {
    validate(recordTypes[type])
    if (!(type in transforms)) transforms[type] = []
  }

  /*!
   * Adapter singleton that is coupled to the Fortune instance.
   *
   * @type {Adapter}
   */
  adapter = new AdapterSingleton({
    adapter: options.adapter,
    recordTypes: recordTypes,
    transforms: transforms
  })

  // Internal properties.
  Object.defineProperties(self, {
    // 0 = not started, 1 = started, 2 = done.
    connectionStatus: { value: 0, writable: true },

    // Configuration settings.
    options: { value: options },
    transforms: { value: transforms },
    recordTypes: { value: recordTypes, enumerable: true },

    // Singleton instances.
    adapter: { value: adapter, enumerable: true, configurable: true },

    // Dispatch.
    flows: { value: flows }
  })
}

/**
 * This is the primary method for initiating a request. The options object
 * may contain the following keys:
 *
 * - `method`: The method is a either a function or a constant, which is keyed
 *   under `Fortune.methods` and may be one of `find`, `create`, `update`,  or
 *   `delete`. To implement a custom method, pass a function that accepts
 *   one argument, the context. It may return the context synchronously or
 *   as a Promise. Default: `find`.
 *
 * - `type`: Name of a type. Required.
 *
 * - `ids`: An array of IDs. Used for `find` and `delete` methods only.
 *
 * - `include`: A 2-dimensional array specifying links to include. The first
 *   dimension is a list, the second dimension is depth. For example:
 *   `[['comments'], ['comments', 'author', { ... }]]`. The last item within
 *   the list may be an `options` object, useful for specifying how the
 *   included records should appear. Optional.
 *
 * - `options`: Exactly the same as the adapter's `find` method options. The
 *   options apply only to the primary type on `find` requests. Optional.
 *
 * - `meta`: Meta-information object of the request. Optional.
 *
 * - `payload`: Payload of the request. Used for `create` and `update` methods
 *   only, and must be an array of objects. The objects must be the records
 *   to create, or update objects as expected by the Adapter.
 *
 * The response object may contain the following keys:
 *
 * - `meta`: Meta-info of the response.
 *
 * - `payload`: An object containing the following keys:
 *   - `records`: An array of records returned.
 *   - `count`: Total number of records without options applied (only for
 *     responses to the `find` method).
 *   - `include`: An object keyed by type, valued by arrays of included
 *     records.
 *
 * The resolved response object should always be an instance of a response
 * type.
 *
 * @param {Object} options
 * @return {Promise}
 */
Fortune.prototype.request = function (options) {
  var self = this
  var Promise = promise.Promise
  var connectionStatus = self.connectionStatus

  if (connectionStatus === 0)
    return self.connect()
    .then(function () { return dispatch(self, options) })

  else if (connectionStatus === 1)
    return new Promise(function (resolve, reject) {
      // Wait for changes to connection status.
      self.once(events.failure, function () {
        reject(new Error('Connection failed.'))
      })
      self.once(events.connect, function () {
        resolve(dispatch(self, options))
      })
    })

  return dispatch(self, options)
}

/**
 * This method does not need to be called manually, it is automatically called
 * upon the first request if it is not connected already. However, it may be
 * useful if manually reconnect is needed. The resolved value is the instance
 * itself.
 *
 * @return {Promise}
 */
Fortune.prototype.connect = function () {
  var self = this
  var Promise = promise.Promise

  if (self.connectionStatus === 1)
    return Promise.reject(new Error('Connection is in progress.'))

  else if (self.connectionStatus === 2)
    return Promise.reject(new Error('Connection is already done.'))

  self.connectionStatus = 1

  return new Promise(function (resolve, reject) {
    ensureTypes(self.recordTypes)

    self.adapter.connect().then(function () {
      self.connectionStatus = 2
      self.emit(events.connect)
      return resolve(self)
    }, function (error) {
      self.connectionStatus = 0
      self.emit(events.failure)
      return reject(error)
    })
  })
}

/**
 * Close adapter connection, and reset connection state. The resolved value is
 * the instance itself.
 *
 * @return {Promise}
 */
Fortune.prototype.disconnect = function () {
  var self = this
  var Promise = promise.Promise

  if (self.connectionStatus !== 2)
    return Promise.reject(new Error('Instance has not been connected.'))

  self.connectionStatus = 1

  return new Promise(function (resolve, reject) {
    return self.adapter.disconnect().then(function () {
      self.connectionStatus = 0
      self.emit(events.disconnect)
      return resolve(self)
    }, function (error) {
      self.connectionStatus = 2
      self.emit(events.failure)
      return reject(error)
    })
  })
}

// Assign useful static properties to the default export.
assign(Fortune, {
  Adapter: Adapter,
  errors: errors,
  methods: methods,
  message: message,
  events: events
})

// Internal helper function.
function bindMiddleware (scope, method) {
  return function (x) {
    return method.call(scope, x)
  }
}

module.exports = Fortune
<commit_msg>update documentation<commit_after>'use strict'

var EventLite = require('event-lite')

// Local modules.
var memoryAdapter = require('./adapter/adapters/memory')
var AdapterSingleton = require('./adapter/singleton')
var assign = require('./common/assign')
var validate = require('./record_type/validate')
var ensureTypes = require('./record_type/ensure_types')
var dispatch = require('./dispatch')
var promise = require('./common/promise')
var middlewares = dispatch.middlewares

// Static re-exports.
var Adapter = require('./adapter')
var errors = require('./common/errors')
var methods = require('./common/methods')
var events = require('./common/events')
var message = require('./common/message')

/**
 * This is the default export of the `fortune` package. It implements a
 * subset of `EventEmitter`, and it has a few static properties attached to it
 * that may be useful to access:
 *
 * - `Adapter`: abstract base class for the Adapter.
 * - `adapters`: included adapters, defaults to memory adapter. Note that the
 * browser build also includes `indexedDB` and `webStorage` adapters.
 * - `net`: network protocol helpers, varies based on client or server build.
 * - `errors`: custom typed errors, useful for throwing errors in transform
 * functions.
 * - `methods`: a hash that maps to string constants. Available are: `find`,
 * `create`, `update`, and `delete`.
 * - `events`: names for events on the Fortune instance.
 * - `message`: a function which accepts the arguments (`id`, `language`,
 * `data`). It has properties keyed by two-letter language codes, which by
 * default includes only `en`.
 * - `Promise`: by default, the native Promise implementation is used in the
 * browser, or [Bluebird](https://github.com/petkaantonov/bluebird/) in
 * Node.js. If an alternative is desired, simply assign this property with the
 * new Promise class. This will affect all instances of Fortune.
 */
function Fortune (options) {
  this.constructor(options)
}

// Inherit from EventLite class.
Fortune.prototype = Object.create(EventLite.prototype)

/**
 * Create a new instance, the only required input is record type definitions.
 * The first argument must be an object keyed by name, valued by definition
 * objects. Here are some example field definitions:
 *
 * ```js
 * {
 *   // A singular value.
 *   name: { type: String },
 *
 *   // An array containing values of a single type.
 *   luckyNumbers: { type: Number, isArray: true },
 *
 *   // Creates a to-many link to `animal` record type. If the field `owner`
 *   // on the `animal` record type is not an array, this is a many-to-one
 *   // relationship, otherwise it is many-to-many.
 *   pets: { link: 'animal', isArray: true, inverse: 'owner' },
 *
 *   // The `min` and `max` keys are open to interpretation by the specific
 *   // adapter, which may introspect the field definition.
 *   thing: { type: Number, min: 0, max: 100 },
 *
 *   // Nested field definitions are invalid. Use `Object` type instead.
 *   nested: { thing: { ... } } // Will throw an error.
 * }
 * ```
 *
 * The allowed native types are `String`, `Number`, `Boolean`, `Date`,
 * `Object`, and `Buffer`. Note that the `Object` type should be a JSON
 * serializable object that may be persisted. The only other allowed type is
 * a `Function`, which may be used to define custom types.
 *
 * A type function should accept one argument, the value, and return a
 * boolean based on whether the value is valid for the type or not. It may
 * optionally have properties `sort` and `equal`, which should be valued as
 * functions.
 *
 * - `compare`: same signature as comparing with `Array.prototype.sort`.
 * - `equal`: return a boolean value if the two arguments are equal.
 *
 * These optional functions are used for the memory adapter and web browser
 * adapters, but may not be run by other adapters.
 *
 * The options object may contain the following keys:
 *
 * - `adapter`: configuration array for the adapter. The default type is the
 *   memory adapter. If the value is not an array, its settings will be
 *   considered omitted.
 *
 *   ```js
 *   [
 *     // Must be a class that extends `Fortune.Adapter`, or a function
 *     // that accepts the Adapter class and returns a subclass. Required.
 *     Adapter => { ... },
 *
 *     // An options object that is specific to the adapter. Optional.
 *     { ... }
 *   ]
 *   ```
 *
 * - `transforms`: keyed by type name, valued by an array containing an `input`
 *   and/or `output` function at indices `0` and `1` respectively.
 *
 *   A transform function takes at least two arguments, the internal `context`
 *   object and a single `record`. A special case is the `update` argument for
 *   the `update` method.
 *
 *   There are two kinds of transforms, before a record is written to transform
 *   input, and after it is read to transform output, both are optional. If an
 *   error occurs within an transform function, it will be forwarded to the
 *   response. Use typed errors to provide the appropriate feedback. It is
 *   varant to note that `output` transforms are run every time a record is
 *   shown in a response, so it should be idempotent.
 *
 *   For a create request, the input transform must return the second argument
 *   `record` either synchronously, or asynchronously as a Promise. The return
 *   value of a delete request is inconsequential, but it may return a value or
 *   a Promise. There is a special case of the `update` method accepting a
 *   `update` object as a third parameter, which must be returned synchronously
 *   or as a Promise.
 *
 *   An example transform to apply a timestamp on a record before creation,
 *   and displaying the timestamp in the server's locale:
 *
 *   ```js
 *   [
 *     (context, record, update) => {
 *       const method = context.request.method
 *
 *       if (method === 'create') {
 *         record.timestamp = new Date()
 *         return record
 *       }
 *
 *       if (update) return update
 *
 *       // If we get here, return value of the delete method doesn't matter.
 *       return null
 *     },
 *     (context, record) => {
 *       record.timestamp = record.timestamp.toLocaleString()
 *       return record
 *     }
 *   ]
 *   ```
 *
 *   Requests to update a record will **NOT** have the updates already applied
 *   to the record.
 *
 *   Another feature of the input transform is that it will have access to a
 *   temporary field `context.transaction`. This is useful for ensuring that
 *   bulk write operations are all or nothing. Each request is treated as a
 *   single transaction.
 *
 * - `settings`: internal settings to configure.
 *
 *   ```js
 *   {
 *     // Whether or not to enforce referential integrity. Default: `true` for
 *     // server, `false` for browser.
 *     enforceLinks: true
 *   }
 *   ```
 *
 * The return value of the constructor is the instance itself.
 *
 * @param {Object} recordTypes
 * @param {Object} [options]
 * @return {Fortune}
 */
Fortune.prototype.constructor = function (recordTypes, options) {
  var self = this
  var adapter, method, stack, flows, type, transforms, i, j

  if (typeof recordTypes !== 'object')
    throw new TypeError('First argument must be an object.')

  if (!Object.keys(recordTypes).length)
    throw new Error('At least one type must be specified.')

  if (!('adapter' in options)) options.adapter = [ memoryAdapter ]
  if (!('settings' in options)) options.settings = {}
  if (!('transforms' in options)) options.transforms = {}
  if (!('enforceLinks' in options.settings))
    options.settings.enforceLinks = true

  // Bind middleware methods to instance.
  flows = {}
  for (method in methods) {
    stack = [ middlewares[method], middlewares.include, middlewares.end ]

    for (i = 0, j = stack.length; i < j; i++)
      stack[i] = bindMiddleware(self, stack[i])

    flows[methods[method]] = stack
  }

  transforms = options.transforms

  // Validate transforms.
  for (type in transforms) {
    if (!(type in recordTypes)) throw new Error(
      'Attempted to define transform on "' + type + '" type ' +
      'which does not exist.')
    if (!Array.isArray(transforms[type]))
      throw new TypeError('Transform value for "' + type + '" type ' +
        'must be an array.')
  }

  // Validate record types.
  for (type in recordTypes) {
    validate(recordTypes[type])
    if (!(type in transforms)) transforms[type] = []
  }

  /*!
   * Adapter singleton that is coupled to the Fortune instance.
   *
   * @type {Adapter}
   */
  adapter = new AdapterSingleton({
    adapter: options.adapter,
    recordTypes: recordTypes,
    transforms: transforms
  })

  // Internal properties.
  Object.defineProperties(self, {
    // 0 = not started, 1 = started, 2 = done.
    connectionStatus: { value: 0, writable: true },

    // Configuration settings.
    options: { value: options },
    transforms: { value: transforms },
    recordTypes: { value: recordTypes, enumerable: true },

    // Singleton instances.
    adapter: { value: adapter, enumerable: true, configurable: true },

    // Dispatch.
    flows: { value: flows }
  })
}

/**
 * This is the primary method for initiating a request. The options object
 * may contain the following keys:
 *
 * - `method`: The method is a either a function or a constant, which is keyed
 *   under `Fortune.methods` and may be one of `find`, `create`, `update`,  or
 *   `delete`. To implement a custom method, pass a function that accepts
 *   one argument, the context. It may return the context synchronously or
 *   as a Promise. Default: `find`.
 *
 * - `type`: Name of a type. **Required**.
 *
 * - `ids`: An array of IDs. Used for `find` and `delete` methods only. This is
 *   optional for the `find` method.
 *
 * - `include`: A 2-dimensional array specifying links to include. The first
 *   dimension is a list, the second dimension is depth. For example:
 *   `[['comments'], ['comments', 'author', { ... }]]`. The last item within
 *   the list may be an `options` object, useful for specifying how the
 *   included records should appear. Optional.
 *
 * - `options`: Exactly the same as the [`find` method](#adapter-find)
 *   options in the adapter. These options do not apply on methods other than
 *   `find`, and do not affect the records returned from `include`. Optional.
 *
 * - `meta`: Meta-information object of the request. Optional.
 *
 * - `payload`: Payload of the request. **Required** for `create` and `update`
 *   methods only, and must be an array of objects. The objects must be the
 *   records to create, or update objects as expected by the Adapter.
 *
 * The response object may contain the following keys:
 *
 * - `meta`: Meta-info of the response.
 *
 * - `payload`: An object containing the following keys:
 *   - `records`: An array of records returned.
 *   - `count`: Total number of records without options applied (only for
 *     responses to the `find` method).
 *   - `include`: An object keyed by type, valued by arrays of included
 *     records.
 *
 * The resolved response object should always be an instance of a response
 * type.
 *
 * @param {Object} options
 * @return {Promise}
 */
Fortune.prototype.request = function (options) {
  var self = this
  var Promise = promise.Promise
  var connectionStatus = self.connectionStatus

  if (connectionStatus === 0)
    return self.connect()
    .then(function () { return dispatch(self, options) })

  else if (connectionStatus === 1)
    return new Promise(function (resolve, reject) {
      // Wait for changes to connection status.
      self.once(events.failure, function () {
        reject(new Error('Connection failed.'))
      })
      self.once(events.connect, function () {
        resolve(dispatch(self, options))
      })
    })

  return dispatch(self, options)
}

/**
 * This method does not need to be called manually, it is automatically called
 * upon the first request if it is not connected already. However, it may be
 * useful if manually reconnect is needed. The resolved value is the instance
 * itself.
 *
 * @return {Promise}
 */
Fortune.prototype.connect = function () {
  var self = this
  var Promise = promise.Promise

  if (self.connectionStatus === 1)
    return Promise.reject(new Error('Connection is in progress.'))

  else if (self.connectionStatus === 2)
    return Promise.reject(new Error('Connection is already done.'))

  self.connectionStatus = 1

  return new Promise(function (resolve, reject) {
    ensureTypes(self.recordTypes)

    self.adapter.connect().then(function () {
      self.connectionStatus = 2
      self.emit(events.connect)
      return resolve(self)
    }, function (error) {
      self.connectionStatus = 0
      self.emit(events.failure)
      return reject(error)
    })
  })
}

/**
 * Close adapter connection, and reset connection state. The resolved value is
 * the instance itself.
 *
 * @return {Promise}
 */
Fortune.prototype.disconnect = function () {
  var self = this
  var Promise = promise.Promise

  if (self.connectionStatus !== 2)
    return Promise.reject(new Error('Instance has not been connected.'))

  self.connectionStatus = 1

  return new Promise(function (resolve, reject) {
    return self.adapter.disconnect().then(function () {
      self.connectionStatus = 0
      self.emit(events.disconnect)
      return resolve(self)
    }, function (error) {
      self.connectionStatus = 2
      self.emit(events.failure)
      return reject(error)
    })
  })
}

// Assign useful static properties to the default export.
assign(Fortune, {
  Adapter: Adapter,
  errors: errors,
  methods: methods,
  message: message,
  events: events
})

// Internal helper function.
function bindMiddleware (scope, method) {
  return function (x) {
    return method.call(scope, x)
  }
}

module.exports = Fortune
<|endoftext|>


<commit_before>var express = require('express'),
    app = express(),
    fs = require('fs'),
    join = require('path').join,
    cheerio = require('cheerio'),
    concat = require('concat-stream'),
    reloadFlag = 0;

exports.server = function (dirPath, option, callback) {

    app.configure(function () {
        if(option.log === "true"){
            app.use(express.logger());
        }
        app.use(express.compress());
        app.use(app.router);
        app.use(express.static(__dirname+'/client'));
        app.use(express.static(dirPath));
    });

    app.get('/', function (req, res, next) {
        var filename = option.homepage + ".html",
            path = join(dirPath, filename),
            stream = fs.createReadStream(path, {encoding: 'utf-8'});

        stream.pipe(concat(function (data) {
            var $ = cheerio.load(data),

            // hack for a good fuck!
            injectan = ""
            var sel = $('body').append(injectan);

            if(sel.length === 0){
                $('*').after(injectan);
            }

            res.setHeader('Content-Type', 'text/html');
            res.write($.html());
            res.end();
        }))

        //res.sendfile(filename, {root:dirPath});
    });

    app.get('/reload', function(req, res, next){

        var event = "reload";
        client = {
            "reload" : option.reload,
            "reloadFlag": reloadFlag
        };

        res.writeHead(200, {
            "Content-Type":"text/event-stream",
            "Cache-Control":"no-cache",
            "Connection":"keep-alive"
        });

        var id = (new Date()).toLocaleTimeString();

        res.write("id: " + id + '\n');
        res.write("event: " + event + "\n");
        res.write("data: "+JSON.stringify(client)+"\n\n");
        res.write('retry: 500\n');

        // Reset
        reloadFlag = 0;

        res.end();
    });

    // 404
    app.get('*', function(req, res){
        res.setHeader('Content-Type', 'text/html');
        res.send('
Ups! Not found...

We are developers are so persistent! :)
',404)
        res.end();
    });

    var server = app.listen(option.port, callback);
    return server;
};

exports.setReload = function(data){
    reloadFlag = data;
}
<commit_msg>fix text/html mime messed up!.<commit_after>var express = require('express'),
    app = express(),
    fs = require('fs'),
    join = require('path').join,
    cheerio = require('cheerio'),
    concat = require('concat-stream'),
    notfound = require('./middleware/404'),
    reloadFlag = 0;

exports.server = function (dirPath, option, callback) {

    app.configure(function () {
        if(option.log === "true"){
            app.use(express.logger());
        }
        app.use(express.compress());
        app.use(app.router);
        app.use(express.static(__dirname+'/client'));
        app.use(express.static(dirPath));
    });

    app.get('/', function (req, res, next) {
        var filename = option.homepage + ".html",
            path = join(dirPath, filename),
            stream = fs.createReadStream(path, {encoding: 'utf-8'});

        stream.pipe(concat(function (data) {
            var $ = cheerio.load(data),

            // hack for a good fuck!
            injectan = ""
            var sel = $('body').append(injectan);

            if(sel.length === 0){
                $('*').after(injectan);
            }

            res.setHeader('Content-Type', 'text/html');
            res.write($.html());
            res.end();
        }))

        //res.sendfile(filename, {root:dirPath});
    });

    app.get('/reload', function(req, res, next){

        var event = "reload";
        client = {
            "reload" : option.reload,
            "reloadFlag": reloadFlag
        };

        res.writeHead(200, {
            "Content-Type":"text/event-stream",
            "Cache-Control":"no-cache",
            "Connection":"keep-alive"
        });

        var id = (new Date()).toLocaleTimeString();

        res.write("id: " + id + '\n');
        res.write("event: " + event + "\n");
        res.write("data: "+JSON.stringify(client)+"\n\n");
        res.write('retry: 500\n');

        // Reset
        reloadFlag = 0;

        res.end();
    });

    app.use(notfound.index);

    var server = app.listen(option.port, callback);
    return server;
};

exports.setReload = function(data){
    reloadFlag = data;
}
<|endoftext|>


<commit_before>const _isEqual = require('lodash');
const React = require('react');
const Radium = require('radium');
const d3 = require('d3');

const StyleConstants = require('../constants/Style');

const DonutChart = React.createClass({
  propTypes: {
    activeOffset: React.PropTypes.number,
    animateOnHover: React.PropTypes.bool,
    animationDuration: React.PropTypes.number,
    animationTypeOnLoad: React.PropTypes.oneOf(['roll', 'pop']),
    arcWidth: React.PropTypes.number,
    baseArcColor: React.PropTypes.string,
    chartTotal: React.PropTypes.number,
    children: React.PropTypes.node,
    colors: React.PropTypes.array,
    data: React.PropTypes.array.isRequired,
    dataPointColors: React.PropTypes.array,
    dataPointRadius: React.PropTypes.number,
    dataPoints: React.PropTypes.array,
    defaultLabelText: React.PropTypes.string,
    defaultLabelValue: React.PropTypes.string,
    formatter: React.PropTypes.func,
    height: React.PropTypes.number,
    hoverExpandDistance: React.PropTypes.number,
    id: React.PropTypes.string,
    onClick: React.PropTypes.func,
    opacity: React.PropTypes.number,
    padAngle: React.PropTypes.number,
    showBaseArc: React.PropTypes.bool,
    showDataLabel: React.PropTypes.bool,
    width: React.PropTypes.number
  },

  getDefaultProps () {
    return {
      activeOffset: 0,
      animateOnHover: false,
      animationDuration: 500,
      animationTypeOnLoad: 'roll',
      arcWidth: 10,
      baseArcColor: StyleConstants.Colors.BASE_ARC,
      colors: [StyleConstants.Colors.PRIMARY].concat(d3.scale.category20().range()),
      data: [],
      dataPointColors: [StyleConstants.Colors.LIME].concat(d3.scale.category20b().range()),
      dataPointRadius: 5,
      dataPoints: [],
      formatter (value) {
        return value;
      },
      height: 150,
      hoverExpandDistance: 5,
      id: 'donut-chart',
      onClick () {},
      opacity: 1,
      padAngle: 0.02,
      showBaseArc: true,
      showDataLabel: true,
      width: 150
    };
  },

  componentWillMount () {
    this._setupD3Functions();
  },

  componentDidMount () {
    if (this.props.data.length) {
      this._animateChart();
    }
  },

  componentWillReceiveProps (newProps) {
    if (!_isEqual(this.props.data, newProps.data) && this.props.data.length) {
      this._setupD3Functions();
      this._animateChart();
    }
  },

  _setupD3Functions () {
    const dataSets = this.props.data.map(item => {
      return item.value;
    });
    const valueTotal = dataSets.reduce((a, b) => {
      return a + b;
    });
    const endAngle = this.props.chartTotal ? valueTotal / this.props.chartTotal : 1;
    const pie = d3.layout.pie().sort(null).padAngle(this.props.padAngle).endAngle(endAngle * 2 * Math.PI);
    const values = pie(dataSets);
    const radius = Math.min(this.props.width, this.props.height) / 2.5;
    const standardArc = d3.svg.arc().outerRadius(radius - this.props.activeOffset).innerRadius(radius - this.props.arcWidth);
    const hoveredArc = d3.svg.arc().outerRadius(radius + this.props.hoverExpandDistance).innerRadius(radius - this.props.arcWidth);
    const baseArc = d3.svg.arc().outerRadius(radius - this.props.activeOffset).innerRadius(radius - this.props.arcWidth).startAngle(0).endAngle(2 * Math.PI);
    const loadBouncePaths = values.map(point => {
      return hoveredArc(point);
    });

    this.setState({
      baseArc,
      endAngle,
      hoveredArc,
      pie,
      radius,
      standardArc,
      values,
      loadBouncePaths
    });
  },

  _animateChart () {
    switch (this.props.animationTypeOnLoad) {
      case 'roll':
        this._rollAnimate();
        break;
      case 'pop':
        this._bounceAnimate();
        break;
      default:
        break;
    }
  },

  _bounceAnimate () {
    d3.selectAll('.arc-' + this.props.id)
      .transition()
      .ease('bounce')
      .duration(this.props.animationDuration)
      .attrTween('d', (d, i, a) => {
        return d3.interpolate(this.state.bounceArcAnimationStartPaths[i], a);
      });
  },

  _rollAnimate () {
    d3.selectAll('.arc-' + this.props.id)
      .transition()
      .ease('bounce')
      .duration(this.props.animationDuration)
      .attrTween('transform', function () {
        return d3.interpolateString('rotate(0)', 'rotate(360)');
      });
  },

  _handleClick () {

  },

  _handleMouseEnter (point) {
    if (this.props.animateOnHover) {
      d3.select(this.refs[point.ref]).transition().duration(500).attr('d', this.state.hoveredArc(point.arc));
    }
  },

  _handleMouseLeave (point) {
    if (this.props.animateOnHover) {
      d3.select(this.refs[point.ref]).transition().duration(500).attr('d', this.state.standardArc(point.arc));
    }
  },

  _renderArcs () {
    if (this.props.data.length) {
      return this.state.values.map((point, i) => {
        return (
          <g
            key={i}
            onClick={this._handleClick.bind(null, i)}
            onMouseEnter={this._handleMouseEnter.bind(null, { arc: point, ref: 'arc-' + this.props.id + i })}
            onMouseLeave={this._handleMouseLeave.bind(null, { arc: point, ref: 'arc-' + this.props.id + i })}
          >
            <path
              className={'arc-' + this.props.id}
              d={this.state.standardArc(point)}
              fill={this.props.colors[i]}
              opacity={this.props.opacity}
              ref={'arc-' + this.props.id + i}
            />
          </g>
        );
      });
    } else {
      return null;
    }
  },

  _renderBaseArc () {
    if (this.props.showBaseArc) {
      return (
        <g>
          <path d={this.state.baseArc()} fill={this.props.baseArcColor}></path>
        </g>
      );
    } else {
      return null;
    }
  },

  _renderDataPoints () {
    const dataPoints = this.props.dataPoints.map(dataPoint => {
      return dataPoint.value;
    });

    return dataPoints.map((dataPoint, index) => {
      const endAngle = dataPoint / this.props.chartTotal;

      const dataPointArc = d3.svg.arc()
        .outerRadius(this.state.radius - this.props.activeOffset)
        .innerRadius(this.state.radius - this.props.arcWidth)
        .startAngle(0)
        .endAngle(endAngle * 2 * 2 * Math.PI);

      return (
        <circle
          cx='0'
          cy='0'
          fill={this.props.dataPointColors[index]}
          key={index}
          r={this.props.dataPointRadius}
          transform={'translate(' + dataPointArc.centroid() + ')'}
        />
      );
    });
  },

  _renderDataLabel () {
    if (this.props.showDataLabel) {
      if (this.props.children) {
        return (

            {this.props.children}

        );
      } else {
        const activeDataSet = this.props.data[this.state.activeIndex] || {};
        const color = this.state.activeIndex === -1 ? this.props.colors[0] : this.props.colors[this.state.activeIndex];
        const text = this.state.activeIndex === -1 ? this.props.defaultLabelText : activeDataSet.name;
        const value = this.state.activeIndex === -1 ? this.props.formatter(this.props.defaultLabelValue) : this.props.formatter(activeDataSet.value);

        return (

              {value}

              {text}

        );
      }
    } else {
      return null;
    }
  },

  render () {
    const position = 'translate(' + this.props.width / 2 + ',' + this.props.height / 2 + ')';
    const fontSize = Math.min(this.props.width, this.props.height) * 0.2 + 'px';

    return (

        {this._renderDataLabel()}
        <svg
          className='mx-donutchart-svg'
          height={this.props.height}
          ref={this.props.id}
          width={this.props.width}
        >
          <g className='mx-donutchart-g' transform={position}>
            {this._renderBaseArc()}
            {this._renderArcs()}
            {this._renderDataPoints()}
          </g>
        </svg>

    );
  }
});

const styles = {
  component: {
    position: 'relative',
    fontFamily: StyleConstants.FontFamily
  },
  center: {
    position: 'absolute',
    top: '50%',
    left: '50%',
    textAlign: 'center',
    transform: 'translate(-50%, -50%)'
  },
  label: {
    color: StyleConstants.Colors.FOG,
    fontSize: '0.4em',
    marginTop: '5px'
  },
  value: {
    fontWeight: 300
  }
};

module.exports = Radium(DonutChart);
<commit_msg>Rename bounce arc animation variables for clarity<commit_after>const _isEqual = require('lodash');
const React = require('react');
const Radium = require('radium');
const d3 = require('d3');

const StyleConstants = require('../constants/Style');

const DonutChart = React.createClass({
  propTypes: {
    activeOffset: React.PropTypes.number,
    animateOnHover: React.PropTypes.bool,
    animationDuration: React.PropTypes.number,
    animationTypeOnLoad: React.PropTypes.oneOf(['roll', 'pop']),
    arcWidth: React.PropTypes.number,
    baseArcColor: React.PropTypes.string,
    chartTotal: React.PropTypes.number,
    children: React.PropTypes.node,
    colors: React.PropTypes.array,
    data: React.PropTypes.array.isRequired,
    dataPointColors: React.PropTypes.array,
    dataPointRadius: React.PropTypes.number,
    dataPoints: React.PropTypes.array,
    defaultLabelText: React.PropTypes.string,
    defaultLabelValue: React.PropTypes.string,
    formatter: React.PropTypes.func,
    height: React.PropTypes.number,
    hoverExpandDistance: React.PropTypes.number,
    id: React.PropTypes.string,
    onClick: React.PropTypes.func,
    opacity: React.PropTypes.number,
    padAngle: React.PropTypes.number,
    showBaseArc: React.PropTypes.bool,
    showDataLabel: React.PropTypes.bool,
    width: React.PropTypes.number
  },

  getDefaultProps () {
    return {
      activeOffset: 0,
      animateOnHover: false,
      animationDuration: 500,
      arcWidth: 10,
      baseArcColor: StyleConstants.Colors.BASE_ARC,
      colors: [StyleConstants.Colors.PRIMARY].concat(d3.scale.category20().range()),
      data: [],
      dataPointColors: [StyleConstants.Colors.LIME].concat(d3.scale.category20b().range()),
      dataPointRadius: 5,
      dataPoints: [],
      formatter (value) {
        return value;
      },
      height: 150,
      hoverExpandDistance: 5,
      id: 'donut-chart',
      onClick () {},
      opacity: 1,
      padAngle: 0.02,
      showBaseArc: true,
      showDataLabel: true,
      width: 150
    };
  },

  componentWillMount () {
    this._setupD3Functions();
  },

  componentDidMount () {
    if (this.props.data.length) {
      this._animateChart();
    }
  },

  componentWillReceiveProps (newProps) {
    if (!_isEqual(this.props.data, newProps.data) && this.props.data.length) {
      this._setupD3Functions();
      this._animateChart();
    }
  },

  _setupD3Functions () {
    const dataSets = this.props.data.map(item => {
      return item.value;
    });
    const valueTotal = dataSets.reduce((a, b) => {
      return a + b;
    });
    const endAngle = this.props.chartTotal ? valueTotal / this.props.chartTotal : 1;
    const pie = d3.layout.pie().sort(null).padAngle(this.props.padAngle).endAngle(endAngle * 2 * Math.PI);
    const values = pie(dataSets);
    const radius = Math.min(this.props.width, this.props.height) / 2.2;
    const standardArc = d3.svg.arc().outerRadius(radius - this.props.activeOffset).innerRadius(radius - this.props.arcWidth);
    const hoveredArc = d3.svg.arc().outerRadius(radius + this.props.hoverExpandDistance).innerRadius(radius - this.props.arcWidth);
    const baseArc = d3.svg.arc().outerRadius(radius - this.props.activeOffset).innerRadius(radius - this.props.arcWidth).startAngle(0).endAngle(2 * Math.PI);
    const bounceArcAnimationStart = d3.svg.arc().outerRadius(10).innerRadius(5);
    const bounceArcAnimationStartPaths = values.map(point => {
      return bounceArcAnimationStart(point);
    });

    this.setState({
      baseArc,
      endAngle,
      hoveredArc,
      pie,
      radius,
      standardArc,
      values,
      bounceArcAnimationStartPaths
    });
  },

  _animateChart () {
    switch (this.props.animationTypeOnLoad) {
      case 'roll':
        this._rollAnimate();
        break;
      case 'pop':
        this._bounceAnimate();
        break;
      default:
        break;
    }
  },

  _bounceAnimate () {
    d3.selectAll('.arc-' + this.props.id)
      .transition()
      .ease('bounce')
      .duration(this.props.animationDuration)
      .attrTween('d', (d, i, a) => {
        return d3.interpolate(this.state.bounceArcAnimationStartPaths[i], a);
      });
  },

  _rollAnimate () {
    d3.selectAll('.arc-' + this.props.id)
      .transition()
      .ease('bounce')
      .duration(this.props.animationDuration)
      .attrTween('transform', function () {
        return d3.interpolateString('rotate(0)', 'rotate(360)');
      });
  },

  _handleClick () {

  },

  _handleMouseEnter (point) {
    if (this.props.animateOnHover) {
      d3.select(this.refs[point.ref]).transition().duration(500).attr('d', this.state.hoveredArc(point.arc));
    }
  },

  _handleMouseLeave (point) {
    if (this.props.animateOnHover) {
      d3.select(this.refs[point.ref]).transition().duration(500).attr('d', this.state.standardArc(point.arc));
    }
  },

  _renderArcs () {
    if (this.props.data.length) {
      return this.state.values.map((point, i) => {
        return (
          <g
            key={i}
            onClick={this._handleClick.bind(null, i)}
            onMouseEnter={this._handleMouseEnter.bind(null, { arc: point, ref: 'arc-' + this.props.id + i })}
            onMouseLeave={this._handleMouseLeave.bind(null, { arc: point, ref: 'arc-' + this.props.id + i })}
          >
            <path
              className={'arc-' + this.props.id}
              d={this.state.standardArc(point)}
              fill={this.props.colors[i]}
              opacity={this.props.opacity}
              ref={'arc-' + this.props.id + i}
            />
          </g>
        );
      });
    } else {
      return null;
    }
  },

  _renderBaseArc () {
    if (this.props.showBaseArc) {
      return (
        <g>
          <path d={this.state.baseArc()} fill={this.props.baseArcColor}></path>
        </g>
      );
    } else {
      return null;
    }
  },

  _renderDataPoints () {
    const dataPoints = this.props.dataPoints.map(dataPoint => {
      return dataPoint.value;
    });

    return dataPoints.map((dataPoint, index) => {
      const endAngle = dataPoint / this.props.chartTotal;

      const dataPointArc = d3.svg.arc()
        .outerRadius(this.state.radius - this.props.activeOffset)
        .innerRadius(this.state.radius - this.props.arcWidth)
        .startAngle(0)
        .endAngle(endAngle * 2 * 2 * Math.PI);

      return (
        <circle
          cx='0'
          cy='0'
          fill={this.props.dataPointColors[index]}
          key={index}
          r={this.props.dataPointRadius}
          transform={'translate(' + dataPointArc.centroid() + ')'}
        />
      );
    });
  },

  _renderDataLabel () {
    if (this.props.showDataLabel) {
      if (this.props.children) {
        return (

            {this.props.children}

        );
      } else {
        const activeDataSet = this.props.data[this.state.activeIndex] || {};
        const color = this.state.activeIndex === -1 ? this.props.colors[0] : this.props.colors[this.state.activeIndex];
        const text = this.state.activeIndex === -1 ? this.props.defaultLabelText : activeDataSet.name;
        const value = this.state.activeIndex === -1 ? this.props.formatter(this.props.defaultLabelValue) : this.props.formatter(activeDataSet.value);

        return (

              {value}

              {text}

        );
      }
    } else {
      return null;
    }
  },

  render () {
    const position = 'translate(' + this.props.width / 2 + ',' + this.props.height / 2 + ')';
    const fontSize = Math.min(this.props.width, this.props.height) * 0.2 + 'px';

    return (

        {this._renderDataLabel()}
        <svg
          className='mx-donutchart-svg'
          height={this.props.height}
          ref={this.props.id}
          width={this.props.width}
        >
          <g className='mx-donutchart-g' transform={position}>
            {this._renderBaseArc()}
            {this._renderArcs()}
            {this._renderDataPoints()}
          </g>
        </svg>

    );
  }
});

const styles = {
  component: {
    position: 'relative',
    fontFamily: StyleConstants.FontFamily
  },
  center: {
    position: 'absolute',
    top: '50%',
    left: '50%',
    textAlign: 'center',
    transform: 'translate(-50%, -50%)'
  },
  label: {
    color: StyleConstants.Colors.FOG,
    fontSize: '0.4em',
    marginTop: '5px'
  },
  value: {
    fontWeight: 300
  }
};

module.exports = Radium(DonutChart);
<|endoftext|>


<commit_before>const numeral = require('numeral');
const Radium = require('radium');
const React = require('react');

const StyleConstants = require('../constants/Style');

const Button = require('./Button');
const Icon = require('./Icon');

const FileUpload = React.createClass({
  propTypes: {
    allowedFileTypes: React.PropTypes.array,
    imageUrl: React.PropTypes.string,
    maxFileSize: React.PropTypes.number,
    onFileAdd: React.PropTypes.func.isRequired,
    onFileRemove: React.PropTypes.func.isRequired,
    onFileValidation: React.PropTypes.func,
    style: React.PropTypes.oneOfType([React.PropTypes.array, React.PropTypes.object]),
    uploadedFile: React.PropTypes.any
  },

  getInitialState () {
    return {
      dragging: false,
      imageSource: null
    };
  },

  componentDidMount () {
    this._readFile(this.props.uploadedFile);
  },

  componentWillReceiveProps (newProps) {
    if (newProps.uploadedFile !== this.props.uploadedFile) {
      this._readFile(newProps.uploadedFile);
    }
  },

  _handleFileSelect (e) {
    const file = e.target.files[0];

    if (file) {
      this._validateFile(file);
    }
  },

  _onDragOver (e) {
    e.stopPropagation();
    e.preventDefault();

    this.setState({
      dragging: true
    });
  },

  _onDragLeave (e) {
    e.stopPropagation();
    e.preventDefault();

    this.setState({
      dragging: false
    });
  },

  _onDrop (e) {
    e.stopPropagation();
    e.preventDefault();

    const file = e.dataTransfer.files[0];

    this._validateFile(file);
  },

  _onDropzoneClick () {
    this._input.click();
  },

  _readFile (file) {
    if (file) {
      const reader = new FileReader();

      reader.readAsDataURL(file);

      reader.onload = () => {
        const imageSource = file.type.match('image*') ? reader.result : null;

        this.setState({
          dragging: false,
          imageSource
        });
      };
    }
  },

  _removeFile (e) {
    e.stopPropagation();
    e.preventDefault();

    this._input.value = null;

    this.setState({
      imageSource: null
    });
    this.props.onFileRemove();
  },

  _validateFile (file) {
    const isTooBig = this.props.maxFileSize < file.size / 1000;
    const isInvalidType = this.props.allowedFileTypes && this.props.allowedFileTypes.indexOf(file.type) < 0;
    const validationMessages = [];

    if (isTooBig || isInvalidType) {
      this.setState({
        dragging: false
      });

      this.props.onFileRemove(this.props.uploadedFile);
    } else {
      this.props.onFileAdd(file);
    }

    if (this.props.onFileValidation) {
      if (isTooBig) {
        validationMessages.push('file_size');
      }

      if (isInvalidType) {
        validationMessages.push('file_type');
      }

      this.props.onFileValidation(validationMessages);
    }
  },

  render () {
    const dropzoneLoaded = this.props.imageUrl || this.props.uploadedFile;
    const imageSource = this.state.imageSource || this.props.imageUrl;

    return (

        {dropzoneLoaded ? (

            {this.props.children}
            {imageSource ? (

            ) : (
              <Icon size={60} style={styles.documentIcon} type='document' />
            )}
            {this.props.uploadedFile ? (

{this.props.uploadedFile.name}

{numeral(this.props.uploadedFile.size / 1000).format('0.0')}k

                <Button
                  icon='delete'
                  onClick={this._removeFile}
                  style={styles.button}
                  type='secondary'
                />

            ) : null}

        ) : (

            {this.state.dragging ? (

                <Icon size={60} style={styles.importIcon} type='import' />

Drop file here to upload

            ) : (

                {this.props.children}

            )}

        )}
        <input
          name='files'
          onChange={this._handleFileSelect}
          ref={(ref) => (this._input = ref)}
          style={styles.hiddenInput}
          type='file'
        />

    );
  }
});

const styles = {
  dropzone: {
    backgroundColor: StyleConstants.Colors.PORCELAIN,
    border: '1px solid ' + StyleConstants.Colors.FOG,
    borderRadius: 3,
    color: StyleConstants.Colors.ASH,
    fontFamily: StyleConstants.Fonts.REGULAR,
    fontSize: StyleConstants.FontSizes.MEDIUM,
    height: 100,
    position: 'relative',
    textAlign: 'center'
  },
  hiddenInput: {
    display: 'none'
  },

  // Dragging Styles
  dragging: {
    backgroundColor: StyleConstants.Colors.WHITE,
    border: '1px dashed ' + StyleConstants.Colors.PRIMARY,
    height: 150
  },
  importIcon: {
    color: StyleConstants.Colors.PRIMARY
  },
  draggingText: {
    color: StyleConstants.Colors.PRIMARY,
    fontFamily: StyleConstants.Fonts.SEMIBOLD,
    fontSize: StyleConstants.FontSizes.LARGE
  },
  faded: {
    opacity: 0.5
  },

  // Loaded Styles
  dropzoneLoaded: {
    height: 'auto',
    padding: 20
  },
  previewImage: {
    marginTop: 10,
    marginBottom: 10,
    maxWidth: '90%'
  },
  documentIcon: {
    color: StyleConstants.Colors.ASH
  },
  button: {
    marginTop: 10
  },

  // Dropzone Text
  dropzoneChild: {
    height: '100%',
    pointerEvents: 'none'
  },
  centered: {
    left: '50%',
    position: 'absolute',
    top: '50%',
    transform: 'translate(-50%, -50%)'
  },
  invalidMessage: {
    fontSize: StyleConstants.FontSizes.LARGE,
    marginBottom: 10
  },
  invalidIcon: {
    color: StyleConstants.Colors.ASH
  }
};

module.exports = Radium(FileUpload);
<commit_msg>change fileupload icon styles from color to fill<commit_after>const numeral = require('numeral');
const Radium = require('radium');
const React = require('react');

const StyleConstants = require('../constants/Style');

const Button = require('./Button');
const Icon = require('./Icon');

const FileUpload = React.createClass({
  propTypes: {
    allowedFileTypes: React.PropTypes.array,
    imageUrl: React.PropTypes.string,
    maxFileSize: React.PropTypes.number,
    onFileAdd: React.PropTypes.func.isRequired,
    onFileRemove: React.PropTypes.func.isRequired,
    onFileValidation: React.PropTypes.func,
    style: React.PropTypes.oneOfType([React.PropTypes.array, React.PropTypes.object]),
    uploadedFile: React.PropTypes.any
  },

  getInitialState () {
    return {
      dragging: false,
      imageSource: null
    };
  },

  componentDidMount () {
    this._readFile(this.props.uploadedFile);
  },

  componentWillReceiveProps (newProps) {
    if (newProps.uploadedFile !== this.props.uploadedFile) {
      this._readFile(newProps.uploadedFile);
    }
  },

  _handleFileSelect (e) {
    const file = e.target.files[0];

    if (file) {
      this._validateFile(file);
    }
  },

  _onDragOver (e) {
    e.stopPropagation();
    e.preventDefault();

    this.setState({
      dragging: true
    });
  },

  _onDragLeave (e) {
    e.stopPropagation();
    e.preventDefault();

    this.setState({
      dragging: false
    });
  },

  _onDrop (e) {
    e.stopPropagation();
    e.preventDefault();

    const file = e.dataTransfer.files[0];

    this._validateFile(file);
  },

  _onDropzoneClick () {
    this._input.click();
  },

  _readFile (file) {
    if (file) {
      const reader = new FileReader();

      reader.readAsDataURL(file);

      reader.onload = () => {
        const imageSource = file.type.match('image*') ? reader.result : null;

        this.setState({
          dragging: false,
          imageSource
        });
      };
    }
  },

  _removeFile (e) {
    e.stopPropagation();
    e.preventDefault();

    this._input.value = null;

    this.setState({
      imageSource: null
    });
    this.props.onFileRemove();
  },

  _validateFile (file) {
    const isTooBig = this.props.maxFileSize < file.size / 1000;
    const isInvalidType = this.props.allowedFileTypes && this.props.allowedFileTypes.indexOf(file.type) < 0;
    const validationMessages = [];

    if (isTooBig || isInvalidType) {
      this.setState({
        dragging: false
      });

      this.props.onFileRemove(this.props.uploadedFile);
    } else {
      this.props.onFileAdd(file);
    }

    if (this.props.onFileValidation) {
      if (isTooBig) {
        validationMessages.push('file_size');
      }

      if (isInvalidType) {
        validationMessages.push('file_type');
      }

      this.props.onFileValidation(validationMessages);
    }
  },

  render () {
    const dropzoneLoaded = this.props.imageUrl || this.props.uploadedFile;
    const imageSource = this.state.imageSource || this.props.imageUrl;

    return (

        {dropzoneLoaded ? (

            {this.props.children}
            {imageSource ? (

            ) : (
              <Icon size={60} style={styles.documentIcon} type='document' />
            )}
            {this.props.uploadedFile ? (

{this.props.uploadedFile.name}

{numeral(this.props.uploadedFile.size / 1000).format('0.0')}k

                <Button
                  icon='delete'
                  onClick={this._removeFile}
                  style={styles.button}
                  type='secondary'
                />

            ) : null}

        ) : (

            {this.state.dragging ? (

                <Icon size={60} style={styles.importIcon} type='import' />

Drop file here to upload

            ) : (

                {this.props.children}

            )}

        )}
        <input
          name='files'
          onChange={this._handleFileSelect}
          ref={(ref) => (this._input = ref)}
          style={styles.hiddenInput}
          type='file'
        />

    );
  }
});

const styles = {
  dropzone: {
    backgroundColor: StyleConstants.Colors.PORCELAIN,
    border: '1px solid ' + StyleConstants.Colors.FOG,
    borderRadius: 3,
    color: StyleConstants.Colors.ASH,
    fontFamily: StyleConstants.Fonts.REGULAR,
    fontSize: StyleConstants.FontSizes.MEDIUM,
    height: 100,
    position: 'relative',
    textAlign: 'center'
  },
  hiddenInput: {
    display: 'none'
  },

  // Dragging Styles
  dragging: {
    backgroundColor: StyleConstants.Colors.WHITE,
    border: '1px dashed ' + StyleConstants.Colors.PRIMARY,
    height: 150
  },
  importIcon: {
    fill: StyleConstants.Colors.PRIMARY
  },
  draggingText: {
    color: StyleConstants.Colors.PRIMARY,
    fontFamily: StyleConstants.Fonts.SEMIBOLD,
    fontSize: StyleConstants.FontSizes.LARGE
  },
  faded: {
    opacity: 0.5
  },

  // Loaded Styles
  dropzoneLoaded: {
    height: 'auto',
    padding: 20
  },
  previewImage: {
    marginTop: 10,
    marginBottom: 10,
    maxWidth: '90%'
  },
  documentIcon: {
    fill: StyleConstants.Colors.ASH
  },
  button: {
    marginTop: 10
  },

  // Dropzone Text
  dropzoneChild: {
    height: '100%',
    pointerEvents: 'none'
  },
  centered: {
    left: '50%',
    position: 'absolute',
    top: '50%',
    transform: 'translate(-50%, -50%)'
  },
  invalidMessage: {
    fontSize: StyleConstants.FontSizes.LARGE,
    marginBottom: 10
  },
  invalidIcon: {
    color: StyleConstants.Colors.ASH
  }
};

module.exports = Radium(FileUpload);
<|endoftext|>


<commit_before>var React = require('react/addons');
var classNames = require('classnames');

module.exports = React.createClass({
	displayName: 'Pagination',
	propTypes: {
		className: React.PropTypes.string,
		currentPage: React.PropTypes.number.isRequired,
		onPageSelect: React.PropTypes.func,
		pageSize: React.PropTypes.number.isRequired,
		plural: React.PropTypes.string,
		singular: React.PropTypes.string,
		style: React.PropTypes.object,
		total: React.PropTypes.number.isRequired
	},
	renderCount () {
		let count = '';
		let { currentPage, pageSize, plural, singular, total } = this.props;
		if (!total) {
			count = 'No ' + (plural || 'records');
		} else if (total > pageSize) {
			let start = pageSize * currentPage;
			let end = Math.min(start + pageSize, total);
			count = `Showing ${start} to ${end} of ${total}`;
		} else {
			count = 'Showing ' + total;
			if (total > 1 && plural) {
				count += ' ' + plural;
			} else if (total === 1 && singular) {
				count += ' ' + singular;
			}
		}
		return (

{count}

		);
	},
	renderPages () {
		if (this.props.total <= this.props.pageSize) return null;

		let pages = [];
		let { currentPage, onPageSelect, pageSize, total } = this.props;

		for (let i = 0; i < Math.ceil(total / pageSize); i++) {
			let page = i + 1;
			let current = (page === currentPage);
			let className = classNames('Pagination__list__item', {
				'is-selected': current
			});
			pages.push(<button key={'page_' + page} className={className} onClick={onPageSelect}>{page}</button>);
		}

		return (

				{pages}

		);

	},
	render () {
		var className = classNames('Pagination', this.props.className);
		return (

				{this.renderCount()}
				{this.renderPages()}

		);
	}
});
<commit_msg>Fixing Pagination onPageSelect handler<commit_after>var React = require('react/addons');
var classNames = require('classnames');

module.exports = React.createClass({
	displayName: 'Pagination',
	propTypes: {
		className: React.PropTypes.string,
		currentPage: React.PropTypes.number.isRequired,
		onPageSelect: React.PropTypes.func,
		pageSize: React.PropTypes.number.isRequired,
		plural: React.PropTypes.string,
		singular: React.PropTypes.string,
		style: React.PropTypes.object,
		total: React.PropTypes.number.isRequired
	},
	renderCount () {
		let count = '';
		let { currentPage, pageSize, plural, singular, total } = this.props;
		if (!total) {
			count = 'No ' + (plural || 'records');
		} else if (total > pageSize) {
			let start = pageSize * currentPage;
			let end = Math.min(start + pageSize, total);
			count = `Showing ${start} to ${end} of ${total}`;
		} else {
			count = 'Showing ' + total;
			if (total > 1 && plural) {
				count += ' ' + plural;
			} else if (total === 1 && singular) {
				count += ' ' + singular;
			}
		}
		return (

{count}

		);
	},
	onPageSelect (i) {
		if (!this.props.onPageSelect) return;
		this.props.onPageSelect(i);
	},
	renderPages () {
		if (this.props.total <= this.props.pageSize) return null;

		let pages = [];
		let { currentPage, pageSize, total } = this.props;

		for (let i = 0; i < Math.ceil(total / pageSize); i++) {
			let page = i + 1;
			let current = (page === currentPage);
			let className = classNames('Pagination__list__item', {
				'is-selected': current
			});
			pages.push(<button key={'page_' + page} className={className} onClick={() => this.onPageSelect(page)}>{page}</button>);
		}

		return (

				{pages}

		);

	},
	render () {
		var className = classNames('Pagination', this.props.className);
		return (

				{this.renderCount()}
				{this.renderPages()}

		);
	}
});
<|endoftext|>


<commit_before>/** @jsx hJSX */
import 'babel/polyfill';
import {Rx} from '@cycle/core';
import {hJSX} from '@cycle/dom';
import renderNavBar from './nav-bar';
import renderDataTable from './data-table';
import LocationFilter from './LocationFilter';
import TextFilter from './TextFilter';
import AvailabilityFilter from './AvailabilityFilter';
import {URL_ROOT, smartStateFold} from '../utils';
import _ from 'lodash';
hJSX();

function makeUpdate$(peopleArray$, props$) {
  const updatePeopleArray$ = peopleArray$
    .map(peopleArray => function updateStateWithPeopleArray(oldState) {
      return {...oldState, people: peopleArray};
    });

  const updateTribes$ = props$
    .map(tribes => function updateStateWithTribes(oldState) {
      return {...oldState, tribes};
    });

  return Rx.Observable.merge(updatePeopleArray$, updateTribes$);
}

function model(update$) {
  const state$ = update$
    .startWith({
      people: [],
      filtered: [],
      tribes: [],
      filters: {
        location: 'all',
        search: null,
        availability: null,
      },
    })
    .scan(smartStateFold)
    .shareReplay(1);
  return state$;
}

function makeFilterFn$(selectedLocation$, searchValue$, availabilityValue$) {
  const locationFilterFn$ = selectedLocation$.map(location =>
    function filterStateByLocation(oldState) {
      const newPeople = oldState.people.filter(person =>
        location === 'all'
        || location === person.tribe.name
        || location === person.tribe.country
        || location === person.tribe.site.name
      );
      return {...oldState, people: newPeople};
    }
  ).startWith(_.identity); // identity means "allow anything"

  const searchFilterFn$ = searchValue$.map(searchValue =>
    function filterStateBySearch(oldState) {
      const newPeople = oldState.people.filter(person => {
        const lowerCaseSearch = searchValue.toLowerCase();
        return (
          lowerCaseSearch === null
          || lowerCaseSearch.length === 0
          || person.name.toLowerCase().indexOf(lowerCaseSearch) !== -1
          || person.skills.toLowerCase().indexOf(lowerCaseSearch) !== -1
        );
      });
      return {...oldState, people: newPeople};
    }
  ).startWith(_.identity);

  const availabilityFilterFn$ = availabilityValue$.map(availabilityValue =>
    function filterStateByAvailability(oldState) {
      const newPeople = oldState.people.filter(person => {
        const man_days_available = parseInt(person.man_days_available);
        return (
          availabilityValue === null
          || man_days_available >= availabilityValue
        );
      });
      return {...oldState, people: newPeople};
    }
  ).startWith(_.identity);

  // AND-combine filter functions and compose them (`_.flow`) calling them one
  // after the other.
  return Rx.Observable.combineLatest(
    locationFilterFn$, searchFilterFn$, availabilityFilterFn$,
    _.flow
  );
}

function view(state$, locationFilterVTree$ = null, textFilterVTree$ = null,
              availabilityFilterVTree$ = null) {
  return state$.map(state => {
    return (

        {renderNavBar()}

People

              {locationFilterVTree$}

Filter tools

Find a person or specific skills

                  {textFilterVTree$}

                {availabilityFilterVTree$}

            {renderDataTable(state.people)}

    );
  });
}

// Handle all HTTP networking logic of this page
function PeoplePageHTTP(sources, urlRoot) {
  const request$ = Rx.Observable.just(`${urlRoot}/people/`);
  const response$ = sources.HTTP
    .filter(res$ => res$.request === `${urlRoot}/people/`)
    .mergeAll()
    .map(responseObj => responseObj.body.results);
  const sink = {
    request$,
    response$,
  };
  return sink;
}

function locationFilterWrapper(state$, sourceDOM) {
  // Some preprocessing step to make the props for the location filter
  const locationFilterProps$ = state$.map(state => {
    return {
      selectedLocation: state.filters.location,
      tribes: state.tribes,
    };
  });

  // Call the location filter program
  const locationFilter = LocationFilter({
    DOM: sourceDOM,
    props$: locationFilterProps$,
  });

  // Some postprocessing step to handle the location filter's virtual DOM
  const vtree$ = locationFilter.DOM.publishValue(null);
  vtree$.connect();

  return {
    DOM: vtree$,
    selected$: locationFilter.selectedLocation$,
  };
}

function textFilterWrapper(state$, sourceDOM) {
  // Some preprocessing step to make the props from state$
  const props$ = state$
    .map(state => ({value: state.filters.search}))
    .distinctUntilChanged(state => state.searchString);
  const sinks = TextFilter({DOM: sourceDOM, props$});
  return sinks;
}

function availabilityFilterWrapper(state$, sourceDOM) {
  // Some preprocessing step to make the props from state$
  const props$ = state$
    .map(state => ({value: state.filters.availability}))
    .distinctUntilChanged(state => state.value);
  const sinks = AvailabilityFilter({DOM: sourceDOM, props$});
  return sinks;
}

function PeoplePage(sources) {
  const peoplePageHTTP = PeoplePageHTTP(sources, URL_ROOT);
  const update$ = makeUpdate$(peoplePageHTTP.response$, sources.props$);
  const state$ = model(update$);
  const locationFilter = locationFilterWrapper(state$, sources.DOM);
  const textFilter = textFilterWrapper(state$, sources.DOM);
  const availabilityFilter = availabilityFilterWrapper(state$, sources.DOM);
  const filterFn$ = makeFilterFn$(
    locationFilter.selected$,
    textFilter.value$,
    availabilityFilter.value$
  );
  const filteredState$ = Rx.Observable.combineLatest(state$, filterFn$,
    (state, filterFn) => filterFn(state)
  );
  const vtree$ = view(
    filteredState$,
    locationFilter.DOM,
    textFilter.DOM,
    availabilityFilter.DOM
  );

  const sinks = {
    DOM: vtree$,
    HTTP: peoplePageHTTP.request$,
  };
  return sinks;
}

export default PeoplePage;
<commit_msg>Load people pages and incrementally update the peoples array<commit_after>/** @jsx hJSX */
import 'babel/polyfill';
import {Rx} from '@cycle/core';
import {hJSX} from '@cycle/dom';
import renderNavBar from './nav-bar';
import renderDataTable from './data-table';
import LocationFilter from './LocationFilter';
import TextFilter from './TextFilter';
import AvailabilityFilter from './AvailabilityFilter';
import {URL_ROOT, smartStateFold} from '../utils';
import _ from 'lodash';
hJSX();

function makeUpdate$(peopleArray$, props$) {
  const updatePeopleArray$ = peopleArray$
    .map(peopleArray => function updateStateWithPeopleArray(oldState) {
      return {...oldState, people: peopleArray};
    });

  const updateTribes$ = props$
    .map(tribes => function updateStateWithTribes(oldState) {
      return {...oldState, tribes};
    });

  return Rx.Observable.merge(updatePeopleArray$, updateTribes$);
}

function model(update$) {
  const state$ = update$
    .startWith({
      people: [],
      filtered: [],
      tribes: [],
      filters: {
        location: 'all',
        search: null,
        availability: null,
      },
    })
    .scan(smartStateFold)
    .shareReplay(1);
  return state$;
}

function makeFilterFn$(selectedLocation$, searchValue$, availabilityValue$) {
  const locationFilterFn$ = selectedLocation$.map(location =>
    function filterStateByLocation(oldState) {
      const newPeople = oldState.people.filter(person =>
        location === 'all'
        || location === person.tribe.name
        || location === person.tribe.country
        || location === person.tribe.site.name
      );
      return {...oldState, people: newPeople};
    }
  ).startWith(_.identity); // identity means "allow anything"

  const searchFilterFn$ = searchValue$.map(searchValue =>
    function filterStateBySearch(oldState) {
      const newPeople = oldState.people.filter(person => {
        const lowerCaseSearch = searchValue.toLowerCase();
        return (
          lowerCaseSearch === null
          || lowerCaseSearch.length === 0
          || person.name.toLowerCase().indexOf(lowerCaseSearch) !== -1
          || person.skills.toLowerCase().indexOf(lowerCaseSearch) !== -1
        );
      });
      return {...oldState, people: newPeople};
    }
  ).startWith(_.identity);

  const availabilityFilterFn$ = availabilityValue$.map(availabilityValue =>
    function filterStateByAvailability(oldState) {
      const newPeople = oldState.people.filter(person => {
        const man_days_available = parseInt(person.man_days_available);
        return (
          availabilityValue === null
          || man_days_available >= availabilityValue
        );
      });
      return {...oldState, people: newPeople};
    }
  ).startWith(_.identity);

  // AND-combine filter functions and compose them (`_.flow`) calling them one
  // after the other.
  return Rx.Observable.combineLatest(
    locationFilterFn$, searchFilterFn$, availabilityFilterFn$,
    _.flow
  );
}

function view(state$, locationFilterVTree$ = null, textFilterVTree$ = null,
              availabilityFilterVTree$ = null) {
  return state$.map(state => {
    return (

        {renderNavBar()}

People

              {locationFilterVTree$}

Filter tools

Find a person or specific skills

                  {textFilterVTree$}

                {availabilityFilterVTree$}

            {renderDataTable(state.people)}

    );
  });
}

// Handle all HTTP networking logic of this page
function PeoplePageHTTP(sources, urlRoot) {
  const PEOPLE_URL = `${urlRoot}/people/`;
  const atomicResponse$ = sources.HTTP
    .filter(res$ => _.startsWith(res$.request, PEOPLE_URL))
    .mergeAll()
    .shareReplay(1);
  const request$ = atomicResponse$
    .filter(responseObj => responseObj.body.next)
    .map(responseObj => responseObj.body.next.replace('limit=5', 'limit=40'))
    .startWith(PEOPLE_URL + '?limit=5');
  const arrayOfPeople$ = atomicResponse$
    .map(responseObj => responseObj.body.results)
    .scan((acc, curr) => acc.concat(curr));
  const response$ = arrayOfPeople$;
  const sink = {
    request$,
    response$,
  };
  return sink;
}

function locationFilterWrapper(state$, sourceDOM) {
  // Some preprocessing step to make the props for the location filter
  const locationFilterProps$ = state$.map(state => {
    return {
      selectedLocation: state.filters.location,
      tribes: state.tribes,
    };
  });

  // Call the location filter program
  const locationFilter = LocationFilter({
    DOM: sourceDOM,
    props$: locationFilterProps$,
  });

  // Some postprocessing step to handle the location filter's virtual DOM
  const vtree$ = locationFilter.DOM.publishValue(null);
  vtree$.connect();

  return {
    DOM: vtree$,
    selected$: locationFilter.selectedLocation$,
  };
}

function textFilterWrapper(state$, sourceDOM) {
  // Some preprocessing step to make the props from state$
  const props$ = state$
    .map(state => ({value: state.filters.search}))
    .distinctUntilChanged(state => state.searchString);
  const sinks = TextFilter({DOM: sourceDOM, props$});
  return sinks;
}

function availabilityFilterWrapper(state$, sourceDOM) {
  // Some preprocessing step to make the props from state$
  const props$ = state$
    .map(state => ({value: state.filters.availability}))
    .distinctUntilChanged(state => state.value);
  const sinks = AvailabilityFilter({DOM: sourceDOM, props$});
  return sinks;
}

function PeoplePage(sources) {
  const peoplePageHTTP = PeoplePageHTTP(sources, URL_ROOT);
  const update$ = makeUpdate$(peoplePageHTTP.response$, sources.props$);
  const state$ = model(update$);
  const locationFilter = locationFilterWrapper(state$, sources.DOM);
  const textFilter = textFilterWrapper(state$, sources.DOM);
  const availabilityFilter = availabilityFilterWrapper(state$, sources.DOM);
  const filterFn$ = makeFilterFn$(
    locationFilter.selected$,
    textFilter.value$,
    availabilityFilter.value$
  );
  const filteredState$ = Rx.Observable.combineLatest(state$, filterFn$,
    (state, filterFn) => filterFn(state)
  );
  const vtree$ = view(
    filteredState$,
    locationFilter.DOM,
    textFilter.DOM,
    availabilityFilter.DOM
  );

  const sinks = {
    DOM: vtree$,
    HTTP: peoplePageHTTP.request$,
  };
  return sinks;
}

export default PeoplePage;
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import TextInput from './common/TextInput';
import SelectInput from './common/SelectInput';

const SearchForm = ({ query, placeholder ,onSave, onChange, saving, errors }) => {
  const repos = [{text: 'GitHub', value: 'github'},{text: 'BitBucket', value: 'bitbucket'}];
  return (
    <form onSubmit={onSave}>

      <SelectInput
        name="repo"
        label="Repository *"
        value={query.repo}
        defaultOption="Select Repository"
        options={repos}
        onChange={onChange}
        error={errors.repo} />

      <TextInput
        name="keyword"
        label="Keyword *"
        value={query.keyword}
        placeholder={placeholder}
        onChange={onChange}
        error={errors.keyword} />

      {/* <input
        type="submit"
        disabled={saving}
        value={saving ? 'Searching...' : 'Search'}
        className="btn btn-primary"
        onClick={onSave} /> */}
    </form>
  );
};

SearchForm.propTypes = {
  query: PropTypes.object.isRequired,
  placeholder: PropTypes.string.isRequired,
  onSave: React.PropTypes.func,
  onChange: React.PropTypes.func,
  saving: React.PropTypes.bool,
  errors: React.PropTypes.object
};

export default SearchForm;<commit_msg>Fix search form test failure<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import TextInput from './common/TextInput';
import SelectInput from './common/SelectInput';

const SearchForm = ({ query, placeholder ,onSave, onChange, saving, errors }) => {
  const repos = [{text: 'GitHub', value: 'github'},{text: 'BitBucket', value: 'bitbucket'}];
  return (
    <form onSubmit={onSave} disabled={saving}>

      <SelectInput
        name="repo"
        label="Repository *"
        value={query.repo}
        defaultOption="Select Repository"
        options={repos}
        onChange={onChange}
        error={errors.repo} />

      <TextInput
        name="keyword"
        label="Keyword *"
        value={query.keyword}
        placeholder={placeholder}
        onChange={onChange}
        error={errors.keyword} />

      {/* <input
        type="submit"
        disabled={saving}
        value={saving ? 'Searching...' : 'Search'}
        className="btn btn-primary"
        onClick={onSave} /> */}
    </form>
  );
};

SearchForm.propTypes = {
  query: PropTypes.object.isRequired,
  placeholder: PropTypes.string.isRequired,
  onSave: React.PropTypes.func,
  onChange: React.PropTypes.func,
  saving: React.PropTypes.bool,
  errors: React.PropTypes.object
};

export default SearchForm;<|endoftext|>


<commit_before>require('es6-promise').polyfill();
require('isomorphic-fetch');

/**
 * pnut-butter.js
 *
 * Wrapper library for the pnut.io API.
 *
 * @returns {Object}
 */
const pnut = () => {
  const _base = 'https://api.pnut.io/v0'

  let _token = '';

  const call = (uri, {httpMethod = 'GET', resultAs = 'json'} = {}) => {

    let auth = {};
    if (_token !== '') {
      auth = {
        "Authorization": `Bearer ${_token}`
      }
    }

    return fetch(uri, {
      method: httpMethod,
      headers: auth
    }).then(function (response) {
      if (resultAs === 'response') {
        return response;
      } else {
        return response[resultAs]();
      }
    });
  }

  return {
    /**
     * Set auth token
     * @param {string} newToken - API access token
     */
    set token(newToken) {
      _token = newToken;
    },

    /**
     * Send request with custom endpoint / parameters
     * @param {string} endpoint - The custom call
     * @param {string} method - HTTP verb, defaults to GET
     * @return {Promise}
     */
     custom(endpoint, method = 'GET') {
      endpoint = endpoint.startsWith('/') ? `${endpoint}` : `/${endpoint}`
      return call(`${_base}${endpoint}`, {httpMethod: method})
     },

    /**
     * The global timeline
     * @returns {Promise}
     */
    global() {
      return call(`${_base}/posts/streams/global`);
    },

    /**
     * Retrieve a user object.
     * @param {string} userId - The user id
     * @returns {Promise}
     */
    user(userId) {
      return call(`${_base}/users/${userId}`);
    },

    /**
     * Retrieve a users avatar image.
     * @param {string} userId - The user id
     * @param {boolean} [true] - Should the return value be prebuffered via the fetch API?
     */
    avatar(userId, buffered = true) {
      options = buffered ? {resultAs: 'buffer'} : {resultAs: 'response'}
      return call(`${_base}/users/${userId}/avatar`, options);
    },

    /**
     * Retrieve a users cover image.
     * @param {string} userId - The user id
     * @param {boolean} [true] - Should the return value be prebuffered via the fetch API?
     */
    cover(userId, buffered = true) {
      options = buffered ? {resultAs: 'buffer'} : {resultAs: 'response'}
      return call(`${_base}/users/${userId}/cover`, options);
    },

    /**
     * Retrieve a list of specified user objects. Only retrieves the first 200 found
     * @param {array} userIds - An array of user ids
     * @returns {Promise}
     */
    users(userIds) {
      return call(`${_base}/users?ids=${userIds.join(',')}`);
    },

    /**
     * Retrieve a post object.
     * @param {string} postId - The post id
     * @returns {Promise}
     */
    post(postId) {
      return call(`${_base}/posts/${postId}`);
    },

    /**
     * Retrieve a list of specified post objects. Only retrieves the first 200 found
     * @param {array} ids - An array of post ids
     * @returns {Promise}
     */
    posts(ids) {
      return call(`${_base}/posts?ids=${ids.join(',')}`);
    },

    /**
     * Retrieve a list of previous versions of a post, not including the most recent.
     * @param {string} postId - The post id
     * @returns {Promise}
     */
    revisions(postId) {
      return call(`${_base}/posts/${postId}/revisions`);
    },

    /**
     * Retrieve posts mentioning the specified user.
     * @param {string} userid - The user id
     * @returns {Promise}
     */
    mentions(userId) {
      return call(`${_base}/users/${userId}/mentions`);
    },

    /**
     * Retrieve posts created by a specific user
     * @param {string} userId - The user id
     * @returns {Promise}
     */
    postsFrom(userId) {
      return call(`${_base}/users/${userId}/posts`);
    },

    /**
     * Retrieve a stream of all posts that include the specified tag.
     * @param {string} tag – The tag name
     * @returns {Promise}
     */
    postsTaggedWith(tag) {
      return call(`${_base}/posts/tag/${encodeURI(tag)}`);
    },

    /**
     * Retrieve posts within a thread.
     * @param {string} postId - The post id
     * @returns {Promise}
     */
    thread(postId) {
      return call(`${_base}/posts/${postId}/thread`);
    },

    /**
     * Retrieve actions executed against a post.
     * @param {string} postId - The post id
     * @returns {Promise}
     */
    actions(postId) {
      return call(`${_base}/posts/${postId}/actions`);
    }
  }
}

module.exports = pnut();<commit_msg>Add missing declarations<commit_after>require('es6-promise').polyfill();
require('isomorphic-fetch');

/**
 * pnut-butter.js
 *
 * Wrapper library for the pnut.io API.
 *
 * @returns {Object}
 */
const pnut = () => {
  const _base = 'https://api.pnut.io/v0'

  let _token = '';

  const call = (uri, {httpMethod = 'GET', resultAs = 'json'} = {}) => {

    let auth = {};
    if (_token !== '') {
      auth = {
        "Authorization": `Bearer ${_token}`
      }
    }

    return fetch(uri, {
      method: httpMethod,
      headers: auth
    }).then(function (response) {
      if (resultAs === 'response') {
        return response;
      } else {
        return response[resultAs]();
      }
    });
  }

  return {
    /**
     * Set auth token
     * @param {string} newToken - API access token
     */
    set token(newToken) {
      _token = newToken;
    },

    /**
     * Send request with custom endpoint / parameters
     * @param {string} endpoint - The custom call
     * @param {string} method - HTTP verb, defaults to GET
     * @return {Promise}
     */
     custom(endpoint, method = 'GET') {
      endpoint = endpoint.startsWith('/') ? `${endpoint}` : `/${endpoint}`
      return call(`${_base}${endpoint}`, {httpMethod: method})
     },

    /**
     * The global timeline
     * @returns {Promise}
     */
    global() {
      return call(`${_base}/posts/streams/global`);
    },

    /**
     * Retrieve a user object.
     * @param {string} userId - The user id
     * @returns {Promise}
     */
    user(userId) {
      return call(`${_base}/users/${userId}`);
    },

    /**
     * Retrieve a users avatar image.
     * @param {string} userId - The user id
     * @param {boolean} [true] - Should the return value be prebuffered via the fetch API?
     */
    avatar(userId, buffered = true) {
      let options = buffered ? {resultAs: 'buffer'} : {resultAs: 'response'}
      return call(`${_base}/users/${userId}/avatar`, options);
    },

    /**
     * Retrieve a users cover image.
     * @param {string} userId - The user id
     * @param {boolean} [true] - Should the return value be prebuffered via the fetch API?
     */
    cover(userId, buffered = true) {
      let options = buffered ? {resultAs: 'buffer'} : {resultAs: 'response'}
      return call(`${_base}/users/${userId}/cover`, options);
    },

    /**
     * Retrieve a list of specified user objects. Only retrieves the first 200 found
     * @param {array} userIds - An array of user ids
     * @returns {Promise}
     */
    users(userIds) {
      return call(`${_base}/users?ids=${userIds.join(',')}`);
    },

    /**
     * Retrieve a post object.
     * @param {string} postId - The post id
     * @returns {Promise}
     */
    post(postId) {
      return call(`${_base}/posts/${postId}`);
    },

    /**
     * Retrieve a list of specified post objects. Only retrieves the first 200 found
     * @param {array} ids - An array of post ids
     * @returns {Promise}
     */
    posts(ids) {
      return call(`${_base}/posts?ids=${ids.join(',')}`);
    },

    /**
     * Retrieve a list of previous versions of a post, not including the most recent.
     * @param {string} postId - The post id
     * @returns {Promise}
     */
    revisions(postId) {
      return call(`${_base}/posts/${postId}/revisions`);
    },

    /**
     * Retrieve posts mentioning the specified user.
     * @param {string} userid - The user id
     * @returns {Promise}
     */
    mentions(userId) {
      return call(`${_base}/users/${userId}/mentions`);
    },

    /**
     * Retrieve posts created by a specific user
     * @param {string} userId - The user id
     * @returns {Promise}
     */
    postsFrom(userId) {
      return call(`${_base}/users/${userId}/posts`);
    },

    /**
     * Retrieve a stream of all posts that include the specified tag.
     * @param {string} tag – The tag name
     * @returns {Promise}
     */
    postsTaggedWith(tag) {
      return call(`${_base}/posts/tag/${encodeURI(tag)}`);
    },

    /**
     * Retrieve posts within a thread.
     * @param {string} postId - The post id
     * @returns {Promise}
     */
    thread(postId) {
      return call(`${_base}/posts/${postId}/thread`);
    },

    /**
     * Retrieve actions executed against a post.
     * @param {string} postId - The post id
     * @returns {Promise}
     */
    actions(postId) {
      return call(`${_base}/posts/${postId}/actions`);
    }
  }
}

module.exports = pnut();<|endoftext|>