<commit_before>const config = require('./config')
const BugsnagEvent = require('./event')
const BugsnagBreadcrumb = require('./breadcrumb')
const BugsnagSession = require('./session')
const { map, includes } = require('./lib/es-utils')
const inferReleaseStage = require('./lib/infer-release-stage')
const isError = require('./lib/iserror')
const runCallbacks = require('./lib/callback-runner')
const metadataDelegate = require('./lib/metadata-delegate')

const LOG_USAGE_ERR_PREFIX = 'Usage error.'
const REPORT_USAGE_ERR_PREFIX = 'Bugsnag usage error.'

class BugsnagClient {
  constructor (configuration, schema = config.schema, notifier) {
    // notifier id
    this._notifier = notifier

    // intialise opts and config
    this._opts = configuration
    this._config = {}
    this._schema = schema

    // // i/o
    this._delivery = { sendSession: () => {}, sendEvent: () => {} }
    this._logger = { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} }

    // plugins
    this._plugins = {}

    this._session = null

    this._metadata = {}

    this.breadcrumbs = []

    // setable props
    this.app = {}
    this.context = undefined
    this.device = undefined
    this.request = undefined
    this._user = {}

    // expose internal constructors
    this.BugsnagClient = BugsnagClient
    this.BugsnagEvent = BugsnagEvent
    this.BugsnagBreadcrumb = BugsnagBreadcrumb
    this.BugsnagSession = BugsnagSession

    this._extractConfiguration()

    const self = this
    const notify = this.notify
    this.notify = function () {
      return notify.apply(self, arguments)
    }
  }

  addMetadata (section, ...args) {
    return metadataDelegate.add(this._metadata, section, ...args)
  }

  getMetadata (section, key) {
    return metadataDelegate.get(this._metadata, section, key)
  }

  clearMetadata (section, key) {
    return metadataDelegate.clear(this._metadata, section, key)
  }

  _extractConfiguration (partialSchema = this._schema) {
    const conf = config.mergeDefaults(this._opts, partialSchema)
    const validity = config.validate(conf, partialSchema)

    if (!validity.valid === true) throw new Error(generateConfigErrorMessage(validity.errors))

    // update and elevate some special options if they were passed in at this point
    if (typeof conf.onError === 'function') conf.onError = [conf.onError]
    if (conf.appVersion) this.app.version = conf.appVersion
    if (conf.appType) this.app.type = conf.appType
    if (conf.metadata) this._metadata = conf.metadata
    if (conf.user) this._user = conf.user
    if (conf.logger) this._logger = conf.logger

    // merge with existing config
    this._config = { ...this._config, ...conf }

    return this
  }

  getUser () {
    return this._user
  }

  setUser (id, email, name) {
    this._user = { id, email, name }
  }

  use (plugin, ...args) {
    if (plugin.configSchema) this._extractConfiguration(plugin.configSchema)
    const result = plugin.init(this, ...args)
    // JS objects are not the safest way to store arbitrarily keyed values,
    // so bookend the key with some characters that prevent tampering with
    // stuff like __proto__ etc. (only store the result if the plugin had a
    // name)
    if (plugin.name) this._plugins[`~${plugin.name}~`] = result
    return this
  }

  getPlugin (name) {
    return this._plugins[`~${name}~`]
  }

  _setDelivery (d) {
    this._delivery = d(this)
    return this
  }

  startSession () {
    if (!this._sessionDelegate) {
      this._logger.warn('No session implementation is installed')
      return this
    }
    return this._sessionDelegate.startSession(this)
  }

  leaveBreadcrumb (message, metadata, type) {
    // coerce bad values so that the defaults get set
    message = typeof message === 'string' ? message : ''
    type = typeof type === 'string' ? type : 'manual'
    metadata = typeof metadata === 'object' && metadata !== null ? metadata : {}

    // if no message, discard
    if (!message) return

    // check the breadcrumb is the list of enabled types
    if (!this._config.enabledBreadcrumbTypes || !includes(this._config.enabledBreadcrumbTypes, type)) return

    const crumb = new BugsnagBreadcrumb(message, metadata, type)

    // push the valid crumb onto the queue and maintain the length
    this.breadcrumbs.push(crumb)
    if (this.breadcrumbs.length > this._config.maxBreadcrumbs) {
      this.breadcrumbs = this.breadcrumbs.slice(this.breadcrumbs.length - this._config.maxBreadcrumbs)
    }

    return this
  }

  notify (error, onError, cb = () => {}) {
    // releaseStage can be set via config.releaseStage or client.app.releaseStage
    const releaseStage = inferReleaseStage(this)

    // ensure we have an error (or a reasonable object representation of an error)
    const { err, errorFramesToSkip } = normaliseError(error, this._logger)

    // create an event from the error, if it isn't one already
    const event = BugsnagEvent.ensureEvent(err, errorFramesToSkip, 2)

    event.app = { ...{ releaseStage }, ...event.app, ...this.app }
    event.context = event.context || this.context || undefined
    event.device = { ...event.device, ...this.device }
    event.request = { ...event.request, ...this.request }
    event._metadata = { ...event._metadata, ...this._metadata }
    event._user = { ...event._user, ...this._user }
    event.breadcrumbs = this.breadcrumbs.slice(0)

    if (this._session) {
      this._session.trackError(event)
      event.session = this._session
    }

    // exit early if events should not be sent on the current releaseStage
    if (this._config.enabledReleaseStages.length > 0 && !includes(this._config.enabledReleaseStages, releaseStage)) {
      this._logger.warn('Event not sent due to releaseStage/enabledReleaseStages configuration')
      return cb(null, event)
    }

    const originalSeverity = event.severity

    const onCallbackError = err => {
      // errors in callbacks are tolerated but we want to log them out
      this._logger.error('Error occurred in onError callback, continuing anyway…')
      this._logger.error(err)
    }

    const callbacks = [].concat(onError).concat(this._config.onError)
    runCallbacks(callbacks, event, onCallbackError, (err, shouldSend) => {
      if (err) onCallbackError(err)

      if (!shouldSend) {
        this._logger.debug('Event not sent due to onError callback')
        return cb(null, event)
      }

      // only leave a crumb for the error if actually got sent
      BugsnagClient.prototype.leaveBreadcrumb.call(this, event.errorClass, {
        errorClass: event.errorClass,
        errorMessage: event.errorMessage,
        severity: event.severity
      }, 'error')

      if (originalSeverity !== event.severity) {
        event._handledState.severityReason = { type: 'userCallbackSetSeverity' }
      }

      this._delivery.sendEvent({
        apiKey: event.apiKey || this._config.apiKey,
        notifier: this._notifier,
        events: [event]
      }, (err) => cb(err, event))
    })
  }
}

const normaliseError = (error, logger) => {
  const synthesizedErrorFramesToSkip = 3

  const createAndLogUsageError = reason => {
    const msg = generateNotifyUsageMessage(reason)
    logger.warn(`${LOG_USAGE_ERR_PREFIX} ${msg}`)
    return new Error(`${REPORT_USAGE_ERR_PREFIX} ${msg}`)
  }

  let err
  let errorFramesToSkip = 0
  switch (typeof error) {
    case 'string':
      err = new Error(String(error))
      errorFramesToSkip = synthesizedErrorFramesToSkip
      break
    case 'number':
    case 'boolean':
      err = new Error(String(error))
      break
    case 'function':
      err = createAndLogUsageError('function')
      break
    case 'object':
      if (error !== null && (isError(error) || error.__isBugsnagEvent)) {
        err = error
      } else if (error !== null && hasNecessaryFields(error)) {
        err = new Error(error.message || error.errorMessage)
        err.name = error.name || error.errorClass
        errorFramesToSkip = synthesizedErrorFramesToSkip
      } else {
        err = createAndLogUsageError(error === null ? 'null' : 'unsupported object')
      }
      break
    default:
      err = createAndLogUsageError('nothing')
  }
  return { err, errorFramesToSkip }
}

const hasNecessaryFields = error =>
  (typeof error.name === 'string' || typeof error.errorClass === 'string') &&
  (typeof error.message === 'string' || typeof error.errorMessage === 'string')

const generateConfigErrorMessage = errors =>
  `Bugsnag configuration error\n${map(errors, (err) => `"${err.key}" ${err.message} \n    got ${stringify(err.value)}`).join('\n\n')}`

const generateNotifyUsageMessage = actual =>
  `notify(err) expected an error, got ${actual}`

const stringify = val => typeof val === 'object' ? JSON.stringify(val) : String(val)

module.exports = BugsnagClient
<commit_msg>refactor(core): Remove unnecessary "return this"<commit_after>const config = require('./config')
const BugsnagEvent = require('./event')
const BugsnagBreadcrumb = require('./breadcrumb')
const BugsnagSession = require('./session')
const { map, includes } = require('./lib/es-utils')
const inferReleaseStage = require('./lib/infer-release-stage')
const isError = require('./lib/iserror')
const runCallbacks = require('./lib/callback-runner')
const metadataDelegate = require('./lib/metadata-delegate')

const LOG_USAGE_ERR_PREFIX = 'Usage error.'
const REPORT_USAGE_ERR_PREFIX = 'Bugsnag usage error.'

class BugsnagClient {
  constructor (configuration, schema = config.schema, notifier) {
    // notifier id
    this._notifier = notifier

    // intialise opts and config
    this._opts = configuration
    this._config = {}
    this._schema = schema

    // // i/o
    this._delivery = { sendSession: () => {}, sendEvent: () => {} }
    this._logger = { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} }

    // plugins
    this._plugins = {}

    this._session = null

    this._metadata = {}

    this.breadcrumbs = []

    // setable props
    this.app = {}
    this.context = undefined
    this.device = undefined
    this.request = undefined
    this._user = {}

    // expose internal constructors
    this.BugsnagClient = BugsnagClient
    this.BugsnagEvent = BugsnagEvent
    this.BugsnagBreadcrumb = BugsnagBreadcrumb
    this.BugsnagSession = BugsnagSession

    this._extractConfiguration()

    const self = this
    const notify = this.notify
    this.notify = function () {
      return notify.apply(self, arguments)
    }
  }

  addMetadata (section, ...args) {
    return metadataDelegate.add(this._metadata, section, ...args)
  }

  getMetadata (section, key) {
    return metadataDelegate.get(this._metadata, section, key)
  }

  clearMetadata (section, key) {
    return metadataDelegate.clear(this._metadata, section, key)
  }

  _extractConfiguration (partialSchema = this._schema) {
    const conf = config.mergeDefaults(this._opts, partialSchema)
    const validity = config.validate(conf, partialSchema)

    if (!validity.valid === true) throw new Error(generateConfigErrorMessage(validity.errors))

    // update and elevate some special options if they were passed in at this point
    if (typeof conf.onError === 'function') conf.onError = [conf.onError]
    if (conf.appVersion) this.app.version = conf.appVersion
    if (conf.appType) this.app.type = conf.appType
    if (conf.metadata) this._metadata = conf.metadata
    if (conf.user) this._user = conf.user
    if (conf.logger) this._logger = conf.logger

    // merge with existing config
    this._config = { ...this._config, ...conf }

    return this
  }

  getUser () {
    return this._user
  }

  setUser (id, email, name) {
    this._user = { id, email, name }
  }

  use (plugin, ...args) {
    if (plugin.configSchema) this._extractConfiguration(plugin.configSchema)
    const result = plugin.init(this, ...args)
    // JS objects are not the safest way to store arbitrarily keyed values,
    // so bookend the key with some characters that prevent tampering with
    // stuff like __proto__ etc. (only store the result if the plugin had a
    // name)
    if (plugin.name) this._plugins[`~${plugin.name}~`] = result
    return this
  }

  getPlugin (name) {
    return this._plugins[`~${name}~`]
  }

  _setDelivery (d) {
    this._delivery = d(this)
  }

  startSession () {
    if (!this._sessionDelegate) {
      this._logger.warn('No session implementation is installed')
      return this
    }
    return this._sessionDelegate.startSession(this)
  }

  leaveBreadcrumb (message, metadata, type) {
    // coerce bad values so that the defaults get set
    message = typeof message === 'string' ? message : ''
    type = typeof type === 'string' ? type : 'manual'
    metadata = typeof metadata === 'object' && metadata !== null ? metadata : {}

    // if no message, discard
    if (!message) return

    // check the breadcrumb is the list of enabled types
    if (!this._config.enabledBreadcrumbTypes || !includes(this._config.enabledBreadcrumbTypes, type)) return

    const crumb = new BugsnagBreadcrumb(message, metadata, type)

    // push the valid crumb onto the queue and maintain the length
    this.breadcrumbs.push(crumb)
    if (this.breadcrumbs.length > this._config.maxBreadcrumbs) {
      this.breadcrumbs = this.breadcrumbs.slice(this.breadcrumbs.length - this._config.maxBreadcrumbs)
    }

    return this
  }

  notify (error, onError, cb = () => {}) {
    // releaseStage can be set via config.releaseStage or client.app.releaseStage
    const releaseStage = inferReleaseStage(this)

    // ensure we have an error (or a reasonable object representation of an error)
    const { err, errorFramesToSkip } = normaliseError(error, this._logger)

    // create an event from the error, if it isn't one already
    const event = BugsnagEvent.ensureEvent(err, errorFramesToSkip, 2)

    event.app = { ...{ releaseStage }, ...event.app, ...this.app }
    event.context = event.context || this.context || undefined
    event.device = { ...event.device, ...this.device }
    event.request = { ...event.request, ...this.request }
    event._metadata = { ...event._metadata, ...this._metadata }
    event._user = { ...event._user, ...this._user }
    event.breadcrumbs = this.breadcrumbs.slice(0)

    if (this._session) {
      this._session.trackError(event)
      event.session = this._session
    }

    // exit early if events should not be sent on the current releaseStage
    if (this._config.enabledReleaseStages.length > 0 && !includes(this._config.enabledReleaseStages, releaseStage)) {
      this._logger.warn('Event not sent due to releaseStage/enabledReleaseStages configuration')
      return cb(null, event)
    }

    const originalSeverity = event.severity

    const onCallbackError = err => {
      // errors in callbacks are tolerated but we want to log them out
      this._logger.error('Error occurred in onError callback, continuing anyway…')
      this._logger.error(err)
    }

    const callbacks = [].concat(onError).concat(this._config.onError)
    runCallbacks(callbacks, event, onCallbackError, (err, shouldSend) => {
      if (err) onCallbackError(err)

      if (!shouldSend) {
        this._logger.debug('Event not sent due to onError callback')
        return cb(null, event)
      }

      // only leave a crumb for the error if actually got sent
      BugsnagClient.prototype.leaveBreadcrumb.call(this, event.errorClass, {
        errorClass: event.errorClass,
        errorMessage: event.errorMessage,
        severity: event.severity
      }, 'error')

      if (originalSeverity !== event.severity) {
        event._handledState.severityReason = { type: 'userCallbackSetSeverity' }
      }

      this._delivery.sendEvent({
        apiKey: event.apiKey || this._config.apiKey,
        notifier: this._notifier,
        events: [event]
      }, (err) => cb(err, event))
    })
  }
}

const normaliseError = (error, logger) => {
  const synthesizedErrorFramesToSkip = 3

  const createAndLogUsageError = reason => {
    const msg = generateNotifyUsageMessage(reason)
    logger.warn(`${LOG_USAGE_ERR_PREFIX} ${msg}`)
    return new Error(`${REPORT_USAGE_ERR_PREFIX} ${msg}`)
  }

  let err
  let errorFramesToSkip = 0
  switch (typeof error) {
    case 'string':
      err = new Error(String(error))
      errorFramesToSkip = synthesizedErrorFramesToSkip
      break
    case 'number':
    case 'boolean':
      err = new Error(String(error))
      break
    case 'function':
      err = createAndLogUsageError('function')
      break
    case 'object':
      if (error !== null && (isError(error) || error.__isBugsnagEvent)) {
        err = error
      } else if (error !== null && hasNecessaryFields(error)) {
        err = new Error(error.message || error.errorMessage)
        err.name = error.name || error.errorClass
        errorFramesToSkip = synthesizedErrorFramesToSkip
      } else {
        err = createAndLogUsageError(error === null ? 'null' : 'unsupported object')
      }
      break
    default:
      err = createAndLogUsageError('nothing')
  }
  return { err, errorFramesToSkip }
}

const hasNecessaryFields = error =>
  (typeof error.name === 'string' || typeof error.errorClass === 'string') &&
  (typeof error.message === 'string' || typeof error.errorMessage === 'string')

const generateConfigErrorMessage = errors =>
  `Bugsnag configuration error\n${map(errors, (err) => `"${err.key}" ${err.message} \n    got ${stringify(err.value)}`).join('\n\n')}`

const generateNotifyUsageMessage = actual =>
  `notify(err) expected an error, got ${actual}`

const stringify = val => typeof val === 'object' ? JSON.stringify(val) : String(val)

module.exports = BugsnagClient
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import * as LocalPropTypes from './prop-types'
import * as ErrorCodes from './error-codes'
import style from './style.css'
import SVG from 'svg-inline-react'
import { ALL_DIGITS, CODE_LENGTH, normalizedValue, passesCheckDigitVerification } from '../../../lib/upc-a'

export default class extends Component {
  static propTypes = {
    onDelete: PropTypes.func.isRequired,
    onChange: PropTypes.func.isRequired,
    onValid: PropTypes.func.isRequired,
    onInvalid: PropTypes.func.isRequired,
    productCode: LocalPropTypes.PRODUCT_CODE_SHAPE.isRequired,
    index: PropTypes.number.isRequired
  }

  handleChange = ({ target: { value } }) => {
    const { onChange, onValid, onInvalid, index } = this.props
    const { length } = value

    if (ALL_DIGITS.test(value) || !length) {
      if (length > CODE_LENGTH) {
        const error = { error: ErrorCodes.TOO_MANY_DIGITS, value, index }

        onInvalid(error)
      } else {
        const normalizedCode = normalizedValue(value)

        if (normalizedCode.length === CODE_LENGTH) {
          if (passesCheckDigitVerification(normalizedCode)) {
            onValid(normalizedCode, index)
          } else {
            const error = { error: ErrorCodes.CHECK_DIGIT_ERROR, value: normalizedCode, index }

            onInvalid(error)
          }
        } else {
          onChange(normalizedCode, index)
        }
      }
    } else {
      const error = { error: ErrorCodes.NON_NUMBER, value, index }

      onInvalid(error)
    }
  }

  handleDelete = () => {
    const { onDelete, index } = this.props

    onDelete(index)
  }

  deleteButton = () => {
    return (
      <button title="remove UPC" className={style.remove} onClick={this.handleDelete} type="button">
        <SVG src={require('../../../fontawesome/red/svg/trash-o.svg')} />
      </button>
    )
  }

  successIcon = () => {
    return (

        <SVG title="you entered a valid UPC" src={require('../../../fontawesome/green/svg/check-circle.svg')} />

    )
  }

  errorMessage = (value, error) => {
    switch (error) {
      case ErrorCodes.NON_NUMBER:
        return UPC must be a {CODE_LENGTH} digit number
      case ErrorCodes.TOO_MANY_DIGITS:
        const { length } = value

        return You entered { length - CODE_LENGTH } digit(s) too many
      case ErrorCodes.CHECK_DIGIT_ERROR:
        return Check digit verification failed (did you mistype a digit?)
      default:
        throw new Error('invalid error code')
    }
  }

  digitsRemaining = (value) => {CODE_LENGTH - value.length} digits remaining

  render () {
    const { productCode: { value, error, valid }, index } = this.props

    return (

        <input type="text" onChange={this.handleChange} value={value} placeholder="UPC (i.e. 011111111111)" />
        { index === 0 ? null : this.deleteButton() }
        { valid ? this.successIcon() : null }
        { error ? this.errorMessage(value, error) : null }
        { value.length && !valid && !error ? this.digitsRemaining(value) : null }

    )
  }
}
<commit_msg>fix the display<commit_after>import React, { Component, PropTypes } from 'react'
import * as LocalPropTypes from './prop-types'
import * as ErrorCodes from './error-codes'
import style from './style.css'
import SVG from 'svg-inline-react'
import { ALL_DIGITS, CODE_LENGTH, normalizedValue, passesCheckDigitVerification } from '../../../lib/upc-a'

export default class extends Component {
  static propTypes = {
    onDelete: PropTypes.func.isRequired,
    onChange: PropTypes.func.isRequired,
    onValid: PropTypes.func.isRequired,
    onInvalid: PropTypes.func.isRequired,
    productCode: LocalPropTypes.PRODUCT_CODE_SHAPE.isRequired,
    index: PropTypes.number.isRequired
  }

  handleChange = ({ target: { value } }) => {
    const { onChange, onValid, onInvalid, index } = this.props
    const { length } = value

    if (ALL_DIGITS.test(value) || !length) {
      if (length > CODE_LENGTH) {
        const error = { error: ErrorCodes.TOO_MANY_DIGITS, value, index }

        onInvalid(error)
      } else {
        const normalizedCode = normalizedValue(value)

        if (normalizedCode.length === CODE_LENGTH) {
          if (passesCheckDigitVerification(normalizedCode)) {
            onValid(normalizedCode, index)
          } else {
            const error = { error: ErrorCodes.CHECK_DIGIT_ERROR, value: normalizedCode, index }

            onInvalid(error)
          }
        } else {
          onChange(normalizedCode, index)
        }
      }
    } else {
      const error = { error: ErrorCodes.NON_NUMBER, value, index }

      onInvalid(error)
    }
  }

  handleDelete = () => {
    const { onDelete, index } = this.props

    onDelete(index)
  }

  deleteButton = () => {
    return (
      <button title="remove UPC" className={style.remove} onClick={this.handleDelete} type="button">
        <SVG src={require('../../../fontawesome/red/svg/trash-o.svg')} />
      </button>
    )
  }

  successIcon = () => {
    return (

        <SVG title="you entered a valid UPC" src={require('../../../fontawesome/green/svg/check-circle.svg')} />

    )
  }

  errorMessage = (value, error) => {
    switch (error) {
      case ErrorCodes.NON_NUMBER:
        return UPC must be a {CODE_LENGTH} digit number
      case ErrorCodes.TOO_MANY_DIGITS:
        const { length } = value

        return You entered { length - CODE_LENGTH } digit(s) too many
      case ErrorCodes.CHECK_DIGIT_ERROR:
        return Check digit verification failed (did you mistype a digit?)
      default:
        throw new Error('invalid error code')
    }
  }

  digitsRemaining = (value) => {CODE_LENGTH - value.length} digit(s) remaining

  render () {
    const { productCode: { value, error, valid }, index } = this.props

    return (

        <input type="text" onChange={this.handleChange} value={value} placeholder="UPC (i.e. 011111111111)" />
        { index === 0 ? null : this.deleteButton() }
        { valid ? this.successIcon() : null }
        { error ? this.errorMessage(value, error) : null }
        { value.length && !valid && !error ? this.digitsRemaining(value) : null }

    )
  }
}
<|endoftext|>


<commit_before>function createStore(json, payload) {
  const store = global.__ocotlinker_cache || {};

  payload.forEach(({ registry, target }, index) => {
    store[registry] = store[registry] || {};
    store[registry][target] = json[index];
  });

  global.__ocotlinker_cache = store;
}

async function runLiveQuery(matches) {
  if (!matches.length) {
    return [];
  }

  const payload = [].concat(...matches.map(match => match.urls));

  const response = await fetch('https://githublinker.herokuapp.com/bulk', {
    method: 'POST',
    body: JSON.stringify(payload),
    headers: new Headers({
      'Content-Type': 'application/json',
    }),
  });
  const json = await response.json();

  createStore(json, payload);
}

function filterLiveResolver(matches) {
  return matches.reduce((memo, match) => {
    match.urls.forEach(url => {
      if (url.type !== 'registry') {
        return;
      }

      memo.push(match);
    });

    return memo;
  }, []);
}

export default function(matches) {
  const registryMatch = filterLiveResolver(matches);
  runLiveQuery(registryMatch);
}
<commit_msg>Fix to not duplicate package send to live-resolver (#531)<commit_after>function createStore(json, payload) {
  const store = global.__ocotlinker_cache || {};

  payload.forEach(({ registry, target }, index) => {
    store[registry] = store[registry] || {};
    store[registry][target] = json[index];
  });

  global.__ocotlinker_cache = store;
}

async function runLiveQuery(matches) {
  if (!matches.length) {
    return [];
  }

  const payload = [].concat(...matches.map(match => match.urls));

  const response = await fetch('https://githublinker.herokuapp.com/bulk', {
    method: 'POST',
    body: JSON.stringify(payload),
    headers: new Headers({
      'Content-Type': 'application/json',
    }),
  });
  const json = await response.json();

  createStore(json, payload);
}

function matchContainsOnlyRegistyMatches(match) {
  return match.urls.every(url => url.type === 'registry');
}

function filterLiveResolver(matches) {
  return matches.reduce((memo, match) => {
    if (matchContainsOnlyRegistyMatches(match)) {
      memo.push(match);
    }

    return memo;
  }, []);
}

export default function(matches) {
  const registryMatch = filterLiveResolver(matches);
  runLiveQuery(registryMatch);
}
<|endoftext|>


<commit_before>var Future = Npm.require('fibers/future');
var urlModule = Npm.require('url');
var nodemailer = Npm.require('node4mailer');

Email = {};
EmailTest = {};

EmailInternals = {
  NpmModules: {
    mailcomposer: {
      version: Npm.require('node4mailer/package.json').version,
      module: Npm.require('node4mailer/lib/mail-composer')
    },
    nodemailer: {
      version: Npm.require('node4mailer/package.json').version,
      module: Npm.require('node4mailer')
    }
  }
};

var MailComposer = EmailInternals.NpmModules.mailcomposer.module;

var makeTransport = function (mailUrlString) {
  var mailUrl = urlModule.parse(mailUrlString, true);

  if (mailUrl.protocol !== 'smtp:' && mailUrl.protocol !== 'smtps:') {
    throw new Error("Email protocol in $MAIL_URL (" +
                    mailUrlString + ") must be 'smtp' or 'smtps'");
  }

  if (mailUrl.protocol === 'smtp:' && mailUrl.port === '465') {
    Meteor._debug("$MAIL_URL is of the form smtp://...:465 -- did you mean smtps://...:465? (Note added 's' to enable SSL.)");
  }

  // Allow overriding pool setting, but default to true.
  if (!mailUrl.query) {
    mailUrl.query = {};
  }

  if (!mailUrl.query.pool) {
    mailUrl.query.pool = 'true';
  }

  var transport = nodemailer.createTransport(
    urlModule.format(mailUrl));

  transport._syncSendMail = Meteor.wrapAsync(transport.sendMail, transport);
  return transport;
};

var getTransport = function() {
  // We delay this check until the first call to Email.send, in case someone
  // set process.env.MAIL_URL in startup code. Then we store in a cache until
  // process.env.MAIL_URL changes.
  var url = process.env.MAIL_URL;
  if (this.cacheKey === undefined || this.cacheKey !== url) {
    this.cacheKey = url;
    this.cache = url ? makeTransport(url) : null;
  }
  return this.cache;
}

var nextDevModeMailId = 0;
var output_stream = process.stdout;

// Testing hooks
EmailTest.overrideOutputStream = function (stream) {
  nextDevModeMailId = 0;
  output_stream = stream;
};

EmailTest.restoreOutputStream = function () {
  output_stream = process.stdout;
};

var devModeSend = function (mail) {
  var devModeMailId = nextDevModeMailId++;

  var stream = output_stream;

  // This approach does not prevent other writers to stdout from interleaving.
  stream.write("====== BEGIN MAIL #" + devModeMailId + " ======\n");
  stream.write("(Mail not sent; to enable sending, set the MAIL_URL " +
               "environment variable.)\n");
  var readStream = new MailComposer(mail).compile().createReadStream();
  readStream.pipe(stream, {end: false});
  var future = new Future;
  readStream.on('end', function () {
    stream.write("====== END MAIL #" + devModeMailId + " ======\n");
    future.return();
  });
  future.wait();
};

var smtpSend = function (transport, mail) {
  transport._syncSendMail(mail);
};

/**
 * Mock out email sending (eg, during a test.) This is private for now.
 *
 * f receives the arguments to Email.send and should return true to go
 * ahead and send the email (or at least, try subsequent hooks), or
 * false to skip sending.
 */
var sendHooks = [];
EmailTest.hookSend = function (f) {
  sendHooks.push(f);
};

/**
 * @summary Send an email. Throws an `Error` on failure to contact mail server
 * or if mail server returns an error. All fields should match
 * [RFC5322](http://tools.ietf.org/html/rfc5322) specification.
 *
 * If the `MAIL_URL` environment variable is set, actually sends the email.
 * Otherwise, prints the contents of the email to standard out.
 *
 * Note that this package is based on **mailcomposer 4**, so make sure to refer to
 * [the documentation](https://github.com/nodemailer/mailcomposer/blob/v4.0.1/README.md)
 * for that version when using the `attachments` or `mailComposer` options.
 *
 * @locus Server
 * @param {Object} options
 * @param {String} [options.from] "From:" address (required)
 * @param {String|String[]} options.to,cc,bcc,replyTo
 *   "To:", "Cc:", "Bcc:", and "Reply-To:" addresses
 * @param {String} [options.inReplyTo] Message-ID this message is replying to
 * @param {String|String[]} [options.references] Array (or space-separated string) of Message-IDs to refer to
 * @param {String} [options.messageId] Message-ID for this message; otherwise, will be set to a random value
 * @param {String} [options.subject]  "Subject:" line
 * @param {String} [options.text|html] Mail body (in plain text and/or HTML)
 * @param {String} [options.watchHtml] Mail body in HTML specific for Apple Watch
 * @param {String} [options.icalEvent] iCalendar event attachment
 * @param {Object} [options.headers] Dictionary of custom headers
 * @param {Object[]} [options.attachments] Array of attachment objects, as
 * described in the [mailcomposer documentation](https://github.com/nodemailer/mailcomposer/blob/v4.0.1/README.md#attachments).
 * @param {MailComposer} [options.mailComposer] A [MailComposer](https://nodemailer.com/extras/mailcomposer/#e-mail-message-fields)
 * object representing the message to be sent.  Overrides all other options.
 * You can create a `MailComposer` object via
 * `new EmailInternals.NpmModules.mailcomposer.module`.
 */
Email.send = function (options) {
  for (var i = 0; i < sendHooks.length; i++)
    if (! sendHooks[i](options))
      return;

  if (options.mailComposer) {
    options = options.mailComposer.mail;
  }

  var transport = getTransport();
  if (transport) {
    smtpSend(transport, options);
  } else {
    devModeSend(options);
  }
};
<commit_msg>Slight adjustment to smtp:// warning message.<commit_after>var Future = Npm.require('fibers/future');
var urlModule = Npm.require('url');
var nodemailer = Npm.require('node4mailer');

Email = {};
EmailTest = {};

EmailInternals = {
  NpmModules: {
    mailcomposer: {
      version: Npm.require('node4mailer/package.json').version,
      module: Npm.require('node4mailer/lib/mail-composer')
    },
    nodemailer: {
      version: Npm.require('node4mailer/package.json').version,
      module: Npm.require('node4mailer')
    }
  }
};

var MailComposer = EmailInternals.NpmModules.mailcomposer.module;

var makeTransport = function (mailUrlString) {
  var mailUrl = urlModule.parse(mailUrlString, true);

  if (mailUrl.protocol !== 'smtp:' && mailUrl.protocol !== 'smtps:') {
    throw new Error("Email protocol in $MAIL_URL (" +
                    mailUrlString + ") must be 'smtp' or 'smtps'");
  }

  if (mailUrl.protocol === 'smtp:' && mailUrl.port === '465') {
    Meteor._debug("The $MAIL_URL is 'smtp://...:465'.  " +
                  "You probably want 'smtps://' (The 's' enables TLS/SSL) " +
                  "since '465' is typically a secure port.");
  }

  // Allow overriding pool setting, but default to true.
  if (!mailUrl.query) {
    mailUrl.query = {};
  }

  if (!mailUrl.query.pool) {
    mailUrl.query.pool = 'true';
  }

  var transport = nodemailer.createTransport(
    urlModule.format(mailUrl));

  transport._syncSendMail = Meteor.wrapAsync(transport.sendMail, transport);
  return transport;
};

var getTransport = function() {
  // We delay this check until the first call to Email.send, in case someone
  // set process.env.MAIL_URL in startup code. Then we store in a cache until
  // process.env.MAIL_URL changes.
  var url = process.env.MAIL_URL;
  if (this.cacheKey === undefined || this.cacheKey !== url) {
    this.cacheKey = url;
    this.cache = url ? makeTransport(url) : null;
  }
  return this.cache;
}

var nextDevModeMailId = 0;
var output_stream = process.stdout;

// Testing hooks
EmailTest.overrideOutputStream = function (stream) {
  nextDevModeMailId = 0;
  output_stream = stream;
};

EmailTest.restoreOutputStream = function () {
  output_stream = process.stdout;
};

var devModeSend = function (mail) {
  var devModeMailId = nextDevModeMailId++;

  var stream = output_stream;

  // This approach does not prevent other writers to stdout from interleaving.
  stream.write("====== BEGIN MAIL #" + devModeMailId + " ======\n");
  stream.write("(Mail not sent; to enable sending, set the MAIL_URL " +
               "environment variable.)\n");
  var readStream = new MailComposer(mail).compile().createReadStream();
  readStream.pipe(stream, {end: false});
  var future = new Future;
  readStream.on('end', function () {
    stream.write("====== END MAIL #" + devModeMailId + " ======\n");
    future.return();
  });
  future.wait();
};

var smtpSend = function (transport, mail) {
  transport._syncSendMail(mail);
};

/**
 * Mock out email sending (eg, during a test.) This is private for now.
 *
 * f receives the arguments to Email.send and should return true to go
 * ahead and send the email (or at least, try subsequent hooks), or
 * false to skip sending.
 */
var sendHooks = [];
EmailTest.hookSend = function (f) {
  sendHooks.push(f);
};

/**
 * @summary Send an email. Throws an `Error` on failure to contact mail server
 * or if mail server returns an error. All fields should match
 * [RFC5322](http://tools.ietf.org/html/rfc5322) specification.
 *
 * If the `MAIL_URL` environment variable is set, actually sends the email.
 * Otherwise, prints the contents of the email to standard out.
 *
 * Note that this package is based on **mailcomposer 4**, so make sure to refer to
 * [the documentation](https://github.com/nodemailer/mailcomposer/blob/v4.0.1/README.md)
 * for that version when using the `attachments` or `mailComposer` options.
 *
 * @locus Server
 * @param {Object} options
 * @param {String} [options.from] "From:" address (required)
 * @param {String|String[]} options.to,cc,bcc,replyTo
 *   "To:", "Cc:", "Bcc:", and "Reply-To:" addresses
 * @param {String} [options.inReplyTo] Message-ID this message is replying to
 * @param {String|String[]} [options.references] Array (or space-separated string) of Message-IDs to refer to
 * @param {String} [options.messageId] Message-ID for this message; otherwise, will be set to a random value
 * @param {String} [options.subject]  "Subject:" line
 * @param {String} [options.text|html] Mail body (in plain text and/or HTML)
 * @param {String} [options.watchHtml] Mail body in HTML specific for Apple Watch
 * @param {String} [options.icalEvent] iCalendar event attachment
 * @param {Object} [options.headers] Dictionary of custom headers
 * @param {Object[]} [options.attachments] Array of attachment objects, as
 * described in the [mailcomposer documentation](https://github.com/nodemailer/mailcomposer/blob/v4.0.1/README.md#attachments).
 * @param {MailComposer} [options.mailComposer] A [MailComposer](https://nodemailer.com/extras/mailcomposer/#e-mail-message-fields)
 * object representing the message to be sent.  Overrides all other options.
 * You can create a `MailComposer` object via
 * `new EmailInternals.NpmModules.mailcomposer.module`.
 */
Email.send = function (options) {
  for (var i = 0; i < sendHooks.length; i++)
    if (! sendHooks[i](options))
      return;

  if (options.mailComposer) {
    options = options.mailComposer.mail;
  }

  var transport = getTransport();
  if (transport) {
    smtpSend(transport, options);
  } else {
    devModeSend(options);
  }
};
<|endoftext|>


<commit_before>var Future = Npm.require('fibers/future');
var urlModule = Npm.require('url');

Email = {};
EmailTest = {};

EmailInternals = {
  NpmModules: {
    mailcomposer: {
      version: Npm.require('mailcomposer/package.json').version,
      module: Npm.require('mailcomposer')
    }
  }
};

var MailComposer = EmailInternals.NpmModules.mailcomposer.module.MailComposer;

var makePool = function (mailUrlString) {
  var mailUrl = urlModule.parse(mailUrlString);
  if (mailUrl.protocol !== 'smtp:' && mailUrl.protocol !== 'smtps:')
    throw new Error("Email protocol in $MAIL_URL (" +
                    mailUrlString + ") must be 'smtp' or 'smtps'");

  var port = +(mailUrl.port);
  var auth = false;
  if (mailUrl.auth) {
    var parts = mailUrl.auth.split(':', 2);
    auth = {user: parts[0] && decodeURIComponent(parts[0]),
            pass: parts[1] && decodeURIComponent(parts[1])};
  }

  var simplesmtp = Npm.require('simplesmtp');
  var pool = simplesmtp.createClientPool(
    port,  // Defaults to 25
    mailUrl.hostname,  // Defaults to "localhost"
    { secureConnection: (port === 465),
      // XXX allow maxConnections to be configured?
      auth: auth });

  pool._future_wrapped_sendMail = _.bind(Future.wrap(pool.sendMail), pool);
  return pool;
};

var getPool = function() {
  // We delay this check until the first call to Email.send, in case someone
  // set process.env.MAIL_URL in startup code. Then we store in a cache until
  // process.env.MAIL_URL changes.
  var url = process.env.MAIL_URL;
  if (this.cacheKey === undefined || this.cacheKey !== url) {
    this.cacheKey = url;
    this.cache = url ? makePool(url) : null;
  }
  return this.cache;
}

var next_devmode_mail_id = 0;
var output_stream = process.stdout;

// Testing hooks
EmailTest.overrideOutputStream = function (stream) {
  next_devmode_mail_id = 0;
  output_stream = stream;
};

EmailTest.restoreOutputStream = function () {
  output_stream = process.stdout;
};

var devModeSend = function (mc) {
  var devmode_mail_id = next_devmode_mail_id++;

  var stream = output_stream;

  // This approach does not prevent other writers to stdout from interleaving.
  stream.write("====== BEGIN MAIL #" + devmode_mail_id + " ======\n");
  stream.write("(Mail not sent; to enable sending, set the MAIL_URL " +
               "environment variable.)\n");
  mc.streamMessage();
  mc.pipe(stream, {end: false});
  var future = new Future;
  mc.on('end', function () {
    stream.write("====== END MAIL #" + devmode_mail_id + " ======\n");
    future['return']();
  });
  future.wait();
};

var smtpSend = function (pool, mc) {
  pool._future_wrapped_sendMail(mc).wait();
};

/**
 * Mock out email sending (eg, during a test.) This is private for now.
 *
 * f receives the arguments to Email.send and should return true to go
 * ahead and send the email (or at least, try subsequent hooks), or
 * false to skip sending.
 */
var sendHooks = [];
EmailTest.hookSend = function (f) {
  sendHooks.push(f);
};

// Old comment below
/**
 * Send an email.
 *
 * Connects to the mail server configured via the MAIL_URL environment
 * variable. If unset, prints formatted message to stdout. The "from" option
 * is required, and at least one of "to", "cc", and "bcc" must be provided;
 * all other options are optional.
 *
 * @param options
 * @param options.from {String} RFC5322 "From:" address
 * @param options.to {String|String[]} RFC5322 "To:" address[es]
 * @param options.cc {String|String[]} RFC5322 "Cc:" address[es]
 * @param options.bcc {String|String[]} RFC5322 "Bcc:" address[es]
 * @param options.replyTo {String|String[]} RFC5322 "Reply-To:" address[es]
 * @param options.subject {String} RFC5322 "Subject:" line
 * @param options.text {String} RFC5322 mail body (plain text)
 * @param options.html {String} RFC5322 mail body (HTML)
 * @param options.headers {Object} custom RFC5322 headers (dictionary)
 */

// New API doc comment below
/**
 * @summary Send an email. Throws an `Error` on failure to contact mail server
 * or if mail server returns an error. All fields should match
 * [RFC5322](http://tools.ietf.org/html/rfc5322) specification.
 *
 * If the `MAIL_URL` environment variable is set, actually sends the email.
 * Otherwise, prints the contents of the email to standard out.
 *
 * Note that this package is based on mailcomposer version `0.1.15`, so make
 * sure to refer to the documentation for that version if using the
 * `attachments` or `mailComposer` options.
 * [Click here to read the mailcomposer 0.1.15 docs](https://github.com/andris9/mailcomposer/blob/7c0422b2de2dc61a60ba27cfa3353472f662aeb5/README.md).
 *
 * @locus Server
 * @param {Object} options
 * @param {String} options.from "From:" address (required)
 * @param {String|String[]} options.to,cc,bcc,replyTo
 *   "To:", "Cc:", "Bcc:", and "Reply-To:" addresses
 * @param {String} [options.subject]  "Subject:" line
 * @param {String} [options.text|html] Mail body (in plain text and/or HTML)
 * @param {Object} [options.headers] Dictionary of custom headers
 * @param {Object[]} [options.attachments] Array of attachment objects, as
 * described in the [mailcomposer documentation](https://github.com/andris9/mailcomposer/blob/7c0422b2de2dc61a60ba27cfa3353472f662aeb5/README.md#add-attachments).
 * @param {MailComposer} [options.mailComposer] A [MailComposer](https://github.com/andris9/mailcomposer)
 * object representing the message to be sent. Overrides all other options. You
 * can access the `mailcomposer` npm module at
 * `EmailInternals.NpmModules.mailcomposer.module`.
 */
Email.send = function (options) {
  for (var i = 0; i < sendHooks.length; i++)
    if (! sendHooks[i](options))
      return;

  var mc;
  if (options.mailComposer) {
    mc = options.mailComposer;
  } else {
    mc = new MailComposer();

    // setup message data
    mc.setMessageOption({
      from: options.from,
      to: options.to,
      cc: options.cc,
      bcc: options.bcc,
      replyTo: options.replyTo,
      subject: options.subject,
      text: options.text,
      html: options.html
    });

    _.each(options.headers, function (value, name) {
      mc.addHeader(name, value);
    });

    _.each(options.attachments, function(attachment){
      mc.addAttachment(attachment);
    });
  }

  var pool = getPool();
  if (pool) {
    smtpSend(pool, mc);
  } else {
    devModeSend(mc);
  }
};
<commit_msg>use secureConnection if smtps: or 465<commit_after>var Future = Npm.require('fibers/future');
var urlModule = Npm.require('url');

Email = {};
EmailTest = {};

EmailInternals = {
  NpmModules: {
    mailcomposer: {
      version: Npm.require('mailcomposer/package.json').version,
      module: Npm.require('mailcomposer')
    }
  }
};

var MailComposer = EmailInternals.NpmModules.mailcomposer.module.MailComposer;

var makePool = function (mailUrlString) {
  var mailUrl = urlModule.parse(mailUrlString);
  if (mailUrl.protocol !== 'smtp:' && mailUrl.protocol !== 'smtps:')
    throw new Error("Email protocol in $MAIL_URL (" +
                    mailUrlString + ") must be 'smtp' or 'smtps'");

  var port = +(mailUrl.port);
  var auth = false;
  if (mailUrl.auth) {
    var parts = mailUrl.auth.split(':', 2);
    auth = {user: parts[0] && decodeURIComponent(parts[0]),
            pass: parts[1] && decodeURIComponent(parts[1])};
  }

  var simplesmtp = Npm.require('simplesmtp');
  var pool = simplesmtp.createClientPool(
    port,  // Defaults to 25
    mailUrl.hostname,  // Defaults to "localhost"
    { secureConnection: (port === 465) || (mailUrl.protocol === 'smtps:'),
      // XXX allow maxConnections to be configured?
      auth: auth });

  pool._future_wrapped_sendMail = _.bind(Future.wrap(pool.sendMail), pool);
  return pool;
};

var getPool = function() {
  // We delay this check until the first call to Email.send, in case someone
  // set process.env.MAIL_URL in startup code. Then we store in a cache until
  // process.env.MAIL_URL changes.
  var url = process.env.MAIL_URL;
  if (this.cacheKey === undefined || this.cacheKey !== url) {
    this.cacheKey = url;
    this.cache = url ? makePool(url) : null;
  }
  return this.cache;
}

var next_devmode_mail_id = 0;
var output_stream = process.stdout;

// Testing hooks
EmailTest.overrideOutputStream = function (stream) {
  next_devmode_mail_id = 0;
  output_stream = stream;
};

EmailTest.restoreOutputStream = function () {
  output_stream = process.stdout;
};

var devModeSend = function (mc) {
  var devmode_mail_id = next_devmode_mail_id++;

  var stream = output_stream;

  // This approach does not prevent other writers to stdout from interleaving.
  stream.write("====== BEGIN MAIL #" + devmode_mail_id + " ======\n");
  stream.write("(Mail not sent; to enable sending, set the MAIL_URL " +
               "environment variable.)\n");
  mc.streamMessage();
  mc.pipe(stream, {end: false});
  var future = new Future;
  mc.on('end', function () {
    stream.write("====== END MAIL #" + devmode_mail_id + " ======\n");
    future['return']();
  });
  future.wait();
};

var smtpSend = function (pool, mc) {
  pool._future_wrapped_sendMail(mc).wait();
};

/**
 * Mock out email sending (eg, during a test.) This is private for now.
 *
 * f receives the arguments to Email.send and should return true to go
 * ahead and send the email (or at least, try subsequent hooks), or
 * false to skip sending.
 */
var sendHooks = [];
EmailTest.hookSend = function (f) {
  sendHooks.push(f);
};

// Old comment below
/**
 * Send an email.
 *
 * Connects to the mail server configured via the MAIL_URL environment
 * variable. If unset, prints formatted message to stdout. The "from" option
 * is required, and at least one of "to", "cc", and "bcc" must be provided;
 * all other options are optional.
 *
 * @param options
 * @param options.from {String} RFC5322 "From:" address
 * @param options.to {String|String[]} RFC5322 "To:" address[es]
 * @param options.cc {String|String[]} RFC5322 "Cc:" address[es]
 * @param options.bcc {String|String[]} RFC5322 "Bcc:" address[es]
 * @param options.replyTo {String|String[]} RFC5322 "Reply-To:" address[es]
 * @param options.subject {String} RFC5322 "Subject:" line
 * @param options.text {String} RFC5322 mail body (plain text)
 * @param options.html {String} RFC5322 mail body (HTML)
 * @param options.headers {Object} custom RFC5322 headers (dictionary)
 */

// New API doc comment below
/**
 * @summary Send an email. Throws an `Error` on failure to contact mail server
 * or if mail server returns an error. All fields should match
 * [RFC5322](http://tools.ietf.org/html/rfc5322) specification.
 *
 * If the `MAIL_URL` environment variable is set, actually sends the email.
 * Otherwise, prints the contents of the email to standard out.
 *
 * Note that this package is based on mailcomposer version `0.1.15`, so make
 * sure to refer to the documentation for that version if using the
 * `attachments` or `mailComposer` options.
 * [Click here to read the mailcomposer 0.1.15 docs](https://github.com/andris9/mailcomposer/blob/7c0422b2de2dc61a60ba27cfa3353472f662aeb5/README.md).
 *
 * @locus Server
 * @param {Object} options
 * @param {String} options.from "From:" address (required)
 * @param {String|String[]} options.to,cc,bcc,replyTo
 *   "To:", "Cc:", "Bcc:", and "Reply-To:" addresses
 * @param {String} [options.subject]  "Subject:" line
 * @param {String} [options.text|html] Mail body (in plain text and/or HTML)
 * @param {Object} [options.headers] Dictionary of custom headers
 * @param {Object[]} [options.attachments] Array of attachment objects, as
 * described in the [mailcomposer documentation](https://github.com/andris9/mailcomposer/blob/7c0422b2de2dc61a60ba27cfa3353472f662aeb5/README.md#add-attachments).
 * @param {MailComposer} [options.mailComposer] A [MailComposer](https://github.com/andris9/mailcomposer)
 * object representing the message to be sent. Overrides all other options. You
 * can access the `mailcomposer` npm module at
 * `EmailInternals.NpmModules.mailcomposer.module`.
 */
Email.send = function (options) {
  for (var i = 0; i < sendHooks.length; i++)
    if (! sendHooks[i](options))
      return;

  var mc;
  if (options.mailComposer) {
    mc = options.mailComposer;
  } else {
    mc = new MailComposer();

    // setup message data
    mc.setMessageOption({
      from: options.from,
      to: options.to,
      cc: options.cc,
      bcc: options.bcc,
      replyTo: options.replyTo,
      subject: options.subject,
      text: options.text,
      html: options.html
    });

    _.each(options.headers, function (value, name) {
      mc.addHeader(name, value);
    });

    _.each(options.attachments, function(attachment){
      mc.addAttachment(attachment);
    });
  }

  var pool = getPool();
  if (pool) {
    smtpSend(pool, mc);
  } else {
    devModeSend(mc);
  }
};
<|endoftext|>


<commit_before>'use strict';

var app = angular.module('myApp.brands.directives', []);

app.directive('userBrand', ['Brand', 'BrandName', 'User', '$routeParams', '$location', '$rootScope', 'Auth', '$pusher', 'showErrors', 'showToast', '$mdDialog', 'gettextCatalog', function(Brand, BrandName, User, $routeParams, $location, $rootScope, Auth, $pusher, showErrors, showToast, $mdDialog, gettextCatalog) {

  var link = function(scope) {

    var brand_id;
    scope.brandName = BrandName;
    scope.user      = {};
    // scope.user      = Auth.currentUser();
    scope.brand     = { creating: true, network_location: 'eu-west' };
    scope.locations = ['eu-west', 'us-central', 'us-west', 'asia-east'];

    var getUser = function() {
      User.query({id: $routeParams.id}).$promise.then(function (res) {
        scope.user = res;
        init();
      });
    };

    var init = function() {
      Brand.get(
        {
          id: scope.user.brand_id
        }
      ).$promise.then(function(results) {
        scope.brand           = results;
        scope.loading         = undefined;
        scope.originalUrl     = scope.brand.url;
        scope.brandName.name  = scope.brand.brand_name;
        subscribe();
      }, function(err) {
        scope.loading = undefined;
      });
    };

    scope.save = function(form) {
      form.$setPristine();
      if (scope.brand.id) {
        confirmChange();
      } else {
        create();
      }
    };

    var create = function() {
      Brand.create({
        brand: {
          cname: scope.brand.cname,
          brand_image: scope.brand.brand_image,
          brand_name: scope.brandName.name,
          url: scope.brand.url
        }
      }).$promise.then(function(results) {
        scope.brand = results;
        showToast(gettextCatalog.getString('Successfully updated brand'));
        switchBrand();
      }, function(err) {
        showErrors(err);
      });
    };

    var confirmChange = function() {
      var confirm = $mdDialog.confirm()
      .title(gettextCatalog.getString('Change Brand?'))
      .textContent(gettextCatalog.getString('Please resync all your boxes after updating your brand.'))
      .ariaLabel(gettextCatalog.getString('Change'))
      .ok(gettextCatalog.getString('Change'))
      .cancel(gettextCatalog.getString('Cancel'));
      $mdDialog.show(confirm).then(function() {
        scope.update();
      }, function() {
      });
    };

    scope.update = function() {
      Brand.update(
        {
          id: scope.brand.id,
          brand:
            {
              brand_name:         scope.brandName.name,
              url:                scope.brand.url,
              cname:              scope.brand.cname,
              brand_image:        scope.brand.brand_image,
              remove_image:       scope.brand.remove_image,
              from_email:         scope.brand.from_email,
              website:            scope.brand.website,
              from_name:          scope.brand.from_name,
              network_location:   scope.brand.network_location
            }
        }).$promise.then(function(results) {
          scope.brand       = results;
          scope.errors      = undefined;
          scope.updating    = undefined;
          scope.updateBrand = undefined;
          if (scope.brand.url !== scope.originalUrl) {
            switchBrand();
          } else {
            showToast(gettextCatalog.getString('Successfully updated brand'));
          }
        }, function(err) {
          showErrors(err);
        });
    };

    function subscribe() {
      if (typeof client !== 'undefined' && scope.subscribed === undefined) {
        scope.subscribe   = true;
        var pusher        = $pusher(client);
        var channel       = pusher.subscribe(scope.user.key);
        channel.bind('general', function(data) {
          if (data.type === 'updated_cname') {
            scope.brand.cname_status = undefined;
            showToast(gettextCatalog.getString('Updated CNAME, please login to finalise changes.'));
          }
        });
      }
    }

    var switchBrand = function() {
      var search;
      var path        = $location.path();
      if (scope.user) {
        scope.user.url = scope.brand.url;
      }
      var loginEvent  = 'login';
      var loginArgs   = {data: scope.user, path: path, search: search};
      $rootScope.$broadcast(loginEvent, loginArgs);
    };

    // scope.deleteImage = function() {
    //   scope.brand.brand_image = null;
    //   scope.brand.remove_image = true;
    // };

    getUser();

  };

  return {
    link: link,
    scope: {
      loading: '='
    },
    templateUrl: 'components/users/branding/_form.html'
  };

}]);
<commit_msg>Removes comments from brands.dir<commit_after>'use strict';

var app = angular.module('myApp.brands.directives', []);

app.directive('userBrand', ['Brand', 'BrandName', 'User', '$routeParams', '$location', '$rootScope', 'Auth', '$pusher', 'showErrors', 'showToast', '$mdDialog', 'gettextCatalog', function(Brand, BrandName, User, $routeParams, $location, $rootScope, Auth, $pusher, showErrors, showToast, $mdDialog, gettextCatalog) {

  var link = function(scope) {

    var brand_id;
    scope.brandName = BrandName;
    scope.user      = {};
    scope.brand     = { creating: true, network_location: 'eu-west' };
    scope.locations = ['eu-west', 'us-central', 'us-west', 'asia-east'];

    var init = function() {
      User.query({id: $routeParams.id}).$promise.then(function (res) {
        scope.user = res;
        getBrand();
      });
    };

    var getBrand = function() {
      Brand.get(
        {
          id: scope.user.brand_id
        }
      ).$promise.then(function(results) {
        scope.brand           = results;
        scope.loading         = undefined;
        scope.originalUrl     = scope.brand.url;
        scope.brandName.name  = scope.brand.brand_name;
        subscribe();
      }, function(err) {
        scope.loading = undefined;
      });
    };

    scope.save = function(form) {
      form.$setPristine();
      if (scope.brand.id) {
        confirmChange();
      } else {
        create();
      }
    };

    var create = function() {
      Brand.create({
        brand: {
          cname: scope.brand.cname,
          brand_image: scope.brand.brand_image,
          brand_name: scope.brandName.name,
          url: scope.brand.url
        }
      }).$promise.then(function(results) {
        scope.brand = results;
        showToast(gettextCatalog.getString('Successfully updated brand'));
        switchBrand();
      }, function(err) {
        showErrors(err);
      });
    };

    var confirmChange = function() {
      var confirm = $mdDialog.confirm()
      .title(gettextCatalog.getString('Change Brand?'))
      .textContent(gettextCatalog.getString('Please resync all your boxes after updating your brand.'))
      .ariaLabel(gettextCatalog.getString('Change'))
      .ok(gettextCatalog.getString('Change'))
      .cancel(gettextCatalog.getString('Cancel'));
      $mdDialog.show(confirm).then(function() {
        scope.update();
      }, function() {
      });
    };

    scope.update = function() {
      Brand.update(
        {
          id: scope.brand.id,
          brand:
            {
              brand_name:         scope.brandName.name,
              url:                scope.brand.url,
              cname:              scope.brand.cname,
              brand_image:        scope.brand.brand_image,
              remove_image:       scope.brand.remove_image,
              from_email:         scope.brand.from_email,
              website:            scope.brand.website,
              from_name:          scope.brand.from_name,
              network_location:   scope.brand.network_location
            }
        }).$promise.then(function(results) {
          scope.brand       = results;
          scope.errors      = undefined;
          scope.updating    = undefined;
          scope.updateBrand = undefined;
          if (scope.brand.url !== scope.originalUrl) {
            switchBrand();
          } else {
            showToast(gettextCatalog.getString('Successfully updated brand'));
          }
        }, function(err) {
          showErrors(err);
        });
    };

    function subscribe() {
      if (typeof client !== 'undefined' && scope.subscribed === undefined) {
        scope.subscribe   = true;
        var pusher        = $pusher(client);
        var channel       = pusher.subscribe(scope.user.key);
        channel.bind('general', function(data) {
          if (data.type === 'updated_cname') {
            scope.brand.cname_status = undefined;
            showToast(gettextCatalog.getString('Updated CNAME, please login to finalise changes.'));
          }
        });
      }
    }

    var switchBrand = function() {
      var search;
      var path        = $location.path();
      if (scope.user) {
        scope.user.url = scope.brand.url;
      }
      var loginEvent  = 'login';
      var loginArgs   = {data: scope.user, path: path, search: search};
      $rootScope.$broadcast(loginEvent, loginArgs);
    };

    init();
  };

  return {
    link: link,
    scope: {
      loading: '='
    },
    templateUrl: 'components/users/branding/_form.html'
  };

}]);
<|endoftext|>


<commit_before>'use strict';

var app = angular.module('myApp.social.directives', []);

app.directive('showSocial', ['Social', '$routeParams', '$timeout', '$location', function(Social, $routeParams, $timeout, $location) {

  var link = function(scope) {

    scope.page      = $routeParams.page;
    scope.client    = { id: $routeParams.client_id };
    scope.location  = { slug: $routeParams.id };

    var init = function() {
      Social.query({id: $routeParams.social_id}).$promise.then(function(results) {
        scope.social     = results.social;
        scope.clients    = results.clients;
        scope.locations  = results.locations;
        scope.loading    = undefined;
      }, function(err) {
        scope.loading = undefined;
      });
    };

    scope.update = function() {
      scope.social.state = 'updating';
      Social.update({id: $routeParams.id, social: { notes: scope.social.notes }}).$promise.then(function(results) {
        scope.social.notes  = results.social.notes;
        scope.social.state  = 'updated';
      }, function(err) {
        scope.social.errors  = 'There was a problem updating this user.';
        scope.social.state  = undefined;
      });
    };

    scope.back = function() {
      window.location.href = '/#/locations/' + scope.location.slug + '/clients/' + scope.client.id;
    };

    init();

  };

  return {
    link: link,
    scope: {
      loading: '='
    },
    templateUrl: 'components/reports/social/_show.html'
  };

}]);

app.directive('listSocial', ['Social', '$routeParams', '$location', 'Location', function(Social, $routeParams, $location, Location) {

  var link = function(scope,element,attrs,controller) {

    scope.loading         = true;
    scope.location_name   = $routeParams.location_name;
    scope.location_id     = $routeParams.location_id;

    scope.init = function() {
      Social.get({page: scope.page, location_id: scope.location_id}).$promise.then(function(results) {
        scope.test = results;
        scope.socials     = results.social;
        scope.loading     = undefined;
        scope.predicate   = '-created_at';
        scope._links      = results._links;
      }, function(err) {
        scope.loading = undefined;
      });
    };

    scope.icon = function() {
      var hash = {};
    };

    scope.updatePage = function(page) {
      scope.page      = scope._links.current_page;
      var hash        = {};
      hash.q          = scope.query;
      hash.activated  = scope.activated;
      hash.page       = scope.page;
      $location.search(hash);
      scope.init();
    };

    function appendSearch () {
      var hash = $location.search();
      hash.q = undefined;
      $location.search(hash);
      scope.init();
    }

    scope.clearQuery = function() {
      scope.query = undefined;
      scope.searching_ct = undefined;
      appendSearch();
    };

    scope.locationSearch = function(val) {
      return Location.query({q: val, size: 10}).$promise.then(function (res) {
        scope.locations = res.locations;
      });
    };

    scope.selectLocation = function(item) {
      controller.selectLocation(item);
      scope.location_name   = item.location_name;
    };

    scope.clearLocations = function() {
      controller.clearLocations();
    };

    scope.init();

  };

  return {
    link: link,
    scope: {},
    require: '^analytics',
    template:
      '
'+
      '
'+
      '
' +
      '
Loading...
'+
      '
'+
      '
' +
      '
'+
      '
'+
      '
'+
      '
'+
      '
Nothing to be seen.
'+
      '
' +
      '
'+
      '
' +
      '
' +
      '	'+
      'User	'+
      'Last Seen	'+
      'Checkins	'+
      'Locations	'+
      '
' +
      '
'+
      ''+
      ''+
      ''+
      ''+
      ''+
      ''+
      '	'+
      ''+
      '{{ social.firstName }} {{ social.lastName }}
'+
      '  {{ social.currentLocation || "Unknown"}}'+
      '	'+
      ''+
      '
{{ social.updated_at | humanTime }}
'+
      'Created: {{ social.created_at | humanTime }}
'+
      '	'+
      '{{ social.checkins }}	'+
      '{{ social.location_ids.length }}	'+
      '
' +
      '
'+
      ' 1">'+
      '<pagination ng-click="updatePage()" total-items="_links.total_entries" page="_links.current_page" max-size="5" class="pagination-sm" boundary-links="false" rotate="false" num-pages="_links.total_pages" items-per-page="100"></pagination>'+
      ''+
      '
' +
      '
' +
      '
' +
      '
' +
      '
' +
      '
'
  };

}]);
<commit_msg>deleted list social<commit_after>'use strict';

var app = angular.module('myApp.social.directives', []);

app.directive('showSocial', ['Social', '$routeParams', '$timeout', '$location', function(Social, $routeParams, $timeout, $location) {

  var link = function(scope) {

    scope.page      = $routeParams.page;
    scope.client    = { id: $routeParams.client_id };
    scope.location  = { slug: $routeParams.id };

    var init = function() {
      Social.query({id: $routeParams.social_id}).$promise.then(function(results) {
        scope.social     = results.social;
        scope.clients    = results.clients;
        scope.locations  = results.locations;
        scope.loading    = undefined;
      }, function(err) {
        scope.loading = undefined;
      });
    };

    scope.update = function() {
      scope.social.state = 'updating';
      Social.update({id: $routeParams.id, social: { notes: scope.social.notes }}).$promise.then(function(results) {
        scope.social.notes  = results.social.notes;
        scope.social.state  = 'updated';
      }, function(err) {
        scope.social.errors  = 'There was a problem updating this user.';
        scope.social.state  = undefined;
      });
    };

    scope.back = function() {
      window.location.href = '/#/locations/' + scope.location.slug + '/clients/' + scope.client.id;
    };

    init();

  };

  return {
    link: link,
    scope: {
      loading: '='
    },
    templateUrl: 'components/reports/social/_show.html'
  };

}]);
<|endoftext|>


<commit_before>import { logoutInactiveUsers, workspaceTimeoutInMinutes } from '../localconf';
import getReduxStore from '../reduxStore';
import { fetchUser, fetchUserNoRefresh } from '../actions';

/* eslint-disable class-methods-use-this */
export class SessionMonitor {
  constructor(updateSessionTime, inactiveTimeLimit) {
    this.updateSessionTime = updateSessionTime || 5 * 60 * 1000;
    this.inactiveTimeLimit = inactiveTimeLimit || 30 * 60 * 1000;
    this.inactiveWorkspaceTimeLimit = Math.min(workspaceTimeoutInMinutes, 480) * 60 * 1000;
    this.mostRecentActivityTimestamp = Date.now();
    this.interval = null;
    this.popupShown = false;
    this.fetchUserAttemptCounter = 0;
  }

  start() {
    if (this.interval) { // interval already started
      return;
    }
    window.addEventListener('mousedown', () => this.updateUserActivity(), false);
    window.addEventListener('keypress', () => this.updateUserActivity(), false);
    this.interval = setInterval(
      () => this.updateSession(),
      this.updateSessionTime,
    ); // check session every X min
  }

  stop() {
    if (this.interval) {
      clearInterval(this.interval);
      window.removeEventListener('mousedown', this.updateUserActivity(), false);
      window.removeEventListener('keypress', this.updateUserActivity(), false);
    }
  }

  updateUserActivity() {
    this.mostRecentActivityTimestamp = Date.now();
  }

  pageFromURL(currentURL) {
    const paths = currentURL.split('/').filter(x => x !== 'dev.html' && x !== '');
    return paths[paths.length - 1];
  }

  isUserOnPage(pageName) {
    return this.pageFromURL(window.location.href) === pageName;
  }

  updateSession() {
    if (this.isUserOnPage('login')) {
      return Promise.resolve(0);
    }

    const timeSinceLastActivity = Date.now() - this.mostRecentActivityTimestamp;
    // If user has been inactive for Y min, and they are not in a workspace
    if (timeSinceLastActivity >= this.inactiveTimeLimit
        && !this.isUserOnPage('workspace')
        && logoutInactiveUsers) {
      // Allow Fence to log out the user. If we don't refresh, Fence will mark them as inactive.
      this.notifyUserIfTheyAreNotLoggedIn();
      return Promise.resolve(0);
    }

    // If the user has been inactive for this.inactiveWorkspaceTimeLimit minutes
    // and they *are* in a workspace
    if (timeSinceLastActivity >= this.inactiveWorkspaceTimeLimit
        && this.isUserOnPage('workspace')
        && logoutInactiveUsers) {
      this.notifyUserIfTheyAreNotLoggedIn();
      return Promise.resolve(0);
    }

    return this.refreshSession();
  }

  refreshSession() {
    if (this.isUserOnPage('login')) {
      return Promise.resolve(0);
    }
    // hitting Fence endpoint refreshes token
    return getReduxStore().then((store) => {
      store.dispatch(fetchUser).then((response) => {
        this.fetchUserAttemptCounter = 0;
        if (response.type === 'UPDATE_POPUP') {
          this.popupShown = true;
        }
      }).catch(() => {
        this.fetchUserAttemptCounter += 1;
        // try again after 5 seconds only retry 9 times
        // stop after 9 times as this will be triggered again after updateSessionTime
        if (this.fetchUserAttemptCounter < 10) {
          setTimeout(() => this.refreshSession(), 5 * 1000);
        } else {
          this.notifyUserIfTheyAreNotLoggedIn();
        }
      });
    });
  }

  notifyUserIfTheyAreNotLoggedIn() {
    /* If a logged-out user is browsing a page with ProtectedContent, this code will
     * display the popup that informs them their session has expired.
     * This function is similar to refreshSession() in that it checks user
     * auth (401/403 vs 200), but it does not refresh
     * the access token nor extend the session.
     */
    if (this.popupShown) {
      return;
    }

    getReduxStore().then((store) => {
      store.dispatch(fetchUserNoRefresh).then((response) => {
        if (response.type === 'UPDATE_POPUP') {
          this.popupShown = true;
        }
      });
    });
  }
}

const singleton = new SessionMonitor();
export default singleton;
<commit_msg>fix/workspaceLogou: remove unneeded looping of retrying user api calls<commit_after>import { logoutInactiveUsers, workspaceTimeoutInMinutes } from '../localconf';
import getReduxStore from '../reduxStore';
import { fetchUser, fetchUserNoRefresh } from '../actions';

/* eslint-disable class-methods-use-this */
export class SessionMonitor {
  constructor(updateSessionTime, inactiveTimeLimit) {
    this.updateSessionTime = updateSessionTime || 5 * 60 * 1000;
    this.inactiveTimeLimit = inactiveTimeLimit || 30 * 60 * 1000;
    this.inactiveWorkspaceTimeLimit = Math.min(workspaceTimeoutInMinutes, 480) * 60 * 1000;
    this.mostRecentActivityTimestamp = Date.now();
    this.interval = null;
    this.popupShown = false;
  }

  start() {
    if (this.interval) { // interval already started
      return;
    }
    window.addEventListener('mousedown', () => this.updateUserActivity(), false);
    window.addEventListener('keypress', () => this.updateUserActivity(), false);
    this.interval = setInterval(
      () => this.updateSession(),
      this.updateSessionTime,
    ); // check session every X min
  }

  stop() {
    if (this.interval) {
      clearInterval(this.interval);
      window.removeEventListener('mousedown', this.updateUserActivity(), false);
      window.removeEventListener('keypress', this.updateUserActivity(), false);
    }
  }

  updateUserActivity() {
    this.mostRecentActivityTimestamp = Date.now();
  }

  pageFromURL(currentURL) {
    const paths = currentURL.split('/').filter(x => x !== 'dev.html' && x !== '');
    return paths[paths.length - 1];
  }

  isUserOnPage(pageName) {
    return this.pageFromURL(window.location.href) === pageName;
  }

  updateSession() {
    if (this.isUserOnPage('login')) {
      return Promise.resolve(0);
    }

    const timeSinceLastActivity = Date.now() - this.mostRecentActivityTimestamp;
    // If user has been inactive for Y min, and they are not in a workspace
    if (timeSinceLastActivity >= this.inactiveTimeLimit
        && !this.isUserOnPage('workspace')
        && logoutInactiveUsers) {
      // Allow Fence to log out the user. If we don't refresh, Fence will mark them as inactive.
      this.notifyUserIfTheyAreNotLoggedIn();
      return Promise.resolve(0);
    }

    // If the user has been inactive for this.inactiveWorkspaceTimeLimit minutes
    // and they *are* in a workspace
    if (timeSinceLastActivity >= this.inactiveWorkspaceTimeLimit
        && this.isUserOnPage('workspace')
        && logoutInactiveUsers) {
      this.notifyUserIfTheyAreNotLoggedIn();
      return Promise.resolve(0);
    }

    return this.refreshSession();
  }

  refreshSession() {
    if (this.isUserOnPage('login')) {
      return Promise.resolve(0);
    }
    // hitting Fence endpoint refreshes token
    return getReduxStore().then((store) => {
      store.dispatch(fetchUser).then((response) => {
        if (response.type === 'UPDATE_POPUP') {
          this.popupShown = true;
        }
      }).catch(() => {
        // if API failed check if user is still logged in
        this.notifyUserIfTheyAreNotLoggedIn();
      });
    });
  }

  notifyUserIfTheyAreNotLoggedIn() {
    /* If a logged-out user is browsing a page with ProtectedContent, this code will
     * display the popup that informs them their session has expired.
     * This function is similar to refreshSession() in that it checks user
     * auth (401/403 vs 200), but it does not refresh
     * the access token nor extend the session.
     */
    if (this.popupShown) {
      return;
    }

    getReduxStore().then((store) => {
      store.dispatch(fetchUserNoRefresh).then((response) => {
        if (response.type === 'UPDATE_POPUP') {
          this.popupShown = true;
        }
      });
    });
  }
}

const singleton = new SessionMonitor();
export default singleton;
<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React, { Component } from 'react'

import { Button, Col, Row, Table, Overlay, Popover, FormGroup } from 'react-bootstrap'
import { Classes, Icon } from '@blueprintjs/core'
import { IconNames } from '@blueprintjs/icons'
import classNames from 'classnames'

import {Person, Position} from 'models'
import LinkTo from 'components/LinkTo'
import UltimatePagination from 'components/UltimatePagination'
import './AttendeesMultiSelect.css'

import { Field } from 'formik'
import { renderInputField } from 'components/FieldHelper'

import API from 'api'
import _debounce from 'lodash/debounce'

const AttendeesTable = (props) => {
	const { attendees, addItem } = props
	return (

					Name
					Position
					Location
					Organization

				{Person.map(attendees, person => {
					return

							<button
								type="button"
								className={classNames(Classes.BUTTON)}
								title="Add attendee"
								onClick={() => addItem(person)}
							>
								<Icon icon={IconNames.ADD} />
							</button>

							<LinkTo person={person}/>

						<LinkTo position={person.position} />{person.position && person.position.code ? `, ${person.position.code}`: ``}
						<LinkTo whenUnspecified="" anetLocation={person.position && person.position.location} />
						{person.position && person.position.organization && <LinkTo organization={person.position.organization} />}

				})}

	)
}

export default class AttendeesMultiSelect extends Component {
	static propTypes = {
		addFieldName: PropTypes.string.isRequired, // name of the autocomplete field
		addFieldLabel: PropTypes.string, // label of the autocomplete field
		selectedItems: PropTypes.array.isRequired,
		renderSelected: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired, // how to render the selected items
		onAddItem: PropTypes.func.isRequired,
		onRemoveItem: PropTypes.func,
		filterDefs: PropTypes.object,
		renderExtraCol: PropTypes.bool, // set to false if you want this column completely removed
		addon: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),

		//Required: ANET Object Type (Person, Report, etc) to search for.
		objectType: PropTypes.func.isRequired,
		//Optional: Parameters to pass to search function.
		queryParams: PropTypes.object,
		//Optional: GraphQL string of fields to return from search.
		fields: PropTypes.string,
		currentUser: PropTypes.instanceOf(Person),
	}

	static defaultProps = {
		addFieldLabel: 'Add item',
		filterDefs: {},
		renderExtraCol: true,
	}

	state = {
		searchTerms: '',
		filterType: Object.keys(this.props.filterDefs)[0], // per default use the first filter
		results: {},
		showOverlay: false,
		inputFocused: false,
	}

	componentDidUpdate(prevProps, prevState) {
		if (prevProps.selectedItems !== this.props.selectedItems) {
			// Update the list of filter results to consider the already selected items
			this.fetchResults()
		}
	}

	handleInputFocus = () => {
		if (this.state.inputFocused === true) {
			return
		}
		this.setState({
			inputFocused: true,
			showOverlay: true,
		})
	}

	handleInputBlur = () => {
		this.setState({
			inputFocused: false,
		})
	}

	handleHideOverlay = () => {
		if (this.state.inputFocused) {
			return
		}
		this.setState({
			showOverlay: false
		})
	}

	render() {
		const {addFieldName, addFieldLabel, renderSelected, selectedItems, onAddItem, onRemoveItem, filterDefs, renderExtraCol, addon, ...autocompleteProps} = this.props
		const { results, filterType } = this.state
		const renderSelectedWithDelete = React.cloneElement(renderSelected, {onDelete: this.removeItem})
		const attendees = results && results[filterType] ? results[filterType].list : []
		return (
			<React.Fragment>
				<Field
					name={addFieldName}
					label={addFieldLabel}
					component={renderInputField}
					value={this.state.searchTerms}
					onChange={this.changeSearchTerms}
					onFocus={this.handleInputFocus}
					onBlur={this.handleInputBlur}
					innerRef={el => {this.overlayTarget = el}}
				/>
				<Overlay
					show={this.state.showOverlay}
					container={this.overlayContainer}
					target={this.overlayTarget}
					rootClose={true}
					onHide={this.handleHideOverlay}
					placement="bottom"
					animation={false}
					delayHide={200}
				>
					<Popover id={addFieldName} title={null} placement="bottom" style={{width: '100%', maxWidth: '100%'}}>
						<Row className="border-between">
							<Col sm={3}>

									{Object.keys(filterDefs).map(filterType =>
										<Button key={filterType} style={{textAlign: 'right'}} bsStyle="link" onClick={() => this.changeFilterType(filterType)}>{filterDefs[filterType].label}</Button>
									)}

							</Col>
							<Col sm={9}>
								{this.paginationFor(this.state.filterType)}
								<AttendeesTable
									attendees={attendees}
									addItem={this.addItem}
								/>
							</Col>
						</Row>
					</Popover>
				</Overlay>
				<Row>
					<Col sm={9} className="form-group" ref={el => {this.overlayContainer = el}} style={{position: 'relative'}} />
					<Col sm={3} />
				</Row>
				<Row>
					<Col sm={2} />
					<Col sm={7}>{renderSelectedWithDelete}</Col>
					<Col sm={3} />
				</Row>
			</React.Fragment>
		)
	}

	changeSearchTerms = (event) => {
		this.setState({searchTerms: event.target.value}, () => this.fetchResultsDebounced(0))
	}

	changeFilterType = (filterType) => {
		this.setState({filterType}, () => this.fetchResults(0))
	}

	_getSelectedItemsUuids = () => {
		const {selectedItems} = this.props
		if (Array.isArray(selectedItems)) {
			return selectedItems.map(object => object.uuid)
		}
		return []
	}

	filterItems = (items) => {
		const excludedUuids =  this._getSelectedItemsUuids()
		if (excludedUuids) {
			items = items.filter(suggestion => suggestion && suggestion.uuid && excludedUuids.indexOf(suggestion.uuid) === -1)
		}
		return items
	}

	fetchResults = (pageNum) => {
		const { filterType, results } = this.state
		if (pageNum === undefined) {
			pageNum = results && results[filterType] ? results[filterType].pageNum : 0
		}
		const filterDefs = this.props.filterDefs[filterType]
		const resourceName = this.props.objectType.resourceName
		const listName = filterDefs.listName || this.props.objectType.listName
		if (filterDefs.searchQuery) {
			// GraphQL search type of query
			let graphQlQuery = listName + ' (query: $query) { '
			+ 'pageNum, pageSize, totalCount, list { ' + this.props.fields + '}'
			+ '}'
			const variableDef = '($query: ' + resourceName + 'SearchQueryInput)'
			let queryVars = {pageNum: pageNum, pageSize: 6}
			if (this.props.queryParams) {
				Object.assign(queryVars, this.props.queryParams)
			}
			if (filterDefs.queryVars) {
				Object.assign(queryVars, filterDefs.queryVars)
			}
			if (this.state.searchTerms) {
				Object.assign(queryVars, {text: this.state.searchTerms + "*"})
			}
			API.query(graphQlQuery, {query: queryVars}, variableDef).then(data => {
				data[listName].list = this.filterItems(data[listName].list)
				this.setState({
					results: {
						...results,
						[filterType]: data[listName]
					}
				})
			})
		}
		else {
			API.query(/* GraphQL */`
					` + listName + `(` + filterDefs.listArgs + `) {
				pageNum, pageSize, totalCount, list { ` + this.props.fields + ` }
					}`
			).then(data => {
				data[listName].list = this.filterItems(data[listName].list)
				this.setState({
					results: {
						...results,
						[filterType]: data[listName]
					}
				})
			})
		}
	}

	fetchResultsDebounced = _debounce(this.fetchResults, 200)

	addItem = (newItem) => {
		if (!newItem || !newItem.uuid) {
			return
		}
		if (!this.props.selectedItems.find(obj => obj.uuid === newItem.uuid)) {
			this.props.onAddItem(newItem)
		}
	}

	removeItem = (oldItem) => {
		if (this.props.selectedItems.find(obj => obj.uuid === oldItem.uuid)) {
			this.props.onRemoveItem(oldItem)
		}
	}

	paginationFor = (filterType) => {
		const {results} = this.state
		const pageSize = results && results[filterType] ? results[filterType].pageSize : 6
		const pageNum = results && results[filterType] ? results[filterType].pageNum : 0
		const totalCount = results && results[filterType] ? results[filterType].totalCount : 0
		const numPages = (pageSize <= 0) ? 1 : Math.ceil(totalCount / pageSize)
		if (numPages <= 1) { return }
		return
			<UltimatePagination
				className="pull-right"
				currentPage={pageNum + 1}
				totalPages={numPages}
				boundaryPagesRange={1}
				siblingPagesRange={2}
				hideEllipsis={false}
				hidePreviousAndNextPageLinks={false}
				hideFirstAndLastPageLinks={true}
				onChange={(value) => this.goToPage(value - 1)}
			/>

	}

	goToPage = (pageNum) => {
		this.fetchResults(pageNum)
	}
}
<commit_msg>NCI-Agency/anet#255: Add bottom pagination to attendees overlay<commit_after>import PropTypes from 'prop-types'
import React, { Component } from 'react'

import { Button, Col, Row, Table, Overlay, Popover, FormGroup } from 'react-bootstrap'
import { Classes, Icon } from '@blueprintjs/core'
import { IconNames } from '@blueprintjs/icons'
import classNames from 'classnames'

import {Person, Position} from 'models'
import LinkTo from 'components/LinkTo'
import UltimatePagination from 'components/UltimatePagination'
import './AttendeesMultiSelect.css'

import { Field } from 'formik'
import { renderInputField } from 'components/FieldHelper'

import API from 'api'
import _debounce from 'lodash/debounce'

const AttendeesTable = (props) => {
	const { attendees, addItem } = props
	return (

					Name
					Position
					Location
					Organization

				{Person.map(attendees, person => {
					return

							<button
								type="button"
								className={classNames(Classes.BUTTON)}
								title="Add attendee"
								onClick={() => addItem(person)}
							>
								<Icon icon={IconNames.ADD} />
							</button>

							<LinkTo person={person}/>

						<LinkTo position={person.position} />{person.position && person.position.code ? `, ${person.position.code}`: ``}
						<LinkTo whenUnspecified="" anetLocation={person.position && person.position.location} />
						{person.position && person.position.organization && <LinkTo organization={person.position.organization} />}

				})}

	)
}

export default class AttendeesMultiSelect extends Component {
	static propTypes = {
		addFieldName: PropTypes.string.isRequired, // name of the autocomplete field
		addFieldLabel: PropTypes.string, // label of the autocomplete field
		selectedItems: PropTypes.array.isRequired,
		renderSelected: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired, // how to render the selected items
		onAddItem: PropTypes.func.isRequired,
		onRemoveItem: PropTypes.func,
		filterDefs: PropTypes.object,
		renderExtraCol: PropTypes.bool, // set to false if you want this column completely removed
		addon: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),

		//Required: ANET Object Type (Person, Report, etc) to search for.
		objectType: PropTypes.func.isRequired,
		//Optional: Parameters to pass to search function.
		queryParams: PropTypes.object,
		//Optional: GraphQL string of fields to return from search.
		fields: PropTypes.string,
		currentUser: PropTypes.instanceOf(Person),
	}

	static defaultProps = {
		addFieldLabel: 'Add item',
		filterDefs: {},
		renderExtraCol: true,
	}

	state = {
		searchTerms: '',
		filterType: Object.keys(this.props.filterDefs)[0], // per default use the first filter
		results: {},
		showOverlay: false,
		inputFocused: false,
	}

	componentDidUpdate(prevProps, prevState) {
		if (prevProps.selectedItems !== this.props.selectedItems) {
			// Update the list of filter results to consider the already selected items
			this.fetchResults()
		}
	}

	handleInputFocus = () => {
		if (this.state.inputFocused === true) {
			return
		}
		this.setState({
			inputFocused: true,
			showOverlay: true,
		})
	}

	handleInputBlur = () => {
		this.setState({
			inputFocused: false,
		})
	}

	handleHideOverlay = () => {
		if (this.state.inputFocused) {
			return
		}
		this.setState({
			showOverlay: false
		})
	}

	render() {
		const {addFieldName, addFieldLabel, renderSelected, selectedItems, onAddItem, onRemoveItem, filterDefs, renderExtraCol, addon, ...autocompleteProps} = this.props
		const { results, filterType } = this.state
		const renderSelectedWithDelete = React.cloneElement(renderSelected, {onDelete: this.removeItem})
		const attendees = results && results[filterType] ? results[filterType].list : []
		return (
			<React.Fragment>
				<Field
					name={addFieldName}
					label={addFieldLabel}
					component={renderInputField}
					value={this.state.searchTerms}
					onChange={this.changeSearchTerms}
					onFocus={this.handleInputFocus}
					onBlur={this.handleInputBlur}
					innerRef={el => {this.overlayTarget = el}}
				/>
				<Overlay
					show={this.state.showOverlay}
					container={this.overlayContainer}
					target={this.overlayTarget}
					rootClose={true}
					onHide={this.handleHideOverlay}
					placement="bottom"
					animation={false}
					delayHide={200}
				>
					<Popover id={addFieldName} title={null} placement="bottom" style={{width: '100%', maxWidth: '100%'}}>
						<Row className="border-between">
							<Col sm={3}>

									{Object.keys(filterDefs).map(filterType =>
										<Button key={filterType} style={{textAlign: 'right'}} bsStyle="link" onClick={() => this.changeFilterType(filterType)}>{filterDefs[filterType].label}</Button>
									)}

							</Col>
							<Col sm={9}>

									{this.paginationFor(this.state.filterType)}

								<AttendeesTable
									attendees={attendees}
									addItem={this.addItem}
								/>

									{this.paginationFor(this.state.filterType)}

							</Col>
						</Row>
					</Popover>
				</Overlay>
				<Row>
					<Col sm={9} className="form-group" ref={el => {this.overlayContainer = el}} style={{position: 'relative'}} />
					<Col sm={3} />
				</Row>
				<Row>
					<Col sm={2} />
					<Col sm={7}>{renderSelectedWithDelete}</Col>
					<Col sm={3} />
				</Row>
			</React.Fragment>
		)
	}

	changeSearchTerms = (event) => {
		this.setState({searchTerms: event.target.value}, () => this.fetchResultsDebounced(0))
	}

	changeFilterType = (filterType) => {
		this.setState({filterType}, () => this.fetchResults(0))
	}

	_getSelectedItemsUuids = () => {
		const {selectedItems} = this.props
		if (Array.isArray(selectedItems)) {
			return selectedItems.map(object => object.uuid)
		}
		return []
	}

	filterItems = (items) => {
		const excludedUuids =  this._getSelectedItemsUuids()
		if (excludedUuids) {
			items = items.filter(suggestion => suggestion && suggestion.uuid && excludedUuids.indexOf(suggestion.uuid) === -1)
		}
		return items
	}

	fetchResults = (pageNum) => {
		const { filterType, results } = this.state
		if (pageNum === undefined) {
			pageNum = results && results[filterType] ? results[filterType].pageNum : 0
		}
		const filterDefs = this.props.filterDefs[filterType]
		const resourceName = this.props.objectType.resourceName
		const listName = filterDefs.listName || this.props.objectType.listName
		if (filterDefs.searchQuery) {
			// GraphQL search type of query
			let graphQlQuery = listName + ' (query: $query) { '
			+ 'pageNum, pageSize, totalCount, list { ' + this.props.fields + '}'
			+ '}'
			const variableDef = '($query: ' + resourceName + 'SearchQueryInput)'
			let queryVars = {pageNum: pageNum, pageSize: 6}
			if (this.props.queryParams) {
				Object.assign(queryVars, this.props.queryParams)
			}
			if (filterDefs.queryVars) {
				Object.assign(queryVars, filterDefs.queryVars)
			}
			if (this.state.searchTerms) {
				Object.assign(queryVars, {text: this.state.searchTerms + "*"})
			}
			API.query(graphQlQuery, {query: queryVars}, variableDef).then(data => {
				data[listName].list = this.filterItems(data[listName].list)
				this.setState({
					results: {
						...results,
						[filterType]: data[listName]
					}
				})
			})
		}
		else {
			API.query(/* GraphQL */`
					` + listName + `(` + filterDefs.listArgs + `) {
				pageNum, pageSize, totalCount, list { ` + this.props.fields + ` }
					}`
			).then(data => {
				data[listName].list = this.filterItems(data[listName].list)
				this.setState({
					results: {
						...results,
						[filterType]: data[listName]
					}
				})
			})
		}
	}

	fetchResultsDebounced = _debounce(this.fetchResults, 200)

	addItem = (newItem) => {
		if (!newItem || !newItem.uuid) {
			return
		}
		if (!this.props.selectedItems.find(obj => obj.uuid === newItem.uuid)) {
			this.props.onAddItem(newItem)
		}
	}

	removeItem = (oldItem) => {
		if (this.props.selectedItems.find(obj => obj.uuid === oldItem.uuid)) {
			this.props.onRemoveItem(oldItem)
		}
	}

	paginationFor = (filterType) => {
		const {results} = this.state
		const pageSize = results && results[filterType] ? results[filterType].pageSize : 6
		const pageNum = results && results[filterType] ? results[filterType].pageNum : 0
		const totalCount = results && results[filterType] ? results[filterType].totalCount : 0
		const numPages = (pageSize <= 0) ? 1 : Math.ceil(totalCount / pageSize)
		if (numPages <= 1) { return }
		return <UltimatePagination
			className="pull-right"
			currentPage={pageNum + 1}
			totalPages={numPages}
			boundaryPagesRange={1}
			siblingPagesRange={2}
			hideEllipsis={false}
			hidePreviousAndNextPageLinks={false}
			hideFirstAndLastPageLinks={true}
			onChange={(value) => this.goToPage(value - 1)}
		/>
	}

	goToPage = (pageNum) => {
		this.fetchResults(pageNum)
	}
}
<|endoftext|>


<commit_before>// ==UserScript==
// @name          UDBraaains
// @namespace     http://code.google.com/p/udbraaains/
// @description   Reports and downloads metadata for Urban Dead
// @include       http://*urbandead.com/map.cgi*
// @require       http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js
// ==/UserScript==

(function ($) {

   function appendScript(source, loadHandler) {
   	var head = document.getElementsByTagName('head')[0] || document.documentElement;
   	var script = document.createElement('script');

   	script.src = source;
   	script.type = 'text/javascript';
   	script.addEventListener('load', loadHandler, false);
   	head.appendChild(script);
   }
   function reportData (data, reporturl) {
      	GM_xmlhttpRequest({
      		method: 'POST',
      //		url: 'http://www.alloscomp.com/udbrain/api2.php'+qs,
      //		url: 'http://127.0.0.1:8080/udb'+qs,
      		url: 'http://udbrains.kimihia.org.nz:50609/udb'+data[1],
      		headers: {
      			"Accept": "text/html",
      			"Content-type": "application/x-www-form-urlencoded"
      		},
      		data: encodeURI(data[0]),
      		onload: function(xhr) {
      			// Debugging: print response
      			//alert(xhr.responseText);

      			// If Error, throw alert box
                  if(xhr.responseText.match(/Error:/))
                      alert('Sorry, something is broken :(');

      			// Begin to parse the response data
      			var arr = xhr.responseText.split('|');
      			for(var i=0; i < arr.length; i++) {
      				// Build and exec regexps
      				var version_matches = arr[i].match(/^v(.*)/); // Contains version info?
      				var alert_matches = arr[i].match(/^(Alert: .*?)ENDALERT/); // Is an alert?

      				// Add a div with alert text
      				if(alert_matches)
      					unsafeWindow.divAdd(alert_matches[1]);

      				// Check to see if upgrade is needed
      				else if(version_matches)
      					unsafeWindow.checkVersion(version_matches[1]);

      				else if (arr[i].match(/^S:/))
      					unsafeWindow.colorSurvivor(arr[i].split(':'));
      				else if (arr[i].match(/^T:/))
      					unsafeWindow.processTastyData(arr[i].split(':'));
      				else if (arr[i].match(/^N:/))
      					unsafeWindow.processNews(arr[i]);
      				// Handle the report
      				else
      					unsafeWindow.displayData(arr[i]);
      			}
      		}
      	});

   }

   appendScript('http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js', function () {
      appendScript('http://localhost:8000/udbraaains.js', function () {
         var data = unsafeWindow.exchangeData();
         reportData(data);
      });
   });

})(jQuery);<commit_msg>Changed URL for the script injection to point at github<commit_after>// ==UserScript==
// @name          UDBraaains
// @namespace     http://code.google.com/p/udbraaains/
// @description   Reports and downloads metadata for Urban Dead
// @include       http://*urbandead.com/map.cgi*
// @require       http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js
// ==/UserScript==

(function ($) {

   function appendScript(source, loadHandler) {
   	var head = document.getElementsByTagName('head')[0] || document.documentElement;
   	var script = document.createElement('script');

   	script.src = source;
   	script.type = 'text/javascript';
   	script.addEventListener('load', loadHandler, false);
   	head.appendChild(script);
   }
   function reportData (data, reporturl) {
      	GM_xmlhttpRequest({
      		method: 'POST',
      //		url: 'http://www.alloscomp.com/udbrain/api2.php'+qs,
      //		url: 'http://127.0.0.1:8080/udb'+qs,
      		url: 'http://udbrains.kimihia.org.nz:50609/udb'+data[1],
      		headers: {
      			"Accept": "text/html",
      			"Content-type": "application/x-www-form-urlencoded"
      		},
      		data: encodeURI(data[0]),
      		onload: function(xhr) {
      			// Debugging: print response
      			//alert(xhr.responseText);

      			// If Error, throw alert box
                  if(xhr.responseText.match(/Error:/))
                      alert('Sorry, something is broken :(');

      			// Begin to parse the response data
      			var arr = xhr.responseText.split('|');
      			for(var i=0; i < arr.length; i++) {
      				// Build and exec regexps
      				var version_matches = arr[i].match(/^v(.*)/); // Contains version info?
      				var alert_matches = arr[i].match(/^(Alert: .*?)ENDALERT/); // Is an alert?

      				// Add a div with alert text
      				if(alert_matches)
      					unsafeWindow.divAdd(alert_matches[1]);

      				// Check to see if upgrade is needed
      				else if(version_matches)
      					unsafeWindow.checkVersion(version_matches[1]);

      				else if (arr[i].match(/^S:/))
      					unsafeWindow.colorSurvivor(arr[i].split(':'));
      				else if (arr[i].match(/^T:/))
      					unsafeWindow.processTastyData(arr[i].split(':'));
      				else if (arr[i].match(/^N:/))
      					unsafeWindow.processNews(arr[i]);
      				// Handle the report
      				else
      					unsafeWindow.displayData(arr[i]);
      			}
      		}
      	});

   }

   appendScript('http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js', function () {
      // appendScript('http://localhost:8000/udbraaains.js', function () {
      appendScript('https://github.com/underisk/udbraaains/raw/master/udbraaains.js', function () {
         var data = unsafeWindow.exchangeData();
         reportData(data);
      });
   });

})(jQuery);<|endoftext|>