<commit_before>const cheerio = require('cheerio')
const request = require('../request')
const parser = require('../parser')
const urls = require('../urls')

const getAttr = parser.getAttr
const getText = parser.getText
const getData = parser.getData
const toNum = parser.toNum

exports = module.exports = User

function User(_user) {
    if (!(this instanceof User)) {
        return new User(_user)
    }

    if (typeof _user === 'string') {
        this._user = {
            uname: _user
        }
    } else {
        this._user = _user
    }
}

var proto = User.prototype

proto.detail = function() {
    var uname = this._user.uname
    return request(urls.user.detail(uname))
        .then(_parseUserDetail)
        .catch(err => {
            throw new Error('user detail: ' + uname)
        })
}

proto.profile = function() {
    var uname = this._user.uname
    return request(urls.user.profile(uname))
        .then(_parseProfileCard)
        .catch(err => {
            throw new Error('user profile card: ' + uname)
        })
}

proto.followers = function(offset) {
    var hash = this._user.hash
    if (hash) {
        return listFollows('followers', hash, offset)
    } else {
        return this.profile()
            .then(profile => this._user.hash = profile.hash)
            .then(() => {
                return listFollows('followers', this._user.hash, offset)
            })
    }
}

proto.followees = function(offset) {
    var hash = this._user.hash
    if (hash) {
        return listFollows('followees', hash, offset)
    } else {
        return this.profile()
            .then(profile => this._user.hash = profile.hash)
            .then(() => {
                return listFollows('followees', this._user.hash, offset)
            })
    }
}

function listFollows(follow, hash_id, offset) {
    var followUrl = {
        followers: urls.user.followers(),
        followees: urls.user.followees()
    }
    return request.xsrf()
        .then(_xsrf => ({
            method: 'next',
            params: JSON.stringify({
                order_by: 'created',
                offset: offset || 0,
                hash_id
            }),
            _xsrf
        }))
        .then(data => request(followUrl[follow], data))
        .then(data => {
            data = JSON.parse(data)
            return _parseFollows(data.msg)
        })
        .catch(err => {
            var params = JSON.stringify({
                hash_id,
                offset
            })
            throw new Error('user ' + follow + ': ' + params)
        })
}

// function listFollows(follow, hash_id) {
//     var followUrl = {
//         followers: urls.user.followers(),
//         followees: urls.user.followees()
//     }
//     return function(offset) {
//         return request.xsrf()
//             .then(_xsrf => ({
//                 method: 'next',
//                 params: JSON.stringify({
//                     order_by: 'created',
//                     hash_id,
//                     offset
//                 }),
//                 _xsrf
//             }))
//             .then(data => request(followUrl[follow], data))
//             .then(data => {
//                 data = JSON.parse(data)
//                 return _parseFollows(data.msg)
//             })
//             .catch(err => {
//                 var params = JSON.stringify({
//                     hash_id,
//                     offset
//                 })
//                 throw new Error('user ' + follow + ': ' + params)
//             })
//     }
// }

function _parseUserDetail(html) {
    var $ = cheerio.load(html)
    var profileMainEle = $('.zm-profile-header-main')
    var profileInfoEle = $('.zm-profile-header-info')
    var profileOpeEle = $('.zm-profile-header-operation')
    var profileNavEle = $('.profile-navbar')
    var followEle = $('.zm-profile-side-following')

    var nameEle = $('.name', profileMainEle)
    var bioEle = $('.bio', profileMainEle)
    var weiboEle = $('.zm-profile-header-user-weibo', profileMainEle)
    var avatarEle = $('.Avatar', profileMainEle)

    var locationEle = $('.item.location', profileInfoEle)
    var businessEle = $('.item.business', profileInfoEle)
    var genderEle = $('.item.gender .icon', profileInfoEle)
    var companyEle = $('.item.employment', profileInfoEle)
    var positionEle = $('.item.position', profileInfoEle)
    var schoolEle = $('.item.education', profileInfoEle)
    var majorEle = $('.item.education-extra', profileInfoEle)
    var descriptionEle = $('.description.unfold-item > .content', profileInfoEle)

    var agreesEle = $('.zm-profile-header-user-agree > strong', profileOpeEle)
    var thanksEle = $('.zm-profile-header-user-thanks > strong', profileOpeEle)
    var hashEle = $('.zm-rich-follow-btn', profileOpeEle)

    var navEles = $('.item', profileNavEle)
    var asksEle = $('.num', navEles[1])
    var answersEle = $('.num', navEles[2])
    var postsEle = $('.num', navEles[3])
    var collectionsEle = $('.num', navEles[4])
    var logsEle = $('.num', navEles[5])

    var followeeEle = $('.item:first-child > strong', followEle)
    var followerEle = $('.item:last-child > strong', followEle)

    var link = getAttr(nameEle, 'href')

    var profile = {
        name: getText(nameEle),
        uname: link.substring('/people/'.length),
        link: urls._baseurl + link,
        biology: getAttr(bioEle, 'title'),
        weibo: getAttr(weiboEle, 'href'),
        avatar: getAttr(avatarEle, 'src'),
        location: getAttr(locationEle, 'title'),
        business: getAttr(businessEle, 'title'),
        gender: getAttr(genderEle, 'class').substring('icon icon-profile-'.length),
        company: getAttr(companyEle, 'title'),
        position: getAttr(positionEle, 'title'),
        school: getAttr(schoolEle, 'title'),
        major: getAttr(majorEle, 'title'),
        description: getText(descriptionEle).trim(),
        hash: getData(hashEle, 'id'),
        agrees: toNum(getText(agreesEle)),
        thanks: toNum(getText(thanksEle)),
        asks: toNum(getText(asksEle)),
        answers: toNum(getText(answersEle)),
        posts: toNum(getText(postsEle)),
        collections: toNum(getText(collectionsEle)),
        logs: toNum(getText(logsEle)),
        followees: toNum(getText(followeeEle)),
        followers: toNum(getText(followerEle)),
        crawltime: Date.now()
    }

    return profile
}

function _parseProfileCard(html) {
    var $ = cheerio.load(html)
    var upperEle = $('.upper')
    var lowerEle = $('.lower')

    var linkEle = $('.avatar-link', upperEle)
    var avatarEle = $('.Avatar', upperEle)
    var nameEle = $('.name', upperEle)
    var genderEle = $('i.icon', upperEle)
    var bioEle = $('.tagline', upperEle)

    var metaEles = $('.meta .item', lowerEle)
    var answersEle = $('.value', metaEles[0])
    var postsEle = $('.value', metaEles[1])
    var followerEle = $('.value', metaEles[2])
    var hashEle = $('.zm-rich-follow-btn', lowerEle)

    var link = getAttr(linkEle, 'href')

    var profile = {
        name: getText(nameEle),
        uname: link.substring('/people/'.length),
        link: urls._baseurl + getAttr(linkEle, 'href'),
        avatar: getAttr(avatarEle, 'src'),
        gender: getAttr(genderEle, 'class').substring('icon icon-profile-'.length),
        biology: getText(bioEle),
        answers: getText(answersEle),
        posts: getText(postsEle),
        followers: getText(followerEle),
        hash: getData(hashEle, 'id'),
        crawltime: Date.now()
    }

    return profile
}

function _parseFollows(htmls) {
    var follows = htmls.map(html => {
        var $ = cheerio.load(html)
        var hashEle = $('.zm-rich-follow-btn')
        var linkEle = $('.zm-item-link-avatar')

        var link = getAttr(linkEle, 'href')

        var profile = {
            name: getAttr(linkEle, 'title'),
            uname: link.substring('/people/'.length),
            link: urls._baseurl + link,
            hash: getData(hashEle, 'id')
        }

        return profile
    })

    return follows
}
<commit_msg>remove unused code<commit_after>const cheerio = require('cheerio')
const request = require('../request')
const parser = require('../parser')
const urls = require('../urls')

const getAttr = parser.getAttr
const getText = parser.getText
const getData = parser.getData
const toNum = parser.toNum

exports = module.exports = User

function User(_user) {
    if (!(this instanceof User)) {
        return new User(_user)
    }

    if (typeof _user === 'string') {
        this._user = {
            uname: _user
        }
    } else {
        this._user = _user
    }
}

var proto = User.prototype

proto.detail = function() {
    var uname = this._user.uname
    return request(urls.user.detail(uname))
        .then(_parseUserDetail)
        .catch(err => {
            throw new Error('user detail: ' + uname)
        })
}

proto.profile = function() {
    var uname = this._user.uname
    return request(urls.user.profile(uname))
        .then(_parseProfileCard)
        .catch(err => {
            throw new Error('user profile card: ' + uname)
        })
}

proto.followers = function(offset) {
    var hash = this._user.hash
    if (hash) {
        return listFollows('followers', hash, offset)
    } else {
        return this.profile()
            .then(profile => this._user.hash = profile.hash)
            .then(() => {
                return listFollows('followers', this._user.hash, offset)
            })
    }
}

proto.followees = function(offset) {
    var hash = this._user.hash
    if (hash) {
        return listFollows('followees', hash, offset)
    } else {
        return this.profile()
            .then(profile => this._user.hash = profile.hash)
            .then(() => {
                return listFollows('followees', this._user.hash, offset)
            })
    }
}

function listFollows(follow, hash_id, offset) {
    var followUrl = {
        followers: urls.user.followers(),
        followees: urls.user.followees()
    }
    return request.xsrf()
        .then(_xsrf => ({
            method: 'next',
            params: JSON.stringify({
                order_by: 'created',
                offset: offset || 0,
                hash_id
            }),
            _xsrf
        }))
        .then(data => request(followUrl[follow], data))
        .then(data => {
            data = JSON.parse(data)
            return _parseFollows(data.msg)
        })
        .catch(err => {
            var params = JSON.stringify({
                hash_id,
                offset
            })
            throw new Error('user ' + follow + ': ' + params)
        })
}

function _parseUserDetail(html) {
    var $ = cheerio.load(html)
    var profileMainEle = $('.zm-profile-header-main')
    var profileInfoEle = $('.zm-profile-header-info')
    var profileOpeEle = $('.zm-profile-header-operation')
    var profileNavEle = $('.profile-navbar')
    var followEle = $('.zm-profile-side-following')

    var nameEle = $('.name', profileMainEle)
    var bioEle = $('.bio', profileMainEle)
    var weiboEle = $('.zm-profile-header-user-weibo', profileMainEle)
    var avatarEle = $('.Avatar', profileMainEle)

    var locationEle = $('.item.location', profileInfoEle)
    var businessEle = $('.item.business', profileInfoEle)
    var genderEle = $('.item.gender .icon', profileInfoEle)
    var companyEle = $('.item.employment', profileInfoEle)
    var positionEle = $('.item.position', profileInfoEle)
    var schoolEle = $('.item.education', profileInfoEle)
    var majorEle = $('.item.education-extra', profileInfoEle)
    var descriptionEle = $('.description.unfold-item > .content', profileInfoEle)

    var agreesEle = $('.zm-profile-header-user-agree > strong', profileOpeEle)
    var thanksEle = $('.zm-profile-header-user-thanks > strong', profileOpeEle)
    var hashEle = $('.zm-rich-follow-btn', profileOpeEle)

    var navEles = $('.item', profileNavEle)
    var asksEle = $('.num', navEles[1])
    var answersEle = $('.num', navEles[2])
    var postsEle = $('.num', navEles[3])
    var collectionsEle = $('.num', navEles[4])
    var logsEle = $('.num', navEles[5])

    var followeeEle = $('.item:first-child > strong', followEle)
    var followerEle = $('.item:last-child > strong', followEle)

    var link = getAttr(nameEle, 'href')

    var profile = {
        name: getText(nameEle),
        uname: link.substring('/people/'.length),
        link: urls._baseurl + link,
        biology: getAttr(bioEle, 'title'),
        weibo: getAttr(weiboEle, 'href'),
        avatar: getAttr(avatarEle, 'src'),
        location: getAttr(locationEle, 'title'),
        business: getAttr(businessEle, 'title'),
        gender: getAttr(genderEle, 'class').substring('icon icon-profile-'.length),
        company: getAttr(companyEle, 'title'),
        position: getAttr(positionEle, 'title'),
        school: getAttr(schoolEle, 'title'),
        major: getAttr(majorEle, 'title'),
        description: getText(descriptionEle).trim(),
        hash: getData(hashEle, 'id'),
        agrees: toNum(getText(agreesEle)),
        thanks: toNum(getText(thanksEle)),
        asks: toNum(getText(asksEle)),
        answers: toNum(getText(answersEle)),
        posts: toNum(getText(postsEle)),
        collections: toNum(getText(collectionsEle)),
        logs: toNum(getText(logsEle)),
        followees: toNum(getText(followeeEle)),
        followers: toNum(getText(followerEle)),
        crawltime: Date.now()
    }

    return profile
}

function _parseProfileCard(html) {
    var $ = cheerio.load(html)
    var upperEle = $('.upper')
    var lowerEle = $('.lower')

    var linkEle = $('.avatar-link', upperEle)
    var avatarEle = $('.Avatar', upperEle)
    var nameEle = $('.name', upperEle)
    var genderEle = $('i.icon', upperEle)
    var bioEle = $('.tagline', upperEle)

    var metaEles = $('.meta .item', lowerEle)
    var answersEle = $('.value', metaEles[0])
    var postsEle = $('.value', metaEles[1])
    var followerEle = $('.value', metaEles[2])
    var hashEle = $('.zm-rich-follow-btn', lowerEle)

    var link = getAttr(linkEle, 'href')

    var profile = {
        name: getText(nameEle),
        uname: link.substring('/people/'.length),
        link: urls._baseurl + getAttr(linkEle, 'href'),
        avatar: getAttr(avatarEle, 'src'),
        gender: getAttr(genderEle, 'class').substring('icon icon-profile-'.length),
        biology: getText(bioEle),
        answers: getText(answersEle),
        posts: getText(postsEle),
        followers: getText(followerEle),
        hash: getData(hashEle, 'id'),
        crawltime: Date.now()
    }

    return profile
}

function _parseFollows(htmls) {
    var follows = htmls.map(html => {
        var $ = cheerio.load(html)
        var hashEle = $('.zm-rich-follow-btn')
        var linkEle = $('.zm-item-link-avatar')

        var link = getAttr(linkEle, 'href')

        var profile = {
            name: getAttr(linkEle, 'title'),
            uname: link.substring('/people/'.length),
            link: urls._baseurl + link,
            hash: getData(hashEle, 'id')
        }

        return profile
    })

    return follows
}
<|endoftext|>


<commit_before>import axios from 'axios'
import R from 'ramda'
import RS from 'ramdasauce'

// check for an invalid config
const isInvalidConfig = R.anyPass([
  R.isNil,
  R.isEmpty,
  R.complement(R.has('baseURL')),
  R.complement(R.propIs(String, 'baseURL')),
  R.propSatisfies(R.isEmpty, 'baseURL')
])

// the default headers given to axios
export const DEFAULT_HEADERS = {
  'Accept': 'application/json',
  'Content-Type': 'application/json'
}

// the default configuration for axios, default headers will also be merged in
const DEFAULT_CONFIG = {
  timeout: 0
}

export const NONE = null
export const CLIENT_ERROR = 'CLIENT_ERROR'
export const SERVER_ERROR = 'SERVER_ERROR'
export const TIMEOUT_ERROR = 'TIMEOUT_ERROR'
export const CONNECTION_ERROR = 'CONNECTION_ERROR'
export const NETWORK_ERROR = 'NETWORK_ERROR'
export const UNKNOWN_ERROR = 'UNKNOWN_ERROR'

const TIMEOUT_ERROR_CODES = ['ECONNABORTED']
const NODEJS_CONNECTION_ERROR_CODES = ['ENOTFOUND', 'ECONNREFUSED', 'ECONNRESET']
const in200s = RS.isWithin(200, 299)
const in400s = RS.isWithin(400, 499)
const in500s = RS.isWithin(500, 599)

/**
  Creates a instance of our API using the configuration.
 */
export const create = (config) => {
  // quick sanity check
  if (isInvalidConfig(config)) throw new Error('config must have a baseURL')

  // combine the user's defaults with ours
  const mergedHeaders = R.merge(DEFAULT_HEADERS, config.headers || {})
  const combinedConfig = R.merge(DEFAULT_CONFIG, R.merge(config, {headers: mergedHeaders}))

  // create the axios instance
  const instance = axios.create(combinedConfig)

  // immediate reset headers because axios kept its own defaults
  instance.defaults.headers = combinedConfig.headers

  const monitors = []
  const addMonitor = (monitor) => {
    monitors.push(monitor)
  }

  const requestTransforms = []
  const responseTransforms = []

  const addRequestTransform = transform => requestTransforms.push(transform)
  const addResponseTransform = transform => responseTransforms.push(transform)

  // convenience for setting new request headers
  const setHeader = (name, value) => {
    instance.defaults.headers[name] = value
    return instance
  }

  // sets headers in bulk
  const setHeaders = (headers) => {
    const keys = R.keys(headers)
    R.forEach(header => setHeader(header, headers[header]), keys)
    return instance
  }

  /**
    Make the request for GET, HEAD, DELETE
   */
  const doRequestWithoutBody = (method, url, params = {}, axiosConfig = {}) => {
    return doRequest(R.merge({url, params, method}, axiosConfig))
  }

  /**
    Make the request for POST, PUT, PATCH
   */
  const doRequestWithBody = (method, url, data = null, axiosConfig = {}) => {
    const clonedData = R.clone(data)
    // give an opportunity for anything to the response transforms to change stuff along the way
    R.forEach(transform => { transform({ data: clonedData, method, url }) }, requestTransforms)

    return doRequest(R.merge({url, method, data: clonedData}, axiosConfig))
  }

  /**
    Make the request with this config!
   */
  const doRequest = (axiosRequestConfig) => {
    const startedAt = RS.toNumber(new Date())

    // first convert the axios response, then execute our callback
    const chain = R.pipe(
      R.partial(convertResponse, [startedAt]),
      runMonitors
    )

    // Make the request and execute the identical pipeline for both promise paths.
    return instance
      .request(axiosRequestConfig)
      .then(chain)
      .catch(chain)
  }

  /**
    Fires after we convert from axios' response into our response.  Exceptions
    raised for each monitor will be ignored.
   */
  const runMonitors = (ourResponse) => {
    monitors.forEach((fn) => {
      try {
        fn(ourResponse)
      } catch (error) {
        // all monitor complaints will be ignored
      }
    })
    return ourResponse
  }

  /**
    Converts an axios response/error into our response.
   */
  const convertResponse = (startedAt, axiosResponse) => {
    const end = RS.toNumber(new Date())
    const duration = (end - startedAt)

    // new in Axios 0.13 -- some data could be buried 1 level now
    const isError = axiosResponse instanceof Error
    const response = isError ? axiosResponse.response : axiosResponse
    const status = response && response.status || null
    const problem = isError ? getProblemFromError(axiosResponse) : getProblemFromStatus(status)
    const ok = in200s(status)
    const config = axiosResponse.config || null
    const headers = response && response.headers || null
    let data = response && response.data || null

    // give an opportunity for anything to the response transforms to change stuff along the way
    if (responseTransforms.length > 0) {
      R.forEach(transform => {
        transform({ duration, problem, ok, status, headers, config, data })
      }, responseTransforms)
    }

    return { duration, problem, ok, status, headers, config, data }
  }

  /**
    What's the problem for this response?

    TODO: We're losing some error granularity, but i'm cool with that
    until someone cares.
   */
  const getProblemFromError = (error) => {
    // first check if the error message is Network Error (set by axios at 0.12) on platforms other than NodeJS.
    if (error.message === 'Network Error') return NETWORK_ERROR
    // then check the specific error code
    return R.cond([
      // if we don't have an error code, we have a response status
      [R.isNil, () => getProblemFromStatus(error.response.status)],
      [R.contains(R.__, TIMEOUT_ERROR_CODES), R.always(TIMEOUT_ERROR)],
      [R.contains(R.__, NODEJS_CONNECTION_ERROR_CODES), R.always(CONNECTION_ERROR)],
      [R.T, R.always(UNKNOWN_ERROR)]
    ])(error.code)
  }

  /**
   * Given a HTTP status code, return back the appropriate problem enum.
   */
  const getProblemFromStatus = status => {
    return R.cond([
      [R.isNil, R.always(UNKNOWN_ERROR)],
      [in200s, R.always(NONE)],
      [in400s, R.always(CLIENT_ERROR)],
      [in500s, R.always(SERVER_ERROR)],
      [R.T, R.always(UNKNOWN_ERROR)]
    ])(status)
  }

  // create the base object
  const sauce = {
    axiosInstance: instance,
    monitors,
    addMonitor,
    requestTransforms,
    responseTransforms,
    addRequestTransform,
    addResponseTransform,
    setHeader,
    setHeaders,
    get: R.partial(doRequestWithoutBody, ['get']),
    delete: R.partial(doRequestWithoutBody, ['delete']),
    head: R.partial(doRequestWithoutBody, ['head']),
    post: R.partial(doRequestWithBody, ['post']),
    put: R.partial(doRequestWithBody, ['put']),
    patch: R.partial(doRequestWithBody, ['patch'])
  }
  // send back the sauce
  return sauce
}

export default {
  DEFAULT_HEADERS,
  NONE,
  CLIENT_ERROR,
  SERVER_ERROR,
  TIMEOUT_ERROR,
  CONNECTION_ERROR,
  NETWORK_ERROR,
  UNKNOWN_ERROR,
  create
}
<commit_msg>Performs data clone only if required by request transforms.<commit_after>import axios from 'axios'
import R from 'ramda'
import RS from 'ramdasauce'

// check for an invalid config
const isInvalidConfig = R.anyPass([
  R.isNil,
  R.isEmpty,
  R.complement(R.has('baseURL')),
  R.complement(R.propIs(String, 'baseURL')),
  R.propSatisfies(R.isEmpty, 'baseURL')
])

// the default headers given to axios
export const DEFAULT_HEADERS = {
  'Accept': 'application/json',
  'Content-Type': 'application/json'
}

// the default configuration for axios, default headers will also be merged in
const DEFAULT_CONFIG = {
  timeout: 0
}

export const NONE = null
export const CLIENT_ERROR = 'CLIENT_ERROR'
export const SERVER_ERROR = 'SERVER_ERROR'
export const TIMEOUT_ERROR = 'TIMEOUT_ERROR'
export const CONNECTION_ERROR = 'CONNECTION_ERROR'
export const NETWORK_ERROR = 'NETWORK_ERROR'
export const UNKNOWN_ERROR = 'UNKNOWN_ERROR'

const TIMEOUT_ERROR_CODES = ['ECONNABORTED']
const NODEJS_CONNECTION_ERROR_CODES = ['ENOTFOUND', 'ECONNREFUSED', 'ECONNRESET']
const in200s = RS.isWithin(200, 299)
const in400s = RS.isWithin(400, 499)
const in500s = RS.isWithin(500, 599)

/**
  Creates a instance of our API using the configuration.
 */
export const create = (config) => {
  // quick sanity check
  if (isInvalidConfig(config)) throw new Error('config must have a baseURL')

  // combine the user's defaults with ours
  const mergedHeaders = R.merge(DEFAULT_HEADERS, config.headers || {})
  const combinedConfig = R.merge(DEFAULT_CONFIG, R.merge(config, {headers: mergedHeaders}))

  // create the axios instance
  const instance = axios.create(combinedConfig)

  // immediate reset headers because axios kept its own defaults
  instance.defaults.headers = combinedConfig.headers

  const monitors = []
  const addMonitor = (monitor) => {
    monitors.push(monitor)
  }

  const requestTransforms = []
  const responseTransforms = []

  const addRequestTransform = transform => requestTransforms.push(transform)
  const addResponseTransform = transform => responseTransforms.push(transform)

  // convenience for setting new request headers
  const setHeader = (name, value) => {
    instance.defaults.headers[name] = value
    return instance
  }

  // sets headers in bulk
  const setHeaders = (headers) => {
    const keys = R.keys(headers)
    R.forEach(header => setHeader(header, headers[header]), keys)
    return instance
  }

  /**
    Make the request for GET, HEAD, DELETE
   */
  const doRequestWithoutBody = (method, url, params = {}, axiosConfig = {}) => {
    return doRequest(R.merge({url, params, method}, axiosConfig))
  }

  /**
    Make the request for POST, PUT, PATCH
   */
  const doRequestWithBody = (method, url, data = null, axiosConfig = {}) => {
    if (!R.isNil(data) && requestTransforms.length > 0) {
      const clonedData = R.clone(data)
      // give an opportunity for anything to the response transforms to change stuff along the way
      R.forEach(transform => { transform({ data: clonedData, method, url }) }, requestTransforms)
      return doRequest(R.merge({ url, method, data: clonedData }, axiosConfig))
    } else {
      return doRequest(R.merge({ url, method, data }, axiosConfig))
    }
  }

  /**
    Make the request with this config!
   */
  const doRequest = (axiosRequestConfig) => {
    const startedAt = RS.toNumber(new Date())

    // first convert the axios response, then execute our callback
    const chain = R.pipe(
      R.partial(convertResponse, [startedAt]),
      runMonitors
    )

    // Make the request and execute the identical pipeline for both promise paths.
    return instance
      .request(axiosRequestConfig)
      .then(chain)
      .catch(chain)
  }

  /**
    Fires after we convert from axios' response into our response.  Exceptions
    raised for each monitor will be ignored.
   */
  const runMonitors = (ourResponse) => {
    monitors.forEach((fn) => {
      try {
        fn(ourResponse)
      } catch (error) {
        // all monitor complaints will be ignored
      }
    })
    return ourResponse
  }

  /**
    Converts an axios response/error into our response.
   */
  const convertResponse = (startedAt, axiosResponse) => {
    const end = RS.toNumber(new Date())
    const duration = (end - startedAt)

    // new in Axios 0.13 -- some data could be buried 1 level now
    const isError = axiosResponse instanceof Error
    const response = isError ? axiosResponse.response : axiosResponse
    const status = response && response.status || null
    const problem = isError ? getProblemFromError(axiosResponse) : getProblemFromStatus(status)
    const ok = in200s(status)
    const config = axiosResponse.config || null
    const headers = response && response.headers || null
    let data = response && response.data || null

    // give an opportunity for anything to the response transforms to change stuff along the way
    if (responseTransforms.length > 0) {
      R.forEach(transform => {
        transform({ duration, problem, ok, status, headers, config, data })
      }, responseTransforms)
    }

    return { duration, problem, ok, status, headers, config, data }
  }

  /**
    What's the problem for this response?

    TODO: We're losing some error granularity, but i'm cool with that
    until someone cares.
   */
  const getProblemFromError = (error) => {
    // first check if the error message is Network Error (set by axios at 0.12) on platforms other than NodeJS.
    if (error.message === 'Network Error') return NETWORK_ERROR
    // then check the specific error code
    return R.cond([
      // if we don't have an error code, we have a response status
      [R.isNil, () => getProblemFromStatus(error.response.status)],
      [R.contains(R.__, TIMEOUT_ERROR_CODES), R.always(TIMEOUT_ERROR)],
      [R.contains(R.__, NODEJS_CONNECTION_ERROR_CODES), R.always(CONNECTION_ERROR)],
      [R.T, R.always(UNKNOWN_ERROR)]
    ])(error.code)
  }

  /**
   * Given a HTTP status code, return back the appropriate problem enum.
   */
  const getProblemFromStatus = status => {
    return R.cond([
      [R.isNil, R.always(UNKNOWN_ERROR)],
      [in200s, R.always(NONE)],
      [in400s, R.always(CLIENT_ERROR)],
      [in500s, R.always(SERVER_ERROR)],
      [R.T, R.always(UNKNOWN_ERROR)]
    ])(status)
  }

  // create the base object
  const sauce = {
    axiosInstance: instance,
    monitors,
    addMonitor,
    requestTransforms,
    responseTransforms,
    addRequestTransform,
    addResponseTransform,
    setHeader,
    setHeaders,
    get: R.partial(doRequestWithoutBody, ['get']),
    delete: R.partial(doRequestWithoutBody, ['delete']),
    head: R.partial(doRequestWithoutBody, ['head']),
    post: R.partial(doRequestWithBody, ['post']),
    put: R.partial(doRequestWithBody, ['put']),
    patch: R.partial(doRequestWithBody, ['patch'])
  }
  // send back the sauce
  return sauce
}

export default {
  DEFAULT_HEADERS,
  NONE,
  CLIENT_ERROR,
  SERVER_ERROR,
  TIMEOUT_ERROR,
  CONNECTION_ERROR,
  NETWORK_ERROR,
  UNKNOWN_ERROR,
  create
}
<|endoftext|>


<commit_before>// umd boilerplate for CommonJS and AMD
if (typeof exports === 'object' && typeof define !== 'function') {
	var define = function (factory) {
		factory(require, exports, module);
	};
}

define(function(require, exports, module) {
	var arrayLib;

	var _getValue = function(myVar, propPath) {
		var temp = myVar;
		propPath.forEach(function(val, i) {
			temp = temp[val];
		});

		return temp;
	}

	var index = function(arr, key, multiple) {
		multiple = multiple === undefined ? false : multiple;

		var indexed = {};

		var keyPath = key instanceof Array ? key : [key];

		arr.forEach(function(val, i) {
			var tempKey = _getValue(val, keyPath);

			if (multiple) {
				if (indexed[tempKey] === undefined) {
					indexed[tempKey] = [];
				}

				indexed[tempKey].push(val);
			} else {
				indexed[tempKey] = val;
			}
		});

		return indexed;
	}

	var unique = function(arr) {
		var results = [];

		arr.forEach(function(val, i) {
			if (results.indexOf(val) === -1) {
				results.push(val);
			}
		});

		return results;
	}

	/**
	 * This function performs a single or multi-column sort on an array.
	 * There are two different functional call signatures depending on
	 * whether the desired sort is a single or multi-column sort.
	 *
	 * For a single-column sort, the prop value can be an array of nested
	 * key values to lookup, or a string value for the object key to sort on.
	 *
	 * ####Example - Single-column sort:
	 *
	 *		arrayLib.sortBy(array, ["node", "val"], "alpha", "asc")
	 *
	 * ####Example - Multi-column sort:
	 *
	 *		arrayLib.sortBy(array, [[["node", "val"], "alpha", "desc"], ["val", "numeric", "asc"]])
	 *
	 */
	var sortBy = function(arr, prop, type, dir) {
		var temp = [].slice.call(arr);
		var columns = null;

		// Are we attempting a multi-column sort?
		if (prop instanceof Array && prop.length >= 1 && prop[0] instanceof Array) {

			prop.forEach(function(sortColumns) {
				if (sortColumns.length !== 3) {
					throw new Error("arrayLib#sortBy(): Sort columns must be in the format ['prop', sortOrder, sortDir]");
				}
			});

			columns = prop
		} else {

			// Single column sort
			if (dir !== "asc" && dir !== "desc") {
				throw new Error("arrayLib#sortBy(): Direction must be 'asc' or 'desc'");
			}

			if (type !== "alpha" && type !== "numeric" && type !== "natural") {
				throw new Error("arrayLib#sortBy(): Type must be 'alpha', 'numeric', 'natural'");
			}

			columns = [[prop, type, dir]];
		}

		temp.sort(function(a, b) {
			return multiPropertyObjectCompare(a, b, columns);
		});

		return temp;
	}

	/**
	 * Given two objects, perform a comparison on them using the desired
	 * type - numeric, alphabetic or natural. This method supports sorting on
	 * nested object elements.
	 *
	 * @param {Object} an object to compare
	 * @param {Object} an object to compare
	 * @param {Array} an array of sort properties
	 *
	 * ####Example:
	 *
	 *		arrayLib.multiPropertyObjectCompare(
	 *					obj1, obj2, [[["node", "val"], "alpha", "asc"], ["val", "alpha", "asc"]])
	 */
	var multiPropertyObjectCompare = function(obj1, obj2, compareColumnsArray) {
		var aVal;
		var bVal;
		var column;
		var columnIndex = 0;
		var compare = 0;
		var factor;
		var type;
		var prop;
		var propPath;

		// Compare the first properties of the sort columns on each object,
		// if they are equal, go to the next column to determine the ordering.
		 while ((columnIndex < compareColumnsArray.length) && (compare === 0)) {

			column = compareColumnsArray[columnIndex];
			prop = column[0]; // Property path
			type = column[1]; // sort type (alpha, numeric, natural)
			dir = column[2];  // type (asc or desc)
			factor = (dir === "asc") ? 1 : -1;

			propPath = prop instanceof Array ? prop : [prop];
			aVal = _getValue(obj1, propPath);
			bVal = _getValue(obj2, propPath);

			if (type === "alpha") {
				compare = alphaCompare(aVal, bVal);
			} else if (type === "numeric") { // numeric
				compare = (aVal - bVal);
			} else if (type === "natural") {
				compare = naturalCompare(aVal, bVal);
			}

			compare *= factor;
			columnIndex++;
		}

		return compare;
	}

	/**
	 * This function compares two string values ignoring the case.
	 */
	var alphaCompare = function(a, b) {
		var compare = a.toLowerCase().localeCompare(b.toLowerCase());
		return compare ? compare : (a > b ? 1 : (a < b ? -1 : 0));
	}

	/**
	 * This function performs a "natural sort" comparison of two items. This
	 * algorithm is taken from Jim Palmer's natural sort algorith on his site:
	 * http://www.overset.com/2008/09/01/javascript-natural-sort-algorithm-with-unicode-support/
	 */
	function naturalCompare (a, b) {
		var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi,
			sre = /(^[ ]*|[ ]*$)/g,
			dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
			hre = /^0x[0-9a-f]+$/i,
			ore = /^0/,
			i = function(s) { return naturalCompare.insensitive && (''+s).toLowerCase() || ''+s },
			// convert all to strings strip whitespace
			x = i(a).replace(sre, '') || '',
			y = i(b).replace(sre, '') || '',
			// chunk/tokenize
			xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
			yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
			// numeric, hex or date detection
			xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)),
			yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null,
			oFxNcL, oFyNcL;
		// first try and sort Hex codes or Dates
		if (yD)
			if ( xD < yD ) return -1;
			else if ( xD > yD ) return 1;
		// natural sorting through split numeric strings and default strings
		for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
			// find floats not starting with '0', string or 0 if not defined (Clint Priest)
			oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
			oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
			// handle numeric vs string comparison - number < string - (Kyle Adams)
			if (isNaN(oFxNcL) !== isNaN(oFyNcL)) { return (isNaN(oFxNcL)) ? 1 : -1; }
			// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
			else if (typeof oFxNcL !== typeof oFyNcL) {
				oFxNcL += '';
				oFyNcL += '';
			}
			if (oFxNcL < oFyNcL) return -1;
			if (oFxNcL > oFyNcL) return 1;
		}
		return 0;
	}

	var sortByArray = function(arr, key, arrOrder) {
		var tempIndex = index(arr, key);

		var data = [];

		arrOrder.forEach(function(val, i) {
			if (tempIndex[val] !== undefined) {
				data.push(tempIndex[val]);
			}
		});

		return data;
	}

	// "inside-out" version of Fisher and Yates' algorithm from wikipedia
	var randomize = function(arr) {
		var items = [];

		arr.forEach(function(val, i) {
			var index = Math.floor(Math.random() * (items.length + 1));
			if (index === items.length) {
				items.push(val);
			} else {
				items.push(items[index]);
				items[index] = val;
			}
		});

		return items;
	}

	var leftJoin = function(args) {
		// args.leftKey
		// args.rightKey
		// args.mergeKey
		// args.leftArray
		// args.rightArray

		var index = arrayLib.index(args.rightArray, args.rightKey);

		args.leftArray.forEach(function(val, i) {
			if (val[args.leftKey] instanceof Array) {
				var newArray = [];

				val[args.leftKey].forEach(function(val, i) {
					if (index[val] !== undefined) {
						newArray.push(index[val]);
					}
				});

				val[args.mergeKey] = newArray;
			} else if (val[args.leftKey] !== undefined && index[val[args.leftKey]] !== undefined) {
				val[args.mergeKey] = index[val[args.leftKey]];
			}
		});

		return args.leftArray;
	}

	module.exports = arrayLib = {
		index : index,
		unique : unique,
		sortBy : sortBy,
		sortByArray : sortByArray,
		randomize : randomize,
		leftJoin : leftJoin,
		multiPropertyObjectCompare : multiPropertyObjectCompare,
		_getValue : _getValue
	}
});<commit_msg>newer version of arrayLib with some optimizations for hot pathways.<commit_after>// umd boilerplate for CommonJS and AMD
if (typeof exports === 'object' && typeof define !== 'function') {
	var define = function (factory) {
		factory(require, exports, module);
	};
}

define(function(require, exports, module) {
	var arrayLib;

	var _getValue = function(myVar, propPath) {
		var temp = myVar;
		for(var i = 0; i < propPath.length; i++) {
			temp = temp[propPath[i]];
		}

		return temp;
	}

	// magic function to lookup values in objects and arrays, see unit test for examples
	var _getValues = function(val, path) {
		var tempPath = path.slice(0);
		var tempVal = val[tempPath.shift()];

		if (tempVal === undefined) {
			return tempVal;
		}

		if (tempPath.length === 0) {
			return tempVal;
		}

		if (tempVal instanceof Array === false) {
			return _getValues(tempVal, tempPath);
		}

		var temp = [];
		tempVal.forEach(function(val, i) {
			if (typeof val === "object" && Object.getPrototypeOf(val) === Object.prototype) {
				temp.push(_getValues(val, tempPath));
			} else {
				temp.push(val);
			}
		});

		return temp;
	}

	var index = function(arr, key, multiple) {
		multiple = multiple === undefined ? false : multiple;

		var indexed = {};

		var keyPath = key instanceof Array ? key : [key];

		for(var i = 0; i < arr.length; i++) {
			var val = arr[i];
			var tempKey = _getValue(val, keyPath);

			if (multiple) {
				if (indexed[tempKey] === undefined) {
					indexed[tempKey] = [];
				}

				indexed[tempKey].push(val);
			} else {
				indexed[tempKey] = val;
			}
		}

		return indexed;
	}

	var unique = function(arr) {
		var results = [];

		for(var i = 0; i < arr.length; i++) {
			var val = arr[i];
			if (results.indexOf(val) === -1) {
				results.push(val);
			}
		}

		return results;
	}

	var duplicates = function(arr) {
		var results = [];
		var temp = [];

		arr.forEach(function(val) {
			if (temp.indexOf(val) !== -1 && results.indexOf(val) === -1) {
				results.push(val);
			}

			temp.push(val);
		});

		return results;
	}

	/**
	 * This function performs a single or multi-column sort on an array.
	 * There are two different functional call signatures depending on
	 * whether the desired sort is a single or multi-column sort.
	 *
	 * For a single-column sort, the prop value can be an array of nested
	 * key values to lookup, or a string value for the object key to sort on.
	 *
	 * ####Example - Single-column sort:
	 *
	 *		arrayLib.sortBy(array, ["node", "val"], "alpha", "asc")
	 *
	 * ####Example - Multi-column sort:
	 *
	 *		arrayLib.sortBy(array, [[["node", "val"], "alpha", "desc"], ["val", "numeric", "asc"]])
	 *
	 */
	var sortBy = function(arr, prop, type, dir) {
		if (arr.length <= 1) { return arr; } // if the array is length 0 or 1 bail early

		var isSingle = prop instanceof Array && prop[0] instanceof Array ? false : true;
		var sortOps;
		if (isSingle === true) {
			sortOps = [{ prop : prop, type : type, dir : dir }];
		} else {
			// if multiple the arguments come in on prop so we need to unfold to an object
			sortOps = new Array(prop.length);
			for(var i = 0; i < prop.length; i++) {
				sortOps[i] = { prop : prop[i][0], type : prop[i][1], dir : prop[i][2] };
			}
		}

		// our prop should always be an array for lookup purposes
		for(var i = 0; i < sortOps.length; i++) {
			var op = sortOps[i];
			if (op.prop instanceof Array === false) { op.prop = [op.prop]; }
		}

		// go through the array and extract the values from it so that way we don't have to do a look-up on each comparison
		var temp = new Array(arr.length);
		for(var i = 0; i < arr.length; i++) {
			var item = arr[i];
			var result = { values : new Array(sortOps.length), index : i, sortOps : sortOps };
			for(var j = 0; j < sortOps.length; j++) {
				var op = sortOps[j];

				var value = item;
				for(var k = 0; k < op.prop.length; k++) {
					value = value[op.prop[k]];
					if (value === undefined) { break; }
				}

				result.values[j] = {
					raw : value, // original value needed in some comparisons
					clean : op.type === "alpha" && value !== undefined ? value.toLowerCase() : value
				}
			}

			temp[i] = result;
		}

		temp.sort(propCompare);

		// after the sort is completed, recompose our data array based on the indexes from our mapped array
		var done = new Array(temp.length);
		for(var i = 0; i < temp.length; i++) {
			done[i] = arr[temp[i].index];
		}
		return done;
	}

	var propCompare = function(a, b) {
		for(var i = 0; i < a.sortOps.length; i++) {
			var op = a.sortOps[i];
			var aVal = a.values[i].clean;
			var aValRaw = a.values[i].raw;
			var bVal = b.values[i].clean;
			var bValRaw = b.values[i].raw;
			var comp;

			if (aVal === undefined && bVal !== undefined) {
				comp = 1; // in an ascending sort undefined sorts to last, so if aVal is undefined bVal is smaller
			} else if (aVal !== undefined && bVal === undefined) {
				comp = -1; // in an ascending sort undefined sorts to last, so if bVal is undefined aVal is smaller
			} else if (aVal === undefined && bVal === undefined) {
				comp = 0; // both undefined, consider them equal
			} else if (op.type === "alpha") {
				// in alpha compare we want to compare without case first (all lower), and then with case, this way A goes before a and both go before B
				comp = aVal > bVal ? 1 : aVal < bVal ? -1 : aValRaw > bValRaw ? 1 : aValRaw < bValRaw ? -1 : 0;
			} else if (op.type === "numeric") {
				comp = aVal - bVal;
			} else if (op.type === "natural") {
				comp = naturalCompare(aVal, bVal);
			}

			if (comp !== 0) {
				comp *= op.dir === "asc" ? 1 : -1; // with a descending query we reverse the polarity
				return comp;
			}
		}

		return 0;
	}

	/**
	 * This function performs a "natural sort" comparison of two items. This
	 * algorithm is taken from Jim Palmer's natural sort algorith on his site:
	 * http://www.overset.com/2008/09/01/javascript-natural-sort-algorithm-with-unicode-support/
	 */
	function naturalCompare (a, b) {
		var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi,
			sre = /(^[ ]*|[ ]*$)/g,
			dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
			hre = /^0x[0-9a-f]+$/i,
			ore = /^0/,
			i = function(s) { return naturalCompare.insensitive && (''+s).toLowerCase() || ''+s },
			// convert all to strings strip whitespace
			x = i(a).replace(sre, '') || '',
			y = i(b).replace(sre, '') || '',
			// chunk/tokenize
			xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
			yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
			// numeric, hex or date detection
			xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)),
			yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null,
			oFxNcL, oFyNcL;
		// first try and sort Hex codes or Dates
		if (yD)
			if ( xD < yD ) return -1;
			else if ( xD > yD ) return 1;
		// natural sorting through split numeric strings and default strings
		for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
			// find floats not starting with '0', string or 0 if not defined (Clint Priest)
			oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
			oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
			// handle numeric vs string comparison - number < string - (Kyle Adams)
			if (isNaN(oFxNcL) !== isNaN(oFyNcL)) { return (isNaN(oFxNcL)) ? 1 : -1; }
			// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
			else if (typeof oFxNcL !== typeof oFyNcL) {
				oFxNcL += '';
				oFyNcL += '';
			}
			if (oFxNcL < oFyNcL) return -1;
			if (oFxNcL > oFyNcL) return 1;
		}
		return 0;
	}

	var sortByArray = function(arr, key, arrOrder) {
		var tempIndex = index(arr, key, true);

		var data = [];

		arrOrder.forEach(function(val, i) {
			if (tempIndex[val] !== undefined) {
				data.push.apply(data, tempIndex[val]);
			}
		});

		return data;
	}

	// "inside-out" version of Fisher and Yates' algorithm from wikipedia
	var randomize = function(arr) {
		var items = [];

		arr.forEach(function(val, i) {
			var index = Math.floor(Math.random() * (items.length + 1));
			if (index === items.length) {
				items.push(val);
			} else {
				items.push(items[index]);
				items[index] = val;
			}
		});

		return items;
	}

	var leftJoin = function(args) {
		// args.leftKey
		// args.rightKey
		// args.mergeKey
		// args.leftArray
		// args.rightArray

		var index = arrayLib.index(args.rightArray, args.rightKey);

		args.leftArray.forEach(function(val, i) {
			var chain = args.leftKey instanceof Array ? args.leftKey : [args.leftKey];
			var value = _getValues(val, chain);

			if (value instanceof Array) {
				var newArray = [];

				value.forEach(function(val, i) {
					if (index[val] !== undefined) {
						newArray.push(index[val]);
					}
				});

				val[args.mergeKey] = newArray;
			} else if (value !== undefined && index[value] !== undefined) {
				val[args.mergeKey] = index[value];
			}
		});

		return args.leftArray;
	}

	module.exports = arrayLib = {
		duplicates : duplicates,
		index : index,
		unique : unique,
		sortBy : sortBy,
		sortByArray : sortByArray,
		randomize : randomize,
		leftJoin : leftJoin,
		_getValue : _getValue,
		_getValues : _getValues
	}
});<|endoftext|>


<commit_before>'use strict';
import React        from 'react';
import objectAssign from 'object-assign';
import styles       from '../styles/SweetAlertStyles';

export default class Buttons extends React.Component {

    static propTypes = {
        confirmBtnText    : React.PropTypes.oneOfType([React.PropTypes.node, React.PropTypes.string]),
        cancelBtnText     : React.PropTypes.oneOfType([React.PropTypes.node, React.PropTypes.string]),
        cancelBtnBsStyle  : React.PropTypes.oneOf(['default', 'primary', 'link', 'info', 'success', 'warning', 'danger']),
        confirmBtnBsStyle : React.PropTypes.oneOf(['default', 'primary', 'link', 'info', 'success', 'warning', 'danger']),
        onCancel          : React.PropTypes.func,
        onConfirm         : React.PropTypes.func,
        showCancel        : React.PropTypes.bool,
        focusConfirmBtn   : React.PropTypes.bool
    };

    static defaultProps = {
        confirmBtnText    : 'OK',
        cancelBtnText     : 'Cancel',
        cancelBtnBsStyle  : 'link',
        confirmBtnBsStyle : 'primary',
        focusConfirmBtn   : true,
        showCancel        : false
    };

    componentDidMount() {
        if (this.props.focusConfirmBtn && this.refs.confirmBtn) {
            try {
                this.refs.confirmBtn.focus();
            } catch (e) {
                // whoops
            }
        }
    }

    getButtonStyle = (bsStyle) => {
        let borderColor = '';
        let shadowColor = '';
        switch (bsStyle) {
            case 'primary':
                borderColor = '#286090';
                shadowColor = 'rgb(165, 202, 234)';
                break;
            case 'success':
                borderColor = '#4cae4c';
                shadowColor = 'rgba(76, 174, 76, 0.6)';
                break;
            case 'info':
                borderColor = '#46b8da';
                shadowColor = 'rgba(70, 184, 218, 0.6)';
                break;
            case 'danger':
            case 'error':
                borderColor = '#d43f3a';
                shadowColor = 'rgba(212, 63, 58, 0.6)';
                break;
            case 'warning':
                borderColor = '#eea236';
                shadowColor = 'rgba(238, 162, 54, 0.6)';
                break;
            case 'default':
            default:
                borderColor = '#cccccc;';
                shadowColor = 'rgba(204, 204, 204, 0.6)';
                break;
        }
        return ` borderColor: ${borderColor} !important; boxShadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px ${shadowColor} !important;`;
    };

    render() {
        const cancelBtnBsStyle = this.props.cancelBtnBsStyle === 'error' ? 'danger' : this.props.cancelBtnBsStyle;
        const confirmBtnBsStyle = this.props.confirmBtnBsStyle === 'error' ? 'danger' : this.props.confirmBtnBsStyle;
        return (

                {this.props.showCancel && (

                        <button
                            style={styles.button}
                            className={'btn btn-lg btn-' + cancelBtnBsStyle}
                            onClick={this.props.onCancel}
                            type="button" >
                            {this.props.cancelBtnText}
                        </button>

                )}

                    <button
                        ref="confirmBtn"
                        disabled={this.props.disabled}
                        style={styles.button}
                        className={'btn btn-lg btn-' + confirmBtnBsStyle}
                        onClick={this.props.onConfirm}
                        type="button" >
                        {this.props.confirmBtnText}
                    </button>

        );
    }
}<commit_msg>optimize style generation for buttons<commit_after>'use strict';
import React        from 'react';
import objectAssign from 'object-assign';
import styles       from '../styles/SweetAlertStyles';

export default class Buttons extends React.Component {

    static propTypes = {
        confirmBtnText    : React.PropTypes.oneOfType([React.PropTypes.node, React.PropTypes.string]),
        cancelBtnText     : React.PropTypes.oneOfType([React.PropTypes.node, React.PropTypes.string]),
        cancelBtnBsStyle  : React.PropTypes.oneOf(['default', 'primary', 'link', 'info', 'success', 'warning', 'danger']),
        confirmBtnBsStyle : React.PropTypes.oneOf(['default', 'primary', 'link', 'info', 'success', 'warning', 'danger']),
        onCancel          : React.PropTypes.func,
        onConfirm         : React.PropTypes.func,
        showCancel        : React.PropTypes.bool,
        focusConfirmBtn   : React.PropTypes.bool
    };

    static defaultProps = {
        confirmBtnText    : 'OK',
        cancelBtnText     : 'Cancel',
        cancelBtnBsStyle  : 'link',
        confirmBtnBsStyle : 'primary',
        focusConfirmBtn   : true,
        showCancel        : false
    };

    static styles = {
        primary : {
            borderColor : '#286090',
            shadowColor : 'rgb(165, 202, 234)'
        },
        success : {
            borderColor : '#4cae4c',
            shadowColor : 'rgba(76, 174, 76, 0.6)'
        },
        info : {
            borderColor : '#46b8da',
            shadowColor : 'rgba(70, 184, 218, 0.6)'
        },
        danger : {
            borderColor : '#d43f3a',
            shadowColor : 'rgba(212, 63, 58, 0.6)'
        },
        warning : {
            borderColor : '#eea236',
            shadowColor : 'rgba(238, 162, 54, 0.6)'
        },
        default : {
            borderColor : '#cccccc',
            shadowColor : 'rgba(204, 204, 204, 0.6)'
        }
    };

    buttonStyles = {};

    componentDidMount() {
        if (this.props.focusConfirmBtn && this.refs.confirmBtn) {
            try {
                this.refs.confirmBtn.focus();
            } catch (e) {
                // whoops
            }
        }
    }

    getButtonStyle = (bsStyle) => {
        if (bsStyle === 'error') bsStyle = 'danger';
        if (this.buttonStyles[bsStyle] == null) {
            const style       = Buttons.styles[bsStyle] || Buttons.styles.default;
            const borderColor = `borderColor: ${style.borderColor} !important`;
            const boxShadow   = `boxShadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px ${style.shadowColor} !important;`;
            this.buttonStyles[bsStyle] = ` ${borderColor} ${boxShadow}`;
        }
        return this.buttonStyles[bsStyle];
    };

    render() {
        const cancelBtnBsStyle = this.props.cancelBtnBsStyle === 'error' ? 'danger' : this.props.cancelBtnBsStyle;
        const confirmBtnBsStyle = this.props.confirmBtnBsStyle === 'error' ? 'danger' : this.props.confirmBtnBsStyle;
        return (

                {this.props.showCancel && (

                        <button
                            style={styles.button}
                            className={'btn btn-lg btn-' + cancelBtnBsStyle}
                            onClick={this.props.onCancel}
                            type="button" >
                            {this.props.cancelBtnText}
                        </button>

                )}

                    <button
                        ref="confirmBtn"
                        disabled={this.props.disabled}
                        style={styles.button}
                        className={'btn btn-lg btn-' + confirmBtnBsStyle}
                        onClick={this.props.onConfirm}
                        type="button" >
                        {this.props.confirmBtnText}
                    </button>

        );
    }
}<|endoftext|>


<commit_before>var uuid   = require('node-uuid');    // uuid/guid
var mongo  = require('mongodb');      // Db
var marked = require('marked');       // Markdown compilation
var hljs   = require('highlight.js'); // Syntax colouring

// Setup the markdown compiler
marked.setOptions({
    gfm: true,
    highlight: function (code, lang) {
        // TODO: support lang since it's provided
        return hljs.highlightAuto(code).value;
    },
    tables: true,
    breaks: false,
    pedantic: false,
    sanitize: true,
    smartLists: true,
    smartypants: false,
    langPrefix: 'lang-'
});

/**
 * Articles constructor
 */
Articles = function (host, port) {
    this.db = new mongo.Db('blog', new mongo.Server(host, port, {
        auto_reconnect: true,
        safe: false,
        w: 0
    }, {}));

    this.db.open(function () {
    });
};

Articles.prototype.getArticlesFromDb = function (callback) {
    this.db.collection('articles', function (error, coll) {
        if (error) callback(error);
        else callback(null, coll);
    });
};

Articles.prototype.getDraftsFromDb = function (callback) {
    this.db.collection('drafts', function (error, coll) {
        if (error) callback(error);
        else callback(null, coll);
    });
};

/**
 * Find every article.
 */
Articles.prototype.findAll = function (callback) {
    this.getArticlesFromDb(function (error, coll) {
        if (error) return callback(error);

        coll.find().toArray(function (error, array) {
            if (error) callback(error);
            else callback(null, array);
        });
    });
};

/**
 * Find every draft.
 */
Articles.prototype.findDrafts = function (callback) {
    this.getDraftsFromDb(function (error, coll) {
        if (error) return callback(error);

        coll.find().toArray(function (error, array) {
            if (error) callback(error);
            else callback(null, array);
        });
    });
};

/*
 * Find a specific article by ID.
 */
Articles.prototype.findById = function (id, callback) {
    this.getArticlesFromDb(function (error, coll) {
        if (error) return callback(error);

        coll.findOne({
            _id: coll.db.bson_serializer.ObjectID.createFromHexString(id)
        }, function (error, result) {
            if (error) callback(error);
            else callback(null, result);
        });
    });
};

/*
 * Find a specific article by ID.
 */
Articles.prototype.findDraftById = function (id, callback) {
    this.getDraftsFromDb(function (error, coll) {
        if (error) return callback(error);

        coll.findOne({
            _id: coll.db.bson_serializer.ObjectID.createFromHexString(id)
        }, function (error, result) {
            if (error) callback(error);
            else callback(null, result);
        });
    });
};

/**
 * Given an article, with an existing _id, update the matching
 * article in the database. Doing it this way allows comments and
 * metadata to persist.
 */
Articles.prototype.update = function (article, callback) {
    var that = this;

    this.getArticlesFromDb(function (error, coll) {
        if (error) return callback(error);

        that.findById(article._id, function (error, old) {
            if (error) return callback(error);

            // Replace the old body with the new
            old.title  = article.title;
            old.body   = article.body;
            old.github = article.github;

            // Devel toggle to clear comments
            old.comments = [];

            // Save the article with the new body in place
            coll.save(old, function () {
                if (callback) callback(null, old);
            });
        });
    });
};

/**
 */
Articles.prototype.replyTo = function (parentID, comment, callback) {
    var that = this;

    this.getArticlesFromDb(function (error, coll) {
        if (error) return callback(error);

        that.findById(comment._id, function (error, article) {
            if (error) return callback(error);

            comment.created_at = new Date();
            comment.replies    = [];
            comment._id        = uuid.v4();  // Create a new uuid for the comment

            // Find the parent comment
            var i, j;
            for (i = 0, j = article.comments.length; i < j; ++i) {
                // If not the correct parent comment, skip it
                if (article.comments[i]._id !== parentID)
                    continue;

                // Add the comment as a reply to the parent
                article.comments[i].replies.push(comment);
                break;
            }

            // Save the article with the new body in place
            coll.save(article, function () {
                if (callback) callback(null, article);
            });
        });
    });
};

/**
 * Given a comment with the same _id as an article, add the comment
 * to the array of comments for the article and timestamp it.
 */
Articles.prototype.comment = function (comment, callback) {
    var that = this;

    this.getArticlesFromDb(function (error, coll) {
        if (error) return callback(error);

        that.findById(comment._id, function (error, article) {
            if (error) return callback(error);

            comment.created_at = new Date();
            comment.replies    = [];
            comment._id        = uuid.v4();  // Create a new uuid for the comment

            article.comments.push(comment);

            // Save the article with the new body in place
            coll.save(article, function () {
                if (callback) callback(null, article);
            });
        });
    });
};

/**
 * Save lists of articles of individual articles. Takes a
 * markdown article and converts it to html, while saving the
 * markdown source in a separate table.
 */
Articles.prototype.publish = function (articles, callback) {
    this.getArticlesFromDb(function (error, coll) {
        if (error) return callback(error);

        var article = null;

        // Enlist the articles data if it is a single article
        if (typeof(articles.length) === "undefined")
            articles = [articles];

        articles = articles.map(function (entry) {

            // Create the new article entry
            article = {
                 title      : entry.title,
                 created_at : entry.created,
                 github     : entry.github,
                 comments   : []
            };

            // Save markdown entries
            if (entry.markdown)
            article.body = marked(entry.markdown);

            return article;
        });

        coll.insert(articles, function () {
            if (callback) callback(null, articles);
        });
    });
};

/**
 * Save lists of articles of individual articles. Takes a
 * markdown article and converts it to html, while saving the
 * markdown source in a separate table.
 */
Articles.prototype.save = function (articles, callback) {
    this.getDraftsFromDb(function (error, coll) {
        if (error) return callback(error);

        var article = null;

        // Enlist the articles data if it is a single article
        if (typeof(articles.length) === "undefined")
            articles = [articles];

        articles = articles.map(function (entry) {
            return {
                 title      : entry.title,
                 github     : entry.github,
                 body       : entry.markdown
            };
        });

        coll.insert(articles, function () {
            if (callback) callback(null, articles);
        });
    });
};

// Finally, export the provider
exports.provider = Articles;
<commit_msg>Indent fix<commit_after>var uuid   = require('node-uuid');    // uuid/guid
var mongo  = require('mongodb');      // Db
var marked = require('marked');       // Markdown compilation
var hljs   = require('highlight.js'); // Syntax colouring

// Setup the markdown compiler
marked.setOptions({
    gfm: true,
    highlight: function (code, lang) {
        // TODO: support lang since it's provided
        return hljs.highlightAuto(code).value;
    },
    tables: true,
    breaks: false,
    pedantic: false,
    sanitize: true,
    smartLists: true,
    smartypants: false,
    langPrefix: 'lang-'
});

/**
 * Articles constructor
 */
Articles = function (host, port) {
    this.db = new mongo.Db('blog', new mongo.Server(host, port, {
        auto_reconnect: true,
        safe: false,
        w: 0
    }, {}));

    this.db.open(function () {
    });
};

Articles.prototype.getArticlesFromDb = function (callback) {
    this.db.collection('articles', function (error, coll) {
        if (error) callback(error);
        else callback(null, coll);
    });
};

Articles.prototype.getDraftsFromDb = function (callback) {
    this.db.collection('drafts', function (error, coll) {
        if (error) callback(error);
        else callback(null, coll);
    });
};

/**
 * Find every article.
 */
Articles.prototype.findAll = function (callback) {
    this.getArticlesFromDb(function (error, coll) {
        if (error) return callback(error);

        coll.find().toArray(function (error, array) {
            if (error) callback(error);
            else callback(null, array);
        });
    });
};

/**
 * Find every draft.
 */
Articles.prototype.findDrafts = function (callback) {
    this.getDraftsFromDb(function (error, coll) {
        if (error) return callback(error);

        coll.find().toArray(function (error, array) {
            if (error) callback(error);
            else callback(null, array);
        });
    });
};

/*
 * Find a specific article by ID.
 */
Articles.prototype.findById = function (id, callback) {
    this.getArticlesFromDb(function (error, coll) {
        if (error) return callback(error);

        coll.findOne({
            _id: coll.db.bson_serializer.ObjectID.createFromHexString(id)
        }, function (error, result) {
            if (error) callback(error);
            else callback(null, result);
        });
    });
};

/*
 * Find a specific article by ID.
 */
Articles.prototype.findDraftById = function (id, callback) {
    this.getDraftsFromDb(function (error, coll) {
        if (error) return callback(error);

        coll.findOne({
            _id: coll.db.bson_serializer.ObjectID.createFromHexString(id)
        }, function (error, result) {
            if (error) callback(error);
            else callback(null, result);
        });
    });
};

/**
 * Given an article, with an existing _id, update the matching
 * article in the database. Doing it this way allows comments and
 * metadata to persist.
 */
Articles.prototype.update = function (article, callback) {
    var that = this;

    this.getArticlesFromDb(function (error, coll) {
        if (error) return callback(error);

        that.findById(article._id, function (error, old) {
            if (error) return callback(error);

            // Replace the old body with the new
            old.title  = article.title;
            old.body   = article.body;
            old.github = article.github;

            // Devel toggle to clear comments
            old.comments = [];

            // Save the article with the new body in place
            coll.save(old, function () {
                if (callback) callback(null, old);
            });
        });
    });
};

/**
 */
Articles.prototype.replyTo = function (parentID, comment, callback) {
    var that = this;

    this.getArticlesFromDb(function (error, coll) {
        if (error) return callback(error);

        that.findById(comment._id, function (error, article) {
            if (error) return callback(error);

            comment.created_at = new Date();
            comment.replies    = [];
            comment._id        = uuid.v4();  // Create a new uuid for the comment

            // Find the parent comment
            var i, j;
            for (i = 0, j = article.comments.length; i < j; ++i) {
                // If not the correct parent comment, skip it
                if (article.comments[i]._id !== parentID)
                    continue;

                // Add the comment as a reply to the parent
                article.comments[i].replies.push(comment);
                break;
            }

            // Save the article with the new body in place
            coll.save(article, function () {
                if (callback) callback(null, article);
            });
        });
    });
};

/**
 * Given a comment with the same _id as an article, add the comment
 * to the array of comments for the article and timestamp it.
 */
Articles.prototype.comment = function (comment, callback) {
    var that = this;

    this.getArticlesFromDb(function (error, coll) {
        if (error) return callback(error);

        that.findById(comment._id, function (error, article) {
            if (error) return callback(error);

            comment.created_at = new Date();
            comment.replies    = [];
            comment._id        = uuid.v4();  // Create a new uuid for the comment

            article.comments.push(comment);

            // Save the article with the new body in place
            coll.save(article, function () {
                if (callback) callback(null, article);
            });
        });
    });
};

/**
 * Save lists of articles of individual articles. Takes a
 * markdown article and converts it to html, while saving the
 * markdown source in a separate table.
 */
Articles.prototype.publish = function (articles, callback) {
    this.getArticlesFromDb(function (error, coll) {
        if (error) return callback(error);

        var article = null;

        // Enlist the articles data if it is a single article
        if (typeof(articles.length) === "undefined")
            articles = [articles];

        articles = articles.map(function (entry) {

            // Create the new article entry
            article = {
                 title      : entry.title,
                 created_at : entry.created,
                 github     : entry.github,
                 comments   : []
            };

            // Save markdown entries
            if (entry.markdown)
                article.body = marked(entry.markdown);

            return article;
        });

        coll.insert(articles, function () {
            if (callback) callback(null, articles);
        });
    });
};

/**
 * Save lists of articles of individual articles. Takes a
 * markdown article and converts it to html, while saving the
 * markdown source in a separate table.
 */
Articles.prototype.save = function (articles, callback) {
    this.getDraftsFromDb(function (error, coll) {
        if (error) return callback(error);

        var article = null;

        // Enlist the articles data if it is a single article
        if (typeof(articles.length) === "undefined")
            articles = [articles];

        articles = articles.map(function (entry) {
            return {
                 title      : entry.title,
                 github     : entry.github,
                 body       : entry.markdown
            };
        });

        coll.insert(articles, function () {
            if (callback) callback(null, articles);
        });
    });
};

// Finally, export the provider
exports.provider = Articles;
<|endoftext|>


<commit_before>/* adapted from
 * https://github.com/naturalatlas/node-gdal/blob/master/examples/gdalinfo.js
 */
var gdal       = require('gdal')
var util       = require('util')
var jsonFormat = require('json-format')
var fs         = require('fs')

/* Read GeoTIF and extract metadata e.g. size, reference coordinates */
exports.getMetadata = function (filename) {
	var ds = gdal.open(filename)

	var driver = ds.driver;
	var driver_metadata = driver.getMetadata();
	if (driver_metadata['DCAP_RASTER'] !== 'YES') {
		console.error('Source file is not a raster')
		process.exit(1);
	}

	// raster size
	var size = ds.rasterSize;

	// geotransform
	var geotransform = ds.geoTransform;

	// corners
	var corners = {
		upper_left   : {x: 0, y: 0},
		upper_right  : {x: size.x, y: 0},
		bottom_right : {x: size.x, y: size.y},
		bottom_left  : {x: 0, y: size.y},
		center       : {x: size.x / 2, y: size.y / 2}
	};

	var wgs84 = gdal.SpatialReference.fromEPSG(4326)
	var coord_transform = new gdal.CoordinateTransformation(ds.srs, wgs84)

	var coordinates = {}
	var corner_names = Object.keys(corners)
	corner_names.forEach(function(corner_name) {
		// convert pixel x,y to the coordinate system of the raster
		// then transform it to WGS84

		var corner      = corners[corner_name]
		var pt_orig     = {
			x: geotransform[0] + corner.x * geotransform[1] + corner.y * geotransform[2],
			y: geotransform[3] + corner.x * geotransform[4] + corner.y * geotransform[5]
		};
		var pt_wgs84    = coord_transform.transformPoint(pt_orig)
		var lat_dms = gdal.decToDMS(pt_wgs84.y, 'Lat')
		var lon_dms = gdal.decToDMS(pt_wgs84.x, 'Long')
		var dd = dmsToDd( lat_dms, lon_dms ) // convert to decimal degrees
		var description = util.format('%s (%s, %s)', corner_name, dd.lat, dd.lon );
		coordinates[corner_name] = {lat: dd.lat, lon: dd.lon }
	});

	// // debugging code
	// process.stdout.write("SIZE: \n" + jsonFormat(size) );
	// process.stdout.write("COORDINATES: \n" + jsonFormat(coordinates) );

	return { size: size, reference_coordinates: coordinates }
}

/* Append/replace image corner coordinates in JSON file */
exports.writeMetaToJSON = function (filename, metadata) {
	var file_content = fs.readFileSync( filename )
	var content = JSON.parse(file_content)
	content['metadata'] = metadata
	fs.writeFileSync(filename, jsonFormat(content));
}

/* Convert DMS to decimal degrees */
function dmsToDd (lat, lon) {
	lat = parseFloat( lat.split('d')[0] ) + parseFloat( lat.split('\'')[0].split('d')[1] )/60 + parseFloat( lat.split('\'')[1].split('\"')[0] )/3600
	lon = parseFloat( lon.split('d')[0] ) + parseFloat( lon.split('\'')[0].split('d')[1] )/60 + parseFloat( lon.split('\'')[1].split('\"')[0] )/3600
	return {lat: lat, lon: lon}
}
<commit_msg>Remove redundant lat long conversions. Fixes #16.<commit_after>/* adapted from
 * https://github.com/naturalatlas/node-gdal/blob/master/examples/gdalinfo.js
 */
var gdal       = require('gdal')
var util       = require('util')
var jsonFormat = require('json-format')
var fs         = require('fs')

/* Read GeoTIF and extract metadata e.g. size, reference coordinates */
exports.getMetadata = function (filename) {
	var ds = gdal.open(filename)

	var driver = ds.driver;
	var driver_metadata = driver.getMetadata();
	if (driver_metadata['DCAP_RASTER'] !== 'YES') {
		console.error('Source file is not a raster')
		process.exit(1);
	}

	// raster size
	var size = ds.rasterSize;

	// geotransform
	var geotransform = ds.geoTransform;

	// corners
	var corners = {
		upper_left   : {x: 0, y: 0},
		upper_right  : {x: size.x, y: 0},
		bottom_right : {x: size.x, y: size.y},
		bottom_left  : {x: 0, y: size.y},
		center       : {x: size.x / 2, y: size.y / 2}
	};

	var wgs84 = gdal.SpatialReference.fromEPSG(4326)
	var coord_transform = new gdal.CoordinateTransformation(ds.srs, wgs84)

	var coordinates = {}
	var corner_names = Object.keys(corners)
	corner_names.forEach(function(corner_name) {
		// convert pixel x,y to the coordinate system of the raster
		// then transform it to WGS84

		var corner      = corners[corner_name]
		var pt_orig     = {
			x: geotransform[0] + corner.x * geotransform[1] + corner.y * geotransform[2],
			y: geotransform[3] + corner.x * geotransform[4] + corner.y * geotransform[5]
		};
		var pt_wgs84    = coord_transform.transformPoint(pt_orig)
		coordinates[corner_name] = {lat: pt_wgs84.y, lon: pt_wgs84.x }
	});

	// // debugging code
	// process.stdout.write("SIZE: \n" + jsonFormat(size) );
	// process.stdout.write("COORDINATES: \n" + jsonFormat(coordinates) );

	return { size: size, reference_coordinates: coordinates }
}

/* Append/replace image corner coordinates in JSON file */
exports.writeMetaToJSON = function (filename, metadata) {
	var file_content = fs.readFileSync( filename )
	var content = JSON.parse(file_content)
	content['metadata'] = metadata
	fs.writeFileSync(filename, jsonFormat(content));
}

<|endoftext|>


<commit_before>var fs = require('fs');
var csv = require('csv');
var path = require('path');
var util = require('util');
var utils = require('../util/utils');
var Tbl = require('../common/tbl');
var EventEmitter = require('events').EventEmitter;
var logger = require('pomelo-logger').getLogger('pomelo-data', __filename);

var instance = null;

module.exports = function(app, opts) {
  // singleton
  if(instance) {
    return instance;
  }

	instance = new Component(app, opts);
  app.set('dataService', instance);
	return instance;
};

var Component = function(app, opts) {
  this.app = app;
  this.dir = opts.dir;
  this.idx = opts.idx;
  this.interval = opts.interval;
  this.numDict = {cur: 0, total: 0}; // config files' amount
  this.tmpConfigDataTbl = {};
  this.configDataTbl = {};
  this.csvParsers = {};

  if(!fs.existsSync(this.dir)) {
    logger.error('Dir %s not exist!', this.dir);
    return;
  }
};

util.inherits(Component, EventEmitter);

Component.prototype.start = function(cb) {
  this.loadAll(cb);
};

Component.prototype.stop = function(force, cb) {
  this.tmpConfigDataTbl = null;
  this.configDataTbl = null;
  this.csvParsers = null;
  utils.invokeCallback(cb);
};

Component.prototype.loadFileFunc = function(filename, isLoadAll, cb) {
  var self = this;
  var curFileIdx = path.basename(filename, '.csv');
  self.tmpConfigDataTbl[curFileIdx] = [];

  self.csvParsers[curFileIdx] = csv();
  var tmpParser = self.csvParsers[curFileIdx];

  tmpParser.from.path(filename, {comment: '#'});

  tmpParser.on('record', function(row, index){
    // console.log('#' + index + ' ' + JSON.stringify(row));
    var tmpL = self.tmpConfigDataTbl[curFileIdx];
    if(tmpL) {
      tmpL.push(row);
    }
  });

  tmpParser.on('end', function(){
    // console.log('csvParsers ~ on.end is running ...');
    self.configDataTbl[curFileIdx] = new Tbl(self.tmpConfigDataTbl[curFileIdx], self.idx);
    if(isLoadAll) {
      self.numDict.cur++;
      if(self.numDict.cur === self.numDict.total) {
        utils.invokeCallback(cb);
        self.tmpConfigDataTbl = {}; // release temp data
      }
    }
  });

  tmpParser.on('error', function(err){
    logger.error(err.message);
    utils.invokeCallback(cb, err);
  });
};

Component.prototype.listener4watch = function(filename) {
  var self = this;
  return function(curr, prev) {
    if(curr.mtime.getTime() > prev.mtime.getTime()) {
      self.loadFileFunc(filename);
      /*
      setTimeout(function() {
        console.warn('\n', Date(), ': Listener4watch ~ configDataTbl = ', util.inspect(self.configDataTbl, {showHidden: true, depth: null}))
      }, 2000);
      */
    }
  };
};

Component.prototype.loadAll = function(cb) {
  var self = this;
  self.tmpConfigDataTbl = {};
  self.configDataTbl = {};

  fs.readdirSync(self.dir).forEach(function(filename) {
    if (!/\.csv$/.test(filename)) {
      return;
    }
    var absolutePath = path.join(self.dir, filename);
    if(fs.existsSync(absolutePath)) {
      self.numDict.total++;
    }
  });
  // console.log('self.numDict.total = ', self.numDict.total);

  fs.readdirSync(self.dir).forEach(function(filename) {
    if (!/\.csv$/.test(filename)) {
      return;
    }
    var absolutePath = path.join(self.dir, filename);
    if(!fs.existsSync(absolutePath)) {
      logger.error('Config file %s not exist at %s!', filename, absolutePath);
    } else {
      // console.warn('!!! filename = %s, absolutePath = %s', filename, absolutePath);
      // invoke cb(start next component) when all files have been loaded
      self.loadFileFunc(absolutePath, true, cb);
      fs.watchFile(absolutePath, { persistent: true, interval: self.interval }, self.listener4watch(absolutePath));
    }
  });
  /*
  setTimeout(function() {
    console.warn('\n', Date(), ': LoadAll ~ configDataTbl = ', util.inspect(self.configDataTbl, {showHidden: true, depth: null}))
  }, 2000);
  */
};

Component.prototype.get = function(tblName) {
  return this.configDataTbl[tblName];
};

<commit_msg>* clear console.log<commit_after>var fs = require('fs');
var csv = require('csv');
var path = require('path');
var util = require('util');
var utils = require('../util/utils');
var Tbl = require('../common/tbl');
var EventEmitter = require('events').EventEmitter;
var logger = require('pomelo-logger').getLogger('pomelo-data', __filename);

var instance = null;

module.exports = function(app, opts) {
  // singleton
  if(instance) {
    return instance;
  }

	instance = new Component(app, opts);
  app.set('dataService', instance);
	return instance;
};

var Component = function(app, opts) {
  this.app = app;
  this.dir = opts.dir;
  this.idx = opts.idx;
  this.interval = opts.interval;
  this.numDict = {cur: 0, total: 0}; // config files' amount
  this.tmpConfigDataTbl = {};
  this.configDataTbl = {};
  this.csvParsers = {};

  if(!fs.existsSync(this.dir)) {
    logger.error('Dir %s not exist!', this.dir);
    return;
  }
};

util.inherits(Component, EventEmitter);

Component.prototype.start = function(cb) {
  this.loadAll(cb);
};

Component.prototype.stop = function(force, cb) {
  this.tmpConfigDataTbl = null;
  this.configDataTbl = null;
  this.csvParsers = null;
  utils.invokeCallback(cb);
};

Component.prototype.loadFileFunc = function(filename, isLoadAll, cb) {
  var self = this;
  var curFileIdx = path.basename(filename, '.csv');
  self.tmpConfigDataTbl[curFileIdx] = [];

  self.csvParsers[curFileIdx] = csv();
  var tmpParser = self.csvParsers[curFileIdx];

  tmpParser.from.path(filename, {comment: '#'});

  tmpParser.on('record', function(row, index){
    var tmpL = self.tmpConfigDataTbl[curFileIdx];
    if(tmpL) {
      tmpL.push(row);
    }
  });

  tmpParser.on('end', function(){
    self.configDataTbl[curFileIdx] = new Tbl(self.tmpConfigDataTbl[curFileIdx], self.idx);
    if(isLoadAll) {
      self.numDict.cur++;
      if(self.numDict.cur === self.numDict.total) {
        utils.invokeCallback(cb);
        self.tmpConfigDataTbl = {}; // release temp data
      }
    }
  });

  tmpParser.on('error', function(err){
    logger.error(err.message);
    utils.invokeCallback(cb, err);
  });
};

Component.prototype.listener4watch = function(filename) {
  var self = this;
  return function(curr, prev) {
    if(curr.mtime.getTime() > prev.mtime.getTime()) {
      self.loadFileFunc(filename);
    }
  };
};

Component.prototype.loadAll = function(cb) {
  var self = this;
  self.tmpConfigDataTbl = {};
  self.configDataTbl = {};

  fs.readdirSync(self.dir).forEach(function(filename) {
    if (!/\.csv$/.test(filename)) {
      return;
    }
    var absolutePath = path.join(self.dir, filename);
    if(fs.existsSync(absolutePath)) {
      self.numDict.total++;
    }
  });

  fs.readdirSync(self.dir).forEach(function(filename) {
    if (!/\.csv$/.test(filename)) {
      return;
    }
    var absolutePath = path.join(self.dir, filename);
    if(!fs.existsSync(absolutePath)) {
      logger.error('Config file %s not exist at %s!', filename, absolutePath);
    } else {
      // invoke cb(start next component) when all files have been loaded
      self.loadFileFunc(absolutePath, true, cb);
      fs.watchFile(absolutePath, { persistent: true, interval: self.interval }, self.listener4watch(absolutePath));
    }
  });
};

Component.prototype.get = function(tblName) {
  return this.configDataTbl[tblName];
};

<|endoftext|>


<commit_before>'use strict'
var Observable = require('../observable')
var ISNODE = require('../util/is/node')
var merge = require('../util/merge')
var include = require('../util/include')
var resolveNameSpace = require('./util/resolveNameSpace')
var findPackage = ISNODE && require('./util/findPackage')
var parseArgv = ISNODE && require('./util/parseArgv')
var path = ISNODE && require('path')

const OBJ = 'object'

exports.define = {
  generateConstructor: function () {
    return function Config (options, event, parent, key) {
      var config = this
      var nameSpace = config._nameSpace && config._nameSpace.val

      var val = {}

      var packageDir = (options && options._packageDir)

      var pkg = config._findPackage && config._findPackage.val &&
        findPackage && findPackage(packageDir)
      if (pkg) {
        var pkgObj = resolveNameSpace(pkg.obj, nameSpace)
        merge(val, pkgObj)
      }

      if (ISNODE) {
        var topVal = {}
        // environment variables and inline arguments go in topVal first, to be
        // used to evaluate configFiles

        merge(topVal, val)

        findEnv(topVal)

        var mergeFiles = []

        if (val.mergeFiles) {
          include(mergeFiles, val.mergeFiles)
        }
        if (topVal.mergeFiles) {
          include(mergeFiles, topVal.mergeFiles)
        }

        var setArgv = parseArgv && config._argv && config._argv.val && parseArgv(val)
        if (setArgv) {
          merge(topVal, setArgv)
          if (topVal.mergeFiles) {
            include(mergeFiles, topVal.mergeFiles)
          }
        }

        if (pkg) {
          let configFiles = topVal.configFiles || val.configFiles
          mergeFromFiles(val, configFiles, pkg.dir, nameSpace)
          mergeFromFiles(val, mergeFiles, pkg.dir, nameSpace)
        }

        // now actually apply env and inline on val
        findEnv(val)
        if (setArgv) {
          merge(val, setArgv)
        }

        delete val.configFiles
        delete val.mergeFiles
      }

      if (options) {
        options = resolveNameSpace(options, nameSpace)
        merge(val, options)
      }

      Observable.call(config, val, false, parent, key)
      if (config.autoMerge) {
        config.merge(config.autoMerge)
      }
    }
  }
}

function findEnv (obj) {
  var env = obj._env
  if (env && env in process.env) {
    obj.val = process.env[env]
  }
  for (let key in obj) {
    var val = obj[key]
    if (typeof val === OBJ) {
      findEnv(val)
    }
  }
}

function mergeFromFiles (obj, files, pkgDir, nameSpace) {
  for (let key in files) {
    let configFile = files[key]
    if (!path.isAbsolute(configFile)) {
      configFile = path.join(pkgDir, configFile)
    }
    try {
      let configObj = require(configFile)
      configObj = resolveNameSpace(configObj, nameSpace)
      merge(obj, configObj)
    } catch (e) {
      // console.log('could not require configFile', configFile)
    }
  }
}
<commit_msg>fix mergeFiles and configFiles to work starting from cwd<commit_after>'use strict'
var Observable = require('../observable')
var ISNODE = require('../util/is/node')
var merge = require('../util/merge')
var include = require('../util/include')
var resolveNameSpace = require('./util/resolveNameSpace')
var findPackage = ISNODE && require('./util/findPackage')
var parseArgv = ISNODE && require('./util/parseArgv')
var path = ISNODE && require('path')

const OBJ = 'object'

exports.define = {
  generateConstructor: function () {
    return function Config (options, event, parent, key) {
      var config = this
      var nameSpace = config._nameSpace && config._nameSpace.val

      var val = {}

      var packageDir = (options && options._packageDir)

      var pkg = config._findPackage && config._findPackage.val &&
        findPackage && findPackage(packageDir)
      if (pkg) {
        var pkgObj = resolveNameSpace(pkg.obj, nameSpace)
        merge(val, pkgObj)
      }

      if (ISNODE) {
        var topVal = {}
        // environment variables and inline arguments go in topVal first, to be
        // used to evaluate configFiles

        merge(topVal, val)

        findEnv(topVal)

        var mergeFiles = []

        if (val.mergeFiles) {
          include(mergeFiles, val.mergeFiles)
        }
        if (topVal.mergeFiles) {
          include(mergeFiles, topVal.mergeFiles)
        }

        var setArgv = parseArgv && config._argv && config._argv.val && parseArgv(val)
        if (setArgv) {
          merge(topVal, setArgv)
          if (topVal.mergeFiles) {
            include(mergeFiles, topVal.mergeFiles)
          }
        }

        if (pkg) {
          let configFiles = topVal.configFiles || val.configFiles
          mergeFromFiles(val, configFiles, pkg.dir, nameSpace)
          mergeFromFiles(val, mergeFiles, pkg.dir, nameSpace)
        }

        // now actually apply env and inline on val
        findEnv(val)
        if (setArgv) {
          merge(val, setArgv)
        }

        delete val.configFiles
        delete val.mergeFiles
      }

      if (options) {
        options = resolveNameSpace(options, nameSpace)
        merge(val, options)
      }

      Observable.call(config, val, false, parent, key)
      if (config.autoMerge) {
        config.merge(config.autoMerge)
      }
    }
  }
}

function findEnv (obj) {
  var env = obj._env
  if (env && env in process.env) {
    obj.val = process.env[env]
  }
  for (let key in obj) {
    var val = obj[key]
    if (typeof val === OBJ) {
      findEnv(val)
    }
  }
}

function mergeFromFiles (obj, files, pkgDir, nameSpace) {
  for (let key in files) {
    let configFile = files[key]
    if (!path.isAbsolute(configFile)) {
      configFile = path.join(process.cwd(), configFile)
    }
    try {
      let configObj = require(configFile)
      configObj = resolveNameSpace(configObj, nameSpace)
      merge(obj, configObj)
    } catch (e) {
      // console.log('could not require configFile', configFile)
    }
  }
}
<|endoftext|>