text: import Vue from 'vue'
  
  import QAvatar from '../components/avatar/QAvatar.js'
  import QIcon from '../components/icon/QIcon.js'
  import QBtn from '../components/btn/QBtn.js'
  
  import { noop } from '../utils/event.js'
  import { isSSR } from './Platform.js'
  
  let uid = 0
  let defaults = {}
  
  const attrs = { role: 'alert' }
  
  const positionList = [
    'top-left', 'top-right',
    'bottom-left', 'bottom-right',
    'top', 'bottom', 'left', 'right', 'center'
  ]
  
  const badgePositions = [
    'top-left', 'top-right',
    'bottom-left', 'bottom-right'
  ]
  
  const notifTypes = {
    positive: {
      icon () { return this.$q.iconSet.type.positive },
      color: 'positive'
    },
  
    negative: {
      icon () { return this.$q.iconSet.type.negative },
      color: 'negative'
    },
  
    warning: {
      icon () { return this.$q.iconSet.type.warning },
      color: 'warning',
      textColor: 'dark'
    },
  
    info: {
      icon () { return this.$q.iconSet.type.info },
      color: 'info'
    }
  }
  
  const groups = {}
  const positionClass = {}
  
  const Notifications = {
    name: 'QNotifications',
  
    created () {
      this.notifs = {}
  
      positionList.forEach(pos => {
        this.notifs[pos] = []
  
        const
          vert = ['left', 'center', 'right'].includes(pos) ? 'center' : (pos.indexOf('top') > -1 ? 'top' : 'bottom'),
          align = pos.indexOf('left') > -1 ? 'start' : (pos.indexOf('right') > -1 ? 'end' : 'center'),
          classes = ['left', 'right'].includes(pos) ? `items-${pos === 'left' ? 'start' : 'end'} justify-center` : (pos === 'center' ? 'flex-center' : `items-${align}`)
  
        positionClass[pos] = `q-notifications__list q-notifications__list--${vert} fixed column no-wrap ${classes}`
      })
    },
  
    methods: {
      add (config) {
        if (!config) {
          console.error('Notify: parameter required')
          return false
        }
  
        let notif = { textColor: 'white' }
  
        if (typeof config === 'string' || config.ignoreDefaults !== true) {
          Object.assign(notif, defaults)
        }
  
        if (Object(config) === config) {
          Object.assign(notif, notifTypes[config.type], config)
  
          if (typeof notif.icon === 'function') {
            notif.icon = notif.icon.call(this)
          }
        }
        else {
          Object.assign(notif, { message: config })
        }
  
        notif.meta = {
          hasMedia: Boolean(notif.icon || notif.avatar)
        }
  
        if (notif.position) {
          if (!positionList.includes(notif.position)) {
            console.error(`Notify: wrong position: ${notif.position}`)
            return false
          }
        }
        else {
          notif.position = 'bottom'
        }
  
        if (notif.timeout === void 0) {
          notif.timeout = 5000
        }
        else {
          const t = parseInt(notif.timeout, 10)
          if (isNaN(t) || t < 0) {
            console.error(`Notify: wrong timeout: ${notif.timeout}`)
            return false
          }
          notif.timeout = t
        }
  
        if (notif.timeout === 0) {
          notif.progress = false
        }
        else if (notif.progress === true) {
          notif.meta.progressStyle = {
            animationDuration: `${notif.timeout + 1000}ms`
          }
        }
  
        const actions = (Array.isArray(config.actions) === true ? config.actions : [])
          .concat(config.ignoreDefaults !== true && Array.isArray(defaults.actions) === true ? defaults.actions : [])
  
        notif.closeBtn && actions.push({
          label: typeof notif.closeBtn === 'string'
            ? notif.closeBtn
            : this.$q.lang.label.close
        })
  
        notif.actions = actions.map(({ handler, noDismiss, ...item }) => ({
          props: { flat: true, ...item },
          on: {
            click: typeof handler === 'function'
              ? () => {
                handler()
                noDismiss !== true && notif.meta.close()
              }
              : () => {
                notif.meta.close()
              }
          }
        }))
  
        if (notif.multiLine === void 0) {
          notif.multiLine = notif.actions.length > 1
        }
  
        Object.assign(notif.meta, {
          staticClass: `q-notification row items-stretch` +
            ` q-notification--${notif.multiLine === true ? 'multi-line' : 'standard'}` +
            (notif.color !== void 0 ? ` bg-${notif.color}` : '') +
            (notif.textColor !== void 0 ? ` text-${notif.textColor}` : '') +
            (notif.classes !== void 0 ? ` ${notif.classes}` : ''),
  
          wrapperClass: 'q-notification__wrapper col relative-position border-radius-inherit ' +
            (notif.multiLine === true ? 'column no-wrap justify-center' : 'row items-center'),
  
          contentClass: 'q-notification__content row items-center' +
            (notif.multiLine === true ? '' : ' col')
        })
  
        if (notif.group === false) {
          notif.group = void 0
        }
        else {
          if (notif.group === void 0 || notif.group === true) {
            // do not replace notifications with different buttons
            notif.group = [
              notif.message,
              notif.caption,
              notif.multiline
            ].concat(
              notif.actions.map(a => `${a.props.label}*${a.props.icon}`)
            ).join('|')
          }
  
          notif.group += '|' + notif.position
        }
  
        if (notif.actions.length === 0) {
          notif.actions = void 0
        }
        else {
          notif.meta.actionsClass = 'q-notification__actions row items-center ' +
            (notif.multiLine === true ? 'justify-end' : 'col-auto') +
            (notif.meta.hasMedia === true ? ' q-notification__actions--with-media' : '')
        }
  
        const groupNotif = groups[notif.group]
  
        // wohoo, new notification
        if (groupNotif === void 0) {
          notif.meta.uid = uid++
          notif.meta.badge = 1
  
          if (['left', 'right', 'center'].indexOf(notif.position) !== -1) {
            this.notifs[notif.position].splice(
              Math.floor(this.notifs[notif.position].length / 2),
              0,
              notif
            )
          }
          else {
            const action = notif.position.indexOf('top') > -1 ? 'unshift' : 'push'
            this.notifs[notif.position][action](notif)
          }
  
          if (notif.group !== void 0) {
            groups[notif.group] = notif
          }
        }
        // ok, so it's NOT a new one
        else {
          // reset timeout if any
          if (groupNotif.meta.timer !== void 0) {
            clearTimeout(groupNotif.meta.timer)
          }
  
          const original = groups[notif.group]
  
          if (notif.badgePosition !== void 0) {
            if (badgePositions.includes(notif.badgePosition) === false) {
              console.error(`Notify - wrong badgePosition specified: ${notif.badgePosition}`)
              return false
            }
          }
          else {
            notif.badgePosition = `top-${notif.position.indexOf('left') > -1 ? 'right' : 'left'}`
          }
  
          notif.meta.uid = original.meta.uid
          notif.meta.badge = original.meta.badge + 1
          notif.meta.badgeStaticClass = `q-notification__badge q-notification__badge--${notif.badgePosition}` +
            (notif.badgeColor !== void 0 ? ` bg-${notif.badgeColor}` : '') +
            (notif.badgeTextColor !== void 0 ? ` text-${notif.badgeTextColor}` : '')
  
          notif = Object.assign(original, notif)
        }
  
        notif.meta.close = () => {
          this.remove(notif)
        }
  
        this.$forceUpdate()
  
        if (notif.timeout > 0) {
          notif.meta.timer = setTimeout(() => {
            notif.meta.close()
          }, notif.timeout + /* show duration */ 1000)
        }
  
        return notif.meta.close
      },
  
      remove (notif) {
        if (notif.meta.timer) { clearTimeout(notif.meta.timer) }
  
        const index = this.notifs[notif.position].indexOf(notif)
        if (index !== -1) {
          if (notif.group !== void 0) {
            delete groups[notif.group]
          }
  
          const el = this.$refs[`notif_${notif.meta.uid}`]
  
          if (el) {
            const { width, height } = getComputedStyle(el)
  
            el.style.left = `${el.offsetLeft}px`
            el.style.width = width
            el.style.height = height
          }
  
          this.notifs[notif.position].splice(index, 1)
  
          this.$forceUpdate()
  
          if (typeof notif.onDismiss === 'function') {
            notif.onDismiss()
          }
        }
      }
    },
  
    render (h) {
      return h('div', { staticClass: 'q-notifications' }, positionList.map(pos => {
        return h('transition-group', {
          key: pos,
          staticClass: positionClass[pos],
          tag: 'div',
          props: {
            name: `q-notification--${pos}`,
            mode: 'out-in'
          }
        }, this.notifs[pos].map(notif => {
          let msgChild
  
          const meta = notif.meta
          const msgData = { staticClass: 'q-notification__message col' }
  
          if (notif.html === true) {
            msgData.domProps = {
              innerHTML: notif.caption
                ? `<div>${notif.message}</div><div class="q-notification__caption">${notif.caption}</div>`
                : notif.message
            }
          }
          else {
            const msgNode = [ notif.message ]
            msgChild = notif.caption
              ? [
                h('div', msgNode),
                h('div', { staticClass: 'q-notification__caption' }, [ notif.caption ])
              ]
              : msgNode
          }
  
          const mainChild = []
  
          if (meta.hasMedia === true) {
            if (notif.icon) {
              mainChild.push(
                h(QIcon, {
                  staticClass: 'q-notification__icon col-auto',
                  attrs: { role: 'img' },
                  props: { name: notif.icon }
                })
              )
            }
            else if (notif.avatar) {
              mainChild.push(
                h(QAvatar, { staticClass: 'q-notification__avatar col-auto' }, [
                  h('img', { attrs: { src: notif.avatar, 'aria-hidden': 'true' } })
                ])
              )
            }
          }
  
          mainChild.push(
            h('div', msgData, msgChild)
          )
  
          const child = [
            h('div', { staticClass: meta.contentClass }, mainChild)
          ]
  
          notif.progress === true && child.push(
            h('div', {
              key: `${meta.uid}|p|${meta.badge}`,
              staticClass: 'q-notification__progress',
              style: meta.progressStyle,
              class: notif.progressClass
            })
          )
  
          notif.actions !== void 0 && child.push(
            h('div', {
              staticClass: meta.actionsClass
            }, notif.actions.map(a => h(QBtn, { props: a.props, on: a.on })))
          )
  
          meta.badge > 1 && child.push(
            h('div', {
              key: `${meta.uid}|${meta.badge}`,
              staticClass: meta.badgeStaticClass,
              style: notif.badgeStyle,
              class: notif.badgeClass
            }, [ meta.badge ])
          )
  
          return h('div', {
            ref: `notif_${meta.uid}`,
            key: meta.uid,
            staticClass: meta.staticClass,
            attrs
          }, [
            h('div', { staticClass: meta.wrapperClass }, child)
          ])
        }))
      }))
    }
  }
  
  export default {
    create (opts) {
      if (isSSR === true) { return noop }
      return this.__vm.add(opts)
    },
    setDefaults (opts) {
      opts === Object(opts) && Object.assign(defaults, opts)
    },
    registerType (typeName, typeOpts) {
      if (isSSR !== true && typeOpts === Object(typeOpts)) {
        notifTypes[typeName] = typeOpts
      }
    },
  
    install ({ cfg, $q }) {
      if (isSSR === true) {
        $q.notify = noop
        $q.notify.setDefaults = noop
        return
      }
  
      this.setDefaults(cfg.notify)
  
      $q.notify = this.create.bind(this)
      $q.notify.setDefaults = this.setDefaults
      $q.notify.registerType = this.registerType
  
      const node = document.createElement('div')
      document.body.appendChild(node)
  
      this.__vm = new Vue(Notifications)
      this.__vm.$mount(node)
    }
  }
  