<commit_before>import QInputFrame from '../input-frame/QInputFrame.js'
import FrameMixin from '../../mixins/input-frame.js'
import { humanStorageSize } from '../../utils/format.js'
import QSpinner from '../spinner/QSpinner.js'
import QIcon from '../icon/QIcon.js'
import QProgress from '../progress/QProgress.js'
import QList from '../list/QList.js'
import QItem from '../list/QItem.js'
import QItemSide from '../list/QItemSide.js'
import QItemMain from '../list/QItemMain.js'
import QItemTile from '../list/QItemTile.js'
import QSlideTransition from '../slide-transition/QSlideTransition.js'
import { stopAndPrevent } from '../../utils/event'

function initFile (file) {
  file.__doneUploading = false
  file.__failed = false
  file.__uploaded = 0
  file.__progress = 0
}

export default {
  name: 'QUploader',
  mixins: [FrameMixin],
  props: {
    name: {
      type: String,
      default: 'file'
    },
    headers: Object,
    url: {
      type: String,
      required: true
    },
    urlFactory: {
      type: Function,
      required: false
    },
    uploadFactory: Function,
    additionalFields: {
      type: Array,
      default: () => []
    },
    noContentType: Boolean,
    method: {
      type: String,
      default: 'POST'
    },
    filter: Function,
    extensions: String,
    multiple: Boolean,
    hideUploadButton: Boolean,
    hideUploadProgress: Boolean,
    noThumbnails: Boolean,
    autoExpand: Boolean,
    expandStyle: [Array, String, Object],
    expandClass: [Array, String, Object],
    withCredentials: Boolean,
    sendRaw: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      queue: [],
      files: [],
      uploading: false,
      uploadedSize: 0,
      totalSize: 0,
      xhrs: [],
      focused: false,
      dnd: false,
      expanded: false
    }
  },
  computed: {
    queueLength () {
      return this.queue.length
    },
    hasExpandedContent () {
      return this.files.length > 0
    },
    label () {
      const total = humanStorageSize(this.totalSize)
      return this.uploading
        ? `${(this.progress).toFixed(2)}% (${humanStorageSize(this.uploadedSize)} / ${total})`
        : `${this.queueLength} (${total})`
    },
    progress () {
      return this.totalSize ? Math.min(99.99, this.uploadedSize / this.totalSize * 100) : 0
    },
    addDisabled () {
      return !this.multiple && this.queueLength >= 1
    },
    filesStyle () {
      if (this.maxHeight) {
        return { maxHeight: this.maxHeight }
      }
    },
    dndClass () {
      const cls = [`text-${this.color}`]
      if (this.isInverted) {
        cls.push('inverted')
      }
      return cls
    },
    classes () {
      return {
        'q-uploader-expanded': this.expanded,
        'q-uploader-dark': this.dark,
        'q-uploader-files-no-border': this.isInverted || !this.hideUnderline
      }
    },
    progressColor () {
      return this.dark ? 'white' : 'grey'
    },
    computedExtensions () {
      if (this.extensions) {
        return this.extensions.split(',').map(ext => {
          ext = ext.trim()
          // support "image/*"
          if (ext.endsWith('/*')) {
            ext = ext.slice(0, ext.length - 1)
          }
          return ext
        })
      }
    }
  },
  watch: {
    hasExpandedContent (v) {
      if (v === false) {
        this.expanded = false
      }
      else if (this.autoExpand) {
        this.expanded = true
      }
    }
  },
  methods: {
    add (files) {
      if (files) {
        this.__add(null, files)
      }
    },

    __onDragOver (e) {
      stopAndPrevent(e)
      this.dnd = true
    },
    __onDragLeave (e) {
      stopAndPrevent(e)
      this.dnd = false
    },
    __onDrop (e) {
      stopAndPrevent(e)
      this.dnd = false
      let files = e.dataTransfer.files

      if (files.length === 0) {
        return
      }

      files = this.multiple ? files : [ files[0] ]

      this.__add(null, files)
    },
    __filter (files) {
      return Array.prototype.filter.call(files, file => {
        return this.computedExtensions.some(ext => {
          return file.type.toUpperCase().startsWith(ext.toUpperCase()) ||
            file.name.toUpperCase().endsWith(ext.toUpperCase())
        })
      })
    },
    __add (e, files) {
      if (this.addDisabled) {
        return
      }

      files = Array.prototype.slice.call(files || e.target.files)
      if (this.extensions) {
        files = this.__filter(files)
        if (files.length === 0) {
          return
        }
      }

      this.$refs.file.value = ''

      let filesReady = [] // List of image load promises
      files = files.filter(file => !this.queue.some(f => file.name === f.name))

      if (typeof this.filter === 'function') {
        files = this.filter(files)
      }

      files = files.map(file => {
        initFile(file)
        file.__size = humanStorageSize(file.size)
        file.__timestamp = new Date().getTime()

        if (this.noThumbnails || !file.type.toUpperCase().startsWith('IMAGE')) {
          this.queue.push(file)
        }
        else {
          const reader = new FileReader()
          let p = new Promise((resolve, reject) => {
            reader.onload = e => {
              let img = new Image()
              img.src = e.target.result
              file.__img = img
              this.queue.push(file)
              this.__computeTotalSize()
              resolve(true)
            }
            reader.onerror = e => { reject(e) }
          })

          reader.readAsDataURL(file)
          filesReady.push(p)
        }

        return file
      })

      if (files.length > 0) {
        this.files = this.files.concat(files)
        Promise.all(filesReady).then(() => {
          this.$emit('add', files)
        })
        this.__computeTotalSize()
      }
    },
    __computeTotalSize () {
      this.totalSize = this.queueLength
        ? this.queue.map(f => f.size).reduce((total, size) => total + size)
        : 0
    },
    __remove (file) {
      const
        name = file.name,
        done = file.__doneUploading

      if (this.uploading && !done) {
        this.$emit('remove:abort', file, file.xhr)
        file.xhr && file.xhr.abort()
        this.uploadedSize -= file.__uploaded
      }
      else {
        this.$emit(`remove:${done ? 'done' : 'cancel'}`, file, file.xhr)
      }

      if (!done) {
        this.queue = this.queue.filter(obj => obj.name !== name)
      }

      file.__removed = true
      this.files = this.files.filter(obj => obj.name !== name)

      if (!this.files.length) {
        this.uploading = false
      }

      this.__computeTotalSize()
    },
    __pick () {
      if (!this.addDisabled && this.$q.platform.is.mozilla) {
        this.$refs.file.click()
      }
    },
    __getUploadPromise (file) {
      initFile(file)

      if (this.uploadFactory) {
        const updateProgress = (percentage) => {
          let uploaded = percentage * file.size
          this.uploadedSize += uploaded - file.__uploaded
          file.__uploaded = uploaded
          file.__progress = Math.min(99, parseInt(percentage * 100, 10))
          this.$forceUpdate()
        }

        return new Promise((resolve, reject) => {
          this.uploadFactory(file, updateProgress)
            .then(file => {
              file.__doneUploading = true
              file.__progress = 100
              this.$emit('uploaded', file)
              this.$forceUpdate()
              resolve(file)
            })
            .catch(error => {
              file.__failed = true
              this.$emit('fail', file)
              this.$forceUpdate()
              reject(error)
            })
        })
      }

      const
        form = new FormData(),
        xhr = new XMLHttpRequest()

      try {
        this.additionalFields.forEach(field => {
          form.append(field.name, field.value)
        })
        if (this.noContentType !== true) {
          form.append('Content-Type', file.type || 'application/octet-stream')
        }
        form.append(this.name, file)
      }
      catch (e) {
        return
      }

      file.xhr = xhr
      return new Promise((resolve, reject) => {
        xhr.upload.addEventListener('progress', e => {
          if (file.__removed) { return }
          e.percent = e.total ? e.loaded / e.total : 0
          let uploaded = e.percent * file.size
          this.uploadedSize += uploaded - file.__uploaded
          file.__uploaded = uploaded
          file.__progress = Math.min(99, parseInt(e.percent * 100, 10))
        }, false)

        xhr.onreadystatechange = () => {
          if (xhr.readyState < 4) {
            return
          }
          if (xhr.status && xhr.status < 400) {
            file.__doneUploading = true
            file.__progress = 100
            this.$emit('uploaded', file, xhr)
            resolve(file)
          }
          else {
            file.__failed = true
            this.$emit('fail', file, xhr)
            reject(xhr)
          }
        }

        xhr.onerror = () => {
          file.__failed = true
          this.$emit('fail', file, xhr)
          reject(xhr)
        }

        const resolver = this.urlFactory
          ? this.urlFactory(file)
          : Promise.resolve(this.url)

        resolver.then(url => {
          xhr.open(this.method, url, true)
          if (this.withCredentials) {
            xhr.withCredentials = true
          }
          if (this.headers) {
            Object.keys(this.headers).forEach(key => {
              xhr.setRequestHeader(key, this.headers[key])
            })
          }

          this.xhrs.push(xhr)
          if (this.sendRaw) {
            xhr.send(file)
          }
          else {
            xhr.send(form)
          }
        })
      })
    },
    pick () {
      if (!this.addDisabled) {
        this.$refs.file.click()
      }
    },
    upload () {
      const length = this.queueLength
      if (this.disable || length === 0) {
        return
      }

      let filesDone = 0
      this.uploadedSize = 0
      this.uploading = true
      this.xhrs = []
      this.$emit('start')

      let solved = () => {
        filesDone++
        if (filesDone === length) {
          this.uploading = false
          this.xhrs = []
          this.queue = this.queue.filter(f => !f.__doneUploading)
          this.__computeTotalSize()
          this.$emit('finish')
        }
      }

      this.queue
        .map(file => this.__getUploadPromise(file))
        .forEach(promise => {
          promise.then(solved).catch(solved)
        })
    },
    abort () {
      this.xhrs.forEach(xhr => { xhr.abort() })
      this.uploading = false
      this.$emit('abort')
    },
    reset () {
      this.abort()
      this.files = []
      this.queue = []
      this.expanded = false
      this.__computeTotalSize()
      this.$emit('reset')
    }
  },

  render (h) {
    const child = [
      h('div', {
        staticClass: 'col q-input-target ellipsis',
        'class': this.alignClass
      }, [ this.label ])
    ]

    if (this.uploading) {
      child.push(
        h(QSpinner, {
          slot: 'after',
          staticClass: 'q-if-end self-center',
          props: { size: '24px' }
        }),
        h(QIcon, {
          slot: 'after',
          staticClass: 'q-if-end self-center q-if-control',
          props: {
            name: this.$q.icon.uploader[`clear${this.isInverted ? 'Inverted' : ''}`]
          },
          nativeOn: {
            click: this.abort
          }
        })
      )
    }
    else { // not uploading
      child.push(
        h(QIcon, {
          slot: 'after',
          staticClass: 'q-uploader-pick-button self-center q-if-control relative-position overflow-hidden',
          props: {
            name: this.$q.icon.uploader.add
          },
          attrs: {
            disabled: this.addDisabled
          }
        }, [
          h('input', {
            ref: 'file',
            staticClass: 'q-uploader-input absolute-full cursor-pointer',
            attrs: Object.assign({
              type: 'file',
              accept: this.extensions
            }, this.multiple ? { multiple: true } : {}),
            on: {
              change: this.__add
            }
          })
        ])
      )

      if (!this.hideUploadButton) {
        child.push(
          h(QIcon, {
            slot: 'after',
            staticClass: 'q-if-control self-center',
            props: {
              name: this.$q.icon.uploader.upload
            },
            attrs: {
              disabled: this.queueLength === 0
            },
            nativeOn: {
              click: this.upload
            }
          })
        )
      }
    }

    if (this.hasExpandedContent) {
      child.push(
        h(QIcon, {
          slot: 'after',
          staticClass: 'q-if-control generic_transition self-center',
          'class': { 'rotate-180': this.expanded },
          props: {
            name: this.$q.icon.uploader.expand
          },
          nativeOn: {
            click: () => { this.expanded = !this.expanded }
          }
        })
      )
    }

    return h('div', {
      staticClass: 'q-uploader relative-position',
      'class': this.classes,
      on: { dragover: this.__onDragOver }
    }, [
      h(QInputFrame, {
        ref: 'input',
        props: {
          prefix: this.prefix,
          suffix: this.suffix,
          stackLabel: this.stackLabel,
          floatLabel: this.floatLabel,
          error: this.error,
          warning: this.warning,
          readonly: this.readonly,
          inverted: this.inverted,
          invertedLight: this.invertedLight,
          dark: this.dark,
          hideUnderline: this.hideUnderline,
          before: this.before,
          after: this.after,
          color: this.color,
          align: this.align,
          noParentField: this.noParentField,
          length: this.queueLength,
          additionalLength: true
        }
      }, child),

      h(QSlideTransition, [
        h('div', {
          'class': this.expandClass,
          style: this.expandStyle,
          directives: [{
            name: 'show',
            value: this.expanded
          }]
        }, [
          h(QList, {
            staticClass: 'q-uploader-files q-py-none scroll',
            style: this.filesStyle,
            props: { dark: this.dark }
          }, this.files.map(file => {
            return h(QItem, {
              key: file.name + file.__timestamp,
              staticClass: 'q-uploader-file q-pa-xs'
            }, [
              (!this.hideUploadProgress && h(QProgress, {
                staticClass: 'q-uploader-progress-bg absolute-full',
                props: {
                  color: file.__failed ? 'negative' : this.progressColor,
                  percentage: file.__progress,
                  height: '100%'
                }
              })) || void 0,

              (!this.hideUploadProgress && h('div', {
                staticClass: 'q-uploader-progress-text absolute'
              }, [ file.__progress + '%' ])) || void 0,

              h(QItemSide, {
                props: file.__img
                  ? { image: file.__img.src }
                  : {
                    icon: this.$q.icon.uploader.file,
                    color: this.color
                  }
              }),

              h(QItemMain, {
                props: {
                  label: file.name,
                  sublabel: file.__size
                }
              }),

              h(QItemSide, { props: { right: true } }, [
                h(QItemTile, {
                  staticClass: 'cursor-pointer',
                  props: {
                    icon: this.$q.icon.uploader[file.__doneUploading ? 'done' : 'clear'],
                    color: this.color
                  },
                  nativeOn: {
                    click: () => { this.__remove(file) }
                  }
                })
              ])
            ])
          }))
        ])
      ]),

      (this.dnd && h('div', {
        staticClass: 'q-uploader-dnd flex row items-center justify-center absolute-full',
        'class': this.dndClass,
        on: {
          dragenter: stopAndPrevent,
          dragover: stopAndPrevent,
          dragleave: this.__onDragLeave,
          drop: this.__onDrop
        }
      })) || void 0
    ])
  }
}
<commit_msg>fix: [Bug] Uploader doesn't obey disabled prop #2538<commit_after>import QInputFrame from '../input-frame/QInputFrame.js'
import FrameMixin from '../../mixins/input-frame.js'
import { humanStorageSize } from '../../utils/format.js'
import QSpinner from '../spinner/QSpinner.js'
import QIcon from '../icon/QIcon.js'
import QProgress from '../progress/QProgress.js'
import QList from '../list/QList.js'
import QItem from '../list/QItem.js'
import QItemSide from '../list/QItemSide.js'
import QItemMain from '../list/QItemMain.js'
import QItemTile from '../list/QItemTile.js'
import QSlideTransition from '../slide-transition/QSlideTransition.js'
import { stopAndPrevent } from '../../utils/event'

function initFile (file) {
  file.__doneUploading = false
  file.__failed = false
  file.__uploaded = 0
  file.__progress = 0
}

export default {
  name: 'QUploader',
  mixins: [FrameMixin],
  props: {
    name: {
      type: String,
      default: 'file'
    },
    headers: Object,
    url: {
      type: String,
      required: true
    },
    urlFactory: {
      type: Function,
      required: false
    },
    uploadFactory: Function,
    additionalFields: {
      type: Array,
      default: () => []
    },
    noContentType: Boolean,
    method: {
      type: String,
      default: 'POST'
    },
    filter: Function,
    extensions: String,
    multiple: Boolean,
    hideUploadButton: Boolean,
    hideUploadProgress: Boolean,
    noThumbnails: Boolean,
    autoExpand: Boolean,
    expandStyle: [Array, String, Object],
    expandClass: [Array, String, Object],
    withCredentials: Boolean,
    sendRaw: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      queue: [],
      files: [],
      uploading: false,
      uploadedSize: 0,
      totalSize: 0,
      xhrs: [],
      focused: false,
      dnd: false,
      expanded: false
    }
  },
  computed: {
    queueLength () {
      return this.queue.length
    },
    hasExpandedContent () {
      return this.files.length > 0
    },
    label () {
      const total = humanStorageSize(this.totalSize)
      return this.uploading
        ? `${(this.progress).toFixed(2)}% (${humanStorageSize(this.uploadedSize)} / ${total})`
        : `${this.queueLength} (${total})`
    },
    progress () {
      return this.totalSize ? Math.min(99.99, this.uploadedSize / this.totalSize * 100) : 0
    },
    addDisabled () {
      return this.disable || (!this.multiple && this.queueLength >= 1)
    },
    filesStyle () {
      if (this.maxHeight) {
        return { maxHeight: this.maxHeight }
      }
    },
    dndClass () {
      const cls = [`text-${this.color}`]
      if (this.isInverted) {
        cls.push('inverted')
      }
      return cls
    },
    classes () {
      return {
        'q-uploader-expanded': this.expanded,
        'q-uploader-dark': this.dark,
        'q-uploader-files-no-border': this.isInverted || !this.hideUnderline
      }
    },
    progressColor () {
      return this.dark ? 'white' : 'grey'
    },
    computedExtensions () {
      if (this.extensions) {
        return this.extensions.split(',').map(ext => {
          ext = ext.trim()
          // support "image/*"
          if (ext.endsWith('/*')) {
            ext = ext.slice(0, ext.length - 1)
          }
          return ext
        })
      }
    }
  },
  watch: {
    hasExpandedContent (v) {
      if (v === false) {
        this.expanded = false
      }
      else if (this.autoExpand) {
        this.expanded = true
      }
    }
  },
  methods: {
    add (files) {
      if (files) {
        this.__add(null, files)
      }
    },

    __onDragOver (e) {
      stopAndPrevent(e)
      this.dnd = true
    },
    __onDragLeave (e) {
      stopAndPrevent(e)
      this.dnd = false
    },
    __onDrop (e) {
      stopAndPrevent(e)
      this.dnd = false
      let files = e.dataTransfer.files

      if (files.length === 0) {
        return
      }

      files = this.multiple ? files : [ files[0] ]

      this.__add(null, files)
    },
    __filter (files) {
      return Array.prototype.filter.call(files, file => {
        return this.computedExtensions.some(ext => {
          return file.type.toUpperCase().startsWith(ext.toUpperCase()) ||
            file.name.toUpperCase().endsWith(ext.toUpperCase())
        })
      })
    },
    __add (e, files) {
      if (this.addDisabled) {
        return
      }

      files = Array.prototype.slice.call(files || e.target.files)
      if (this.extensions) {
        files = this.__filter(files)
        if (files.length === 0) {
          return
        }
      }

      this.$refs.file.value = ''

      let filesReady = [] // List of image load promises
      files = files.filter(file => !this.queue.some(f => file.name === f.name))

      if (typeof this.filter === 'function') {
        files = this.filter(files)
      }

      files = files.map(file => {
        initFile(file)
        file.__size = humanStorageSize(file.size)
        file.__timestamp = new Date().getTime()

        if (this.noThumbnails || !file.type.toUpperCase().startsWith('IMAGE')) {
          this.queue.push(file)
        }
        else {
          const reader = new FileReader()
          let p = new Promise((resolve, reject) => {
            reader.onload = e => {
              let img = new Image()
              img.src = e.target.result
              file.__img = img
              this.queue.push(file)
              this.__computeTotalSize()
              resolve(true)
            }
            reader.onerror = e => { reject(e) }
          })

          reader.readAsDataURL(file)
          filesReady.push(p)
        }

        return file
      })

      if (files.length > 0) {
        this.files = this.files.concat(files)
        Promise.all(filesReady).then(() => {
          this.$emit('add', files)
        })
        this.__computeTotalSize()
      }
    },
    __computeTotalSize () {
      this.totalSize = this.queueLength
        ? this.queue.map(f => f.size).reduce((total, size) => total + size)
        : 0
    },
    __remove (file) {
      const
        name = file.name,
        done = file.__doneUploading

      if (this.uploading && !done) {
        this.$emit('remove:abort', file, file.xhr)
        file.xhr && file.xhr.abort()
        this.uploadedSize -= file.__uploaded
      }
      else {
        this.$emit(`remove:${done ? 'done' : 'cancel'}`, file, file.xhr)
      }

      if (!done) {
        this.queue = this.queue.filter(obj => obj.name !== name)
      }

      file.__removed = true
      this.files = this.files.filter(obj => obj.name !== name)

      if (!this.files.length) {
        this.uploading = false
      }

      this.__computeTotalSize()
    },
    __pick () {
      if (!this.addDisabled && this.$q.platform.is.mozilla) {
        this.$refs.file.click()
      }
    },
    __getUploadPromise (file) {
      initFile(file)

      if (this.uploadFactory) {
        const updateProgress = (percentage) => {
          let uploaded = percentage * file.size
          this.uploadedSize += uploaded - file.__uploaded
          file.__uploaded = uploaded
          file.__progress = Math.min(99, parseInt(percentage * 100, 10))
          this.$forceUpdate()
        }

        return new Promise((resolve, reject) => {
          this.uploadFactory(file, updateProgress)
            .then(file => {
              file.__doneUploading = true
              file.__progress = 100
              this.$emit('uploaded', file)
              this.$forceUpdate()
              resolve(file)
            })
            .catch(error => {
              file.__failed = true
              this.$emit('fail', file)
              this.$forceUpdate()
              reject(error)
            })
        })
      }

      const
        form = new FormData(),
        xhr = new XMLHttpRequest()

      try {
        this.additionalFields.forEach(field => {
          form.append(field.name, field.value)
        })
        if (this.noContentType !== true) {
          form.append('Content-Type', file.type || 'application/octet-stream')
        }
        form.append(this.name, file)
      }
      catch (e) {
        return
      }

      file.xhr = xhr
      return new Promise((resolve, reject) => {
        xhr.upload.addEventListener('progress', e => {
          if (file.__removed) { return }
          e.percent = e.total ? e.loaded / e.total : 0
          let uploaded = e.percent * file.size
          this.uploadedSize += uploaded - file.__uploaded
          file.__uploaded = uploaded
          file.__progress = Math.min(99, parseInt(e.percent * 100, 10))
        }, false)

        xhr.onreadystatechange = () => {
          if (xhr.readyState < 4) {
            return
          }
          if (xhr.status && xhr.status < 400) {
            file.__doneUploading = true
            file.__progress = 100
            this.$emit('uploaded', file, xhr)
            resolve(file)
          }
          else {
            file.__failed = true
            this.$emit('fail', file, xhr)
            reject(xhr)
          }
        }

        xhr.onerror = () => {
          file.__failed = true
          this.$emit('fail', file, xhr)
          reject(xhr)
        }

        const resolver = this.urlFactory
          ? this.urlFactory(file)
          : Promise.resolve(this.url)

        resolver.then(url => {
          xhr.open(this.method, url, true)
          if (this.withCredentials) {
            xhr.withCredentials = true
          }
          if (this.headers) {
            Object.keys(this.headers).forEach(key => {
              xhr.setRequestHeader(key, this.headers[key])
            })
          }

          this.xhrs.push(xhr)
          if (this.sendRaw) {
            xhr.send(file)
          }
          else {
            xhr.send(form)
          }
        })
      })
    },
    pick () {
      if (!this.addDisabled) {
        this.$refs.file.click()
      }
    },
    upload () {
      const length = this.queueLength
      if (this.disable || length === 0) {
        return
      }

      let filesDone = 0
      this.uploadedSize = 0
      this.uploading = true
      this.xhrs = []
      this.$emit('start')

      let solved = () => {
        filesDone++
        if (filesDone === length) {
          this.uploading = false
          this.xhrs = []
          this.queue = this.queue.filter(f => !f.__doneUploading)
          this.__computeTotalSize()
          this.$emit('finish')
        }
      }

      this.queue
        .map(file => this.__getUploadPromise(file))
        .forEach(promise => {
          promise.then(solved).catch(solved)
        })
    },
    abort () {
      this.xhrs.forEach(xhr => { xhr.abort() })
      this.uploading = false
      this.$emit('abort')
    },
    reset () {
      this.abort()
      this.files = []
      this.queue = []
      this.expanded = false
      this.__computeTotalSize()
      this.$emit('reset')
    }
  },

  render (h) {
    const child = [
      h('div', {
        staticClass: 'col q-input-target ellipsis',
        'class': this.alignClass
      }, [ this.label ])
    ]

    if (this.uploading) {
      child.push(
        h(QSpinner, {
          slot: 'after',
          staticClass: 'q-if-end self-center',
          props: { size: '24px' }
        }),
        h(QIcon, {
          slot: 'after',
          staticClass: 'q-if-end self-center q-if-control',
          props: {
            name: this.$q.icon.uploader[`clear${this.isInverted ? 'Inverted' : ''}`]
          },
          nativeOn: {
            click: this.abort
          }
        })
      )
    }
    else { // not uploading
      child.push(
        h(QIcon, {
          slot: 'after',
          staticClass: 'q-uploader-pick-button self-center q-if-control relative-position overflow-hidden',
          props: {
            name: this.$q.icon.uploader.add
          },
          attrs: {
            disabled: this.addDisabled
          }
        }, [
          h('input', {
            ref: 'file',
            staticClass: 'q-uploader-input absolute-full cursor-pointer',
            attrs: Object.assign({
              type: 'file',
              accept: this.extensions
            }, this.multiple ? { multiple: true } : {}),
            on: {
              change: this.__add
            }
          })
        ])
      )

      if (!this.hideUploadButton) {
        child.push(
          h(QIcon, {
            slot: 'after',
            staticClass: 'q-if-control self-center',
            props: {
              name: this.$q.icon.uploader.upload
            },
            attrs: {
              disabled: this.queueLength === 0
            },
            nativeOn: {
              click: this.upload
            }
          })
        )
      }
    }

    if (this.hasExpandedContent) {
      child.push(
        h(QIcon, {
          slot: 'after',
          staticClass: 'q-if-control generic_transition self-center',
          'class': { 'rotate-180': this.expanded },
          props: {
            name: this.$q.icon.uploader.expand
          },
          nativeOn: {
            click: () => { this.expanded = !this.expanded }
          }
        })
      )
    }

    return h('div', {
      staticClass: 'q-uploader relative-position',
      'class': this.classes,
      on: { dragover: this.__onDragOver }
    }, [
      h(QInputFrame, {
        ref: 'input',
        props: {
          prefix: this.prefix,
          suffix: this.suffix,
          stackLabel: this.stackLabel,
          floatLabel: this.floatLabel,
          error: this.error,
          warning: this.warning,
          readonly: this.readonly,
          inverted: this.inverted,
          invertedLight: this.invertedLight,
          dark: this.dark,
          hideUnderline: this.hideUnderline,
          before: this.before,
          after: this.after,
          color: this.color,
          align: this.align,
          noParentField: this.noParentField,
          length: this.queueLength,
          additionalLength: true
        }
      }, child),

      h(QSlideTransition, [
        h('div', {
          'class': this.expandClass,
          style: this.expandStyle,
          directives: [{
            name: 'show',
            value: this.expanded
          }]
        }, [
          h(QList, {
            staticClass: 'q-uploader-files q-py-none scroll',
            style: this.filesStyle,
            props: { dark: this.dark }
          }, this.files.map(file => {
            return h(QItem, {
              key: file.name + file.__timestamp,
              staticClass: 'q-uploader-file q-pa-xs'
            }, [
              (!this.hideUploadProgress && h(QProgress, {
                staticClass: 'q-uploader-progress-bg absolute-full',
                props: {
                  color: file.__failed ? 'negative' : this.progressColor,
                  percentage: file.__progress,
                  height: '100%'
                }
              })) || void 0,

              (!this.hideUploadProgress && h('div', {
                staticClass: 'q-uploader-progress-text absolute'
              }, [ file.__progress + '%' ])) || void 0,

              h(QItemSide, {
                props: file.__img
                  ? { image: file.__img.src }
                  : {
                    icon: this.$q.icon.uploader.file,
                    color: this.color
                  }
              }),

              h(QItemMain, {
                props: {
                  label: file.name,
                  sublabel: file.__size
                }
              }),

              h(QItemSide, { props: { right: true } }, [
                h(QItemTile, {
                  staticClass: 'cursor-pointer',
                  props: {
                    icon: this.$q.icon.uploader[file.__doneUploading ? 'done' : 'clear'],
                    color: this.color
                  },
                  nativeOn: {
                    click: () => { this.__remove(file) }
                  }
                })
              ])
            ])
          }))
        ])
      ]),

      (this.dnd && h('div', {
        staticClass: 'q-uploader-dnd flex row items-center justify-center absolute-full',
        'class': this.dndClass,
        on: {
          dragenter: stopAndPrevent,
          dragover: stopAndPrevent,
          dragleave: this.__onDragLeave,
          drop: this.__onDrop
        }
      })) || void 0
    ])
  }
}
<|endoftext|>


<commit_before>"use strict";

/*  ------------------------------------------------------------------------ */

const [processPath, , exchangeId = null, exchangeSymbol = null] = process.argv.filter (x => !x.startsWith ('--'))
const verbose = process.argv.includes ('--verbose') || false

/*  ------------------------------------------------------------------------ */

const asTable   = require ('as-table')
    , util      = require ('util')
    , log       = require ('ololog')
    , ansi      = require ('ansicolor').nice
    , fs        = require ('fs')
    , assert    = require ('assert')
    , ccxt      = require ('../ccxt')
    , countries = require ('../countries')

/*  ------------------------------------------------------------------------ */

const warn = log.bright.yellow.error // .error goes to stderr

/*  ------------------------------------------------------------------------ */

process.on ('uncaughtException',  e => { log.bright.red.error (e); process.exit (1) })
process.on ('unhandledRejection', e => { log.bright.red.error (e); process.exit (1) })

/*  ------------------------------------------------------------------------ */

log.bright ('\nTESTING', { exchange: exchangeId, symbol: exchangeSymbol || 'all' }, '\n')

/*  ------------------------------------------------------------------------ */

let proxies = [
    '',
    'https://cors-anywhere.herokuapp.com/',
    'https://crossorigin.me/',
    // 'http://cors-proxy.htmldriven.com/?url=', // we don't want this for now
]

/*  ------------------------------------------------------------------------ */

const exchange = new (ccxt)[exchangeId] ({ verbose: verbose, enabledRateLimit: true })

//-----------------------------------------------------------------------------

let apiKeys = JSON.parse (fs.readFileSync ('./keys.json', 'utf8'))[exchangeId]

Object.assign (exchange, apiKeys)

if (exchange.urls['test'])
    exchange.urls['api'] = exchange.urls['test']; // move to testnet/sandbox if possible

const verboseList = [ ];
if (verboseList.indexOf (exchange.id) >= 0) {
    exchange.verbose = true
}

//-----------------------------------------------------------------------------

var countryName = function (code) {
    return ((typeof countries[code] !== 'undefined') ? countries[code] : code)
}

//-----------------------------------------------------------------------------

let human_value = function (price) {
    return typeof price == 'undefined' ? 'N/A' : price
}

//-----------------------------------------------------------------------------

let testExchangeSymbolTicker = async (exchange, symbol) => {
    log (symbol.green, 'fetching ticker...')
    let ticker = await exchange.fetchTicker (symbol)
    log (symbol.green, 'ticker',
        ticker['datetime'],
        'high: '    + human_value (ticker['high']),
        'low: '     + human_value (ticker['low']),
        'bid: '     + human_value (ticker['bid']),
        'ask: '     + human_value (ticker['ask']),
        'volume: '  + human_value (ticker['quoteVolume']))

    if (('bid' in ticker && 'ask' in ticker) && (ticker['bid'] && ticker['ask']))
        assert (ticker['bid'] <= ticker['ask'])

    return ticker;
}

let testExchangeSymbolOrderbook = async (exchange, symbol) => {
    log (symbol.green, 'fetching order book...')
    let orderbook = await exchange.fetchOrderBook (symbol)
    log (symbol.green,
        orderbook['datetime'],
        'bid: '       + ((orderbook.bids.length > 0) ? human_value (orderbook.bids[0][0]) : 'N/A'),
        'bidVolume: ' + ((orderbook.bids.length > 0) ? human_value (orderbook.bids[0][1]) : 'N/A'),
        'ask: '       + ((orderbook.asks.length > 0) ? human_value (orderbook.asks[0][0]) : 'N/A'),
        'askVolume: ' + ((orderbook.asks.length > 0) ? human_value (orderbook.asks[0][1]) : 'N/A'))

    let bids = orderbook.bids
    if (bids.length > 1) {
        let first = 0
        let last = bids.length - 1
        if (bids[first][0] < bids[last][0])
            log (symbol, 'bids reversed!'.red.bright, bids[first][0], bids[last][0])
        else if (bids[first][0] > bids[last][0])
            log (symbol.green, 'bids ok')
    }
    let asks = orderbook.asks
    if (asks.length > 1) {
        let first = 0
        let last = asks.length - 1
        if (asks[first][0] > asks[last][0])
            log (symbol, 'asks reversed!'.red.bright, asks[first][0], asks[last][0])
        else if (asks[first][0] < asks[last][0])
            log (symbol.green, 'asks ok')
    }

    if (bids.length && asks.length)
        if (bids[0][0] > asks[0][0])
            log (this.id, symbol, 'order book', 'bid is greater than ask!'.red.bright)

    return orderbook
}

//-----------------------------------------------------------------------------

let testExchangeSymbolTrades = async (exchange, symbol) => {
    log (symbol.green, 'fetching trades...')
    let trades = await exchange.fetchTrades (symbol)
    log (symbol.green, 'fetched', Object.values (trades).length.toString ().green, 'trades')
    return trades
}

//-----------------------------------------------------------------------------

let testExchangeSymbol = async (exchange, symbol) => {

    await testExchangeSymbolTicker (exchange, symbol)

    if (exchange.hasFetchTickers) {

        log ('fetching all tickers at once...')
        let tickers = await exchange.fetchTickers ()
        log ('fetched', Object.keys (tickers).length.toString ().green, 'tickers')

    } else {

        log ('fetching all tickers at once not supported')
    }

    if (exchange.hasFetchOHLCV) {

        try {

            log (symbol.green, 'fetching OHLCV...')
            let ohlcv = await exchange.fetchOHLCV (symbol)
            log (symbol.green, 'fetched', Object.keys (ohlcv).length.toString ().green, 'OHLCVs')

        } catch (e) {

            if (e instanceof ccxt.ExchangeError) {
                warn ('[Exchange Error] ' + e.message)
            } else if (e instanceof ccxt.NotSupported) {
                warn ('[Not Supported] ' + e.message)
            } else {
                throw e;
            }
        }

    } else {

        log ('fetching OHLCV not supported')
    }

    if (exchange.id == 'coinmarketcap') {

        log (await exchange.fetchTickers ());
        log (await exchange.fetchGlobal ());

    } else {

        await testExchangeSymbolOrderbook (exchange, symbol)

        try {

            await testExchangeSymbolTrades (exchange, symbol)

        } catch (e) {

            if (e instanceof ccxt.ExchangeError) {
                warn ('[Exchange Error] ' + e.message)
            } else if (e instanceof ccxt.NotSupported) {
                warn ('[Not Supported] ' + e.message)
            } else {
                throw e;
            }
        }
    }
}

//-----------------------------------------------------------------------------

let testExchangeFetchOrders = async (exchange) => {

    try {

        log ('fetching orders...')
        let orders = await exchange.fetchOrders ()
        log ('fetched', orders.length.toString ().green, 'orders')

    } catch (e) {

        if (e instanceof ccxt.ExchangeError) {
            warn ('[Exchange Error] ' + e.message)
        } else if (e instanceof ccxt.NotSupported) {
            warn ('[Not Supported] ' + e.message)
        } else {
            throw e;
        }
    }
}

//-----------------------------------------------------------------------------

let testExchangeBalance = async (exchange, symbol) => {

    log ('fetching balance...')
    let balance = await exchange.fetchBalance ()

    let currencies = [
        'USD',
        'CNY',
        'EUR',
        'BTC',
        'ETH',
        'JPY',
        'LTC',
        'DASH',
        'DOGE',
        'UAH',
        'RUB',
    ]

    if ('info' in balance) {

        let result = currencies
            .filter (currency => (currency in balance) &&
                (typeof balance[currency]['total'] != 'undefined'))

        if (result.length > 0) {
            result = result.map (currency => currency + ': ' + human_value (balance[currency]['total']))
            if (exchange.currencies.length > result.length)
                result = result.join (', ') + ' + more...'
            else
                result = result.join (', ')

        } else {

            result = 'zero balance'
        }

        log (result)

    } else {

        log (exchange.omit (balance, 'info'))
    }
}

//-----------------------------------------------------------------------------

let loadExchange = async exchange => {

    let markets  = await exchange.loadMarkets ()

    let symbols = [
        'BTC/CNY',
        'BTC/USD',
        'BTC/EUR',
        'BTC/ETH',
        'ETH/BTC',
        'BTC/JPY',
        'ETH/EUR',
        'ETH/JPY',
        'ETH/CNY',
        'LTC/CNY',
        'DASH/BTC',
        'DOGE/BTC',
        'BTC/AUD',
        'BTC/PLN',
        'USD/SLL',
        'BTC/RUB',
        'BTC/UAH',
        'LTC/BTC',
    ]

    let result = exchange.symbols.filter (symbol => symbols.indexOf (symbol) >= 0)
    if (result.length > 0)
        if (exchange.symbols.length > result.length)
            result = result.join (', ') + ' + more...'
        else
            result = result.join (', ')
    log (exchange.symbols.length.toString ().bright.green, 'symbols', result)
}

//-----------------------------------------------------------------------------

let testExchange = async exchange => {

    await loadExchange (exchange)

    let delay = exchange.rateLimit
    let symbol = exchange.symbols[0]
    let symbols = [
        'BTC/USD',
        'BTC/CNY',
        'BTC/EUR',
        'BTC/ETH',
        'ETH/BTC',
        'BTC/JPY',
        'LTC/BTC',
    ]
    for (let s in symbols) {
        if (exchange.symbols.includes (symbols[s])) {
            symbol = symbols[s]
            break
        }
    }

    log.green ('SYMBOL:', symbol)
    if ((symbol.indexOf ('.d') < 0)) {
        await testExchangeSymbol (exchange, symbol)
    }

    if (!exchange.apiKey || (exchange.apiKey.length < 1))
        return true

    await testExchangeBalance (exchange)

    if (exchange.hasFetchOrders) {

            await testExchangeFetchOrders (exchange);

    } else {

        log ('fetching orders not supported')
    }

    // try {
    //     let marketSellOrder =
    //         await exchange.createMarketSellOrder (exchange.symbols[0], 1)
    //     console.log (exchange.id, 'ok', marketSellOrder)
    // } catch (e) {
    //     console.log (exchange.id, 'error', 'market sell', e)
    // }

    // try {
    //     let marketBuyOrder = await exchange.createMarketBuyOrder (exchange.symbols[0], 1)
    //     console.log (exchange.id, 'ok', marketBuyOrder)
    // } catch (e) {
    //     console.log (exchange.id, 'error', 'market buy', e)
    // }

    // try {
    //     let limitSellOrder = await exchange.createLimitSellOrder (exchange.symbols[0], 1, 3000)
    //     console.log (exchange.id, 'ok', limitSellOrder)
    // } catch (e) {
    //     console.log (exchange.id, 'error', 'limit sell', e)
    // }

    // try {
    //     let limitBuyOrder = await exchange.createLimitBuyOrder (exchange.symbols[0], 1, 3000)
    //     console.log (exchange.id, 'ok', limitBuyOrder)
    // } catch (e) {
    //     console.log (exchange.id, 'error', 'limit buy', e)
    // }

}

//-----------------------------------------------------------------------------

let printExchangesTable = function () {
    let astable = asTable.configure ({ delimiter: ' | ' })
    console.log (astable (Object.values (exchanges).map (exchange => {
        let website = Array.isArray (exchange.urls.www) ?
            exchange.urls.www[0] :
            exchange.urls.www
        let countries = Array.isArray (exchange.countries) ?
            exchange.countries.map (countryName).join (', ') :
            countryName (exchange.countries)
        let doc = Array.isArray (exchange.urls.doc) ?
            exchange.urls.doc[0] :
            exchange.urls.doc
        return {
            'id':        exchange.id,
            'name':      exchange.name,
            'countries': countries,
        }
    })))
}

//-----------------------------------------------------------------------------

let tryAllProxies = async function (exchange, proxies) {

    let currentProxy = 0
    let maxRetries   = proxies.length

    // a special case for ccex
    if (exchange.id == 'ccex')
        currentProxy = 1

    for (let numRetries = 0; numRetries < maxRetries; numRetries++) {

        try {

            exchange.proxy = proxies[currentProxy]
            await testExchange (exchange)
            break

        } catch (e) {

            currentProxy = ++currentProxy % proxies.length
            if (e instanceof ccxt.DDoSProtection) {
                warn ('[DDoS Protection] ' + e.message)
            } else if (e instanceof ccxt.RequestTimeout) {
                warn ('[Request Timeout] ' + e.message)
            } else if (e instanceof ccxt.AuthenticationError) {
                warn ('[Authentication Error] ' + e.message)
            } else if (e instanceof ccxt.ExchangeNotAvailable) {
                warn ('[Exchange Not Available] ' + e.message)
            } else if (e instanceof ccxt.NotSupported) {
                warn ('[Not Supported] ' + e.message)
            } else if (e instanceof ccxt.ExchangeError) {
                warn ('[Exchange Error] ' + e.message)
            } else {
                throw e;
            }
        }
    }
}

//-----------------------------------------------------------------------------

;(async function test () {

    // printExchangesTable ()

    if (exchangeSymbol) {

        await loadExchange (exchange)
        await (exchangeSymbol == 'balance') ?
            testExchangeBalance (exchange) :
            testExchangeSymbol (exchange, exchangeSymbol)

    } else {

        await tryAllProxies (exchange, proxies)
    }

    process.exit ()

}) ()

<commit_msg>shorter and more readable function names in test.js<commit_after>"use strict";

/*  ------------------------------------------------------------------------ */

const [processPath, , exchangeId = null, exchangeSymbol = null] = process.argv.filter (x => !x.startsWith ('--'))
const verbose = process.argv.includes ('--verbose') || false

/*  ------------------------------------------------------------------------ */

const asTable   = require ('as-table')
    , util      = require ('util')
    , log       = require ('ololog')
    , ansi      = require ('ansicolor').nice
    , fs        = require ('fs')
    , assert    = require ('assert')
    , ccxt      = require ('../ccxt')
    , countries = require ('../countries')

/*  ------------------------------------------------------------------------ */

const warn = log.bright.yellow.error // .error goes to stderr

/*  ------------------------------------------------------------------------ */

process.on ('uncaughtException',  e => { log.bright.red.error (e); process.exit (1) })
process.on ('unhandledRejection', e => { log.bright.red.error (e); process.exit (1) })

/*  ------------------------------------------------------------------------ */

log.bright ('\nTESTING', { exchange: exchangeId, symbol: exchangeSymbol || 'all' }, '\n')

/*  ------------------------------------------------------------------------ */

let proxies = [
    '',
    'https://cors-anywhere.herokuapp.com/',
    'https://crossorigin.me/',
    // 'http://cors-proxy.htmldriven.com/?url=', // we don't want this for now
]

/*  ------------------------------------------------------------------------ */

const exchange = new (ccxt)[exchangeId] ({ verbose: verbose, enabledRateLimit: true })

//-----------------------------------------------------------------------------

let apiKeys = JSON.parse (fs.readFileSync ('./keys.json', 'utf8'))[exchangeId]

Object.assign (exchange, apiKeys)

if (exchange.urls['test'])
    exchange.urls['api'] = exchange.urls['test']; // move to testnet/sandbox if possible

const verboseList = [ ];
if (verboseList.indexOf (exchange.id) >= 0) {
    exchange.verbose = true
}

//-----------------------------------------------------------------------------

var countryName = function (code) {
    return ((typeof countries[code] !== 'undefined') ? countries[code] : code)
}

//-----------------------------------------------------------------------------

let human_value = function (price) {
    return typeof price == 'undefined' ? 'N/A' : price
}

//-----------------------------------------------------------------------------

let testTicker = async (exchange, symbol) => {
    log (symbol.green, 'fetching ticker...')
    let ticker = await exchange.fetchTicker (symbol)
    log (symbol.green, 'ticker',
        ticker['datetime'],
        'high: '    + human_value (ticker['high']),
        'low: '     + human_value (ticker['low']),
        'bid: '     + human_value (ticker['bid']),
        'ask: '     + human_value (ticker['ask']),
        'volume: '  + human_value (ticker['quoteVolume']))

    if (('bid' in ticker && 'ask' in ticker) && (ticker['bid'] && ticker['ask']))
        assert (ticker['bid'] <= ticker['ask'])

    return ticker;
}

let testOrderBook = async (exchange, symbol) => {
    log (symbol.green, 'fetching order book...')
    let orderbook = await exchange.fetchOrderBook (symbol)
    log (symbol.green,
        orderbook['datetime'],
        'bid: '       + ((orderbook.bids.length > 0) ? human_value (orderbook.bids[0][0]) : 'N/A'),
        'bidVolume: ' + ((orderbook.bids.length > 0) ? human_value (orderbook.bids[0][1]) : 'N/A'),
        'ask: '       + ((orderbook.asks.length > 0) ? human_value (orderbook.asks[0][0]) : 'N/A'),
        'askVolume: ' + ((orderbook.asks.length > 0) ? human_value (orderbook.asks[0][1]) : 'N/A'))

    let bids = orderbook.bids
    if (bids.length > 1) {
        let first = 0
        let last = bids.length - 1
        if (bids[first][0] < bids[last][0])
            log (symbol, 'bids reversed!'.red.bright, bids[first][0], bids[last][0])
        else if (bids[first][0] > bids[last][0])
            log (symbol.green, 'bids ok')
    }
    let asks = orderbook.asks
    if (asks.length > 1) {
        let first = 0
        let last = asks.length - 1
        if (asks[first][0] > asks[last][0])
            log (symbol, 'asks reversed!'.red.bright, asks[first][0], asks[last][0])
        else if (asks[first][0] < asks[last][0])
            log (symbol.green, 'asks ok')
    }

    if (bids.length && asks.length)
        if (bids[0][0] > asks[0][0])
            log (this.id, symbol, 'order book', 'bid is greater than ask!'.red.bright)

    return orderbook
}

//-----------------------------------------------------------------------------

let testTrades = async (exchange, symbol) => {
    log (symbol.green, 'fetching trades...')
    let trades = await exchange.fetchTrades (symbol)
    log (symbol.green, 'fetched', Object.values (trades).length.toString ().green, 'trades')
    return trades
}

//-----------------------------------------------------------------------------

let testSymbol = async (exchange, symbol) => {

    await testTicker (exchange, symbol)

    if (exchange.hasFetchTickers) {

        log ('fetching all tickers at once...')
        let tickers = await exchange.fetchTickers ()
        log ('fetched', Object.keys (tickers).length.toString ().green, 'tickers')

    } else {

        log ('fetching all tickers at once not supported')
    }

    if (exchange.hasFetchOHLCV) {

        try {

            log (symbol.green, 'fetching OHLCV...')
            let ohlcv = await exchange.fetchOHLCV (symbol)
            log (symbol.green, 'fetched', Object.keys (ohlcv).length.toString ().green, 'OHLCVs')

        } catch (e) {

            if (e instanceof ccxt.ExchangeError) {
                warn ('[Exchange Error] ' + e.message)
            } else if (e instanceof ccxt.NotSupported) {
                warn ('[Not Supported] ' + e.message)
            } else {
                throw e;
            }
        }

    } else {

        log ('fetching OHLCV not supported')
    }

    if (exchange.id == 'coinmarketcap') {

        log (await exchange.fetchTickers ());
        log (await exchange.fetchGlobal ());

    } else {

        await testOrderBook (exchange, symbol)

        try {

            await testTrades (exchange, symbol)

        } catch (e) {

            if (e instanceof ccxt.ExchangeError) {
                warn ('[Exchange Error] ' + e.message)
            } else if (e instanceof ccxt.NotSupported) {
                warn ('[Not Supported] ' + e.message)
            } else {
                throw e;
            }
        }
    }
}

//-----------------------------------------------------------------------------

let testFetchOrders = async (exchange) => {

    try {

        log ('fetching orders...')
        let orders = await exchange.fetchOrders ()
        log ('fetched', orders.length.toString ().green, 'orders')

    } catch (e) {

        if (e instanceof ccxt.ExchangeError) {
            warn ('[Exchange Error] ' + e.message)
        } else if (e instanceof ccxt.NotSupported) {
            warn ('[Not Supported] ' + e.message)
        } else {
            throw e;
        }
    }
}

//-----------------------------------------------------------------------------

let testBalance = async (exchange, symbol) => {

    log ('fetching balance...')
    let balance = await exchange.fetchBalance ()

    let currencies = [
        'USD',
        'CNY',
        'EUR',
        'BTC',
        'ETH',
        'JPY',
        'LTC',
        'DASH',
        'DOGE',
        'UAH',
        'RUB',
    ]

    if ('info' in balance) {

        let result = currencies
            .filter (currency => (currency in balance) &&
                (typeof balance[currency]['total'] != 'undefined'))

        if (result.length > 0) {
            result = result.map (currency => currency + ': ' + human_value (balance[currency]['total']))
            if (exchange.currencies.length > result.length)
                result = result.join (', ') + ' + more...'
            else
                result = result.join (', ')

        } else {

            result = 'zero balance'
        }

        log (result)

    } else {

        log (exchange.omit (balance, 'info'))
    }
}

//-----------------------------------------------------------------------------

let loadExchange = async exchange => {

    let markets  = await exchange.loadMarkets ()

    let symbols = [
        'BTC/CNY',
        'BTC/USD',
        'BTC/EUR',
        'BTC/ETH',
        'ETH/BTC',
        'BTC/JPY',
        'ETH/EUR',
        'ETH/JPY',
        'ETH/CNY',
        'LTC/CNY',
        'DASH/BTC',
        'DOGE/BTC',
        'BTC/AUD',
        'BTC/PLN',
        'USD/SLL',
        'BTC/RUB',
        'BTC/UAH',
        'LTC/BTC',
    ]

    let result = exchange.symbols.filter (symbol => symbols.indexOf (symbol) >= 0)
    if (result.length > 0)
        if (exchange.symbols.length > result.length)
            result = result.join (', ') + ' + more...'
        else
            result = result.join (', ')
    log (exchange.symbols.length.toString ().bright.green, 'symbols', result)
}

//-----------------------------------------------------------------------------

let testExchange = async exchange => {

    await loadExchange (exchange)

    let delay = exchange.rateLimit
    let symbol = exchange.symbols[0]
    let symbols = [
        'BTC/USD',
        'BTC/CNY',
        'BTC/EUR',
        'BTC/ETH',
        'ETH/BTC',
        'BTC/JPY',
        'LTC/BTC',
    ]
    for (let s in symbols) {
        if (exchange.symbols.includes (symbols[s])) {
            symbol = symbols[s]
            break
        }
    }

    log.green ('SYMBOL:', symbol)
    if ((symbol.indexOf ('.d') < 0)) {
        await testSymbol (exchange, symbol)
    }

    if (!exchange.apiKey || (exchange.apiKey.length < 1))
        return true

    await testBalance (exchange)

    if (exchange.hasFetchOrders) {

            await testFetchOrders (exchange);

    } else {

        log ('fetching orders not supported')
    }

    // try {
    //     let marketSellOrder =
    //         await exchange.createMarketSellOrder (exchange.symbols[0], 1)
    //     console.log (exchange.id, 'ok', marketSellOrder)
    // } catch (e) {
    //     console.log (exchange.id, 'error', 'market sell', e)
    // }

    // try {
    //     let marketBuyOrder = await exchange.createMarketBuyOrder (exchange.symbols[0], 1)
    //     console.log (exchange.id, 'ok', marketBuyOrder)
    // } catch (e) {
    //     console.log (exchange.id, 'error', 'market buy', e)
    // }

    // try {
    //     let limitSellOrder = await exchange.createLimitSellOrder (exchange.symbols[0], 1, 3000)
    //     console.log (exchange.id, 'ok', limitSellOrder)
    // } catch (e) {
    //     console.log (exchange.id, 'error', 'limit sell', e)
    // }

    // try {
    //     let limitBuyOrder = await exchange.createLimitBuyOrder (exchange.symbols[0], 1, 3000)
    //     console.log (exchange.id, 'ok', limitBuyOrder)
    // } catch (e) {
    //     console.log (exchange.id, 'error', 'limit buy', e)
    // }

}

//-----------------------------------------------------------------------------

let printExchangesTable = function () {
    let astable = asTable.configure ({ delimiter: ' | ' })
    console.log (astable (Object.values (exchanges).map (exchange => {
        let website = Array.isArray (exchange.urls.www) ?
            exchange.urls.www[0] :
            exchange.urls.www
        let countries = Array.isArray (exchange.countries) ?
            exchange.countries.map (countryName).join (', ') :
            countryName (exchange.countries)
        let doc = Array.isArray (exchange.urls.doc) ?
            exchange.urls.doc[0] :
            exchange.urls.doc
        return {
            'id':        exchange.id,
            'name':      exchange.name,
            'countries': countries,
        }
    })))
}

//-----------------------------------------------------------------------------

let tryAllProxies = async function (exchange, proxies) {

    let currentProxy = 0
    let maxRetries   = proxies.length

    // a special case for ccex
    if (exchange.id == 'ccex')
        currentProxy = 1

    for (let numRetries = 0; numRetries < maxRetries; numRetries++) {

        try {

            exchange.proxy = proxies[currentProxy]
            await testExchange (exchange)
            break

        } catch (e) {

            currentProxy = ++currentProxy % proxies.length
            if (e instanceof ccxt.DDoSProtection) {
                warn ('[DDoS Protection] ' + e.message)
            } else if (e instanceof ccxt.RequestTimeout) {
                warn ('[Request Timeout] ' + e.message)
            } else if (e instanceof ccxt.AuthenticationError) {
                warn ('[Authentication Error] ' + e.message)
            } else if (e instanceof ccxt.ExchangeNotAvailable) {
                warn ('[Exchange Not Available] ' + e.message)
            } else if (e instanceof ccxt.NotSupported) {
                warn ('[Not Supported] ' + e.message)
            } else if (e instanceof ccxt.ExchangeError) {
                warn ('[Exchange Error] ' + e.message)
            } else {
                throw e;
            }
        }
    }
}

//-----------------------------------------------------------------------------

;(async function test () {

    // printExchangesTable ()

    if (exchangeSymbol) {

        await loadExchange (exchange)
        await (exchangeSymbol == 'balance') ?
            testBalance (exchange) :
            testSymbol (exchange, exchangeSymbol)

    } else {

        await tryAllProxies (exchange, proxies)
    }

    process.exit ()

}) ()

<|endoftext|>


<commit_before>// Google BSD license http://code.google.com/google_bsd_license.html
// Copyright 2013 Google Inc. johnjbarton@google.com

// One item of trace data

(function() {
  "use strict";

  QuerypointPanel.ValueViewModel = function() {

  }

  QuerypointPanel.ValueViewModel.prototype = {
    inlineView: function(value, valueType) {
      var result = this['inline_' + valueType](value);
      console.log('ValueViewModel ' + value + " type " + valueType + ' = ' + result);
      return result;
    },
    inline_string: function(value) {
      return '\'' + value + '\'';
    },
    inline_undefined: function(value) {
      return 'undefined';
    },
    inline_number: function(value) {
      return value;
    },
    inline_boolean: function(value) {
      return value ? 'true' : 'false';
    },
    inline_object: function(value) {
      if (!value)
        return 'null';
      return '{' + value + '}';
    },
    inline_function: function(value) {
      return '()';
    },
    inline_xml: function(value) {
      return 'xml';
    }
  };

}());
<commit_msg>add debug flag and guard logging<commit_after>// Google BSD license http://code.google.com/google_bsd_license.html
// Copyright 2013 Google Inc. johnjbarton@google.com

// One item of trace data

(function() {
  "use strict";

  var debug = DebugLogger.register('ValueViewModel', function(flag){
    return debug = (typeof flag === 'boolean') ? flag : debug;
  });

  QuerypointPanel.ValueViewModel = function() {

  }

  QuerypointPanel.ValueViewModel.prototype = {
    inlineView: function(value, valueType) {
      var result = this['inline_' + valueType](value);
      if (debug) console.log('ValueViewModel ' + value + " type " + valueType + ' = ' + result);
      return result;
    },
    inline_string: function(value) {
      return '\'' + value + '\'';
    },
    inline_undefined: function(value) {
      return 'undefined';
    },
    inline_number: function(value) {
      return value;
    },
    inline_boolean: function(value) {
      return value ? 'true' : 'false';
    },
    inline_object: function(value) {
      if (!value)
        return 'null';
      return '{' + value + '}';
    },
    inline_function: function(value) {
      return '()';
    },
    inline_xml: function(value) {
      return 'xml';
    }
  };

}());
<|endoftext|>


<commit_before>var Domain    = require('domain');
var async     = require('async');
var ripple    = require('ripple-lib');
var serverLib = require('../lib/server-lib');

const AccountRootFlags = {
  PasswordSpent:   { name: 'password_spent', value: 0x00010000 },
  RequireDestTag:  { name: 'require_destination_tag', value: 0x00020000 },
  RequireAuth:     { name: 'require_authorization', value: 0x00040000 },
  DisallowXRP:     { name: 'disallow_xrp', value: 0x00080000 },
  DisableMaster:   { name: 'disable_master', value: 0x00100000 }
}

const AccountRootFields = {
  Sequence:       { name:  'transaction_sequence' },
  EmailHash:      { name:  'email_hash' },
  WalletLocator:  { name:  'wallet_locator' },
  WalletSize:     { name:  'wallet_size' },
  MessageKey:     { name:  'message_key' },
  Domain:         { name:  'url', encoding: 'hex' },
  TransferRate:   { name:  'transfer_rate' },
  Signers:        { name:  'signers' }
}

function _requestAccountSettings(remote, account, callback) {
  remote.requestAccountInfo(account, function(err, info) {
    if (err) return callback(err);

    var data = info.account_data;

    var settings = {
      account: data.account,
      transfer_rate: 100
    }

    // Attach account flags
    for (var flagName in AccountRootFlags) {
      var flag = AccountRootFlags[flagName];
      settings[flag.name] = Boolean(data.Flags & flag.value);
    }

    // Attach account fields
    for (var fieldName in AccountRootFields) {
      if (!(fieldName in data)) continue;

      var field = AccountRootFields[fieldName];
      var value = data[fieldName];

      if (field.encoding === 'hex') {
        value = new Buffer(value, 'hex').toString();
      }

      settings[field.name] = value;
    }

    callback(null, settings);
  });
};

exports.get = getSettings;

function getSettings($, req, res, next) {
  var remote = $.remote;

  var opts = {
    account: req.params.account
  }

  function validateOptions(callback) {
    if (!ripple.UInt160.is_valid(opts.account)) {
      return res.json(400, { success: false, message: 'Parameter is not a valid Ripple address: account' });
    }

    callback();
  };

  function ensureConnected(callback) {
    serverLib.ensureConnected(remote, callback);
  };

  function getAccountSettings(connected, callback) {
    if (!connected) {
      res.json(500, { success: false, message: 'No connection to rippled' });
    } else {
      _requestAccountSettings(remote, opts.account, callback);
    }
  };

  var steps = [
    validateOptions,
    ensureConnected,
    getAccountSettings
  ]

  async.waterfall(steps, function(err, settings) {
    if (err) {
      next(err);
    } else {
      res.json(200, { success: true, settings: settings });
    }
  });
};

exports.change = changeSettings;

function changeSettings($, req, res, next) {
  var self = this;
  var remote = $.remote;
  var opts = req.params;

  Object.keys(req.body).forEach(function(param) {
    opts[param] = req.body[param];
  });

  function validateOptions(callback) {
    if (!ripple.UInt160.is_valid(opts.account)) {
      return res.json(400, { success: false, message: 'Parameter is not a valid Ripple address: account' });
    }

    if (!opts.secret) {
      return res.json(400, { success: false, message: 'Parameter is not a valid Ripple secret: secret' });
    }

    callback();
  };

  function ensureConnected(callback) {
    serverLib.ensureConnected(remote, callback);
  };

  function changeAccountSettings(connected, callback) {
    if (!connected) {
      return res.json(500, { success: false, message: 'Remote is not connected' });
    }

    var domain = Domain.create();

    domain.once('error', callback);

    domain.run(function() {
      var transaction = remote.transaction().accountSet(opts.account);

      domain.add(transaction);

      transaction.secret(opts.secret);

      var FlagSet = {
        require_destination_tag: {
          unset: 'OptionalDestTag',
          set: 'RequireDestTag',
        },
        require_authorization: {
          unset: 'OptionalAuth',
          set: 'RequireAuth'
        },
        disallow_xrp: {
          unset: 'AllowXRP',
          set: 'DisallowXRP'
        }
      }

      // Set transaction flags
      for (var flagName in FlagSet) {
        if (!(flagName in opts)) continue;

        var flag = FlagSet[flagName];
        var value = opts[flagName];

        if (typeof value !== 'boolean') {
          return callback(new TypeError('Parameter is not boolean: ' + flagName));
        }

        transaction.setFlags(value ? flag.set : flag.unset);
      }

      // Set transaction fields
      for (var fieldName in AccountRootFields) {
        var field = AccountRootFields[fieldName];
        var value = opts[field.name];

        if (typeof value === 'undefined') continue;

        if (field.encoding === 'hex') {
          value = new Buffer(value).toString('hex');
        }

        transaction.tx_json[fieldName] = value;
      }

      transaction.submit(function(err, res) {
        domain.exit();
        callback(err, res);
      });
    });
  };

  function getAccountSettings(tx_res, callback) {
    _requestAccountSettings(remote, opts.account, function(err, settings) {
      if (err) return callback(err);

      var result = {
        success: true,
        settings: settings,
        hash: tx_res.transaction.hash,
        ledger: String(tx_res.ledger_index)
      }

      callback(null, result);
    });
  };

  var steps = [
    validateOptions,
    ensureConnected,
    changeAccountSettings,
    getAccountSettings
  ]

  async.waterfall(steps, function(err, settings) {
    if (err) {
      next(err);
    } else {
      res.json(200, settings);
    }
  });
};
<commit_msg>Remove domains from api/settings, cleanup<commit_after>var async     = require('async');
var ripple    = require('ripple-lib');
var serverLib = require('../lib/server-lib');

const AccountRootFlags = {
  PasswordSpent:   { name: 'password_spent', value: 0x00010000 },
  RequireDestTag:  { name: 'require_destination_tag', value: 0x00020000 },
  RequireAuth:     { name: 'require_authorization', value: 0x00040000 },
  DisallowXRP:     { name: 'disallow_xrp', value: 0x00080000 },
  DisableMaster:   { name: 'disable_master', value: 0x00100000 }
}

const AccountRootFields = {
  Sequence:       { name:  'transaction_sequence' },
  EmailHash:      { name:  'email_hash' },
  WalletLocator:  { name:  'wallet_locator' },
  WalletSize:     { name:  'wallet_size' },
  MessageKey:     { name:  'message_key' },
  Domain:         { name:  'url', encoding: 'hex' },
  TransferRate:   { name:  'transfer_rate' },
  Signers:        { name:  'signers' }
}

function _requestAccountSettings(remote, account, callback) {
  remote.requestAccountInfo(account, function(err, info) {
    if (err) return callback(err);

    var data = info.account_data;

    var settings = {
      account: data.account,
      transfer_rate: 100
    }

    // Attach account flags
    for (var flagName in AccountRootFlags) {
      var flag = AccountRootFlags[flagName];
      settings[flag.name] = Boolean(data.Flags & flag.value);
    }

    // Attach account fields
    for (var fieldName in AccountRootFields) {
      if (!(fieldName in data)) continue;

      var field = AccountRootFields[fieldName];
      var value = data[fieldName];

      if (field.encoding === 'hex') {
        value = new Buffer(value, 'hex').toString();
      }

      settings[field.name] = value;
    }

    callback(null, settings);
  });
};

exports.get = getSettings;

function getSettings($, req, res, next) {
  var remote = $.remote;

  var opts = {
    account: req.params.account
  }

  function validateOptions(callback) {
    if (!ripple.UInt160.is_valid(opts.account)) {
      return res.json(400, { success: false, message: 'Parameter is not a valid Ripple address: account' });
    }

    callback();
  };

  function ensureConnected(callback) {
    serverLib.ensureConnected(remote, function(err, connected) {
      if (err || !connected) {
        res.json(500, { success: false, message: 'Remote is not connected' });
      } else {
        callback();
      }
    });
  };

  function getAccountSettings(callback) {
    _requestAccountSettings(remote, opts.account, callback);
  };

  var steps = [
    validateOptions,
    ensureConnected,
    getAccountSettings
  ]

  async.waterfall(steps, function(err, settings) {
    if (err) {
      next(err);
    } else {
      res.json(200, { success: true, settings: settings });
    }
  });
};

exports.change = changeSettings;

function changeSettings($, req, res, next) {
  var self = this;
  var remote = $.remote;
  var opts = req.params;

  Object.keys(req.body).forEach(function(param) {
    opts[param] = req.body[param];
  });

  function validateOptions(callback) {
    if (!ripple.UInt160.is_valid(opts.account)) {
      return res.json(400, { success: false, message: 'Parameter is not a valid Ripple address: account' });
    }

    if (!opts.secret) {
      return res.json(400, { success: false, message: 'Parameter missing: secret' });
    }

    callback();
  };

  function ensureConnected(callback) {
    serverLib.ensureConnected(remote, function(err, connected) {
      if (err || !connected) {
        res.json(500, { success: false, message: 'Remote is not connected' });
      } else {
        callback();
      }
    });
  };

  function changeAccountSettings(callback) {
    var FlagSet = {
      require_destination_tag: { unset: 'OptionalDestTag', set: 'RequireDestTag', },
      require_authorization: { unset: 'OptionalAuth', set: 'RequireAuth' },
      disallow_xrp: { unset: 'AllowXRP', set: 'DisallowXRP' }
    }

    var transaction = remote.transaction();

    transaction.once('error', callback);
    transaction.once('final', callback.bind(this, null));

    try {
      transaction.accountSet(opts.account);
      transaction.secret(opts.secret);

      // Set transaction flags
      for (var flagName in FlagSet) {
        if (!(flagName in opts)) continue;

        var flag = FlagSet[flagName];
        var value = opts[flagName];

        if (typeof value !== 'boolean') {
          return res.json(400, { success: false, mesage: 'Parameter is not boolean: ' + flagName });
        }

        transaction.setFlags(value ? flag.set : flag.unset);
      }

      // Set transaction fields
      for (var fieldName in AccountRootFields) {
        var field = AccountRootFields[fieldName];
        var value = opts[field.name];

        if (typeof value === 'undefined') continue;

        if (field.encoding === 'hex') {
          value = new Buffer(value).toString('hex');
        }

        transaction.tx_json[fieldName] = value;
      }
    } catch (e) {
      return res.json(500, { success: false, message: e.message });
    }

    transaction.submit();
  };

  function getAccountSettings(tx_res, callback) {
    _requestAccountSettings(remote, opts.account, function(err, settings) {
      if (err) return callback(err);

      var result = {
        success: true,
        settings: settings,
        hash: tx_res.transaction.hash,
        ledger: String(tx_res.ledger_index)
      }

      callback(null, result);
    });
  };

  var steps = [
    validateOptions,
    ensureConnected,
    changeAccountSettings,
    getAccountSettings
  ]

  async.waterfall(steps, function(err, settings) {
    if (err) {
      next(err);
    } else {
      res.json(200, settings);
    }
  });
};
<|endoftext|>


<commit_before>Elm.Native.Ports = {};
Elm.Native.Ports.make = function(localRuntime) {

	localRuntime.Native = localRuntime.Native || {};
	localRuntime.Native.Ports = localRuntime.Native.Ports || {};
	if (localRuntime.Native.Ports.values)
	{
		return localRuntime.Native.Ports.values;
	}

	var NS;

	// INPUTS

	function inputValue(name, type, converter)
	{
		return initialValue(name, type, converter);
	}

	function inputStream(name, type, converter)
	{
		var input = setupSendSignal(name, type, converter);
		localRuntime.foreignInput[name] = { send: input.send };
		return input.signal;
	}

	function inputVarying(name, type, converter)
	{
		var value = initialValue(name, type, converter);
		var input = setupSendSignal(name, type, converter, value);
		localRuntime.foreignInput[name] = { send: input.send };
		return input.signal;
	}

	function setupSendSignal(name, type, converter, initialValue)
	{
		if (!NS)
		{
			NS = Elm.Native.Signal.make(localRuntime);
		}
		var signal = NS.input(name, initialValue);

		function send(jsValue)
		{
			try
			{
				var elmValue = converter(jsValue);
			}
			catch(e)
			{
				throw new Error(inputError(name, type,
					"You just sent the value:\n\n" +
					"    " + JSON.stringify(input.value) + "\n\n" +
					"but it cannot be converted to the necessary type.\n" +
					e.message
				));
			}
			setTimeout(function() {
				localRuntime.notify(signal.id, elmValue);
			}, 0);
		}

		return {
			send: send,
			signal: signal
		};
	}

	function initialValue(name, type, converter)
	{
		var nameExists = name in localRuntime.givenInputs;

		if (!nameExists)
		{
			throw new Error(inputError(name, type,
				"You must provide an initial value! Something like this in JS:\n\n" +
				"    Elm.fullscreen(Elm.MyModule, { " + name + ": ... });"
			));
		}

		var input = localRuntime.givenInputs[name];
		input.used = true;

		try
		{
			return converter(input.value);
		}
		catch(e)
		{
			throw new Error(inputError(name, type,
				"You gave an initial value of:\n\n" +
				"    " + JSON.stringify(input.value) + "\n\n" +
				"but it cannot be converted to the necessary type.\n" +
				e.message
			));
		}
	}

	function inputError(name, type, message)
	{
		return "Input Error:\n" +
			"Regarding the input named '" + name + "' with type:\n\n" +
			"    " + type.split('\n').join('\n        ') + "\n\n" +
			message;
	}

	// OUTPUTS

	function outputValue(name, converter, value)
	{
		localRuntime.foreignOutput[name] = converter(value);
		return value;
	}

	function outputStream(name, converter, value)
	{
		localRuntime.foreignOutput[name] = setupSubscription(converter, value);
		return value;
	}

	function outputVarying(name, converter, value)
	{
		localRuntime.foreignOutput[name] = setupSubscription(converter, value);
		return value;
	}

	function setupSubscription(converter, signal)
	{
		var subscribers = [];

		function subscribe(handler)
		{
			subscribers.push(handler);
		}
		function unsubscribe(handler)
		{
			subscribers.pop(subscribers.indexOf(handler));
		}

		function notify(elmValue)
		{
			var jsValue = converter(elmValue);
			var len = subscribers.length;
			for (var i = 0; i < len; ++i)
			{
				subscribers[i](jsValue);
			}
		}

		if (!NS)
		{
			NS = Elm.Native.Signal.make(localRuntime);
		}
		NS.output('output', notify, signal);

		return {
			subscribe: subscribe,
			unsubscribe: unsubscribe
		};
	}

	return localRuntime.Native.Ports.values = {
		inputValue: inputValue,
		inputStream: inputStream,
		inputVarying: inputVarying,
		outputValue: outputValue,
		outputStream: outputStream,
		outputVarying: outputVarying,
	};
};
<commit_msg>Implement writableStream<commit_after>Elm.Native.Ports = {};
Elm.Native.Ports.make = function(localRuntime) {

	localRuntime.Native = localRuntime.Native || {};
	localRuntime.Native.Ports = localRuntime.Native.Ports || {};
	if (localRuntime.Native.Ports.values)
	{
		return localRuntime.Native.Ports.values;
	}

	var NS;
	var Promise

	// WRITABLE STREAMS

	function writableStream(name)
	{
		if (!NS)
		{
			NS = Elm.Native.Signal.make(localRuntime);
		}
		if (!Promise)
		{
			Promise = Elm.Native.Promise.make(localRuntime);
		}

		var stream = NS.input(name);

		function send(value) {
			return Promise.asyncFunction(function(callback) {
				localRuntime.setTimeout(function() {
					localRuntime.notify(stream.id, value);
				}, 0);
				succeed(Utils.Tuple0);
			});
		}

		return {
			stream: stream,
			mailbox: {
				ctor: 'Mailbox',
				_0: send
			}
		};
	}

	// INPUTS

	function inputValue(name, type, converter)
	{
		return initialValue(name, type, converter);
	}

	function inputStream(name, type, converter)
	{
		var input = setupSendSignal(name, type, converter);
		localRuntime.foreignInput[name] = { send: input.send };
		return input.signal;
	}

	function inputVarying(name, type, converter)
	{
		var value = initialValue(name, type, converter);
		var input = setupSendSignal(name, type, converter, value);
		localRuntime.foreignInput[name] = { send: input.send };
		return input.signal;
	}

	function setupSendSignal(name, type, converter, initialValue)
	{
		if (!NS)
		{
			NS = Elm.Native.Signal.make(localRuntime);
		}
		var signal = NS.input(name, initialValue);

		function send(jsValue)
		{
			try
			{
				var elmValue = converter(jsValue);
			}
			catch(e)
			{
				throw new Error(inputError(name, type,
					"You just sent the value:\n\n" +
					"    " + JSON.stringify(input.value) + "\n\n" +
					"but it cannot be converted to the necessary type.\n" +
					e.message
				));
			}
			setTimeout(function() {
				localRuntime.notify(signal.id, elmValue);
			}, 0);
		}

		return {
			send: send,
			signal: signal
		};
	}

	function initialValue(name, type, converter)
	{
		var nameExists = name in localRuntime.givenInputs;

		if (!nameExists)
		{
			throw new Error(inputError(name, type,
				"You must provide an initial value! Something like this in JS:\n\n" +
				"    Elm.fullscreen(Elm.MyModule, { " + name + ": ... });"
			));
		}

		var input = localRuntime.givenInputs[name];
		input.used = true;

		try
		{
			return converter(input.value);
		}
		catch(e)
		{
			throw new Error(inputError(name, type,
				"You gave an initial value of:\n\n" +
				"    " + JSON.stringify(input.value) + "\n\n" +
				"but it cannot be converted to the necessary type.\n" +
				e.message
			));
		}
	}

	function inputError(name, type, message)
	{
		return "Input Error:\n" +
			"Regarding the input named '" + name + "' with type:\n\n" +
			"    " + type.split('\n').join('\n        ') + "\n\n" +
			message;
	}

	// OUTPUTS

	function outputValue(name, converter, value)
	{
		localRuntime.foreignOutput[name] = converter(value);
		return value;
	}

	function outputStream(name, converter, value)
	{
		localRuntime.foreignOutput[name] = setupSubscription(converter, value);
		return value;
	}

	function outputVarying(name, converter, value)
	{
		localRuntime.foreignOutput[name] = setupSubscription(converter, value);
		return value;
	}

	function setupSubscription(converter, signal)
	{
		var subscribers = [];

		function subscribe(handler)
		{
			subscribers.push(handler);
		}
		function unsubscribe(handler)
		{
			subscribers.pop(subscribers.indexOf(handler));
		}

		function notify(elmValue)
		{
			var jsValue = converter(elmValue);
			var len = subscribers.length;
			for (var i = 0; i < len; ++i)
			{
				subscribers[i](jsValue);
			}
		}

		if (!NS)
		{
			NS = Elm.Native.Signal.make(localRuntime);
		}
		NS.output('output', notify, signal);

		return {
			subscribe: subscribe,
			unsubscribe: unsubscribe
		};
	}

	return localRuntime.Native.Ports.values = {
		inputValue: inputValue,
		inputStream: inputStream,
		inputVarying: inputVarying,
		outputValue: outputValue,
		outputStream: outputStream,
		outputVarying: outputVarying,
		writableStream: writableStream
	};
};
<|endoftext|>


<commit_before>var request = require('request');
var assert = require('assert');
var http = require('http');
var app = require('../app');
var database = require('../routes/database');
var origin = 'http://localhost:3000';
var testLinks = ['/', '/itemdata', '/collectiondata', '/teamdata'];

describe('App', function() {
  before(function(done) {
    this.timeout(20000);
    database.connectDb(function (err) {
      if (err) throw err;
      var server = http.createServer(app);
      server.listen('3000');
      done();
    });
  });
  describe('MongoDB', function() {
    describe('Database connection', function() {
      it('Connects to Mongo Atlas', function(done) {
        assert.ok(database.getDb().serverConfig.isConnected());
        done();
      });
    });
  });
  describe('Pages', function() {
    for (var i = 0; i < testLinks.length; i++) {
      var page = origin + testLinks[i];
      testPages(page);
    }
  });
});

function testPages(page) {
  it(page + ' returns status code 200', function(done) {
    this.timeout(5000);
    request.get(page, function(err, res) {
      assert.equal(200, res.statusCode);
      done();
    });
  });
}
<commit_msg>Extend test links<commit_after>var request = require('request');
var assert = require('assert');
var http = require('http');
var app = require('../app');
var database = require('../routes/database');
var origin = 'http://localhost:3000';
var testLinks = ['/', '/itemdata', '/collectiondata', '/teamdata', '/portfolio'];

describe('App', function() {
  before(function(done) {
    this.timeout(20000);
    database.connectDb(function (err) {
      if (err) throw err;
      var server = http.createServer(app);
      server.listen('3000');
      done();
    });
  });
  describe('MongoDB', function() {
    describe('Database connection', function() {
      it('Connects to Mongo Atlas', function(done) {
        assert.ok(database.getDb().serverConfig.isConnected());
        done();
      });
    });
  });
  describe('Pages', function() {
    for (var i = 0; i < testLinks.length; i++) {
      var page = origin + testLinks[i];
      testPages(page);
    }
  });
});

function testPages(page) {
  it(page + ' returns status code 200', function(done) {
    this.timeout(5000);
    request.get(page, function(err, res) {
      assert.equal(200, res.statusCode);
      done();
    });
  });
}
<|endoftext|>


<commit_before><commit_msg>read server options from mongoose_options<commit_after><|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
//
//	FEEDBACK DELAY
//
///////////////////////////////////////////////////////////////////////////////

define(["Tone/core/Tone", "Tone/effects/FeedbackEffect"], function(Tone){

	//@param {number} delayTime
	Tone.FeedbackDelay = function(delayTime){
		Tone.FeedbackEffect.call(this);

		this.delay = this.context.createDelay(4);
		this.delay.delayTime.value = this.toSeconds(this.defaultArg(delayTime, .25));

		//connect it up
		this.connectEffect(this.delay);
	}

	Tone.extend(Tone.FeedbackDelay, Tone.FeedbackEffect);

	Tone.FeedbackDelay.prototype.setDelayTime = function(delayTime){
		this.rampToValueNow(this.delay.delayTime, this.toSeconds(delayTime));
	}

	return Tone.FeedbackDelay;
});<commit_msg>updated time in feedback delay<commit_after>///////////////////////////////////////////////////////////////////////////////
//
//	FEEDBACK DELAY
//
///////////////////////////////////////////////////////////////////////////////

define(["Tone/core/Tone", "Tone/effects/FeedbackEffect"], function(Tone){

	//@param {number} delayTime
	Tone.FeedbackDelay = function(delayTime){
		Tone.FeedbackEffect.call(this);

		this.delay = this.context.createDelay(4);
		this.delay.delayTime.value = this.toSeconds(this.defaultArg(delayTime, .25));

		//connect it up
		this.connectEffect(this.delay);
	}

	Tone.extend(Tone.FeedbackDelay, Tone.FeedbackEffect);

	/**
	 *  sets the delay time
	 *  @param {Tone.Time} time
	 */
	Tone.FeedbackDelay.prototype.setDelayTime = function(time){
		this.rampToValueNow(this.delay.delayTime, this.toSeconds(time));
	}

	return Tone.FeedbackDelay;
});<|endoftext|>


<commit_before>var tape = require('wrapping-tape');
var $ = require('jquery-browserify');

var MouseTracker = require('../');
var mousetracker;

// Setup and Teardown
var test = tape({
    setup: function(t) {
        document.body.innerHTML = '<input type="checkbox">
<button>Hi There</button>';
        mousetracker = new MouseTracker($, function(args) {
            return !true;
        });
        t.end();
    },
    teardown: function(t) {
        mousetracker.stop();
        t.end();
    }
});

test('click handling', function(t) {
    t.plan(3);
    mousetracker.start(callback);

    click($('button'), 6, 6);

    function callback(args) {
        t.equal(args.selector, 'body *:nth-child(3)', 'selectors are equal');
        t.equal(args.offsetX, 6, 'horizontal offset equal');
        t.equal(args.offsetY, 6, 'vertical offset equal');
        t.end();
    }
});

test('click trigger', function(t) {
    t.plan(3);

    $(document).on('click', callback);

    mousetracker.triggerClick($('button'), 6, 6);

    function callback(event) {
        t.equal(event.target, $('button')[0], 'clicked right element');
        t.equal(event.pageX, $('button').offset().left + 6, 'clicked right horizontal element offset');
        t.equal(event.pageY, $('button').offset().top + 6, 'clicked right vertical element offset');
        t.end();
    }
});

test('absolute mousemove handling', function(t) {
    t.plan(2);
    mousetracker.start(null, callback);

    mousemove($(document), 300, 300);

    function callback(args) {
        t.equal(args.top, 300, 'absolute vertical mousemove correct');
        t.equal(args.left, 300, 'absolute horizontal mousemove correct');
        t.end();
    }
});

test('relative mousemove handling', function(t) {
    t.plan(3);
    mousetracker.start(null, callback);

    var offset = $('button').offset();

    mousemove($(document), offset.left + 6, offset.top + 6, $('button')[0]);

    function callback(args) {
        t.equal(args.selector, 'body *:nth-child(3)', 'correct element mousemove')
        t.equal(args.top, 6, 'relative vertical mousemove correct');
        t.equal(args.left, 6, 'relative horizontal mousemove correct');
        t.end();
    }
});

function click(element, offsetX, offsetY) {
    var offset = element.offset();
    var event = $.Event("click", {
        which: 1,
        pageX: offset.left + offsetX,
        pageY: offset.top + offsetY
    });
    element.trigger(event);
}

function mousemove(element, pageX, pageY, target) {
    var event = $.Event("mousemove", {
        target: target,
        pageX: pageX,
        pageY: pageY
    });
    element.trigger(event);
}<commit_msg>Remove t.end()s<commit_after>var tape = require('wrapping-tape');
var $ = require('jquery-browserify');

var MouseTracker = require('../');
var mousetracker;

// Setup and Teardown
var test = tape({
    setup: function(t) {
        document.body.innerHTML = '<input type="checkbox">
<button>Hi There</button>';
        mousetracker = new MouseTracker($, function(args) {
            return !true;
        });
        t.end();
    },
    teardown: function(t) {
        mousetracker.stop();
        t.end();
    }
});

test('click handling', function(t) {
    t.plan(3);
    mousetracker.start(callback);

    click($('button'), 6, 6);

    function callback(args) {
        t.equal(args.selector, 'body *:nth-child(3)', 'selectors are equal');
        t.equal(args.offsetX, 6, 'horizontal offset equal');
        t.equal(args.offsetY, 6, 'vertical offset equal');
    }
});

test('click trigger', function(t) {
    t.plan(3);

    $(document).on('click', callback);

    mousetracker.triggerClick($('button'), 6, 6);

    function callback(event) {
        t.equal(event.target, $('button')[0], 'clicked right element');
        t.equal(event.pageX, $('button').offset().left + 6, 'clicked right horizontal element offset');
        t.equal(event.pageY, $('button').offset().top + 6, 'clicked right vertical element offset');
    }
});

test('absolute mousemove handling', function(t) {
    t.plan(2);
    mousetracker.start(null, callback);

    mousemove($(document), 300, 300);

    function callback(args) {
        t.equal(args.top, 300, 'absolute vertical mousemove correct');
        t.equal(args.left, 300, 'absolute horizontal mousemove correct');
    }
});

test('relative mousemove handling', function(t) {
    t.plan(3);
    mousetracker.start(null, callback);

    var offset = $('button').offset();

    mousemove($(document), offset.left + 6, offset.top + 6, $('button')[0]);

    function callback(args) {
        t.equal(args.selector, 'body *:nth-child(3)', 'correct element mousemove')
        t.equal(args.top, 6, 'relative vertical mousemove correct');
        t.equal(args.left, 6, 'relative horizontal mousemove correct');
    }
});

function click(element, offsetX, offsetY) {
    var offset = element.offset();
    var event = $.Event("click", {
        which: 1,
        pageX: offset.left + offsetX,
        pageY: offset.top + offsetY
    });
    element.trigger(event);
}

function mousemove(element, pageX, pageY, target) {
    var event = $.Event("mousemove", {
        target: target,
        pageX: pageX,
        pageY: pageY
    });
    element.trigger(event);
}<|endoftext|>


<commit_before><commit_msg>Read only specific ssl parts<commit_after><|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
//
//	PING PONG DELAY
//
///////////////////////////////////////////////////////////////////////////////

define(["Tone/core/Tone", "Tone/effects/FeedbackDelay"], function(Tone){

	//@param {number=} delayTime
	Tone.PingPongDelay = function(delayTime){
		Tone.StereoSplit.call(this);

		//components
		this.leftDelay = new Tone.FeedbackDelay(delayTime);
		this.rightDelay = new Tone.FeedbackDelay(delayTime);

		//connect it up
		this.connectLeft(this.leftDelay);
		this.connectRight(this.rightDelay);

		//disconnect the feedback lines to connect them to the other delay
		// http://jvzaudio.files.wordpress.com/2011/04/delay-f43.gif
		this.leftDelay.feedback.disconnect();
		this.rightDelay.feedback.disconnect();
		this.leftDelay.feedback.connect(this.rightDelay.effectSend);
		this.rightDelay.feedback.connect(this.leftDelay.effectSend);

		//initial vals;
		this.setDelayTime(delayTime);
	}

	Tone.extend(Tone.PingPongDelay, Tone.StereoSplit);

	//@param {number} delayTime
	Tone.PingPongDelay.prototype.setDelayTime = function(delayTime){
		this.leftDelay.setDelayTime(delayTime);
		this.rightDelay.setDelayTime(delayTime * 2);
	}

	//@param {number} feedback (0 - 1)
	Tone.PingPongDelay.prototype.setFeedback = function(feedback){
		this.leftDelay.setFeedback(feedback);
		this.rightDelay.setFeedback(feedback);
	}

	//@param {number} wet (0 - 1)
	Tone.PingPongDelay.prototype.setWet = function(wet){
		this.leftDelay.setWet(wet);
		this.rightDelay.setWet(wet);
	}

	//@param {number} dry (0 - 1)
	Tone.PingPongDelay.prototype.setDry = function(dry){
		this.leftDelay.setDry(dry);
		this.rightDelay.setDry(dry);
	}

	return Tone.PingPongDelay;
});<commit_msg>linting + documentation for pingpongdelay<commit_after>define(["Tone/core/Tone", "Tone/effects/FeedbackDelay"], function(Tone){
	/**
	 * PingPongDelay is a dual delay effect where the echo is heard first in one channel and next in the opposite channel
	 *
	 * @param {Tone.Time} delayTime is the interval between consecutive echos
	 */
	Tone.PingPongDelay = function(delayTime){
		Tone.StereoSplit.call(this);

		//components
		this.leftDelay = new Tone.FeedbackDelay(delayTime);
		this.rightDelay = new Tone.FeedbackDelay(delayTime);

		//connect it up
		this.connectLeft(this.leftDelay);
		this.connectRight(this.rightDelay);

		//disconnect the feedback lines to connect them to the other delay
		// http://jvzaudio.files.wordpress.com/2011/04/delay-f43.gif
		this.leftDelay.feedback.disconnect();
		this.rightDelay.feedback.disconnect();
		this.leftDelay.feedback.connect(this.rightDelay.effectSend);
		this.rightDelay.feedback.connect(this.leftDelay.effectSend);

		//initial vals;
		this.setDelayTime(delayTime);
	};

	Tone.extend(Tone.PingPongDelay, Tone.StereoSplit);

	/**
	 * setDelayTime
	 *
	 * @param {Tone.Time} delayTime
	 */
	Tone.PingPongDelay.prototype.setDelayTime = function(delayTime){
		this.leftDelay.setDelayTime(delayTime);
		this.rightDelay.setDelayTime(delayTime * 2);
	};

	/**
	 * setFeedback
	 *
	 * @param {number} feedback (0 - 1)
	 */
	Tone.PingPongDelay.prototype.setFeedback = function(feedback){
		this.leftDelay.setFeedback(feedback);
		this.rightDelay.setFeedback(feedback);
	};

	/**
	 * setWet
	 *
	 * @param {number} wet (0 - 1)
	 */
	Tone.PingPongDelay.prototype.setWet = function(wet){
		this.leftDelay.setWet(wet);
		this.rightDelay.setWet(wet);
	};

	/**
	 * setDry
	 *
	 * @param {number} dry (0 - 1)
	 */
	Tone.PingPongDelay.prototype.setDry = function(dry){
		this.leftDelay.setDry(dry);
		this.rightDelay.setDry(dry);
	};

	return Tone.PingPongDelay;
});<|endoftext|>