<commit_before><script>
import { ToastServiceBus } from '../services/toastServiceBus'
import Timer from '../utils/timer'
import ToastConfig from '../utils/toastConfig'
import { ADD_TOAST, REMOVE_TOAST } from '../utils/constants'
import Toast from './Toast.vue'

export default {
  name: 'toast-container',

  data: () => ({
    toasts: []
  }),

  components: {
    'toast': Toast
  },

  props: ['toastConfig'],

  methods: {
    addToast(toast, toastConfig) {
      toast.toastConfig = toastConfig

      if (toast.toastContainerId && toastConfig.toastContainerId &&
        toast.toastContainerId !== toastConfig.toastContainerId) {
        return
      }

      if (!toast.type) {
        toast.type = toastConfig.defaultTypeClass
      }

      if (toastConfig.preventDuplicates && this.toasts.length > 0) {
        if (toast.toastId &&
          this.toasts.some(t => t.toastId === toast.toastId)) {
          return
        } else if (this.toasts.some(t => t.body === toast.body)) {
          return
        }
      }

      this.setCloseOptions(toast, toastConfig)

      toast.bodyOutputType = toast.bodyOutputType || toastConfig.bodyOutputType

      Timer.configureTimer(toast)

      if (toastConfig.newestOnTop) {
        this.toasts.unshift(toast)
        if (this.isLimitExceeded(toastConfig)) {
          this.toasts.pop()
        }
      } else {
        this.toasts.push(toast)
        if (this.isLimitExceeded(toastConfig)) {
          this.toasts.shift()
        }
      }

      if (toast.onShowCallback) {
        toast.onShowCallback(toast)
      }
    },

    setCloseOptions(toast, toastConfig) {
      if (toast.showCloseButton === null ||
        typeof toast.showCloseButton === 'undefined') {
        if (typeof toastConfig.showCloseButton === 'object') {
          toast.showCloseButton = toastConfig.showCloseButton[toast.type]
        } else if (typeof toastConfig.showCloseButton === 'boolean') {
          toast.showCloseButton = toastConfig.showCloseButton
        }
      }

      if (toast.showCloseButton) {
        toast.closeHtml = toast.closeHtml || toastConfig.closeHtml
      }
    },

    isLimitExceeded(toastConfig) {
      return toastConfig.limit && this.toasts.length > toastConfig.limit
    },

    removeToast(toast) {
      if (toast === null || typeof toast === 'undefined') return
      var index = this.toasts.indexOf(toast)
      if (index < 0) { return }

      this.toasts.splice(index, 1)

      if (toast.timeoutId) {
        clearTimeout(toast.timeoutId)
        toast.timeoutId = null
      }

      if (toast.onHideCallback) {
        toast.onHideCallback(toast)
      }
    },

    removeToasts(toastId, toastContainerId) {
      if (toastContainerId === null ||
        typeof toastContainerId === 'undefined' ||
        toastContainerId === this._toastConfig.toastContainerId) {
        if (toastId) {
          this.removeToast(this.toasts.filter(t => t.toastId === toastId)[0])
        } else {
          this.removeAllToasts()
        }
      }
    },

    removeAllToasts() {
      for (let i = this.toasts.length - 1; i >= 0; i--) {
        this.removeToast(this.toasts[i])
      }
    }
  },

  computed: {
    _toastConfig() {
      return new ToastConfig(this.toastConfig)
    }
  },

  created() {
    ToastServiceBus.subscribers.push(this)

    ToastServiceBus.$on(ADD_TOAST, (toast) => {
      this.addToast(toast, this._toastConfig)
    })

    ToastServiceBus.$on(REMOVE_TOAST,
      (toastId, toastContainerId) => {
        this.removeToasts(toastId, toastContainerId)
      })
  },

  render(h) {
    return h(
      'transition-group', {
        name: 'toast-container',
        tag: 'div',
        'class': ['toast-container', this._toastConfig.positionClass],
        props: {
          name: this._toastConfig.animation
        }
      }, this.toasts.map((toast) => (h('toast', {
        'class': 'toast',
        props: {
          toast: toast
        },
        key: toast.toastId
      })))
    )<commit_msg>Fix memory leak when SSR<commit_after><script>
import { ToastServiceBus } from '../services/toastServiceBus'
import Timer from '../utils/timer'
import ToastConfig from '../utils/toastConfig'
import { ADD_TOAST, REMOVE_TOAST } from '../utils/constants'
import Toast from './Toast.vue'

export default {
  name: 'toast-container',

  data: () => ({
    toasts: []
  }),

  components: {
    'toast': Toast
  },

  props: ['toastConfig'],

  methods: {
    addToast(toast, toastConfig) {
      toast.toastConfig = toastConfig

      if (toast.toastContainerId && toastConfig.toastContainerId &&
        toast.toastContainerId !== toastConfig.toastContainerId) {
        return
      }

      if (!toast.type) {
        toast.type = toastConfig.defaultTypeClass
      }

      if (toastConfig.preventDuplicates && this.toasts.length > 0) {
        if (toast.toastId &&
          this.toasts.some(t => t.toastId === toast.toastId)) {
          return
        } else if (this.toasts.some(t => t.body === toast.body)) {
          return
        }
      }

      this.setCloseOptions(toast, toastConfig)

      toast.bodyOutputType = toast.bodyOutputType || toastConfig.bodyOutputType

      Timer.configureTimer(toast)

      if (toastConfig.newestOnTop) {
        this.toasts.unshift(toast)
        if (this.isLimitExceeded(toastConfig)) {
          this.toasts.pop()
        }
      } else {
        this.toasts.push(toast)
        if (this.isLimitExceeded(toastConfig)) {
          this.toasts.shift()
        }
      }

      if (toast.onShowCallback) {
        toast.onShowCallback(toast)
      }
    },

    setCloseOptions(toast, toastConfig) {
      if (toast.showCloseButton === null ||
        typeof toast.showCloseButton === 'undefined') {
        if (typeof toastConfig.showCloseButton === 'object') {
          toast.showCloseButton = toastConfig.showCloseButton[toast.type]
        } else if (typeof toastConfig.showCloseButton === 'boolean') {
          toast.showCloseButton = toastConfig.showCloseButton
        }
      }

      if (toast.showCloseButton) {
        toast.closeHtml = toast.closeHtml || toastConfig.closeHtml
      }
    },

    isLimitExceeded(toastConfig) {
      return toastConfig.limit && this.toasts.length > toastConfig.limit
    },

    removeToast(toast) {
      if (toast === null || typeof toast === 'undefined') return
      var index = this.toasts.indexOf(toast)
      if (index < 0) { return }

      this.toasts.splice(index, 1)

      if (toast.timeoutId) {
        clearTimeout(toast.timeoutId)
        toast.timeoutId = null
      }

      if (toast.onHideCallback) {
        toast.onHideCallback(toast)
      }
    },

    removeToasts(toastId, toastContainerId) {
      if (toastContainerId === null ||
        typeof toastContainerId === 'undefined' ||
        toastContainerId === this._toastConfig.toastContainerId) {
        if (toastId) {
          this.removeToast(this.toasts.filter(t => t.toastId === toastId)[0])
        } else {
          this.removeAllToasts()
        }
      }
    },

    removeAllToasts() {
      for (let i = this.toasts.length - 1; i >= 0; i--) {
        this.removeToast(this.toasts[i])
      }
    }
  },

  computed: {
    _toastConfig() {
      return new ToastConfig(this.toastConfig)
    }
  },

  beforeMount() {
    ToastServiceBus.subscribers.push(this)

    ToastServiceBus.$on(ADD_TOAST, (toast) => {
      this.addToast(toast, this._toastConfig)
    })

    ToastServiceBus.$on(REMOVE_TOAST,
      (toastId, toastContainerId) => {
        this.removeToasts(toastId, toastContainerId)
      })
  },

  render(h) {
    return h(
      'transition-group', {
        name: 'toast-container',
        tag: 'div',
        'class': ['toast-container', this._toastConfig.positionClass],
        props: {
          name: this._toastConfig.animation
        }
      }, this.toasts.map((toast) => (h('toast', {
        'class': 'toast',
        props: {
          toast: toast
        },
        key: toast.toastId
      })))
    )
{'fraction_non_alphanumeric': 0.13185166687476588, 'fraction_numerical': 0.0014983143963041578, 'mean_word_length': 2.2389809947432266, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 25, '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': '4918244', 'n_tokens_mistral': 2601, 'n_tokens_neox': 2548, 'n_words': 416}