<commit_before>const Plugin = require('../Plugin')
const Translator = require('../../core/Translator')
const dragDrop = require('drag-drop')
const Dashboard = require('./Dashboard')
const StatusBar = require('../StatusBar')
const Informer = require('../Informer')
const { findAllDOMElements } = require('../../core/Utils')
const prettyBytes = require('prettier-bytes')
const { defaultTabIcon } = require('./icons')

/**
 * Modal Dialog & Dashboard
 */
module.exports = class DashboardUI extends Plugin {
  constructor (core, opts) {
    super(core, opts)
    this.id = 'Dashboard'
    this.title = 'Dashboard'
    this.type = 'orchestrator'

    const defaultLocale = {
      strings: {
        selectToUpload: 'Select files to upload',
        closeModal: 'Close Modal',
        upload: 'Upload',
        importFrom: 'Import files from',
        dashboardWindowTitle: 'Uppy Dashboard Window (Press escape to close)',
        dashboardTitle: 'Uppy Dashboard',
        copyLinkToClipboardSuccess: 'Link copied to clipboard.',
        copyLinkToClipboardFallback: 'Copy the URL below',
        fileSource: 'File source',
        done: 'Done',
        localDisk: 'Local Disk',
        myDevice: 'My Device',
        dropPasteImport: 'Drop files here, paste, import from one of the locations above or',
        dropPaste: 'Drop files here, paste or',
        browse: 'browse',
        fileProgress: 'File progress: upload speed and ETA',
        numberOfSelectedFiles: 'Number of selected files',
        uploadAllNewFiles: 'Upload all new files'
      }
    }

    // set default options
    const defaultOptions = {
      target: 'body',
      getMetaFromForm: true,
      trigger: '#uppy-select-files',
      inline: false,
      width: 750,
      height: 550,
      semiTransparent: false,
      defaultTabIcon: defaultTabIcon(),
      showProgressDetails: false,
      hideUploadButton: false,
      note: null,
      closeModalOnClickOutside: false,
      locale: defaultLocale,
      onRequestCloseModal: () => this.closeModal()
    }

    // merge default options with the ones set by user
    this.opts = Object.assign({}, defaultOptions, opts)

    this.locale = Object.assign({}, defaultLocale, this.opts.locale)
    this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings)

    this.translator = new Translator({locale: this.locale})
    this.containerWidth = this.translator.translate.bind(this.translator)

    this.closeModal = this.closeModal.bind(this)
    this.requestCloseModal = this.requestCloseModal.bind(this)
    this.openModal = this.openModal.bind(this)
    this.isModalOpen = this.isModalOpen.bind(this)

    this.addTarget = this.addTarget.bind(this)
    this.actions = this.actions.bind(this)
    this.hideAllPanels = this.hideAllPanels.bind(this)
    this.showPanel = this.showPanel.bind(this)
    this.initEvents = this.initEvents.bind(this)
    this.handleEscapeKeyPress = this.handleEscapeKeyPress.bind(this)
    this.handleClickOutside = this.handleClickOutside.bind(this)
    this.handleFileCard = this.handleFileCard.bind(this)
    this.handleDrop = this.handleDrop.bind(this)
    this.pauseAll = this.pauseAll.bind(this)
    this.resumeAll = this.resumeAll.bind(this)
    this.cancelAll = this.cancelAll.bind(this)
    this.updateDashboardElWidth = this.updateDashboardElWidth.bind(this)
    this.render = this.render.bind(this)
    this.install = this.install.bind(this)
  }

  addTarget (plugin) {
    const callerPluginId = plugin.id || plugin.constructor.name
    const callerPluginName = plugin.title || callerPluginId
    const callerPluginIcon = plugin.icon || this.opts.defaultTabIcon
    const callerPluginType = plugin.type

    if (callerPluginType !== 'acquirer' &&
        callerPluginType !== 'progressindicator' &&
        callerPluginType !== 'presenter') {
      let msg = 'Dashboard: Modal can only be used by plugins of types: acquirer, progressindicator, presenter'
      this.core.log(msg)
      return
    }

    const target = {
      id: callerPluginId,
      name: callerPluginName,
      icon: callerPluginIcon,
      type: callerPluginType,
      render: plugin.render,
      isHidden: true
    }

    const modal = this.core.getState().modal
    const newTargets = modal.targets.slice()
    newTargets.push(target)

    this.core.setState({
      modal: Object.assign({}, modal, {
        targets: newTargets
      })
    })

    return this.target
  }

  hideAllPanels () {
    const modal = this.core.getState().modal

    this.core.setState({modal: Object.assign({}, modal, {
      activePanel: false
    })})
  }

  showPanel (id) {
    const modal = this.core.getState().modal

    const activePanel = modal.targets.filter((target) => {
      return target.type === 'acquirer' && target.id === id
    })[0]

    this.core.setState({modal: Object.assign({}, modal, {
      activePanel: activePanel
    })})
  }

  requestCloseModal () {
    if (this.opts.onRequestCloseModal) {
      return this.opts.onRequestCloseModal()
    } else {
      this.closeModal()
    }
  }

  openModal () {
    const modal = this.core.getState().modal

    this.core.setState({
      modal: Object.assign({}, modal, {
        isHidden: false
      })
    })

    // save scroll position
    this.savedDocumentScrollPosition = window.scrollY

    // add class to body that sets position fixed, move everything back
    // to scroll position
    document.body.classList.add('is-UppyDashboard-open')
    document.body.style.top = `-${this.savedDocumentScrollPosition}px`

    // focus on modal inner block
    this.target.querySelector('.UppyDashboard-inner').focus()

    // this.updateDashboardElWidth()
    // to be sure, sometimes when the function runs, container size is still 0
    setTimeout(this.updateDashboardElWidth, 500)
  }

  closeModal () {
    const modal = this.core.getState().modal

    this.core.setState({
      modal: Object.assign({}, modal, {
        isHidden: true
      })
    })

    document.body.classList.remove('is-UppyDashboard-open')

    window.scrollTo(0, this.savedDocumentScrollPosition)
  }

  isModalOpen () {
    return !this.core.getState().modal.isHidden || false
  }

  // Close the Modal on esc key press
  handleEscapeKeyPress (event) {
    if (event.keyCode === 27) {
      this.requestCloseModal()
    }
  }

  handleClickOutside () {
    if (this.opts.closeModalOnClickOutside) this.requestCloseModal()
  }

  initEvents () {
    // Modal open button
    const showModalTrigger = findAllDOMElements(this.opts.trigger)
    if (!this.opts.inline && showModalTrigger) {
      showModalTrigger.forEach(trigger => trigger.addEventListener('click', this.openModal))
    }

    if (!this.opts.inline && !showModalTrigger) {
      this.core.log('Dashboard modal trigger not found, you won’t be able to select files. Make sure `trigger` is set correctly in Dashboard options', 'error')
    }

    document.body.addEventListener('keyup', this.handleEscapeKeyPress)

    // Drag Drop
    this.removeDragDropListener = dragDrop(this.el, (files) => {
      this.handleDrop(files)
    })
  }

  removeEvents () {
    const showModalTrigger = findAllDOMElements(this.opts.trigger)
    if (!this.opts.inline && showModalTrigger) {
      showModalTrigger.forEach(trigger => trigger.removeEventListener('click', this.openModal))
    }

    this.removeDragDropListener()
    document.body.removeEventListener('keyup', this.handleEscapeKeyPress)
  }

  actions () {
    this.core.on('core:file-added', this.hideAllPanels)
    this.core.on('dashboard:file-card', this.handleFileCard)

    window.addEventListener('resize', this.updateDashboardElWidth)
  }

  removeActions () {
    window.removeEventListener('resize', this.updateDashboardElWidth)

    this.core.off('core:file-added', this.hideAllPanels)
    this.core.off('dashboard:file-card', this.handleFileCard)
  }

  updateDashboardElWidth () {
    const dashboardEl = this.target.querySelector('.UppyDashboard-inner')
    this.core.log(`Dashboard width: ${dashboardEl.offsetWidth}`)

    const modal = this.core.getState().modal
    this.core.setState({
      modal: Object.assign({}, modal, {
        containerWidth: dashboardEl.offsetWidth
      })
    })
  }

  handleFileCard (fileId) {
    const modal = this.core.state.modal

    this.core.setState({
      modal: Object.assign({}, modal, {
        fileCardFor: fileId || false
      })
    })
  }

  handleDrop (files) {
    this.core.log('[Dashboard] Files were dropped')

    files.forEach((file) => {
      this.core.addFile({
        source: this.id,
        name: file.name,
        type: file.type,
        data: file
      })
    })
  }

  cancelAll () {
    this.core.emit('core:cancel-all')
  }

  pauseAll () {
    this.core.emit('core:pause-all')
  }

  resumeAll () {
    this.core.emit('core:resume-all')
  }

  render (state) {
    const files = state.files

    const newFiles = Object.keys(files).filter((file) => {
      return !files[file].progress.uploadStarted
    })
    const inProgressFiles = Object.keys(files).filter((file) => {
      return !files[file].progress.uploadComplete &&
             files[file].progress.uploadStarted &&
             !files[file].isPaused
    })

    let inProgressFilesArray = []
    inProgressFiles.forEach((file) => {
      inProgressFilesArray.push(files[file])
    })

    let totalSize = 0
    let totalUploadedSize = 0
    inProgressFilesArray.forEach((file) => {
      totalSize = totalSize + (file.progress.bytesTotal || 0)
      totalUploadedSize = totalUploadedSize + (file.progress.bytesUploaded || 0)
    })
    totalSize = prettyBytes(totalSize)
    totalUploadedSize = prettyBytes(totalUploadedSize)

    const acquirers = state.modal.targets.filter((target) => {
      return target.type === 'acquirer'
    })

    const progressindicators = state.modal.targets.filter((target) => {
      return target.type === 'progressindicator'
    })

    const startUpload = (ev) => {
      this.core.upload().catch((err) => {
        // Log error.
        this.core.log(err.stack || err.message || err)
      })
    }

    const pauseUpload = (fileID) => {
      this.core.emit('core:upload-pause', fileID)
    }

    const cancelUpload = (fileID) => {
      this.core.emit('core:upload-cancel', fileID)
      this.core.emit('core:file-remove', fileID)
    }

    const showFileCard = (fileID) => {
      this.core.emit('dashboard:file-card', fileID)
    }

    const fileCardDone = (meta, fileID) => {
      this.core.emit('core:update-meta', meta, fileID)
      this.core.emit('dashboard:file-card')
    }

    return Dashboard({
      state: state,
      modal: state.modal,
      newFiles: newFiles,
      files: files,
      totalFileCount: Object.keys(files).length,
      totalProgress: state.totalProgress,
      acquirers: acquirers,
      activePanel: state.modal.activePanel,
      progressindicators: progressindicators,
      autoProceed: this.core.opts.autoProceed,
      hideUploadButton: this.opts.hideUploadButton,
      id: this.id,
      closeModal: this.requestCloseModal,
      handleClickOutside: this.handleClickOutside,
      showProgressDetails: this.opts.showProgressDetails,
      inline: this.opts.inline,
      semiTransparent: this.opts.semiTransparent,
      showPanel: this.showPanel,
      hideAllPanels: this.hideAllPanels,
      log: this.core.log,
      i18n: this.containerWidth,
      pauseAll: this.pauseAll,
      resumeAll: this.resumeAll,
      addFile: this.core.addFile,
      removeFile: this.core.removeFile,
      info: this.core.info,
      note: this.opts.note,
      metaFields: state.metaFields,
      resumableUploads: this.core.state.capabilities.resumableUploads || false,
      startUpload: startUpload,
      pauseUpload: pauseUpload,
      cancelUpload: cancelUpload,
      fileCardFor: state.modal.fileCardFor,
      showFileCard: showFileCard,
      fileCardDone: fileCardDone,
      updateDashboardElWidth: this.updateDashboardElWidth,
      maxWidth: this.opts.maxWidth,
      maxHeight: this.opts.maxHeight,
      currentWidth: state.modal.containerWidth,
      isWide: state.modal.containerWidth > 400
    })
  }

  discoverProviderPlugins () {
    this.core.iteratePlugins((plugin) => {
      if (plugin && !plugin.target && plugin.opts && plugin.opts.target === this.constructor) {
        this.addTarget(plugin)
      }
    })
  }

  install () {
    // Set default state for Modal
    this.core.setState({modal: {
      isHidden: true,
      showFileCard: false,
      activePanel: false,
      targets: []
    }})

    const target = this.opts.target

    if (target) {
      this.mount(target, this)
    }

    const plugins = this.opts.plugins || []
    plugins.forEach((pluginID) => {
      const plugin = this.core.getPlugin(pluginID)
      if (plugin) plugin.mount(this, plugin)
    })

    if (!this.opts.disableStatusBar) {
      this.core.use(StatusBar, {
        target: this
      })
    }

    if (!this.opts.disableInformer) {
      this.core.use(Informer, {
        target: this
      })
    }

    this.discoverProviderPlugins()

    this.initEvents()
    this.actions()
  }

  uninstall () {
    if (!this.opts.disableInformer) {
      const informer = this.core.getPlugin('Informer')
      if (informer) this.core.removePlugin(informer)
    }

    if (!this.opts.disableStatusBar) {
      const statusBar = this.core.getPlugin('StatusBar')
      // Checking if this plugin exists, in case it was removed by uppy-core
      // before the Dashboard was.
      if (statusBar) this.core.removePlugin(statusBar)
    }

    const plugins = this.opts.plugins || []
    plugins.forEach((pluginID) => {
      const plugin = this.core.getPlugin(pluginID)
      if (plugin) plugin.unmount()
    })

    this.unmount()
    this.removeActions()
    this.removeEvents()
  }
}
<commit_msg>dashboard: Use `get/setPluginState` for modal related state<commit_after>const Plugin = require('../Plugin')
const Translator = require('../../core/Translator')
const dragDrop = require('drag-drop')
const Dashboard = require('./Dashboard')
const StatusBar = require('../StatusBar')
const Informer = require('../Informer')
const { findAllDOMElements } = require('../../core/Utils')
const prettyBytes = require('prettier-bytes')
const { defaultTabIcon } = require('./icons')

/**
 * Modal Dialog & Dashboard
 */
module.exports = class DashboardUI extends Plugin {
  constructor (core, opts) {
    super(core, opts)
    this.id = 'Dashboard'
    this.title = 'Dashboard'
    this.type = 'orchestrator'

    const defaultLocale = {
      strings: {
        selectToUpload: 'Select files to upload',
        closeModal: 'Close Modal',
        upload: 'Upload',
        importFrom: 'Import files from',
        dashboardWindowTitle: 'Uppy Dashboard Window (Press escape to close)',
        dashboardTitle: 'Uppy Dashboard',
        copyLinkToClipboardSuccess: 'Link copied to clipboard.',
        copyLinkToClipboardFallback: 'Copy the URL below',
        fileSource: 'File source',
        done: 'Done',
        localDisk: 'Local Disk',
        myDevice: 'My Device',
        dropPasteImport: 'Drop files here, paste, import from one of the locations above or',
        dropPaste: 'Drop files here, paste or',
        browse: 'browse',
        fileProgress: 'File progress: upload speed and ETA',
        numberOfSelectedFiles: 'Number of selected files',
        uploadAllNewFiles: 'Upload all new files'
      }
    }

    // set default options
    const defaultOptions = {
      target: 'body',
      getMetaFromForm: true,
      trigger: '#uppy-select-files',
      inline: false,
      width: 750,
      height: 550,
      semiTransparent: false,
      defaultTabIcon: defaultTabIcon(),
      showProgressDetails: false,
      hideUploadButton: false,
      note: null,
      closeModalOnClickOutside: false,
      locale: defaultLocale,
      onRequestCloseModal: () => this.closeModal()
    }

    // merge default options with the ones set by user
    this.opts = Object.assign({}, defaultOptions, opts)

    this.locale = Object.assign({}, defaultLocale, this.opts.locale)
    this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings)

    this.translator = new Translator({locale: this.locale})
    this.containerWidth = this.translator.translate.bind(this.translator)

    this.closeModal = this.closeModal.bind(this)
    this.requestCloseModal = this.requestCloseModal.bind(this)
    this.openModal = this.openModal.bind(this)
    this.isModalOpen = this.isModalOpen.bind(this)

    this.addTarget = this.addTarget.bind(this)
    this.actions = this.actions.bind(this)
    this.hideAllPanels = this.hideAllPanels.bind(this)
    this.showPanel = this.showPanel.bind(this)
    this.initEvents = this.initEvents.bind(this)
    this.handleEscapeKeyPress = this.handleEscapeKeyPress.bind(this)
    this.handleClickOutside = this.handleClickOutside.bind(this)
    this.handleFileCard = this.handleFileCard.bind(this)
    this.handleDrop = this.handleDrop.bind(this)
    this.pauseAll = this.pauseAll.bind(this)
    this.resumeAll = this.resumeAll.bind(this)
    this.cancelAll = this.cancelAll.bind(this)
    this.updateDashboardElWidth = this.updateDashboardElWidth.bind(this)
    this.render = this.render.bind(this)
    this.install = this.install.bind(this)
  }

  addTarget (plugin) {
    const callerPluginId = plugin.id || plugin.constructor.name
    const callerPluginName = plugin.title || callerPluginId
    const callerPluginIcon = plugin.icon || this.opts.defaultTabIcon
    const callerPluginType = plugin.type

    if (callerPluginType !== 'acquirer' &&
        callerPluginType !== 'progressindicator' &&
        callerPluginType !== 'presenter') {
      let msg = 'Dashboard: Modal can only be used by plugins of types: acquirer, progressindicator, presenter'
      this.core.log(msg)
      return
    }

    const target = {
      id: callerPluginId,
      name: callerPluginName,
      icon: callerPluginIcon,
      type: callerPluginType,
      render: plugin.render,
      isHidden: true
    }

    const state = this.getPluginState()
    const newTargets = state.targets.slice()
    newTargets.push(target)

    this.setPluginState({
      targets: newTargets
    })

    return this.target
  }

  hideAllPanels () {
    this.setPluginState({
      activePanel: false
    })
  }

  showPanel (id) {
    const { targets } = this.getPluginState()

    const activePanel = targets.filter((target) => {
      return target.type === 'acquirer' && target.id === id
    })[0]

    this.setPluginState({
      activePanel: activePanel
    })
  }

  requestCloseModal () {
    if (this.opts.onRequestCloseModal) {
      return this.opts.onRequestCloseModal()
    } else {
      this.closeModal()
    }
  }

  openModal () {
    this.setPluginState({
      isHidden: false
    })

    // save scroll position
    this.savedDocumentScrollPosition = window.scrollY

    // add class to body that sets position fixed, move everything back
    // to scroll position
    document.body.classList.add('is-UppyDashboard-open')
    document.body.style.top = `-${this.savedDocumentScrollPosition}px`

    // focus on modal inner block
    this.target.querySelector('.UppyDashboard-inner').focus()

    // this.updateDashboardElWidth()
    // to be sure, sometimes when the function runs, container size is still 0
    setTimeout(this.updateDashboardElWidth, 500)
  }

  closeModal () {
    this.setPluginState({
      isHidden: true
    })

    document.body.classList.remove('is-UppyDashboard-open')

    window.scrollTo(0, this.savedDocumentScrollPosition)
  }

  isModalOpen () {
    return !this.getPluginState().isHidden || false
  }

  // Close the Modal on esc key press
  handleEscapeKeyPress (event) {
    if (event.keyCode === 27) {
      this.requestCloseModal()
    }
  }

  handleClickOutside () {
    if (this.opts.closeModalOnClickOutside) this.requestCloseModal()
  }

  initEvents () {
    // Modal open button
    const showModalTrigger = findAllDOMElements(this.opts.trigger)
    if (!this.opts.inline && showModalTrigger) {
      showModalTrigger.forEach(trigger => trigger.addEventListener('click', this.openModal))
    }

    if (!this.opts.inline && !showModalTrigger) {
      this.core.log('Dashboard modal trigger not found, you won’t be able to select files. Make sure `trigger` is set correctly in Dashboard options', 'error')
    }

    document.body.addEventListener('keyup', this.handleEscapeKeyPress)

    // Drag Drop
    this.removeDragDropListener = dragDrop(this.el, (files) => {
      this.handleDrop(files)
    })
  }

  removeEvents () {
    const showModalTrigger = findAllDOMElements(this.opts.trigger)
    if (!this.opts.inline && showModalTrigger) {
      showModalTrigger.forEach(trigger => trigger.removeEventListener('click', this.openModal))
    }

    this.removeDragDropListener()
    document.body.removeEventListener('keyup', this.handleEscapeKeyPress)
  }

  actions () {
    this.core.on('core:file-added', this.hideAllPanels)
    this.core.on('dashboard:file-card', this.handleFileCard)

    window.addEventListener('resize', this.updateDashboardElWidth)
  }

  removeActions () {
    window.removeEventListener('resize', this.updateDashboardElWidth)

    this.core.off('core:file-added', this.hideAllPanels)
    this.core.off('dashboard:file-card', this.handleFileCard)
  }

  updateDashboardElWidth () {
    const dashboardEl = this.target.querySelector('.UppyDashboard-inner')
    this.core.log(`Dashboard width: ${dashboardEl.offsetWidth}`)

    this.setPluginState({
      containerWidth: dashboardEl.offsetWidth
    })
  }

  handleFileCard (fileId) {
    this.setPluginState({
      fileCardFor: fileId || false
    })
  }

  handleDrop (files) {
    this.core.log('[Dashboard] Files were dropped')

    files.forEach((file) => {
      this.core.addFile({
        source: this.id,
        name: file.name,
        type: file.type,
        data: file
      })
    })
  }

  cancelAll () {
    this.core.emit('core:cancel-all')
  }

  pauseAll () {
    this.core.emit('core:pause-all')
  }

  resumeAll () {
    this.core.emit('core:resume-all')
  }

  render (state) {
    const pluginState = this.getPluginState()
    const files = state.files

    const newFiles = Object.keys(files).filter((file) => {
      return !files[file].progress.uploadStarted
    })
    const inProgressFiles = Object.keys(files).filter((file) => {
      return !files[file].progress.uploadComplete &&
             files[file].progress.uploadStarted &&
             !files[file].isPaused
    })

    let inProgressFilesArray = []
    inProgressFiles.forEach((file) => {
      inProgressFilesArray.push(files[file])
    })

    let totalSize = 0
    let totalUploadedSize = 0
    inProgressFilesArray.forEach((file) => {
      totalSize = totalSize + (file.progress.bytesTotal || 0)
      totalUploadedSize = totalUploadedSize + (file.progress.bytesUploaded || 0)
    })
    totalSize = prettyBytes(totalSize)
    totalUploadedSize = prettyBytes(totalUploadedSize)

    const acquirers = pluginState.targets.filter((target) => {
      return target.type === 'acquirer'
    })

    const progressindicators = pluginState.targets.filter((target) => {
      return target.type === 'progressindicator'
    })

    const startUpload = (ev) => {
      this.core.upload().catch((err) => {
        // Log error.
        this.core.log(err.stack || err.message || err)
      })
    }

    const pauseUpload = (fileID) => {
      this.core.emit('core:upload-pause', fileID)
    }

    const cancelUpload = (fileID) => {
      this.core.emit('core:upload-cancel', fileID)
      this.core.emit('core:file-remove', fileID)
    }

    const showFileCard = (fileID) => {
      this.core.emit('dashboard:file-card', fileID)
    }

    const fileCardDone = (meta, fileID) => {
      this.core.emit('core:update-meta', meta, fileID)
      this.core.emit('dashboard:file-card')
    }

    return Dashboard({
      state: state,
      modal: pluginState,
      newFiles: newFiles,
      files: files,
      totalFileCount: Object.keys(files).length,
      totalProgress: state.totalProgress,
      acquirers: acquirers,
      activePanel: pluginState.activePanel,
      progressindicators: progressindicators,
      autoProceed: this.core.opts.autoProceed,
      hideUploadButton: this.opts.hideUploadButton,
      id: this.id,
      closeModal: this.requestCloseModal,
      handleClickOutside: this.handleClickOutside,
      showProgressDetails: this.opts.showProgressDetails,
      inline: this.opts.inline,
      semiTransparent: this.opts.semiTransparent,
      showPanel: this.showPanel,
      hideAllPanels: this.hideAllPanels,
      log: this.core.log,
      i18n: this.containerWidth,
      pauseAll: this.pauseAll,
      resumeAll: this.resumeAll,
      addFile: this.core.addFile,
      removeFile: this.core.removeFile,
      info: this.core.info,
      note: this.opts.note,
      metaFields: state.metaFields,
      resumableUploads: this.core.state.capabilities.resumableUploads || false,
      startUpload: startUpload,
      pauseUpload: pauseUpload,
      cancelUpload: cancelUpload,
      fileCardFor: pluginState.fileCardFor,
      showFileCard: showFileCard,
      fileCardDone: fileCardDone,
      updateDashboardElWidth: this.updateDashboardElWidth,
      maxWidth: this.opts.maxWidth,
      maxHeight: this.opts.maxHeight,
      currentWidth: pluginState.containerWidth,
      isWide: pluginState.containerWidth > 400
    })
  }

  discoverProviderPlugins () {
    this.core.iteratePlugins((plugin) => {
      if (plugin && !plugin.target && plugin.opts && plugin.opts.target === this.constructor) {
        this.addTarget(plugin)
      }
    })
  }

  install () {
    // Set default state for Modal
    this.setPluginState({
      isHidden: true,
      showFileCard: false,
      activePanel: false,
      targets: []
    })

    const target = this.opts.target

    if (target) {
      this.mount(target, this)
    }

    const plugins = this.opts.plugins || []
    plugins.forEach((pluginID) => {
      const plugin = this.core.getPlugin(pluginID)
      if (plugin) plugin.mount(this, plugin)
    })

    if (!this.opts.disableStatusBar) {
      this.core.use(StatusBar, {
        target: this
      })
    }

    if (!this.opts.disableInformer) {
      this.core.use(Informer, {
        target: this
      })
    }

    this.discoverProviderPlugins()

    this.initEvents()
    this.actions()
  }

  uninstall () {
    if (!this.opts.disableInformer) {
      const informer = this.core.getPlugin('Informer')
      if (informer) this.core.removePlugin(informer)
    }

    if (!this.opts.disableStatusBar) {
      const statusBar = this.core.getPlugin('StatusBar')
      // Checking if this plugin exists, in case it was removed by uppy-core
      // before the Dashboard was.
      if (statusBar) this.core.removePlugin(statusBar)
    }

    const plugins = this.opts.plugins || []
    plugins.forEach((pluginID) => {
      const plugin = this.core.getPlugin(pluginID)
      if (plugin) plugin.unmount()
    })

    this.unmount()
    this.removeActions()
    this.removeEvents()
  }
}
<|endoftext|>


<commit_before>'use strict';

angular
.module('report-editor')
.directive('account', function(Session, ACCOUNT_URL){
    return {
        restrict: 'E',
        templateUrl: '/modules/ui/account.html',
        replace: true,
        link: function($scope){
            $scope.user = Session.getUser();
            $scope.name = $scope.user.firstname + ' ' + $scope.user.lastname;
            $scope.accountUrl = ACCOUNT_URL;
            $scope.doLogout = function(){
                Session.logout();
                Session.redirectToLoginPage('/');
            }
        }
    };
});

//

<commit_msg>fixed jshint<commit_after>'use strict';

angular
.module('report-editor')
.directive('account', function(Session, ACCOUNT_URL){
    return {
        restrict: 'E',
        templateUrl: '/modules/ui/account.html',
        replace: true,
        link: function($scope){
            $scope.user = Session.getUser();
            $scope.name = $scope.user.firstname + ' ' + $scope.user.lastname;
            $scope.accountUrl = ACCOUNT_URL;
            $scope.doLogout = function(){
                Session.logout();
                Session.redirectToLoginPage('/');
            };
        }
    };
});<|endoftext|>


<commit_before>var net = require('net');
var EventEmitter = require('events').EventEmitter;
var crypto = require('crypto');
var sys = require('sys');
var fs = require('fs');
var Buffer = require('buffer').Buffer;

var Connection = function (optionArgs) {
	this.socket = new net.Stream();
	this.credentials = crypto.createCredentials();
	this.currentId = 0;
	this.cachedNotes = [];

	var self = this;
	var hasKey = hasCert = false;

	var options =	{ cert: 'cert.pem' /* Certificate file */
					, key:	'key.pem'  /* Key file */
					, gateway: 'gateway.push.apple.com' /* gateway address */
					, port: 2195 /* gateway port */
					, enhanced: false /* enable enhanced format */
					, errorCallback: undefined /* Callback when error occurs */
					, feedback: false /* enable feedback service, set to callback */
					, feedbackInterval: 3600 /* interval in seconds to connect to feedback service */
					, cacheLength: 5 /* Number of notifications to cache for response */
					};

	if (optionArgs) {
		var keys = Object.keys(options);
		for (var i = 0; i < keys.length; i++) {
			var k = keys[i];
			if (optionArgs[k] !== undefined) options[k] = optionArgs[k];
		}
	}

	var startSocket = function () {
		self.socket.connect(options['port'], options['gateway']);
		self.socket.setSecure(self.credentials);
	}

	self.socket.on('connect', function() { console.log("connect."); });
	self.socket.on('data', function(data) { handleTransmissionError(data); });
	self.socket.on('end', function () { console.log('closed'); self.socket.end(); });

	var connect = invoke_after(startSocket);

	fs.readFile(options['cert'], connect(function(err, data) {
		if(err) {
			throw err;
		}
		self.credentials.context.setCert(data.toString());
		hasCert = true;
	}));

	fs.readFile(options['key'], connect(function(err, data) {
		if(err) {
			throw err;
		}
		self.credentials.context.setKey(data.toString());
		hasKey = true;
	}));

	this.sendNotification = function (note) {
		var hexTok = note.device.hexToken();
		var message = JSON.stringify(note.payload);
		var messageLength = Buffer.byteLength(message);
		var pos = 0;

		note._uid = this.currentId++;

		if(options.enhanced) {
			var data = new Buffer(1 + 4 + 4 + 2 + hexTok.length + 2 + messageLength);
			// Command
			data[pos] = 1;
			pos++;

			// Identifier
			pos += data.write(int32val(note._uid), pos, 'binary');

			// Expiry
			pos += data.write(int32val(note.expiry), pos, 'binary');

			self.cachedNotes.push(note);
			tidyCachedNotes();
		}
		else {
			var data = new Buffer(1 + 2 + hexTok.length + 2 + messageLength);
			data[pos] = 0;
			pos++;
		}

		pos += data.write(int16val(hexTok.length), pos, 'binary');
		pos += data.write(hexTok, pos, 'binary');
		pos += data.write(int16val(messageLength), pos, 'binary');
		pos += data.write(message, pos);

		// Need to check notification length at some point
		// If error occurs then slice array and resend all stored notes.

		if(self.socket.readyState != 'open') {
			if(self.socket.readyState == 'closed' && hasKey && hasCert) {
				startSocket();
			}
			self.socket.on('connect',
				function() {
					self.socket.write(data);
					self.socket.removeListener('connect', arguments.callee);
				});
		}
		else {
			self.socket.write(data);
		}
	}

	var tidyCachedNotes = function() {
		// Maybe a timestamp should be stored for each note and kept for a duration?
		if(self.cachedNotes.length > options.cacheLength) {
			self.cachedNotes.shift();
		}
	}

	var handleTransmissionError = function(data) {
		console.log(data);
	}
}

Connection.prototype = new EventEmitter;
Connection.prototype.constructor = Connection;

exports.connection = Connection;

exports.notification = function () {
	this.payload = {aps: {}};
	this.expiry = 0;
	this.identifier = 0;
	this.device;
}

exports.device = function (token) {
	var self = this;
	this.token = token;

	this.hexToken = function() {
		token = self.token.replace(/\s/g, "");
		hexToken = "";
		for(var i=0; i < token.length; i+=2) {
			word = token[i];
			if((i + 1) >= token.length || typeof(token[i+1]) === undefined) {
				word += '0';
			}
			else {
				word += token[i+1];
			}
			hexToken += String.fromCharCode(parseInt(word, 16));
		}
		return hexToken;
	};
}

function int16val(number) {
	return String.fromCharCode(((number >> 8) & 0xff), (number & 0xff));
}

function int32val(number) {
	return String.fromCharCode((number >> 24), ((number >> 16) & 0xff), ((number >> 8) & 0xff), (number & 0xfF));
}

function invoke_after(callback) {
	n = 0;
	return function (delegate) {
		n++;
		return function() {
			delegate.apply(delegate, arguments);
			if(--n == 0) callback();
		};
	};
}<commit_msg>Updated todo<commit_after>var net = require('net');
var EventEmitter = require('events').EventEmitter;
var crypto = require('crypto');
var sys = require('sys');
var fs = require('fs');
var Buffer = require('buffer').Buffer;

var Connection = function (optionArgs) {
	this.socket = new net.Stream();
	this.credentials = crypto.createCredentials();
	this.currentId = 0;
	this.cachedNotes = [];

	var self = this;
	var hasKey = hasCert = false;

	var options =	{ cert: 'cert.pem' /* Certificate file */
					, key:	'key.pem'  /* Key file */
					, gateway: 'gateway.push.apple.com' /* gateway address */
					, port: 2195 /* gateway port */
					, enhanced: false /* enable enhanced format */
					, errorCallback: undefined /* Callback when error occurs */
					, feedback: false /* enable feedback service, set to callback */
					, feedbackInterval: 3600 /* interval in seconds to connect to feedback service */
					, cacheLength: 5 /* Number of notifications to cache for response */
					};

	if (optionArgs) {
		var keys = Object.keys(options);
		for (var i = 0; i < keys.length; i++) {
			var k = keys[i];
			if (optionArgs[k] !== undefined) options[k] = optionArgs[k];
		}
	}

	var startSocket = function () {
		self.socket.connect(options['port'], options['gateway']);
		self.socket.setSecure(self.credentials);
	}

	self.socket.on('connect', function() { console.log("connect."); });
	self.socket.on('data', function(data) { handleTransmissionError(data); });
	self.socket.on('end', function () { console.log('closed'); self.socket.end(); });

	var connect = invoke_after(startSocket);

	fs.readFile(options['cert'], connect(function(err, data) {
		if(err) {
			throw err;
		}
		self.credentials.context.setCert(data.toString());
		hasCert = true;
	}));

	fs.readFile(options['key'], connect(function(err, data) {
		if(err) {
			throw err;
		}
		self.credentials.context.setKey(data.toString());
		hasKey = true;
	}));

	this.sendNotification = function (note) {
		var hexTok = note.device.hexToken();
		var message = JSON.stringify(note.payload);
		var messageLength = Buffer.byteLength(message);
		var pos = 0;

		// Check notification length here. Return non-zero as error

		note._uid = this.currentId++;

		if(options.enhanced) {
			var data = new Buffer(1 + 4 + 4 + 2 + hexTok.length + 2 + messageLength);
			// Command
			data[pos] = 1;
			pos++;

			// Identifier
			pos += data.write(int32val(note._uid), pos, 'binary');

			// Expiry
			pos += data.write(int32val(note.expiry), pos, 'binary');

			self.cachedNotes.push(note);
			tidyCachedNotes();
		}
		else {
			var data = new Buffer(1 + 2 + hexTok.length + 2 + messageLength);
			data[pos] = 0;
			pos++;
		}

		pos += data.write(int16val(hexTok.length), pos, 'binary');
		pos += data.write(hexTok, pos, 'binary');
		pos += data.write(int16val(messageLength), pos, 'binary');
		pos += data.write(message, pos);

		// If error occurs then slice array and resend all stored notes.

		if(self.socket.readyState != 'open') {
			if(self.socket.readyState == 'closed' && hasKey && hasCert) {
				startSocket();
			}
			self.socket.on('connect',
				function() {
					self.socket.write(data);
					self.socket.removeListener('connect', arguments.callee);
				});
		}
		else {
			self.socket.write(data);
		}
	}

	var tidyCachedNotes = function() {
		// Maybe a timestamp should be stored for each note and kept for a duration?
		if(self.cachedNotes.length > options.cacheLength) {
			self.cachedNotes.shift();
		}
	}

	var handleTransmissionError = function(data) {
		console.log(data);
	}
}

Connection.prototype = new EventEmitter;
Connection.prototype.constructor = Connection;

exports.connection = Connection;

exports.notification = function () {
	this.payload = {aps: {}};
	this.expiry = 0;
	this.identifier = 0;
	this.device;
}

exports.device = function (token) {
	var self = this;
	this.token = token;

	this.hexToken = function() {
		token = self.token.replace(/\s/g, "");
		hexToken = "";
		for(var i=0; i < token.length; i+=2) {
			word = token[i];
			if((i + 1) >= token.length || typeof(token[i+1]) === undefined) {
				word += '0';
			}
			else {
				word += token[i+1];
			}
			hexToken += String.fromCharCode(parseInt(word, 16));
		}
		return hexToken;
	};
}

function int16val(number) {
	return String.fromCharCode(((number >> 8) & 0xff), (number & 0xff));
}

function int32val(number) {
	return String.fromCharCode((number >> 24), ((number >> 16) & 0xff), ((number >> 8) & 0xff), (number & 0xfF));
}

function invoke_after(callback) {
	n = 0;
	return function (delegate) {
		n++;
		return function() {
			delegate.apply(delegate, arguments);
			if(--n == 0) callback();
		};
	};
}<|endoftext|>


<commit_before>exports.companies = [
    'ACN',
    'ATVI',
    'ADBE',
    'AMD',
    'AKAM',
    'ADS',
    'GOOGL',
    'GOOG',
    'APH',
    'ADI',
    'ANSS',
    'AAPL',
    'AMAT',
    'ADSK',
    'ADP',
    'AVGO',
    'CA',
    'CSCO',
    'CTXS',
    'CTSH',
    'GLW',
    'CSRA',
    'DXC',
    'EBAY',
    'EA',
    'FFIV',
    'FB',
    'FIS',
    'FISV',
    'FLIR',
    'IT',
    'GPN',
    'HRS',
    'HPE',
    'HPQ',
    'INTC',
    'IBM',
    'INTU',
    'JNPR',
    'KLAC',
    'LRCX',
    'MA',
    'MCHP',
    'MU',
    'MSFT',
    'MSI',
    'NTAP',
    'NVDA',
    'ORCL',
    'PAYX',
    'PYPL',
    'QRVO',
    'QCOM',
    'RHT',
    'CRM',
    'STX',
    'SWKS',
    'SYMC',
    'SNPS',
    'TEL',
    'TXN',
    'TSS',
    'VRSN',
    'V',
    'WDC',
    'WU',
    'XLNX',
    'XRX'
]<commit_msg>added the financial sector to the dashboard<commit_after>exports.companies = [
    //TECH SECTOR
    'ACN',
    'ATVI',
    'ADBE',
    'AMD',
    'AKAM',
    'ADS',
    'GOOGL',
    'GOOG',
    'APH',
    'ADI',
    'ANSS',
    'AAPL',
    'AMAT',
    'ADSK',
    'ADP',
    'AVGO',
    'CA',
    'CSCO',
    'CTXS',
    'CTSH',
    'GLW',
    'CSRA',
    'DXC',
    'EBAY',
    'EA',
    'FFIV',
    'FB',
    'FIS',
    'FISV',
    'FLIR',
    'IT',
    'GPN',
    'HRS',
    'HPE',
    'HPQ',
    'INTC',
    'IBM',
    'INTU',
    'JNPR',
    'KLAC',
    'LRCX',
    'MA',
    'MCHP',
    'MU',
    'MSFT',
    'MSI',
    'NTAP',
    'NVDA',
    'ORCL',
    'PAYX',
    'PYPL',
    'QRVO',
    'QCOM',
    'RHT',
    'CRM',
    'STX',
    'SWKS',
    'SYMC',
    'SNPS',
    'TEL',
    'TXN',
    'TSS',
    'VRSN',
    'V',
    'WDC',
    'WU',
    'XLNX',
    'XRX'
]

exports.financeCompanies =[
    // FINANCIAL SECTOR
    'AMG',
    'AFL',
    'ALL',
    'AXP',
    'AXP',
    'AIG',
    'AMP',
    'AON',
    'AJG',
    'AIZ',
    'BAC',
    'BBT',
    'BRK-B',
    'BLK',
    'HRB',
    'COF',
    'CBOE',
    'SCHW',
    'CB',
    'CINF',
    'C',
    'CFG',
    'CME',
    'CMA',
    'DFS',
    'ETFC',
    'FITB',
    'BEN',
    'GS',
    'HIG',
    'HBAN',
    'ICE',
    'IVZ',
    'JPM',
    'KEY',
    'LUK',
    'LNC',
    'L',
    'MTB',
    'MMC',
    'MET',
    'MCO',
    'MS',
    'NDAQ',
    'NAVI',
    'NTRS',
    'PBCT',
    'PNC',
    'PFG',
    'PGR',
    'PRU',
    'RF',
    'SPGI',
    'STT',
    'STI',
    'SYF',
    'TROW',
    'BK',
    'TRV',
    'TMK',
    'USB',
    'UNM',
    'WFC',
    'WLTW',
    'XL',
    'ZION'
    //END FINANCIAL SECTOR
]

/*
exports.sectorLookup = {
    'ACN' : "TECH",
    'ATVI':"TECH",
    'ADBE' : "TECH",
    'AMD' : "TECH",
    'AKAM' : "TECH",
    'ADS': "TECH",
    'GOOGL': "TECH",
    'GOOG': "TECH",
    'APH': "TECH",
    'ADI': "TECH",
    'ANSS': "TECH",
    'AAPL': "TECH",
    'AMAT': "TECH",
    'ADSK': "TECH",
    'ADP': "TECH",
    'AVGO': "TECH",
    'CA': "TECH",
    'CSCO': "TECH",
    'CTXS': "TECH",
    'CTSH': "TECH",
    'GLW': "TECH",
    'CSRA': "TECH",
    'DXC': "TECH",
    'EBAY': "TECH",
    'EA': "TECH",
    'FFIV': "TECH",
    'FB': "TECH",
    'FIS': "TECH",
    'FISV': "TECH",
    'FLIR': "TECH",
    'IT': "TECH",
    'GPN': "TECH",
    'HRS': "TECH",
    'HPE': "TECH",
    'HPQ': "TECH",
    'INTC': "TECH",
    'IBM': "TECH",
    'INTU': "TECH",
    'JNPR': "TECH",
    'KLAC': "TECH",
    'LRCX': "TECH",
    'MA': "TECH",
    'MCHP': "TECH",
    'MU': "TECH",
    'MSFT': "TECH",
    'MSI': "TECH",
    'NTAP': "TECH",
    'NVDA': "TECH",
    'ORCL': "TECH",
    'PAYX': "TECH",
    'PYPL': "TECH",
    'QRVO': "TECH",
    'QCOM': "TECH",
    'RHT': "TECH",
    'CRM': "TECH",
    'STX': "TECH",
    'SWKS': "TECH",
    'SYMC': "TECH",
    'SNPS': "TECH",
    'TEL': "TECH",
    'TXN': "TECH",
    'TSS': "TECH",
    'VRSN': "TECH",
    'V': "TECH",
    'WDC': "TECH",
    'WU': "TECH",
    'XLNX': "TECH",
    'XRX': "TECH"
}
*/

//INITIALIZING COMPANIES IN TECH SECTOR
/* var sectors = {};

for(var i =0; i <exports.companies.length; i++) {
    sectors[exports.companies[i]] = 'TECH';
}
*/
exports.sectorLookup ={}
for(var i =0; i <exports.companies.length; i++) {
    exports.sectorLookup[exports.companies[i]] = 'TECH';
}

//INITIALIZING COMPANIES IN FINANCIAL SECTOR

for(var i =0; i <exports.financeCompanies.length; i++) {
    exports.sectorLookup[exports.financeCompanies[i]] = 'FINA';
}

//for loop goes here
//test dashboard
var keys = Object.keys(exports.sectorLookup)
 console.log(keys);

 for (var i = 0; i < keys.length; i++) {
     console.log(exports.sectorLookup[keys[i]]);
 }

<|endoftext|>


<commit_before>"use strict";
angular.module('services.xbmc.mock', [])
    .factory('xbmc', ['$rootScope', '$q', '$http', '$parse',
        function ($rootScope, $q, $http, $parse) {
            // We return this object to anything injecting our service
            var factory = {};
            var isConnected = false;
            factory.isConnected = function () {
                return isConnected;
            }

            factory.register = function (method, callback) {

            }

            factory.send = function (method, params, shouldDefer, pathExpr) {
                var defer = $q.defer();
                $http.get('/app/data/' + method + '.json').success(function (data) {
                    var obj = data;
                    if (pathExpr) {
                        var getter = $parse(pathExpr);
                        obj = getter(data);
                    } else {
                        obj = data;
                    }
                    window.setTimeout(function () {
                        $rootScope.$apply(function () {
                            defer.resolve(obj);
                        });
                    }, Math.round(Math.random() * 5000))
                });
                return defer.promise;
            }

            factory.unregister = function (method, callback) {

            }

            factory.connect = function () {
                isConnected = true;
            }

            return factory;
        }
    ])<commit_msg>[Feature] Ability to change seek time<commit_after>"use strict";
angular.module('services.xbmc.mock', [])
    .factory('xbmc', ['$rootScope', '$q', '$http', '$parse',
        function ($rootScope, $q, $http, $parse) {
            // We return this object to anything injecting our service
            var factory = {};
            var isConnected = false;
            factory.isConnected = function () {
                return isConnected;
            }

            factory.register = function (method, callback) {

            }

            factory.send = function (method, params, shouldDefer, pathExpr) {
                var defer = $q.defer();
                $http.get('/js/data/' + method + '.json').success(function (data) {
                    var obj = data;
                    if (pathExpr) {
                        var getter = $parse(pathExpr);
                        obj = getter(data);
                    } else {
                        obj = data;
                    }
                    window.setTimeout(function () {
                        $rootScope.$apply(function () {
                            defer.resolve(obj);
                        });
                    }, Math.round(Math.random() * 1000))
                });
                return defer.promise;
            }

            factory.unregister = function (method, callback) {

            }

            factory.connect = function () {
                isConnected = true;
            }

            return factory;
        }
    ])<|endoftext|>


<commit_before>/**
 Gowalla API wrapper for node.js

 Copyright (c) 2010 Jonathan Spies MIT License

 The Gowalla API is really simple to use but I wanted some syntatic sugar because typing is lame.

 Gowalla uses a REST api and nests tons of their request, i.e. /users/id/pins, /spots/id/events etc
 This lib lets you mimic that. Example:

   user = gowalla.user("jspies", callback);
   user.stamps(callback);

 You can even chain it:

   gowalla.user("jspies").stamps(callback);

 In the chain above, no request is even made on the call to user() because there's no callback. However, you can callback:

   gowalla.user("jspies", callback).stamps(callback);

 And now with Spot searching:

   gowalla.spots(30.2697, -97.7494, 5).search("Torchy");

 */
var http = require('http');
var sys = require('sys');

module.exports = Gowalla;

function Gowalla(api, username, password) {
  this.API_KEY = api;

  this.baseURL = "api.gowalla.com";
  this.requestHeaders = {
    "Host": this.baseURL,
    "Accept": "application/json",
    "X-Gowalla-API-Key" : this.API_KEY
  };
  if (username && password) {
    this.set_user(username, password);
  }

  this.client = http.createClient(80, this.baseURL);

};

Gowalla.prototype = {

  user: function(username, callback) {
    var self = this; // save for nested Objects

    // If no one's going to do anything with it, let's not waste the request
    if (callback) {
      this._get('/users/'+username, callback);
    }

    function User(username) {
      this.base = "/users/"+username;
      this.username = username;
    };

    User.prototype = {
      // stamps takes a limit, but also takes a page if you want to page by 20
      // if you leave limit off, you get more info, like total entries and the ability to page
      stamps: function(callback, limit) {
        if (!limit || limit == 20) {
          self._get(this.base+'/stamps', callback);
        } else {
          self._get(this.base+'/stamps?limit='+limit, callback);
        }
      },
      pins: function(callback) {
        self._get(this.base+"/pins", callback);
      },
      trips: function(callback) {
        self._get(this.base+"/trips", callback);
      },
      items: function(callback) {
        self._get(this.base+"/items", callback);
      },
      topspots: function(callback) {
        self._get(this.base+"/topspots", callback);
      },
      photos: function(callback) {
        self._get(this.base+"/photos", callback);
      },
      friend_activity: function(callback) {
        self._get(this.base+"/activity/friends", callback);
      }
    }

    return new User(username);
  },

  spots: function(lat, lng, radius, callback) {
    var self = this; // save for nested Spots
    this._get('/spots/?lat='+lat+'&lng='+lng+'&radius='+radius, callback);

    function Spots(lat, lng, radius) {
      this.base = '/spots/?lat='+lat+'&lng='+lng+'&radius='+radius;
      this.lat = lat;
      this.lng = lng;
      this.radius = radius;
    }
    Spots.prototype = {
      search: function(str, callback) {
        self._get(this.base+"&q="+str, callback)
      }
    };
    return new Spots(lat, lng, radius);
  },

  spot: function(id, callback) {
    var self = this; // save for nested Objects
    if (callback) {
      this._get('/spots/'+id, callback);
    }

    function Spot(id) {
      this.base = "/spots/"+id;
      this.id = id;
    };
    Spot.prototype = {
      events: function(callback) {
        self._get(this.base+'/events', callback);
      },

      checkins: function(callback) {
        this.events(function(data) {
          var activity = new Array();
          for(var i=0;i<data.activity.length;i++) {
            if (data.activity[i].type == "checkin") {
              activity.push(data.activity[i]);
            }
          }
          callback.call(self, activity);
        });
      },

      items: function(callback) {
        self._get(this.base+'/items', callback);
      },

      flags: function(callback) {
        self._get(this.base+'/flags', callback);
      }
    }

    return new Spot(id);
  },

  flags: function(callback) {
    this._get("/flags", callback);
  },

  flag: function(id, callback) {
    this._get("/flag/"+id, callback);
  },

  categories: function(callback) {
    this._get("/categories", callback);
  },

  category: function(id, callback) {
    this._get("/category/"+id, callback);
  },

  item: function(id, callback) {
    this._get("/item/"+id, callback);
  },

  trips: function(callback) {
    this._get("/trips", callback);
  },

  trip: function(id, callback) {
    this._get("/trip/"+id, callback);
  },

  checking: function(id, callback) {
    this._get("/checkin/"+id, callback);
  },

  /** You can store a user name so you don't have to call it all the time */
  set_user: function(username, password) {
    this.requestHeaders.Authorization = "Basic "+this._encode64(username+':'+password);
    this.username = username;
  },

  /** Anything past here is essentially a private function. That's why I used an underscore
   *  And also, get() is confusing since all the frameworks use that
   */

  /** a simple getter .builds the request and gets the data
      then just calls the callback
   */
  _get: function(path, callback) {
    var request = this.client.request('GET', path, this.requestHeaders);
    var self = this;
    var data = '';

    request.on('response', function(response) {
      response.setEncoding("utf8");
      response.on("data", function(bits) {
        data += bits;
      });

      response.on("end", function() {
        if (callback) {
          callback.call(self, JSON.parse(data));
        }
      });
    });
    request.end();
  },

  _encode64 : function (input) {
    var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
		var output = "";
		var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
		var i = 0;

		while (i < input.length) {

			chr1 = input.charCodeAt(i++);
			chr2 = input.charCodeAt(i++);
			chr3 = input.charCodeAt(i++);

			enc1 = chr1 >> 2;
			enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
			enc4 = chr3 & 63;

			if (isNaN(chr2)) {
				enc3 = enc4 = 64;
			} else if (isNaN(chr3)) {
				enc4 = 64;
			}

			output = output +
			keyStr.charAt(enc1) + keyStr.charAt(enc2) +
			keyStr.charAt(enc3) + keyStr.charAt(enc4);

		}

		return output;
	}
}
<commit_msg>added photos to spots<commit_after>/**
 Gowalla API wrapper for node.js

 Copyright (c) 2010 Jonathan Spies MIT License

 The Gowalla API is really simple to use but I wanted some syntatic sugar because typing is lame.

 Gowalla uses a REST api and nests tons of their request, i.e. /users/id/pins, /spots/id/events etc
 This lib lets you mimic that. Example:

   user = gowalla.user("jspies", callback);
   user.stamps(callback);

 You can even chain it:

   gowalla.user("jspies").stamps(callback);

 In the chain above, no request is even made on the call to user() because there's no callback. However, you can callback:

   gowalla.user("jspies", callback).stamps(callback);

 And now with Spot searching:

   gowalla.spots(30.2697, -97.7494, 5).search("Torchy");

 */
var http = require('http');
var sys = require('sys');

module.exports = Gowalla;

function Gowalla(api, username, password) {
  this.API_KEY = api;

  this.baseURL = "api.gowalla.com";
  this.requestHeaders = {
    "Host": this.baseURL,
    "Accept": "application/json",
    "X-Gowalla-API-Key" : this.API_KEY
  };
  if (username && password) {
    this.set_user(username, password);
  }

  this.client = http.createClient(80, this.baseURL);

};

Gowalla.prototype = {

  user: function(username, callback) {
    var self = this; // save for nested Objects

    // If no one's going to do anything with it, let's not waste the request
    if (callback) {
      this._get('/users/'+username, callback);
    }

    function User(username) {
      this.base = "/users/"+username;
      this.username = username;
    };

    User.prototype = {
      // stamps takes a limit, but also takes a page if you want to page by 20
      // if you leave limit off, you get more info, like total entries and the ability to page
      stamps: function(callback, limit) {
        if (!limit || limit == 20) {
          self._get(this.base+'/stamps', callback);
        } else {
          self._get(this.base+'/stamps?limit='+limit, callback);
        }
      },
      pins: function(callback) {
        self._get(this.base+"/pins", callback);
      },
      trips: function(callback) {
        self._get(this.base+"/trips", callback);
      },
      items: function(callback) {
        self._get(this.base+"/items", callback);
      },
      topspots: function(callback) {
        self._get(this.base+"/topspots", callback);
      },
      photos: function(callback) {
        self._get(this.base+"/photos", callback);
      },
      friend_activity: function(callback) {
        self._get(this.base+"/activity/friends", callback);
      }
    }

    return new User(username);
  },

  spots: function(lat, lng, radius, callback) {
    var self = this; // save for nested Spots
    this._get('/spots/?lat='+lat+'&lng='+lng+'&radius='+radius, callback);

    function Spots(lat, lng, radius) {
      this.base = '/spots/?lat='+lat+'&lng='+lng+'&radius='+radius;
      this.lat = lat;
      this.lng = lng;
      this.radius = radius;
    }
    Spots.prototype = {
      search: function(str, callback) {
        self._get(this.base+"&q="+str, callback)
      }
    };
    return new Spots(lat, lng, radius);
  },

  spot: function(id, callback) {
    var self = this; // save for nested Objects
    if (callback) {
      this._get('/spots/'+id, callback);
    }

    function Spot(id) {
      this.base = "/spots/"+id;
      this.id = id;
    };
    Spot.prototype = {
      events: function(callback) {
        self._get(this.base+'/events', callback);
      },
      photos: function(callback) {
        self._get(this.base+'/photos', callback);
      },

      checkins: function(callback) {
        this.events(function(data) {
          var activity = new Array();
          for(var i=0;i<data.activity.length;i++) {
            if (data.activity[i].type == "checkin") {
              activity.push(data.activity[i]);
            }
          }
          callback.call(self, activity);
        });
      },

      items: function(callback) {
        self._get(this.base+'/items', callback);
      },

      flags: function(callback) {
        self._get(this.base+'/flags', callback);
      }
    }

    return new Spot(id);
  },

  flags: function(callback) {
    this._get("/flags", callback);
  },

  flag: function(id, callback) {
    this._get("/flag/"+id, callback);
  },

  categories: function(callback) {
    this._get("/categories", callback);
  },

  category: function(id, callback) {
    this._get("/category/"+id, callback);
  },

  item: function(id, callback) {
    this._get("/item/"+id, callback);
  },

  trips: function(callback) {
    this._get("/trips", callback);
  },

  trip: function(id, callback) {
    this._get("/trip/"+id, callback);
  },

  checkin: function(id, callback) {
    this._get("/checkin/"+id, callback);
  },

  /** You can store a user name so you don't have to call it all the time */
  set_user: function(username, password) {
    this.requestHeaders.Authorization = "Basic "+this._encode64(username+':'+password);
    this.username = username;
  },

  /** Anything past here is essentially a private function. That's why I used an underscore
   *  And also, get() is confusing since all the frameworks use that
   */

  /** a simple getter .builds the request and gets the data
      then just calls the callback
   */
  _get: function(path, callback) {
    var request = this.client.request('GET', path, this.requestHeaders);
    var self = this;
    var data = '';

    request.on('response', function(response) {
      response.setEncoding("utf8");
      response.on("data", function(bits) {
        data += bits;
      });

      response.on("end", function() {
        if (callback) {
          callback.call(self, JSON.parse(data));
        }
      });
    });
    request.end();
  },

  _encode64 : function (input) {
    var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
		var output = "";
		var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
		var i = 0;

		while (i < input.length) {

			chr1 = input.charCodeAt(i++);
			chr2 = input.charCodeAt(i++);
			chr3 = input.charCodeAt(i++);

			enc1 = chr1 >> 2;
			enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
			enc4 = chr3 & 63;

			if (isNaN(chr2)) {
				enc3 = enc4 = 64;
			} else if (isNaN(chr3)) {
				enc4 = 64;
			}

			output = output +
			keyStr.charAt(enc1) + keyStr.charAt(enc2) +
			keyStr.charAt(enc3) + keyStr.charAt(enc4);

		}

		return output;
	}
}
<|endoftext|>


<commit_before>import qs from "query-string";

export const selectAuthToken = state => state.auth.token;

export const selectConfigurations = state => state.configurations;

export const selectExportRegion = (id, state) => state.hdx.exportRegions[id];

export const selectHDXError = state => state.hdx.error;

export const selectHDXStatus = state => state.hdx.status;

export const selectIntl = state => state.intl;

export const selectIsLoggedIn = state => state.auth.isLoggedIn;

export const selectIsLoggingIn = state => state.auth.isLoggingIn;

export const selectLocationHash = state => qs.parse(state.router.location.hash);

export const selectLocationOptions = state => state.hdx.locationOptions;

export const selectPermissions = state =>
  state.meta.user && state.meta.user.permissions;

export const selectRuns = state => state.exportRuns;

export const selectStatus = state => state.status;

export const selectUsername = state => state.meta.user.username;
<commit_msg>Null check<commit_after>import qs from "query-string";

export const selectAuthToken = state => state.auth.token;

export const selectConfigurations = state => state.configurations;

export const selectExportRegion = (id, state) => state.hdx.exportRegions[id];

export const selectHDXError = state => state.hdx.error;

export const selectHDXStatus = state => state.hdx.status;

export const selectIntl = state => state.intl;

export const selectIsLoggedIn = state => state.auth.isLoggedIn;

export const selectIsLoggingIn = state => state.auth.isLoggingIn;

export const selectLocationHash = state => qs.parse(state.router.location.hash);

export const selectLocationOptions = state => state.hdx.locationOptions;

export const selectPermissions = state =>
  state.meta.user && state.meta.user.permissions;

export const selectRuns = state => state.exportRuns;

export const selectStatus = state => state.status;

export const selectUsername = state => state.meta.user && state.meta.user.username;
<|endoftext|>


<commit_before>import ghUrl from 'github-url-to-object'
import cliPackage from '../../../package.json'
import versionRanges from '../../versionRanges'
import sortObject from 'deep-sort-object'

const manifestPropOrder = [
  'name', 'private', 'version', 'description', 'main', 'author', 'license', 'scripts',
  'keywords', 'dependencies', 'devDependencies'
]

function getCommonManifest(data) {
  const pkg = {
    name: data.name,
    version: '1.0.0',
    description: data.description,
    author: data.author,
    license: data.license,
    devDependencies: {}
  }

  if (pkg.license === 'UNLICENSED') {
    pkg.private = true
  }

  if (data.gitRemote) {
    pkg.repository = {
      type: 'git',
      url: data.gitRemote
    }
  }

  const gh = (data.gitRemote || '').includes('github.com/') && ghUrl(data.gitRemote)
  if (gh) {
    Object.assign(pkg, {
      homepage: `${gh.https_url}#readme`,
      bugs: {url: `${gh.https_url}/issues`}
    })
  }

  return pkg
}

export function createPackageManifest(data) {
  const pkg = Object.assign(getCommonManifest(data), {
    main: 'package.json',
    keywords: ['sanity'],
    scripts: {
      start: 'sanity start',
      test: 'sanity test'
    },
    dependencies: sortObject(
      Object.assign(
        {[cliPackage.name]: `^${cliPackage.version}`},
        versionRanges.core
      )
    )
  })

  return serializeManifest(pkg)
}

function getSanityStyleManifestProps() {
  return {
    main: 'lib/index.js',
    dependencies: versionRanges.plugin.prod,
    devDependencies: versionRanges.plugin.dev,
    scripts: {
      compile: 'babel src --copy-files --out-dir lib',
      prepublish: 'in-publish && npm run compile || not-in-publish',
      postpublish: 'rimraf lib',
      test: 'eslint .'
    }
  }
}

export function createPluginManifest(data, opts = {}) {
  const sanityStyleProps = opts.sanityStyle
    ? getSanityStyleManifestProps()
    : {}

  const pkg = Object.assign(getCommonManifest(data), {
    main: 'src/plugin.js',
    scripts: {test: 'echo "Error: no test specified" && exit 1'},
    keywords: ['sanity', 'sanity-plugin'],
    dependencies: {}
  }, sanityStyleProps)

  return serializeManifest(pkg)
}

function getSanityPluginManifest(data, {isSanityStyle}) {
  const prefix = data.name.replace(/^sanity-plugin-/, '')
  if (isSanityStyle) {
    return {
      paths: {
        source: './src',
        compiled: './lib'
      },

      roles: [{
        name: `component:${prefix}/my-component`,
        path: 'myComponent.js'
      }]
    }
  }

  return {
    roles: [{
      implements: `component:${prefix}/my-component`,
      description: 'Description for this role. Change `implements` to `name` if it should be non-overridable.',
      path: 'lib/myComponent.js'
    }]
  }
}

export function createSanityManifest(data, opts) {
  const manifest = opts.isPlugin ? getSanityPluginManifest(data, opts) : {
    root: true,

    api: {
      dataset: data.dataset
    },

    plugins: [
      '@sanity/base',
      '@sanity/components',
      '@sanity/default-layout',
      '@sanity/default-login',
      '@sanity/desk-tool'
    ],

    roles: [
      {
        name: 'schema:@sanity/base/schema',
        srcPath: './schemas/schema.js',
        path: './schemas/schema.js'
      }
    ]
  }

  return `${JSON.stringify(manifest, null, 2)}\n`
}

function serializeManifest(src) {
  const props = manifestPropOrder.concat(Object.keys(src))
  const ordered = props.reduce((target, prop) => {
    if (typeof src[prop] !== 'undefined' && typeof target[prop] === 'undefined') {
      target[prop] = src[prop]
    }

    return target
  }, {})

  return `${JSON.stringify(ordered, null, 2)}\n`
}
<commit_msg>Remove srcPath reference<commit_after>import ghUrl from 'github-url-to-object'
import cliPackage from '../../../package.json'
import versionRanges from '../../versionRanges'
import sortObject from 'deep-sort-object'

const manifestPropOrder = [
  'name', 'private', 'version', 'description', 'main', 'author', 'license', 'scripts',
  'keywords', 'dependencies', 'devDependencies'
]

function getCommonManifest(data) {
  const pkg = {
    name: data.name,
    version: '1.0.0',
    description: data.description,
    author: data.author,
    license: data.license,
    devDependencies: {}
  }

  if (pkg.license === 'UNLICENSED') {
    pkg.private = true
  }

  if (data.gitRemote) {
    pkg.repository = {
      type: 'git',
      url: data.gitRemote
    }
  }

  const gh = (data.gitRemote || '').includes('github.com/') && ghUrl(data.gitRemote)
  if (gh) {
    Object.assign(pkg, {
      homepage: `${gh.https_url}#readme`,
      bugs: {url: `${gh.https_url}/issues`}
    })
  }

  return pkg
}

export function createPackageManifest(data) {
  const pkg = Object.assign(getCommonManifest(data), {
    main: 'package.json',
    keywords: ['sanity'],
    scripts: {
      start: 'sanity start',
      test: 'sanity test'
    },
    dependencies: sortObject(
      Object.assign(
        {[cliPackage.name]: `^${cliPackage.version}`},
        versionRanges.core
      )
    )
  })

  return serializeManifest(pkg)
}

function getSanityStyleManifestProps() {
  return {
    main: 'lib/index.js',
    dependencies: versionRanges.plugin.prod,
    devDependencies: versionRanges.plugin.dev,
    scripts: {
      compile: 'babel src --copy-files --out-dir lib',
      prepublish: 'in-publish && npm run compile || not-in-publish',
      postpublish: 'rimraf lib',
      test: 'eslint .'
    }
  }
}

export function createPluginManifest(data, opts = {}) {
  const sanityStyleProps = opts.sanityStyle
    ? getSanityStyleManifestProps()
    : {}

  const pkg = Object.assign(getCommonManifest(data), {
    main: 'src/plugin.js',
    scripts: {test: 'echo "Error: no test specified" && exit 1'},
    keywords: ['sanity', 'sanity-plugin'],
    dependencies: {}
  }, sanityStyleProps)

  return serializeManifest(pkg)
}

function getSanityPluginManifest(data, {isSanityStyle}) {
  const prefix = data.name.replace(/^sanity-plugin-/, '')
  if (isSanityStyle) {
    return {
      paths: {
        source: './src',
        compiled: './lib'
      },

      roles: [{
        name: `component:${prefix}/my-component`,
        path: 'myComponent.js'
      }]
    }
  }

  return {
    roles: [{
      implements: `component:${prefix}/my-component`,
      description: 'Description for this role. Change `implements` to `name` if it should be non-overridable.',
      path: 'lib/myComponent.js'
    }]
  }
}

export function createSanityManifest(data, opts) {
  const manifest = opts.isPlugin ? getSanityPluginManifest(data, opts) : {
    root: true,

    api: {
      dataset: data.dataset
    },

    plugins: [
      '@sanity/base',
      '@sanity/components',
      '@sanity/default-layout',
      '@sanity/default-login',
      '@sanity/desk-tool'
    ],

    roles: [
      {
        name: 'schema:@sanity/base/schema',
        path: './schemas/schema.js'
      }
    ]
  }

  return `${JSON.stringify(manifest, null, 2)}\n`
}

function serializeManifest(src) {
  const props = manifestPropOrder.concat(Object.keys(src))
  const ordered = props.reduce((target, prop) => {
    if (typeof src[prop] !== 'undefined' && typeof target[prop] === 'undefined') {
      target[prop] = src[prop]
    }

    return target
  }, {})

  return `${JSON.stringify(ordered, null, 2)}\n`
}
<|endoftext|>


<commit_before>(function() {
  angular.module('KmotionXApp')
   .factory('settings', settings);

  settings.$inject = ['$http','kmxBackend'];

  function settings($http,kmxBackend){
    var mcodes = ['','','','M3', 'M4', 'M5', 'M6', 'M7', 'M8', 'M9', 'S'];
    var mcodesExtended = ["M100", "M101", "M102", "M103", "M104", "M105", "M106", "M107", "M108", "M109", "M110", "M111", "M112", "M113", "M114", "M115", "M116", "M117", "M118", "M119"];

    var data = {
        name: null,
        description: null,
        dimX: 0,
        dimY: 0,
        dimZ: 0,
        initProgram: null,
        initThread: 1,
        axes: axesArr(),
        tplanner: {},
        actions: actionsArr(mcodes),
        extendedActions: actionsArr(mcodesExtended),
        userActions: userActionsArr()
    };

    var service =  {
      machine:data,
      load: load,
      save: save,
      fileName: fileName
    };
    return service;

    function load(file) {
      return $http.get(file).success(function(jsonData) {
        updateSettings(jsonData);
      });
    }

    function updateSettings(jsonData) {
      data.name = jsonData.name;
      data.description = jsonData.description;
      data.dimX = jsonData.dimX;
      data.dimY = jsonData.dimY;
      data.dimZ = jsonData.dimZ;
      data.initProgram = jsonData.initProgram;
      data.initThread = jsonData.initThread;
      data.axes = jsonData.axes || axesArr();
      data.tplanner = jsonData.tplanner || {};
      data.actions = jsonData.actions || actionsArr(mcodes);
      data.extendedActions = jsonData.extendedActions || actionsArr(mcodesExtended);
      data.userActions = jsonData.userActions || userActionsArr();
    }

    function fileName(){
      return "settings/" +data.name + ".cnf";
    }

    function save() {
      var file = fileName();
      var param = {};
      var copy = angular.copy(data);
      param[file] = copy;
      kmxBackend.save(file, angular.toJson(data,true));
    }
    function axesArr(){
      var axes = []
      var axisNames = ['X', 'Y', 'Z', 'A', 'B', 'C'];
      for(var i = 0; i < 6; i++) {
        axes.push({name:axisNames[i]});
      }
      return axes;
    }

    function actionsArr(codes){
      var actions = []

      for(var i = 0; i < codes.length; i++) {
        actions.push({
          action:0,
          name:codes[i]
        });

      }
      return actions;
    }

    function userActionsArr(){
      var actions = []
      for(var i = 0; i < 10; i++) {
        actions.push({
          action:0
        });

      }
      return actions;
    }

  }
})();<commit_msg>Added intermediate machine object on load.<commit_after>(function() {
  angular.module('KmotionXApp')
   .factory('settings', settings);

  settings.$inject = ['$http','kmxBackend'];

  function settings($http,kmxBackend){
    var mcodes = ['','','','M3', 'M4', 'M5', 'M6', 'M7', 'M8', 'M9', 'S'];
    var mcodesExtended = ["M100", "M101", "M102", "M103", "M104", "M105", "M106", "M107", "M108", "M109", "M110", "M111", "M112", "M113", "M114", "M115", "M116", "M117", "M118", "M119"];

    var data = newMachine();

    var service =  {
      machine: data,
      load: load,
      save: save,
      fileName: fileName
    };
    return service;

    function newMachine(){
      return {
        name: null,
        description: null,
        dimX: 0,
        dimY: 0,
        dimZ: 0,
        initProgram: null,
        initThread: 1,
        axes: axesArr(),
        tplanner: {},
        actions: actionsArr(mcodes),
        extendedActions: actionsArr(mcodesExtended),
        userActions: userActionsArr()
      };
    }

    function load(file) {
      return $http.get(file +"?nocache=1").success(function(jsonData) {
        updateSettings(jsonData);
      });
    }

    function updateSettings(jsonData) {
      var machineData = newMachine();
      machineData.name = jsonData.name;
      machineData.description = jsonData.description;
      machineData.dimX = jsonData.dimX;
      machineData.dimY = jsonData.dimY;
      machineData.dimZ = jsonData.dimZ;
      machineData.initProgram = jsonData.initProgram;
      machineData.initThread = jsonData.initThread;
      machineData.axes = jsonData.axes || axesArr();
      machineData.tplanner = jsonData.tplanner || {};
      machineData.actions = jsonData.actions || actionsArr(mcodes);
      machineData.extendedActions = jsonData.extendedActions || actionsArr(mcodesExtended);
      machineData.userActions = jsonData.userActions || userActionsArr();
      angular.copy(machineData, data);
    }

    function fileName(){
      return "settings/" +service.machine.name + ".cnf";
    }

    function save() {
      var file = fileName();
      kmxBackend.save(file, angular.toJson(service.machine,true));
    }
    function axesArr(){
      var axes = []
      var axisNames = ['X', 'Y', 'Z', 'A', 'B', 'C'];
      for(var i = 0; i < 6; i++) {
        axes.push({name:axisNames[i]});
      }
      return axes;
    }

    function actionsArr(codes){
      var actions = []

      for(var i = 0; i < codes.length; i++) {
        actions.push({
          action:0,
          name:codes[i]
        });

      }
      return actions;
    }

    function userActionsArr(){
      var actions = []
      for(var i = 0; i < 10; i++) {
        actions.push({
          action:0
        });

      }
      return actions;
    }

  }
})();<|endoftext|>


<commit_before>'use strict';

const cacheVersion = 'v1';
const pageCache = `${cacheVersion}-pages`;
const assetCache = `${cacheVersion}-assets`;

const cachedPages = [
  '/',
  '/about',
  '/offline'
];

const cachedAssets = [
  '/assets/images/profile.png',
  '/assets/fonts/NotoSans-Regular.woff2',
  '/assets/fonts/NotoSans-Bold.woff2',
  '/assets/global.css',
  '/pages.json'
];

addEventListener('install', event => {
  event.waitUntil(
    caches.open(assetCache).then(cache => {
      caches.open(pageCache).then(pagesCache => pagesCache.addAll(cachedPages));
      return cache.addAll(cachedAssets);
    })
  ).then(() => self.skipWaiting());
});

addEventListener('activate', () => {
  // clean up any old caches
  return caches.keys().then(keys => {
    return Promise.all(keys
      .filter(key => key.indexOf(cacheVersion) !== 0)
      .map(key => caches.delete(key))
    );
  });
});

addEventListener('fetch', event => {
  let request = event.request;

  if(request.method !== 'GET' || request.url.indexOf('?') !== -1) {
    return;
  }

  event.respondWith(networkFirst(event.request));
});

function networkFirst(request) {
  return fetch(request).then(response => {
    if(response && (response.ok || response.status === 302)) {
      caches.open(cachedPages).then(function(cache) {
        cache.put(request, response.clone());
      });
    }
    return response;
  }).catch(() => {
    return caches.match(request);
  }).then(response => {
    return response || caches.match('/offline');
  });
}<commit_msg>clone response before usage<commit_after>'use strict';

const cacheVersion = 'v1';
const pageCache = `${cacheVersion}-pages`;
const assetCache = `${cacheVersion}-assets`;

const cachedPages = [
  '/',
  '/about',
  '/offline'
];

const cachedAssets = [
  '/assets/images/profile.png',
  '/assets/fonts/NotoSans-Regular.woff2',
  '/assets/fonts/NotoSans-Bold.woff2',
  '/assets/global.css',
  '/pages.json'
];

addEventListener('install', event => {
  event.waitUntil(
    caches.open(assetCache).then(cache => {
      caches.open(pageCache).then(pagesCache => pagesCache.addAll(cachedPages));
      return cache.addAll(cachedAssets);
    })
  ).then(() => self.skipWaiting());
});

addEventListener('activate', () => {
  // clean up any old caches
  return caches.keys().then(keys => {
    return Promise.all(keys
      .filter(key => key.indexOf(cacheVersion) !== 0)
      .map(key => caches.delete(key))
    );
  });
});

addEventListener('fetch', event => {
  let request = event.request;

  if(request.method !== 'GET' || request.url.indexOf('?') !== -1) {
    return;
  }

  event.respondWith(networkFirst(event.request));
});

function networkFirst(request) {
  return fetch(request).then(response => {
    let res = response.clone();
    if(res && (res.ok || res.status === 302)) {
      caches.open(cachedPages).then(function(cache) {
        cache.put(request, response);
      });
    }
    return response;
  }).catch(() => {
    return caches.match(request);
  }).then(response => {
    return response || caches.match('/offline');
  });
}<|endoftext|>


<commit_before>/**
 * .reel Unit Tests
 */
(function($){

  module('Issues', { teardown: function teardown(){
    $('.jquery-reel').trigger('teardown');
    $(document).unbind('tick.reel');
  }});

  test( 'GH-4 Proper background positioning range for stitched non-looping panoramas', function(){
    /* Github issue 4 bugfix
     * http://github.com/pisi/Reel/issues/#issue/4
     */
    expect($.browser.msie ? 4 : 2);
    var
      stitched= 1652,
      $pano= $('#stitched_nonlooping').reel({ stitched: stitched, loops: false }),
      travel= stitched - parseInt($pano.css('width'))

    $pano.trigger('frameChange', 1);
    if ($.browser.msie){
      // MSIE returns undefined backgroundPosition, so we need to check individual ones
      equiv($pano.css('backgroundPositionX'), '0px', 'Frame 1 (min, X)');
      equiv($pano.css('backgroundPositionY'), '0px', 'Frame 1 (min, Y)');
    }else{
      equiv($pano.css('backgroundPosition'), '0px 0px', 'Frame 1 (min)');
    }
    $pano.trigger('frameChange', 36);
    if ($.browser.msie){
      // MSIE returns undefined backgroundPosition, so we need to check individual ones
      equiv($pano.css('backgroundPositionX'), -travel+'px', 'Frame 36 (max, X)');
      equiv($pano.css('backgroundPositionY'), '0px', 'Frame 36 (max, Y)');
    }else{
      equiv($pano.css('backgroundPosition'), -travel+'px 0px', 'Frame 36 (max)');
    }
  });

  test( 'GH-6 Proper background positioning range for stitched looping panoramas', function(){
    /* Github issue 6 bugfix
     * http://github.com/pisi/Reel/issues/#issue/6
     */
    expect($.browser.msie ? 4 : 2);
    var
      stitched= 1652,
      $pano= $('#stitched_looping').reel({ stitched: stitched, loops: true }),
      travel= stitched

    $pano.trigger('frameChange', 1);
    if ($.browser.msie){
      // MSIE returns undefined backgroundPosition, so we need to check individual ones
      equiv($pano.css('backgroundPositionX'), '0px', 'Looping - frame 1 (min, X)');
      equiv($pano.css('backgroundPositionY'), '0px', 'Looping - frame 1 (min, Y)');
    }else{
      equiv($pano.css('backgroundPosition'), '0px 0px', 'Looping - frame 1 (min)');
    }
    $pano.trigger('frameChange', 36);
    if ($.browser.msie){
      // MSIE returns undefined backgroundPosition, so we need to check individual ones
      equiv($pano.css('backgroundPositionX'), -travel+'px', 'Looping - frame 36 (max, X)');
      equiv($pano.css('backgroundPositionY'), '0px', 'Looping - frame 36 (max, Y)');
    }else{
      equiv($pano.css('backgroundPosition'), -travel+'px 0px', 'Looping - frame 36 (max)');
    }
  });

  asyncTest( 'GH-11 First frame disappears after image sequence loading is complete', function(){
    /* Github issue 11 bugfix
     * http://github.com/pisi/Reel/issues/#issue/11
     * Replacement of original image source with embedded transparent sprite
     * can not happen when using `images` option
     */
    expect(1);
    var
      $pano= $('#sequence').reel({
        footage:  10,
        cw:       true,
        orbital:  3,
        inversed: true,
        path:     'samples/phone/',
        images:   phone_frames(20)
      })

    $pano.bind('loaded', function(){
      equal($pano.attr('src'), 'samples/phone/01.png', 'Image is from the sequence');
      start();
    });

    /*
      Borrowed from test/sampler.html
    */
    function phone_frames(frames){
      var every= 1, stack= []
      for(var i= 1; i <= frames; i+= every){
        var name= [i, '.png'].join('')
        while (name.length < 6) name= '0'+name
        stack.push(name)
      }
      return stack
    }
  });

  asyncTest( 'GH-30 Broken "play" in IE', function(){
    /* Github issue 30 bugfix
     * http://github.com/pisi/Reel/issues/#issue/30
     * First of all, private `slidable` boolean flag leaked into global scope
     * and also caused Reel to throw JS errors.
     * Then adaptive ticker timeout sometimes went sub-zero causing IE to invalidate such `setTimeout` call
     * and thus effectively ceised the timer completely.
     * Another IE issue: broken image overlay in IE 7 or lower was caused by no support for "data:" protocol URLs,
     * so to workaround it a CDN-served blank image is be used instead.
     */
    expect(4);
    var
      $pano= $('#image').reel(),
      ticks= 0

    equal(typeof slidable, 'undefined', '`slidable` is undefined in the global scope');
    equal(typeof window.slidable, 'undefined', '`window.slidable` is also undefined');

    $(document).bind('tick.reel', function(){
      ticks++;
      if (ticks == 100){
        ok(true, 'Ticked 100 times - ticker runs ;)');

        var
          protocol= $('#image').attr('src').split(':')[0]

        if ($.browser.msie && +$.browser.version <= 7) equal(protocol, 'http', 'CDN-served transparent image.')
        else equal(protocol, 'data', 'Embedded transparent image.');

        start();
      }
    });
  });

  asyncTest( '`stage_pool` private variable leaked into global scope', function()
  {
    expect(1);

    var
      $reel= $('#image').reel()

    setTimeout(function(){
      ok( typeof stage_pool === 'undefined', 'No leaked `stage_pool` accessible in the global scope');
      start();
    }, 100)
  });

  asyncTest( 'Teardown doesn\'t propagate the cloned original image down the chain', function(){
    /*
     * When Reel instance is torn down and `.reel()` is called again in the same chain,
     * the instance isn't properly initialized.
     * The cloned-back original `IMG` tag doesn't propagate as a proper target
     * for subsequent `.reel()` calls. These don't fail (as they are most probably
     * performed upon the old now detached DOM node. The extra selector evaluation
     * is therefore required in order to further manipulate the node.
     */
    expect( 2 );

    $('#image').reel();

    setTimeout( function(){
      $('#image').trigger('teardown').reel();
      ok( $('#image').is('.jquery-reel'), 'IMG tag is flagged as a Reel instance');
      ok( $('#image').parent().is('.jquery-reel-overlay#image-reel'), 'and wrapped in overlay DIV');
      start();
    }, 500 );
  });

})(jQuery);
<commit_msg>Test enriched with testing event binding preservation.<commit_after>/**
 * .reel Unit Tests
 */
(function($){

  module('Issues', { teardown: function teardown(){
    $('.jquery-reel').trigger('teardown');
    $(document).unbind('tick.reel');
  }});

  test( 'GH-4 Proper background positioning range for stitched non-looping panoramas', function(){
    /* Github issue 4 bugfix
     * http://github.com/pisi/Reel/issues/#issue/4
     */
    expect($.browser.msie ? 4 : 2);
    var
      stitched= 1652,
      $pano= $('#stitched_nonlooping').reel({ stitched: stitched, loops: false }),
      travel= stitched - parseInt($pano.css('width'))

    $pano.trigger('frameChange', 1);
    if ($.browser.msie){
      // MSIE returns undefined backgroundPosition, so we need to check individual ones
      equiv($pano.css('backgroundPositionX'), '0px', 'Frame 1 (min, X)');
      equiv($pano.css('backgroundPositionY'), '0px', 'Frame 1 (min, Y)');
    }else{
      equiv($pano.css('backgroundPosition'), '0px 0px', 'Frame 1 (min)');
    }
    $pano.trigger('frameChange', 36);
    if ($.browser.msie){
      // MSIE returns undefined backgroundPosition, so we need to check individual ones
      equiv($pano.css('backgroundPositionX'), -travel+'px', 'Frame 36 (max, X)');
      equiv($pano.css('backgroundPositionY'), '0px', 'Frame 36 (max, Y)');
    }else{
      equiv($pano.css('backgroundPosition'), -travel+'px 0px', 'Frame 36 (max)');
    }
  });

  test( 'GH-6 Proper background positioning range for stitched looping panoramas', function(){
    /* Github issue 6 bugfix
     * http://github.com/pisi/Reel/issues/#issue/6
     */
    expect($.browser.msie ? 4 : 2);
    var
      stitched= 1652,
      $pano= $('#stitched_looping').reel({ stitched: stitched, loops: true }),
      travel= stitched

    $pano.trigger('frameChange', 1);
    if ($.browser.msie){
      // MSIE returns undefined backgroundPosition, so we need to check individual ones
      equiv($pano.css('backgroundPositionX'), '0px', 'Looping - frame 1 (min, X)');
      equiv($pano.css('backgroundPositionY'), '0px', 'Looping - frame 1 (min, Y)');
    }else{
      equiv($pano.css('backgroundPosition'), '0px 0px', 'Looping - frame 1 (min)');
    }
    $pano.trigger('frameChange', 36);
    if ($.browser.msie){
      // MSIE returns undefined backgroundPosition, so we need to check individual ones
      equiv($pano.css('backgroundPositionX'), -travel+'px', 'Looping - frame 36 (max, X)');
      equiv($pano.css('backgroundPositionY'), '0px', 'Looping - frame 36 (max, Y)');
    }else{
      equiv($pano.css('backgroundPosition'), -travel+'px 0px', 'Looping - frame 36 (max)');
    }
  });

  asyncTest( 'GH-11 First frame disappears after image sequence loading is complete', function(){
    /* Github issue 11 bugfix
     * http://github.com/pisi/Reel/issues/#issue/11
     * Replacement of original image source with embedded transparent sprite
     * can not happen when using `images` option
     */
    expect(1);
    var
      $pano= $('#sequence').reel({
        footage:  10,
        cw:       true,
        orbital:  3,
        inversed: true,
        path:     'samples/phone/',
        images:   phone_frames(20)
      })

    $pano.bind('loaded', function(){
      equal($pano.attr('src'), 'samples/phone/01.png', 'Image is from the sequence');
      start();
    });

    /*
      Borrowed from test/sampler.html
    */
    function phone_frames(frames){
      var every= 1, stack= []
      for(var i= 1; i <= frames; i+= every){
        var name= [i, '.png'].join('')
        while (name.length < 6) name= '0'+name
        stack.push(name)
      }
      return stack
    }
  });

  asyncTest( 'GH-30 Broken "play" in IE', function(){
    /* Github issue 30 bugfix
     * http://github.com/pisi/Reel/issues/#issue/30
     * First of all, private `slidable` boolean flag leaked into global scope
     * and also caused Reel to throw JS errors.
     * Then adaptive ticker timeout sometimes went sub-zero causing IE to invalidate such `setTimeout` call
     * and thus effectively ceised the timer completely.
     * Another IE issue: broken image overlay in IE 7 or lower was caused by no support for "data:" protocol URLs,
     * so to workaround it a CDN-served blank image is be used instead.
     */
    expect(4);
    var
      $pano= $('#image').reel(),
      ticks= 0

    equal(typeof slidable, 'undefined', '`slidable` is undefined in the global scope');
    equal(typeof window.slidable, 'undefined', '`window.slidable` is also undefined');

    $(document).bind('tick.reel', function(){
      ticks++;
      if (ticks == 100){
        ok(true, 'Ticked 100 times - ticker runs ;)');

        var
          protocol= $('#image').attr('src').split(':')[0]

        if ($.browser.msie && +$.browser.version <= 7) equal(protocol, 'http', 'CDN-served transparent image.')
        else equal(protocol, 'data', 'Embedded transparent image.');

        start();
      }
    });
  });

  asyncTest( '`stage_pool` private variable leaked into global scope', function()
  {
    expect(1);

    var
      $reel= $('#image').reel()

    setTimeout(function(){
      ok( typeof stage_pool === 'undefined', 'No leaked `stage_pool` accessible in the global scope');
      start();
    }, 100)
  });

  asyncTest( 'Teardown doesn\'t propagate the cloned original image down the chain', function(){
    /*
     * When Reel instance is torn down and `.reel()` is called again in the same chain,
     * the instance isn't properly initialized.
     * The cloned-back original `IMG` tag doesn't propagate as a proper target
     * for subsequent `.reel()` calls. These don't fail (as they are most probably
     * performed upon the old now detached DOM node. The extra selector evaluation
     * is therefore required in order to further manipulate the node.
     */
    expect( 3 );

    $('#image').reel().click(function(){
      ok( true, 'Event binding is preserved');
    });

    setTimeout( function(){
      $('#image').trigger('teardown').reel();
      ok( $('#image').is('.jquery-reel'), 'IMG tag is flagged as a Reel instance');
      ok( $('#image').parent().is('.jquery-reel-overlay#image-reel'), 'and wrapped in overlay DIV');
      $('#image').click();
      start();
    }, 500 );
  });

})(jQuery);
<|endoftext|>


<commit_before>"use strict";

var webpack = require("webpack");
var Directory = require("./inc/Directory");

var config = {
    entry: "./ui/Router.jsx",
    resolve: {
        extensions: ["", ".js", ".jsx"]
    },
    output: {
        path: "./public/",
        filename: "bundle.js"
    },
    module: {
        loaders: [
            {
                test: /(models\/.*|inc)\/.*\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader",
                query: {
                    presets: ["es2015", "react"]
                }
            },
            {
                test: /\.css$/,
                loader: "style-loader!css-loader"
            },
            {
                test: /\.jsx$/,
                exclude: /node_modules/,
                loader: "babel-loader",
                query: {
                    presets: ["es2015", "react"]
                }
            }
        ]
    },
    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery",
            "window.jQuery": "jquery"
        }),
        new webpack.DefinePlugin({
            "process.env": {
                NODE_ENV: JSON.stringify(process.env.NODE_ENV || ""),
                __BROWSER: true
            }
        })
    ]
};

if (process.env.NODE_ENV === "production") {
    config.plugins.push(new webpack.optimize.OccurenceOrderPlugin());
    config.plugins.push(
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            },
            mangle: {
                except: Directory.names()
            }
        })
    );
}
else {
    config.devtool = "source-map";
}

require("./redis/client").disconnect();

module.exports = config;
<commit_msg>Speed up webpack updates with "watch"<commit_after>"use strict";

var webpack = require("webpack");
var Directory = require("./inc/Directory");

var config = {
    entry: "./ui/Router.jsx",
    resolve: {
        extensions: ["", ".js", ".jsx"]
    },
    output: {
        path: "./public/",
        filename: "bundle.js"
    },
    module: {
        loaders: [
            {
                test: /(models\/.*|inc)\/.*\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader",
                query: {
                    presets: ["es2015", "react"]
                }
            },
            {
                test: /\.css$/,
                loader: "style-loader!css-loader"
            },
            {
                test: /\.jsx$/,
                exclude: /node_modules/,
                loader: "babel-loader",
                query: {
                    presets: ["es2015", "react"]
                }
            }
        ]
    },
    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery",
            "window.jQuery": "jquery"
        }),
        new webpack.DefinePlugin({
            "process.env": {
                NODE_ENV: JSON.stringify(process.env.NODE_ENV || ""),
                __BROWSER: true
            }
        })
    ]
};

if (process.env.NODE_ENV === "production") {
    config.plugins.push(new webpack.optimize.OccurenceOrderPlugin());
    config.plugins.push(
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            },
            mangle: {
                except: Directory.names()
            }
        })
    );
}
else {
    config.devtool = "eval-cheap-module-source-map";
}

require("./redis/client").disconnect();

module.exports = config;
<|endoftext|>