<commit_before>import createIdenticon from './identicon'
import icons from './icons'
import createDropdown from './dropdown'
import localeTime from './locale-time'
import theme from './theme'
import { videoToGif } from './gif'

const MESSAGE_LIMIT = 30
const MAX_RECYCLED = 0

const MESSAGE_HTML = `
  <div class="video-container">
    <video class="message-video" muted loop></video>
    <button class="save shadow-1" title="Save as GIF"></button>
  </div>
  <p>
  <div class="message-meta">
    <div class="dropdown">
      <button class="toggle message-overflow" title="Message options"></button>
      <div class="menu shadow-2">
        <button data-action="mute">Mute user</button>
      </div>
    </div>
    <div class="identicon"></div>
    <time></time>
  </div>`

class Message {
  constructor(owner) {
    this._disposed = false
    this._userId = null
    this._srcUrl = null
    this._isVisible = false
    this._playPauseRequest = null
    this.owner = owner

    this.root = document.createElement('li')
    this.root.innerHTML = MESSAGE_HTML
    this.videoContainer = this.root.querySelector('.video-container')
    this.video = this.root.querySelector('.message-video')
    this.saveButton = this.root.querySelector('.save')
    this.chatText = this.root.querySelector('p')
    this.timestamp = this.root.querySelector('time')
    // placeholder div so it can be replaced with the real thing when bound
    this.identicon = this.root.querySelector('.identicon')
    this.muteButton = this.root.querySelector('.menu button')
    this.messageOverflow = this.root.querySelector('.message-overflow')

    // generate icons where needed
    this.saveButton.appendChild(icons.save('invert'))
    this.messageOverflow.appendChild(icons.moreVert('normal'))

    this.saveButton.addEventListener('click', () => this.saveGif())
    this.dropdown = createDropdown(this.messageOverflow.parentElement, {
      mute: () => this.mute(),
    })
  }

  bind({ key, text, sent, userId, from, video, videoMime, videoType }, myId) {
    this._throwIfDisposed()
    this.unbind()

    const blob = new window.Blob([video], { type: videoMime })
    this._srcUrl = window.URL.createObjectURL(blob)
    this.video.src = this._srcUrl

    this.chatText.innerHTML = text

    const sentDate = new Date(sent)
    this.timestamp.datetime = sentDate.toISOString()
    this.timestamp.innerHTML = localeTime(sentDate)

    if (myId === userId) {
      // No mute menu for yourself
      this.messageOverflow.setAttribute('disabled', true)
    }
    this._userId = userId
    this.refreshIdenticon()

    this._key = key
  }

  refreshIdenticon() {
    const newIdenticon = createIdenticon(this._userId)
    this.identicon.parentElement.replaceChild(newIdenticon, this.identicon)
    this.identicon = newIdenticon
  }

  unbind() {
    this._throwIfDisposed()

    if (this._playPauseRequest) {
      cancelAnimationFrame(this._playPauseRequest)
      this._playPauseRequest = null
    }

    this._userId = null
    this._key = null
    this.dropdown.close()

    this._isVisible = false
    delete this.video.src

    if (this._srcUrl) {
      window.URL.revokeObjectURL(this._srcUrl)
      this._srcUrl = null
    }

    this.messageOverflow.removeAttribute('disabled')
  }

  dispose() {
    this._throwIfDisposed()
    this._disposed = true
  }

  // TODO(tec27): need to fix this for video
  saveGif() {
    this._throwIfDisposed()
    this.saveButton.disabled = true
    this.owner.trackSaveGif()

    videoToGif({ videoElem: this.video, numFrames: 10 })
      .then(gifBlob => {
        this.saveButton.disabled = false
        const url = window.URL.createObjectURL(gifBlob)<commit_msg>Remove a todo that was completed.<commit_after>import createIdenticon from './identicon'
import icons from './icons'
import createDropdown from './dropdown'
import localeTime from './locale-time'
import theme from './theme'
import { videoToGif } from './gif'

const MESSAGE_LIMIT = 30
const MAX_RECYCLED = 0

const MESSAGE_HTML = `
  <div class="video-container">
    <video class="message-video" muted loop></video>
    <button class="save shadow-1" title="Save as GIF"></button>
  </div>
  <p>
  <div class="message-meta">
    <div class="dropdown">
      <button class="toggle message-overflow" title="Message options"></button>
      <div class="menu shadow-2">
        <button data-action="mute">Mute user</button>
      </div>
    </div>
    <div class="identicon"></div>
    <time></time>
  </div>`

class Message {
  constructor(owner) {
    this._disposed = false
    this._userId = null
    this._srcUrl = null
    this._isVisible = false
    this._playPauseRequest = null
    this.owner = owner

    this.root = document.createElement('li')
    this.root.innerHTML = MESSAGE_HTML
    this.videoContainer = this.root.querySelector('.video-container')
    this.video = this.root.querySelector('.message-video')
    this.saveButton = this.root.querySelector('.save')
    this.chatText = this.root.querySelector('p')
    this.timestamp = this.root.querySelector('time')
    // placeholder div so it can be replaced with the real thing when bound
    this.identicon = this.root.querySelector('.identicon')
    this.muteButton = this.root.querySelector('.menu button')
    this.messageOverflow = this.root.querySelector('.message-overflow')

    // generate icons where needed
    this.saveButton.appendChild(icons.save('invert'))
    this.messageOverflow.appendChild(icons.moreVert('normal'))

    this.saveButton.addEventListener('click', () => this.saveGif())
    this.dropdown = createDropdown(this.messageOverflow.parentElement, {
      mute: () => this.mute(),
    })
  }

  bind({ key, text, sent, userId, from, video, videoMime, videoType }, myId) {
    this._throwIfDisposed()
    this.unbind()

    const blob = new window.Blob([video], { type: videoMime })
    this._srcUrl = window.URL.createObjectURL(blob)
    this.video.src = this._srcUrl

    this.chatText.innerHTML = text

    const sentDate = new Date(sent)
    this.timestamp.datetime = sentDate.toISOString()
    this.timestamp.innerHTML = localeTime(sentDate)

    if (myId === userId) {
      // No mute menu for yourself
      this.messageOverflow.setAttribute('disabled', true)
    }
    this._userId = userId
    this.refreshIdenticon()

    this._key = key
  }

  refreshIdenticon() {
    const newIdenticon = createIdenticon(this._userId)
    this.identicon.parentElement.replaceChild(newIdenticon, this.identicon)
    this.identicon = newIdenticon
  }

  unbind() {
    this._throwIfDisposed()

    if (this._playPauseRequest) {
      cancelAnimationFrame(this._playPauseRequest)
      this._playPauseRequest = null
    }

    this._userId = null
    this._key = null
    this.dropdown.close()

    this._isVisible = false
    delete this.video.src

    if (this._srcUrl) {
      window.URL.revokeObjectURL(this._srcUrl)
      this._srcUrl = null
    }

    this.messageOverflow.removeAttribute('disabled')
  }

  dispose() {
    this._throwIfDisposed()
    this._disposed = true
  }

  saveGif() {
    this._throwIfDisposed()
    this.saveButton.disabled = true
    this.owner.trackSaveGif()

    videoToGif({ videoElem: this.video, numFrames: 10 })
      .then(gifBlob => {
        this.saveButton.disabled = false
        const url = window.URL.createObjectURL(gifBlob)
{'fraction_non_alphanumeric': 0.12839035027940576, 'fraction_numerical': 0.0021807278179092273, 'mean_word_length': 3.2837127845884413, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4692944', 'n_tokens_mistral': 2250, 'n_tokens_neox': 2222, 'n_words': 461}