<commit_before>/* eslint-env browser */
/* global Vue, S2 */

var s2 = new S2('https://s2.unlessquit.com')

var audioPath = '/examples/audio'
var audioUrl = 'https://s2.unlessquit.com' + audioPath

// TODO: Move to s2.js
S2.prototype.store = function (key, value, options, callback) {
  var req = new XMLHttpRequest()
  req.addEventListener('load', function () {
    if (this.status !== 200) {
      callback(req, null)
      return
    }

    if (callback) {
      callback(null, this.responseText.trim())
    }
  })
  req.open('PUT', this.serverUrl + key)
  if (options['content-type']) {
    req.setRequestHeader('Content-Type', options['content-type'])
  }
  req.send(value)
}

Vue.component('server-audio', {
  render: function (h) {
    return this.$audio = h('audio')
  },
  mounted: function () {
    this.$audio.elm.oncanplaythrough = () => this.$audio.elm.play()
    this.$audio.elm.onended = () => this.$emit('ended')
    this.$audio.elm.onerror = () => this.$emit('ended')
    this.$audio.elm.src = audioUrl
  }
})

Vue.component('local-audio', {
  props: ['recordedAudio'],
  render: function (h) {
    if (this.recordedAudio) {
      console.log('Going to play recorded audio');
    }
    return this.$audio = h('audio')
  },
  mounted: function () {
    this.play()
  },
  updated: function () {
    this.play()
  },
  methods: {
    play: function () {
      if (!this.recordedAudio) return
      this.$audio.elm.src = window.URL.createObjectURL(this.recordedAudio.data)
      this.$audio.elm.play()
    }
  }
})

Vue.component('record-button', {
  props: ['mediaRecorder'],
  data: () => {
    return {
      chunks: [],
      isRecording: false
    }
  },
  render: function (h) {
    return h('div', {attrs: {id: 'record'}}, [
      h('div', {attrs: {id: 'button'},
                on: {mousedown: this.startRecording,
                     mouseup: this.stopRecording,
                     mouseout: this.stopRecording}},
        this.buttonMessage),
      h('span', 'Record message which will be played to others when they visit this page.')
    ])
  },
  computed: {
    buttonMessage: function () {
      if (this.isRecording) return 'Recording...'

      return 'Push to record'
    }
  },
  methods: {
    startRecording: function (e) {
      // 150ms delay so that we don't record mouse click
      setTimeout(() => this.mediaRecorder.start(), 150)
      this.chunks = []
      this.isRecording = true
      this.mediaRecorder.ondataavailable = this.onAudioChunk
    },
    stopRecording: function (e) {
      if (!this.isRecording) return

      this.mediaRecorder.stop()
      this.isRecording = false
    },
    onAudioChunk: function (e) {
      console.log('Recording: ', e.data.type)
      this.chunks.push(e.data)

      if (!this.isRecording) {
        var recording = {
          type: e.data.type,
          data: new Blob(this.chunks, {type: e.data.type})
        }

        this.$emit('recorded', recording)

        s2.store(
          audioPath,
          recording.data,
          {'content-type': recording.type},
          (err, id) => {
            if (err) {
              console.error('Failed to upload audio', err);
              return
            }

            console.log('Stored as', id)
          }
        )
      }
    }
  }
})

var app = new Vue({
  el: '#app',
  data: {
    notSupported: false,
    mediaRecorder: null,
    isPlayingServerAudio: true,
    recordedAudio: null
  },
  render: function (h) {
    return h(
      'div', {attrs: {id: 'app'}},
      this.notSupported
        ? [h('div', {attrs: {id: 'record'}}, ':/')]
        : [h('local-audio', {props: {recordedAudio: this.recordedAudio}}),
           this.isPlayingServerAudio
             ? [h('server-audio', {on: {ended: this.onEndedPlaying}}), 'Playing...']
             : h('record-button', {props: {mediaRecorder: this.mediaRecorder},
                                   on: {recorded: this.onRecorded}})])
  },
  methods: {
    onRecorded: function (e) {
      this.recordedAudio = e
    },
    onEndedPlaying: function (e) {
      this.isPlayingServerAudio = false
    }
  }
})

function init() {
  if (!navigator.getUserMedia) {
    alert('Your browser doesn\'t support getUserMedia')
    app.notSupported = true
    return
  }

  navigator.getUserMedia(
    {audio: true},
    function (stream) {
      app.mediaRecorder = new MediaRecorder(stream)
    },
    function (err) {
      console.log('Failed to initialize MediaRecorder');
    }
  )
}

init()
<commit_msg>Make recording work on mobile (register touch events)<commit_after>/* eslint-env browser */
/* global Vue, S2 */

var s2 = new S2('https://s2.unlessquit.com')

var audioPath = '/examples/audio'
var audioUrl = 'https://s2.unlessquit.com' + audioPath

// TODO: Move to s2.js
S2.prototype.store = function (key, value, options, callback) {
  var req = new XMLHttpRequest()
  req.addEventListener('load', function () {
    if (this.status !== 200) {
      callback(req, null)
      return
    }

    if (callback) {
      callback(null, this.responseText.trim())
    }
  })
  req.open('PUT', this.serverUrl + key)
  if (options['content-type']) {
    req.setRequestHeader('Content-Type', options['content-type'])
  }
  req.send(value)
}

Vue.component('server-audio', {
  render: function (h) {
    return this.$audio = h('audio')
  },
  mounted: function () {
    this.$audio.elm.oncanplaythrough = () => this.$audio.elm.play()
    this.$audio.elm.onended = () => this.$emit('ended')
    this.$audio.elm.onerror = () => this.$emit('ended')
    this.$audio.elm.src = audioUrl
  }
})

Vue.component('local-audio', {
  props: ['recordedAudio'],
  render: function (h) {
    if (this.recordedAudio) {
      console.log('Going to play recorded audio');
    }
    return this.$audio = h('audio')
  },
  mounted: function () {
    this.play()
  },
  updated: function () {
    this.play()
  },
  methods: {
    play: function () {
      if (!this.recordedAudio) return
      this.$audio.elm.src = window.URL.createObjectURL(this.recordedAudio.data)
      this.$audio.elm.play()
    }
  }
})

Vue.component('record-button', {
  props: ['mediaRecorder'],
  data: () => {
    return {
      chunks: [],
      isRecording: false
    }
  },
  render: function (h) {
    return h('div', {attrs: {id: 'record'}}, [
      h('div', {attrs: {id: 'button'},
                on: {touchstart: this.startRecording,
                     touchend: this.stopRecording,
                     mousedown: this.startRecording,
                     mouseup: this.stopRecording,
                     mouseout: this.stopRecording}},
        this.buttonMessage),
      h('span', 'Record message which will be played to others when they visit this page.')
    ])
  },
  computed: {
    buttonMessage: function () {
      if (this.isRecording) return 'Recording...'

      return 'Push to record'
    }
  },
  methods: {
    startRecording: function (e) {
      e.preventDefault()
      // 150ms delay so that we don't record mouse click
      setTimeout(() => this.mediaRecorder.start(), 150)
      this.chunks = []
      this.isRecording = true
      this.mediaRecorder.ondataavailable = this.onAudioChunk
    },
    stopRecording: function (e) {
      e.preventDefault()
      if (!this.isRecording) return

      this.mediaRecorder.stop()
      this.isRecording = false
    },
    onAudioChunk: function (e) {
      console.log('Recording: ', e.data.type)
      this.chunks.push(e.data)

      if (!this.isRecording) {
        var recording = {
          type: e.data.type,
          data: new Blob(this.chunks, {type: e.data.type})
        }

        this.$emit('recorded', recording)

        s2.store(
          audioPath,
          recording.data,
          {'content-type': recording.type},
          (err, id) => {
            if (err) {
              console.error('Failed to upload audio', err);
              return
            }

            console.log('Stored as', id)
          }
        )
      }
    }
  }
})

var app = new Vue({
  el: '#app',
  data: {
    notSupported: false,
    mediaRecorder: null,
    isPlayingServerAudio: true,
    recordedAudio: null
  },
  render: function (h) {
    return h(
      'div', {attrs: {id: 'app'}},
      this.notSupported
        ? [h('div', {attrs: {id: 'record'}}, ':/')]
        : [h('local-audio', {props: {recordedAudio: this.recordedAudio}}),
           this.isPlayingServerAudio
             ? [h('server-audio', {on: {ended: this.onEndedPlaying}}), 'Playing...']
             : h('record-button', {props: {mediaRecorder: this.mediaRecorder},
                                   on: {recorded: this.onRecorded}})])
  },
  methods: {
    onRecorded: function (e) {
      this.recordedAudio = e
    },
    onEndedPlaying: function (e) {
      this.isPlayingServerAudio = false
    }
  }
})

function init() {
  if (!navigator.getUserMedia) {
    alert('Your browser doesn\'t support getUserMedia')
    app.notSupported = true
    return
  }

  navigator.getUserMedia(
    {audio: true},
    function (stream) {
      app.mediaRecorder = new MediaRecorder(stream)
    },
    function (err) {
      console.log('Failed to initialize MediaRecorder');
    }
  )
}

init()

{'fraction_non_alphanumeric': 0.15620580876754053, 'fraction_numerical': 0.0036984662243010986, 'mean_word_length': 2.2788873038516404, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2298562', 'n_tokens_mistral': 3003, 'n_tokens_neox': 2920, 'n_words': 619}