const value = (this.internalValue || '').length

const max = this.counter === true ? this.$attrs.maxlength : this.counter

keep keep keep keep replace keep keep keep

if (this.counter === false || this.counter == null) return null

import { test } from '@/test'
import Vue from 'vue/dist/vue.common'
import VTextField from '@/components/VTextField/VTextField'
import VProgressLinear from '@/components/VProgressLinear'

test('VTextField.js', ({ mount }) => {
  it('should render component and match snapshot', () => {
    const wrapper = mount(VTextField)

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should pass required attr to the input', () => {
    const wrapper = mount(VTextField, {
      attrs: {
        required: true
      }
    })

    const input = wrapper.find('input')[0]
    expect(input.getAttribute('required')).toBe('required')
  })

  it('should pass events to internal input field', () => {
    const keyup = jest.fn()
    const component = {
      render (h) {
        return h(VTextField, { on: { keyUp: keyup }, props: { download: '' }, attrs: {} })
      }
    }
    const wrapper = mount(component)

    const input = wrapper.find('input')[0]
    input.trigger('keyUp', { keyCode: 65 })

    expect(keyup).toBeCalled()
  })

  it('should render aria-label attribute on text field element with label value and no id', () => {
    const wrapper = mount(VTextField, {
      propsData: {
        label: 'Test'
      },
      attrs: {}
    })

    const inputGroup = wrapper.find('input')[0]
    expect(inputGroup.getAttribute('aria-label')).toBe('Test')
  })

  it('should not render aria-label attribute on text field element with no label value or id', () => {
    const wrapper = mount(VTextField, {
      propsData: {
        label: null
      },
      attrs: {}
    })

    const inputGroup = wrapper.find('input')[0]
    expect(inputGroup.element.getAttribute('aria-label')).toBeFalsy()
  })

  it('should not render aria-label attribute on text field element with id', () => {
    const wrapper = mount(VTextField, {
      propsData: {
        label: 'Test'
      },
      attrs: {
        id: 'Test'
      }
    })

    const inputGroup = wrapper.find('input')[0]
    expect(inputGroup.element.getAttribute('aria-label')).toBeFalsy()
  })

  it('should start out as invalid', () => {
    const wrapper = mount(VTextField, {
      propsData: {
        rules: [(v) => !!v || 'Required']
      }
    })

    expect(wrapper.data().valid).toEqual(false)
  })

  it('should start validating on input', async () => {
    const wrapper = mount(VTextField)

    expect(wrapper.vm.shouldValidate).toEqual(false)
    wrapper.setProps({ value: 'asd' })
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.shouldValidate).toEqual(true)
  })

  it('should not start validating on input if validate-on-blur prop is set', async () => {
    const wrapper = mount(VTextField, {
      propsData: {
        validateOnBlur: true
      }
    })

    expect(wrapper.vm.shouldValidate).toEqual(false)
    wrapper.setProps({ value: 'asd' })
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.shouldValidate).toEqual(false)
  })

  it('should not display counter when set to false', async () => {
    const wrapper = mount(VTextField, {
      propsData: {
        counter: true
      },
      attrs: {
        maxlength: 50
      }
    })

    expect(wrapper.find('.v-counter')[0]).not.toBe(undefined)
    expect(wrapper.html()).toMatchSnapshot()

    wrapper.setProps({ counter: false })
    await wrapper.vm.$nextTick()

    expect(wrapper.html()).toMatchSnapshot()
    expect(wrapper.find('.v-counter')[0]).toBe(undefined)
  })

  it('should have readonly attribute', () => {
    const wrapper = mount(VTextField, {
      propsData: {
        readonly: true
      }
    })

    const input = wrapper.find('input')[0]

    expect(input.getAttribute('readonly')).toBe('readonly')
  })

  it('should clear input value', async () => {
    const wrapper = mount(VTextField, {
      propsData: {
        clearable: true,
        value: 'foo'
      }
    })

    const clear = wrapper.find('.v-input__icon--clear .v-icon')[0]
    const input = jest.fn()
    wrapper.vm.$on('input', input)

    expect(wrapper.vm.internalValue).toBe('foo')

    clear.trigger('click')

    await wrapper.vm.$nextTick()

    expect(input).toHaveBeenCalledWith(null)
  })

  it('should not clear input if not clearable and has appended icon (with callback)', async () => {
    const appendIconCb = jest.fn()
    const wrapper = mount(VTextField, {
      propsData: {
        value: 'foo',
        appendIcon: 'block',
        appendIconCb
      }
    })

    const icon = wrapper.find('.v-input__icon--append .v-icon')[0]
    icon.trigger('click')
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.internalValue).toBe('foo')
    expect(appendIconCb.mock.calls).toHaveLength(1)
  })

  it('should not clear input if not clearable and has appended icon (without callback)', async () => {
    const wrapper = mount(VTextField, {
      propsData: {
        value: 'foo',
        appendIcon: 'block',
      }
    })

    const icon = wrapper.find('.v-input__icon--append .v-icon')[0]
    icon.trigger('click')
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.internalValue).toBe('foo')
  })

  it('should start validating on blur', async () => {
    const wrapper = mount(VTextField)

    const input = wrapper.first('input')
    expect(wrapper.vm.shouldValidate).toEqual(false)
    input.trigger('focus')
    await wrapper.vm.$nextTick()
    input.trigger('blur')
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.shouldValidate).toEqual(true)
  })

  it('should keep its value on blur', async () => {
    const wrapper = mount(VTextField, {
      propsData: {
        value: 'asd'
      }
    })

    const input = wrapper.find('input')[0]

    input.element.value = 'fgh'
    input.trigger('input')
    input.trigger('blur')

    expect(input.element.value).toBe('fgh')
  })

  it('should update if value is changed externally', async () => {
    const wrapper = mount(VTextField, {})

    const input = wrapper.find('input')[0]

    wrapper.setProps({ value: 'fgh' })
    expect(input.element.value).toBe('fgh')

    input.trigger('focus')
    wrapper.setProps({ value: 'jkl' })
    expect(input.element.value).toBe('jkl')
  })

  it('should fire a single change event on blur', async () => {
    let value = 'asd'
    const change = jest.fn()

    const component = {
      render (h) {
        return h(VTextField, {
          on: {
            input: (i) => value = i,
            change
          },
          props: { value }
        })
      }
    }
    const wrapper = mount(component)

    const input = wrapper.find('input')[0]

    input.trigger('focus')
    await wrapper.vm.$nextTick()
    input.element.value = 'fgh'
    input.trigger('input')

    await wrapper.vm.$nextTick()
    input.trigger('blur')
    await wrapper.vm.$nextTick()

    expect(change).toBeCalledWith('fgh')
    expect(change.mock.calls).toHaveLength(1)
  })

  it('should not make prepend icon clearable', () => {
    const wrapper = mount(VTextField, {
      propsData: {
        prependIcon: 'check',
        appendIcon: 'check',
        value: 'test',
        clearable: true
      }
    })

    const prepend = wrapper.find('.v-input__icon--append .v-icon')[0]
    expect(prepend.text()).toBe('check')
    expect(prepend.element.classList).not.toContain('input-group__icon-cb')
  })

  it('should not emit change event if value has not changed', async () => {
    const change = jest.fn()
    let value = 'test'
    const component = {
      render (h) {
        return h(VTextField, {
          on: {
            input: i => value = i,
            change
          },
          props: { value }
        })
      }
    }
    const wrapper = mount(component)

    const input = wrapper.find('input')[0]

    input.trigger('focus')
    await wrapper.vm.$nextTick()
    input.trigger('blur')
    await wrapper.vm.$nextTick()

    expect(change.mock.calls).toHaveLength(0)
  })

  it('should render component with async loading and match snapshot', () => {
    const wrapper = mount(VTextField, {
      propsData: {
        loading: true
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should render component with async loading and custom progress and match snapshot', () => {
    const progress = Vue.component('test', {
      render (h) {
        return h(VProgressLinear, {
          props: {
            indeterminate: true,
            height: 7,
            color: 'orange'
          }
        })
      }
    })

    const wrapper = mount(VTextField, {
      propsData: {
        loading: true
      },
      slots: {
        progress: [progress]
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should display the number 0', async () => {
    const wrapper = mount(VTextField, {
      propsData: { value: 0 }
    })

    expect(wrapper.vm.$refs.input.value).toBe('0')
  })

  it('should reset internal change on blur and keydown', async () => {
    const wrapper = mount(VTextField)

    wrapper.setProps({ value: 'foo' })
    wrapper.vm.internalChange = true
    expect(wrapper.vm.internalChange).toBe(true)
    wrapper.vm.onBlur()
    expect(wrapper.vm.internalChange).toBe(false)

    wrapper.first('input').trigger('keydown')

    expect(wrapper.vm.internalChange).toBe(true)
  })

  it('should emit input when externally set value was modified internally', async () => {
    let value = '33'
    const input = jest.fn()
    const wrapper = mount(VTextField, {
      propsData: {
        value,
        mask: '##',
        returnMaskedValue: true
      }
    })

    wrapper.vm.$on('input', (v) => {
      value = v
    })
    wrapper.vm.$on('input', input)

    wrapper.setProps({ value: '4444' })
    await wrapper.vm.$nextTick()

    expect(value).toBe('44')
    expect(input).toBeCalled()
  })

  it('should mask value if return-masked-value is true', async () => {
    let value = '44'
    const component = {
      render (h) {
        return h(VTextField, {
          on: {
            input: i => value = i
          },
          props: {
            value,
            returnMaskedValue: true,
            mask: '#-#',
          }
        })
      }
    }

    const wrapper = mount(component)
    const input = wrapper.find('input')[0]

    expect(value).toBe('4-4')

    input.trigger('focus')
    await wrapper.vm.$nextTick()
    input.element.value = '33'
    input.trigger('input')
    await wrapper.vm.$nextTick()

    expect(value).toBe('3-3')
  })

  it('should not mask value if return-masked-value is false', async () => {
    let value = '44'
    const component = {
      render (h) {
        return h(VTextField, {
          on: {
            input: i => value = i
          },
          props: {
            value,
            returnMaskedValue: false,
            mask: '#-#',
          }
        })
      }
    }

    const wrapper = mount(component)
    const input = wrapper.find('input')[0]

    expect(value).toBe('44')

    input.trigger('focus')
    await wrapper.vm.$nextTick()
    input.element.value = '33'
    input.trigger('input')
    await wrapper.vm.$nextTick()

    expect(value).toBe('33')
  })

  it('should use pre-defined mask if prop matches', async () => {
    let value = '12311999'
    const component = {
      render (h) {
        return h(VTextField, {
          on: {
            input: i => value = i
          },
          props: {
            value,
            returnMaskedValue: true,
            mask: 'date',
          }
        })
      }
    }

    const wrapper = mount(component)

    expect(value).toBe('12/31/1999')
  })

  it('should allow switching mask', async () => {
    const wrapper = mount(VTextField, {
      propsData: {
        mask: '#-#-#',
        value: '1-2-3'
      }
    })

    const input = wrapper.find('input')[0]

    expect(input.element.value).toBe('1-2-3')

    wrapper.setProps({ mask: '#.#.#'})
    await wrapper.vm.$nextTick()

    expect(input.element.value).toBe('1.2.3')

    wrapper.setProps({ mask: '#,#' })
    await wrapper.vm.$nextTick()

    expect(input.element.value).toBe('1,2')
  })

  it('should autofocus', async () => {
    const wrapper = mount(VTextField, {
      attachToDocument: true,
      propsData: {
        autofocus: true
      }
    })

    const focus = jest.fn()
    wrapper.vm.$on('focus', focus)

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.isFocused).toBe(true)
    wrapper.vm.onClick()

    expect(focus.mock.calls.length).toBe(0)

    wrapper.setData({ isFocused: false })

    wrapper.vm.onClick()
    expect(focus.mock.calls.length).toBe(1)

    wrapper.setProps({ disabled: true })

    wrapper.setData({ isFocused: false })

    wrapper.vm.onClick()
    expect(focus.mock.calls.length).toBe(1)

    wrapper.setProps({ disabled: false })

    wrapper.vm.onClick()
    expect(focus.mock.calls.length).toBe(2)

    delete wrapper.vm.$refs.input

    wrapper.vm.onFocus()
    expect(focus.mock.calls.length).toBe(2)
  })

  it('should have prefix and suffix', () => {
    const wrapper = mount(VTextField, {
      propsData: {
        prefix: '$',
        suffix: '.com'
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should use a custom clear callback', async () => {
    const clearIconCb = jest.fn()
    const wrapper = mount(VTextField, {
      propsData: {
        clearIconCb,
        clearable: true,
        value: 'foo'
      }
    })

    wrapper.first('.v-input__icon--clear .v-icon').trigger('click')

    expect(clearIconCb).toBeCalled()
  })

  it('should not generate label', () => {
    const wrapper = mount(VTextField)

    expect(wrapper.vm.genLabel()).toBe(null)

    wrapper.setProps({ singleLine: true })

    expect(wrapper.vm.genLabel()).toBe(null)

    wrapper.setProps({ placeholder: 'foo' })

    expect(wrapper.vm.genLabel()).toBe(null)

    wrapper.setProps({
      placeholder: undefined,
      value: 'bar'
    })

    expect(wrapper.vm.genLabel()).toBe(null)

    wrapper.setProps({
      label: 'bar',
      value: undefined
    })

    expect(wrapper.vm.genLabel()).toBeTruthy()
  })

  it('should propagate id to label for attribute', () => {
    const wrapper = mount(VTextField, {
      propsData: {
        label: 'foo',
        id: 'bar'
      },
      attrs: {
        id: 'bar'
      },
      domProps: {
        id: 'bar'
      }
    })

    const label = wrapper.first('label')

    expect(label.element.getAttribute('for')).toBe('bar')
  })

  it('should render an appended outer icon', () => {
    const wrapper = mount(VTextField, {
      propsData: {
        appendOuterIcon: 'search'
      }
    })

    expect(wrapper.first('.v-input__icon--append-outer .v-icon').element.innerHTML).toBe('search')
  })

  // TODO: revisit this, it seems correct in practice because of onBlur()
  it.skip('should reset internal change', async () => {
    const wrapper = mount(VTextField)

    wrapper.setData({ internalChange: true })

    expect(wrapper.vm.internalChange).toBe(true)

    wrapper.setProps({ value: 'foo' })

    expect(wrapper.vm.internalChange).toBe(false)
  })

  it('should have correct max value', async () => {
    const wrapper = mount(VTextField, {
      attrs: {
        maxlength: 25
      },
      propsData: {
        counter: true
      }
    })

    const counter = wrapper.first('.v-counter')

    expect(counter.element.innerHTML).toBe('0 / 25')

    wrapper.setProps({ counter: '50' })

    expect(counter.element.innerHTML).toBe('0 / 50')
  })

  it('should set bad input on input', () => {
    const wrapper = mount(VTextField)

    expect(wrapper.vm.badInput).toBeFalsy()

    wrapper.vm.onInput({
      target: {}
    })

    expect(wrapper.vm.badInput).toBeFalsy()

    wrapper.vm.onInput({
      target: { validity: { badInput: false } }
    })

    expect(wrapper.vm.badInput).toBeFalsy()

    wrapper.vm.onInput({
      target: { validity: { badInput: true } }
    })

    expect(wrapper.vm.badInput).toBe(true)
  })

  it('should set input autocomplete attr', () => {
    const wrapper = mount(VTextField, {
      propsData: {
        browserAutocomplete: 'off'
      }
    })

    const input = wrapper.first('input')

    expect(input.element.autocomplete).toBe('off')
  })

  it('should not apply id to root element', () => {
    const wrapper = mount(VTextField, {
      attrs: { id: 'foo' }
    })

    const input = wrapper.first('input')
    expect(wrapper.element.id).toBe('')
    expect(input.element.id).toBe('foo')
  })

  it('should fire change event when pressing enter', () => {
    const wrapper = mount(VTextField)
    const input = wrapper.first('input')
    const change = jest.fn()

    wrapper.vm.$on('change', change)

    input.trigger('focus')
    input.element.value = 'foo'
    input.trigger('input')
    input.trigger('keydown.enter')
    input.trigger('keydown.enter')

    expect(change).toHaveBeenCalledTimes(2)
  })

  it('should have focus and blur methods', () => {
    const wrapper = mount(VTextField)
    const focus = jest.fn()
    const blur = jest.fn()
    wrapper.vm.$on('focus', focus)
    wrapper.vm.$on('blur', blur)

    wrapper.vm.focus()
    expect(focus).toHaveBeenCalledTimes(1)

    wrapper.vm.blur()
    expect(blur).toHaveBeenCalledTimes(1)
  })

  it('should activate label when using dirtyTypes', async () => {
    const dirtyTypes = ['color', 'file', 'time', 'date', 'datetime-local', 'week', 'month']
    const wrapper = mount(VTextField, {
      propsData: {
        label: 'Foobar'
      }
    })
    const label = wrapper.first('.v-label')

    for (const type of dirtyTypes) {
      wrapper.setProps({ type })

      await wrapper.vm.$nextTick()

      expect(label.element.classList).toContain('v-label--active')
      expect(wrapper.vm.$el.classList).toContain('v-input--is-label-active')

      wrapper.setProps({ type: undefined })

      await wrapper.vm.$nextTick()

      expect(label.element.classList).not.toContain('v-label--active')
      expect(wrapper.vm.$el.classList).not.toContain('v-input--is-label-active')
    }
  })
})


import Vue from 'vue'
import { test } from '@/test'
import VTextField from '@/components/VTextField'
import VBtn from '@/components/VBtn'
import VForm from '@/components/VForm'

const inputOne = Vue.component('input-one', {
  render (h) {
    return h(VTextField, {
      propsData: [(v) => !!v || 'Required']
    })
  }
})

test('VForm.js', ({ mount }) => {
  it('should pass on listeners to form element', async () => {
    const submit = jest.fn()
    const component = Vue.component('test', {
      render (h) {
        return h(VForm, {
          on: {
            submit
          }
        }, [
          h(VBtn, {
            props: {
              type: 'submit'
            },
            slot: 'default'
          }, ['Submit'])
        ])
      }
    })

    const wrapper = mount(component)

    const btn = wrapper.find('button')[0]

    btn.trigger('click')

    expect(submit).toBeCalled()
  })

  it('should watch the error bag', async () => {
    const wrapper = mount(VForm)

    const input = jest.fn()
    wrapper.vm.$on('input', input)

    Vue.set(wrapper.vm.errorBag, 'foo', true)
    await Vue.nextTick()
    expect(input).toBeCalledWith(false)

    Vue.set(wrapper.vm.errorBag, 'foo', false)
    await Vue.nextTick()
    expect(input).toBeCalledWith(true)
  })

  it('should register input child', async () => {
    const wrapper = mount(VForm, {
      slots: {
        default: [VTextField]
      }
    })

    await wrapper.vm.$nextTick()
    expect(wrapper.vm.inputs.length).toBe(1)
    expect(Object.keys(wrapper.vm.errorBag).length).toBe(1)
  })

  // TODO: Figure out how to test this with the updated v-form
  it.skip('should only watch children if not lazy', async () => {
    const wrapper = mount(VForm, {
      propsData: {
        lazyValidation: true
      },
      slots: {
        default: [VTextField]
      }
    })

    const input = wrapper.vm.getInputs()[0]
    wrapper.vm.watchChild(input)
    input.shouldValidate = true
    wrapper.vm.watchChild(input)
    await wrapper.vm.$nextTick()

    // beware, depends on number of computeds in VTextField
    const watchers = 28
    expect(input._watchers.length).toBe(watchers)
    input.shouldValidate = false
    wrapper.vm.watchChild(input)
    await wrapper.vm.$nextTick()

    expect(input._watchers.length).toBe(watchers + 1)
    input.shouldValidate = true
    await wrapper.vm.$nextTick()

    expect(Object.keys(wrapper.vm.errorBag).length).toBe(1)
  })

  it('should emit input when calling validate on lazy-validated form', async () => {
    const wrapper = mount(VForm, {
      propsData: {
        lazyValidation: true
      },
      slots: {
        default: [{
          render (h) {
            return h(VTextField, {
              props: {
                rules: [v => v === 1 || 'Error']
              }
            })
          }
        }]
      }
    })

    const value = jest.fn()
    wrapper.vm.$on('input', value)

    expect(wrapper.vm.validate()).toBe(false)

    await wrapper.vm.$nextTick()

    expect(value).toBeCalledWith(false)
  })

  it('should reset validation', async () => {
    const wrapper = mount(VForm, {
      slots: {
        default: [VTextField]
      }
    })

    expect(Object.keys(wrapper.vm.errorBag).length).toBe(1)
    wrapper.vm.reset()

    expect(Object.keys(wrapper.vm.errorBag).length).toBe(1)

    wrapper.setProps({ lazyValidation: true })
    expect(Object.keys(wrapper.vm.errorBag).length).toBe(1)

    wrapper.vm.reset()
    await new Promise(resolve => setTimeout(resolve, 0))
    expect(Object.keys(wrapper.vm.errorBag).length).toBe(0)
  })
})


import { VuetifyUseOptions, VuetifyTheme } from 'types'

/* eslint-disable no-multi-spaces */
const THEME_DEFAULTS = {
  primary: '#1976D2',   // blue.darken2
  secondary: '#424242', // grey.darken3
  accent: '#82B1FF',    // blue.accent1
  error: '#FF5252',     // red.accent2
  info: '#2196F3',      // blue.base
  success: '#4CAF50',   // green.base
  warning: '#FFC107'    // amber.base
}

export default function theme (theme: VuetifyUseOptions['theme'] = {}): VuetifyTheme | false {
  if (theme === false) return false

  return {
    ...THEME_DEFAULTS,
    ...theme
  }
}


const semver = require('semver')

const version = process.argv[2]

if (!semver.valid(version)) {
  console.error(`Error: '${version}' is not a valid NPM version string`)
  process.exit(9)
}

const prerelease = semver.prerelease(version)

if (prerelease == null) {
  console.log('latest')
} else {
  console.log('next')
}


it('should not display counter when set to false', async () => {

keep keep keep keep keep replace keep keep keep keep

it('should not display counter when set to false/undefined/null', async () => {


import VSnackbar from './VSnackbar'

/* istanbul ignore next */
VSnackbar.install = function install (Vue) {
  Vue.component(VSnackbar.name, VSnackbar)
}

export { VSnackbar }
export default VSnackbar


/**
 * A modified version of https://gist.github.com/cb109/b074a65f7595cffc21cea59ce8d15f9b
 */

/**
 * A Vue mixin to get the current width/height and the associated breakpoint.
 *
 * Useful to e.g. adapt the user interface from inside a Vue component
 * as opposed to using CSS classes. The breakpoint pixel values and
 * range names are taken from Vuetify (https://github.com/vuetifyjs).
 *
 * Use within a component:
 *
 *   import breakpoint from './breakpoint.js'
 *
 *   export default {
 *     name: 'my-component',
 *     mixins: [breakpoint],
 *     ...
 *
 * Then inside a template:
 *
 *
...

 */
const breakpoint = {
  data () {
    return {
      clientWidth: clientDimensions.getWidth(),
      clientHeight: clientDimensions.getHeight(),
      resizeTimeout: null
    }
  },

  computed: {
    breakpoint () {
      const xs = this.clientWidth < 600
      const sm = this.clientWidth < 960 && !xs
      const md = this.clientWidth < (1280 - 16) && !(sm || xs)
      const lg = this.clientWidth < (1920 - 16) && !(md || sm || xs)
      const xl = this.clientWidth >= (1920 - 16) && !(lg || md || sm || xs)

      const xsOnly = xs
      const smOnly = sm
      const smAndDown = (xs || sm) && !(md || lg || xl)
      const smAndUp = !xs && (sm || md || lg || xl)
      const mdOnly = md
      const mdAndDown = (xs || sm || md) && !(lg || xl)
      const mdAndUp = !(xs || sm) && (md || lg || xl)
      const lgOnly = lg
      const lgAndDown = (xs || sm || md || lg) && !xl
      const lgAndUp = !(xs || sm || md) && (lg || xl)
      const xlOnly = xl

      let name
      switch (true) {
        case (xs):
          name = 'xs'
          break
        case (sm):
          name = 'sm'
          break
        case (md):
          name = 'md'
          break
        case (lg):
          name = 'lg'
          break
        default:
          name = 'xl'
          break
      }

      const result = {
        // Definite breakpoint.
        xs,
        sm,
        md,
        lg,
        xl,

        // Useful e.g. to construct CSS class names dynamically.
        name,

        // Breakpoint ranges.
        xsOnly,
        smOnly,
        smAndDown,
        smAndUp,
        mdOnly,
        mdAndDown,
        mdAndUp,
        lgOnly,
        lgAndDown,
        lgAndUp,
        xlOnly,

        // For custom breakpoint logic.
        width: this.clientWidth,
        height: this.clientHeight
      }

      return result
    }
  },

  watch: {
    breakpoint (val) {
      this.$vuetify.breakpoint = val
    }
  },

  created () {
    this.$vuetify.breakpoint = this.breakpoint
  },

  methods: {
    onResize () {
      clearTimeout(this.resizeTimeout)

      // Added debounce to match what
      // v-resize used to do but was
      // removed due to a memory leak
      // https://github.com/vuetifyjs/vuetify/pull/2997
      this.resizeTimeout = setTimeout(() => {
        this.clientWidth = clientDimensions.getWidth()
        this.clientHeight = clientDimensions.getHeight()
      }, 200)
    }
  }
}

// Cross-browser support as described in:
// https://stackoverflow.com/questions/1248081
const clientDimensions = {
  getWidth () {
    if (typeof document === 'undefined') return 0 // SSR
    return Math.max(
      document.documentElement.clientWidth,
      window.innerWidth || 0
    )
  },
  getHeight () {
    if (typeof document === 'undefined') return 0 // SSR
    return Math.max(
      document.documentElement.clientHeight,
      window.innerHeight || 0
    )
  }
}

export default breakpoint


import VTooltip from '@/components/VTooltip'
import { test } from '@/test'

test('VTooltip.js', ({ mount, compileToFunctions }) => {
  it('should render component and match snapshot', async () => {
    const wrapper = mount(VTooltip, {
      propsData: {
        openDelay: 0
      },
      slots: {
        activator: [compileToFunctions('activator')],
        default: [compileToFunctions('content')]
      }
    })

    expect(wrapper.html()).toMatchSnapshot()

    wrapper.setProps({
      value: true
    })
    await wrapper.vm.$nextTick()
    expect(wrapper.html()).toMatchSnapshot()

    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should render component with custom lazy and match snapshot', () => {
    const wrapper = mount(VTooltip, {
      propsData: {
        lazy: true
      },
      slots: {
        activator: [compileToFunctions('activator')],
        default: [compileToFunctions('content')]
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should render component with value=true and match snapshot', async () => {
    const wrapper = mount(VTooltip, {
      propsData: {
        value: true
      },
      slots: {
        activator: [compileToFunctions('activator')],
        default: [compileToFunctions('content')]
      }
    })

    expect(wrapper.data().isActive).toBe(true)
    expect(wrapper.html()).toMatchSnapshot()
    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should render component with zIndex prop and match snapshot', async () => {
    const wrapper = mount(VTooltip, {
      propsData: {
        zIndex: 42
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should display tooltip after mouseenter and hide after mouseleave', async () => {
    jest.useFakeTimers()
    const wrapper = mount(VTooltip, {
      propsData: {
        openDelay: 123,
        closeDelay: 321
      },
      slots: {
        activator: [compileToFunctions('activator')],
        default: [compileToFunctions('content')]
      }
    })

    const activator = wrapper.find('.v-tooltip__content + span')[0]
    const cb = jest.fn()
    wrapper.instance().$on('input', cb)

    activator.trigger('mouseenter')
    jest.runAllTimers()
    await wrapper.vm.$nextTick()
    expect(setTimeout.mock.calls).toHaveLength(1)
    expect(setTimeout.mock.calls[0][1]).toBe(123)
    expect(cb).toBeCalledWith(true)

    activator.trigger('mouseleave')
    jest.runAllTimers()
    await wrapper.vm.$nextTick()
    expect(setTimeout.mock.calls).toHaveLength(2)
    expect(setTimeout.mock.calls[1][1]).toBe(321)
    expect(cb).toBeCalledWith(false)

    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })
})


it('should have readonly attribute', () => {

keep keep keep keep add keep keep keep keep keep

wrapper.setProps({ counter: undefined })


import VList from '@/components/VList'
import { test } from '@/test'

// TODO: Test actual behaviour instead of classes
test('VList.js', ({ mount }) => {
  it('should render component and match snapshot', () => {
    const wrapper = mount(VList)

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should render a dense component and match snapshot', () => {
    const wrapper = mount(VList, {
      propsData: {
        dense: true
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should render a subheader component and match snapshot', () => {
    const wrapper = mount(VList, {
      propsData: {
        subheader: true
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should render a threeLine component and match snapshot', () => {
    const wrapper = mount(VList, {
      propsData: {
        threeLine: true
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should render a twoLine component and match snapshot', () => {
    const wrapper = mount(VList, {
      propsData: {
        twoLine: true
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })
})


import '../../stylus/components/_chips.styl'

import VIcon from '../VIcon'
import Colorable from '../../mixins/colorable'
import Themeable from '../../mixins/themeable'
import Toggleable from '../../mixins/toggleable'

export default {
  name: 'v-chip',

  mixins: [Colorable, Themeable, Toggleable],

  props: {
    close: Boolean,
    disabled: Boolean,
    label: Boolean,
    outline: Boolean,
    // Used for selects/tagging
    selected: Boolean,
    small: Boolean,
    textColor: String,
    value: {
      type: Boolean,
      default: true
    }
  },

  computed: {
    classes () {
      const classes = this.addBackgroundColorClassChecks({
        'v-chip--disabled': this.disabled,
        'v-chip--selected': this.selected,
        'v-chip--label': this.label,
        'v-chip--outline': this.outline,
        'v-chip--small': this.small,
        'v-chip--removable': this.close,
        'theme--light': this.light,
        'theme--dark': this.dark
      })

      return (this.textColor || this.outline)
        ? this.addTextColorClassChecks(classes, this.textColor || this.color)
        : classes
    }
  },

  methods: {
    genClose (h) {
      const data = {
        staticClass: 'v-chip__close',
        on: {
          click: e => {
            e.stopPropagation()

            this.$emit('input', false)
          }
        }
      }

      return h('div', data, [
        h(VIcon, '$vuetify.icons.delete')
      ])
    },
    genContent (h) {
      const children = [this.$slots.default]

      this.close && children.push(this.genClose(h))

      return h('span', {
        staticClass: 'v-chip__content'
      }, children)
    }
  },

  render (h) {
    const data = {
      staticClass: 'v-chip',
      'class': this.classes,
      attrs: { tabindex: this.disabled ? -1 : 0 },
      directives: [{
        name: 'show',
        value: this.isActive
      }],
      on: this.$listeners
    }

    return h('span', data, [this.genContent(h)])
  }
}


# Vuetify.js Contributing Guide
Hello and thank you for interest in helping make Vuetify better. Please take a few moments to review the following guidelines.

## IMPORTANT INFORMATION
Use https://issues.vuetifyjs.com/ for submitting new bug reports or feature requests, otherwise they will be closed immediately

## Reporting Issues
* The issue list of this repo is exclusively for bug reports and feature requests. Non-conforming issues will be closed immediately.
  * For general questions, please join the Discord chat room.
* Try to search for your issue, it may have been answered.
* See if the error is reproduceable with the latest version.
* If reproduceable, please provide a [simple codepen](https://template.vuetifyjs.com) or repository that can be cloned to produce the expected behavior. It is preferred that you create an initial commit with no changes first, then another one that will cause the issue.
* **Never** comment "+1" or "me too!" on issues without leaving additional information, use the :+1: button in the top right instead.

## Pull Requests
* Always work on a new branch. Making changes on your fork's `dev` or `master` branch can cause problems. (See [The beginner's guide to contributing to a GitHub project](https://akrabat.com/the-beginners-guide-to-contributing-to-a-github-project/))
* Bug fixes should be submitted to the `master` branch.
* New features and breaking changes should be submitted to the `dev` branch.
* Use a descriptive title no more than 64 characters long. This will be used as the commit message when your PR is merged.
* For changes and feature requests, please include an example of what you are trying to solve and an example of the markup. It is preferred that you create an issue first however, as that will allow the team to review your proposal before you start.
* Please reference the issue # that the PR resolves, something like `Fixes #1234` or `Resolves #6458` (See [closing issues using keywords](https://help.github.com/articles/closing-issues-using-keywords/))


keep keep keep keep replace keep keep keep keep

exports[`VTextField.js should not display counter when set to false/undefined/null 1`] = `

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`VTextField.js should have prefix and suffix 1`] = `

          $

        <input tabindex="0"
               type="text"
        >

          .com

`;

exports[`VTextField.js should not display counter when set to false 1`] = `

        <input maxlength="50"
               tabindex="0"
               type="text"
        >

        0 / 50

`;

exports[`VTextField.js should not display counter when set to false 2`] = `

        <input maxlength="50"
               tabindex="0"
               type="text"
        >

`;

exports[`VTextField.js should render component and match snapshot 1`] = `

        <input tabindex="0"
               type="text"
        >

`;

exports[`VTextField.js should render component with async loading and custom progress and match snapshot 1`] = `

        <input tabindex="0"
               type="text"
        >

`;

exports[`VTextField.js should render component with async loading and match snapshot 1`] = `

        <input tabindex="0"
               type="text"
        >

`;


$shadow-key-umbra-opacity := rgba(0,0,0, .2)
$shadow-key-penumbra-opacity := rgba(0,0,0, .14)
$shadow-key-ambient-opacity := rgba(0,0,0, .12)

$shadow-key-umbra := 0px 0px 0px 0px $shadow-key-umbra-opacity,
                    0px 2px 1px -1px $shadow-key-umbra-opacity,
                    0px 3px 1px -2px $shadow-key-umbra-opacity,
                    0px 3px 3px -2px $shadow-key-umbra-opacity,
                    0px 2px 4px -1px $shadow-key-umbra-opacity,
                    0px 3px 5px -1px $shadow-key-umbra-opacity,
                    0px 3px 5px -1px $shadow-key-umbra-opacity,
                    0px 4px 5px -2px $shadow-key-umbra-opacity,
                    0px 5px 5px -3px $shadow-key-umbra-opacity,
                    0px 5px 6px -3px $shadow-key-umbra-opacity,
                    0px 6px 6px -3px $shadow-key-umbra-opacity,
                    0px 6px 7px -4px $shadow-key-umbra-opacity,
                    0px 7px 8px -4px $shadow-key-umbra-opacity,
                    0px 7px 8px -4px $shadow-key-umbra-opacity,
                    0px 7px 9px -4px $shadow-key-umbra-opacity,
                    0px 8px 9px -5px $shadow-key-umbra-opacity,
                    0px 8px 10px -5px $shadow-key-umbra-opacity,
                    0px 8px 11px -5px $shadow-key-umbra-opacity,
                    0px 9px 11px -5px $shadow-key-umbra-opacity,
                    0px 9px 12px -6px $shadow-key-umbra-opacity,
                    0px 10px 13px -6px $shadow-key-umbra-opacity,
                    0px 10px 13px -6px $shadow-key-umbra-opacity,
                    0px 10px 14px -6px $shadow-key-umbra-opacity,
                    0px 11px 14px -7px $shadow-key-umbra-opacity,
                    0px 11px 15px -7px $shadow-key-umbra-opacity

$shadow-key-penumbra := 0px 0px 0px 0px $shadow-key-penumbra-opacity,
                        0px 1px 1px 0px $shadow-key-penumbra-opacity,
                        0px 2px 2px 0px $shadow-key-penumbra-opacity,
                        0px 3px 4px 0px $shadow-key-penumbra-opacity,
                        0px 4px 5px 0px $shadow-key-penumbra-opacity,
                        0px 5px 8px 0px $shadow-key-penumbra-opacity,
                        0px 6px 10px 0px $shadow-key-penumbra-opacity,
                        0px 7px 10px 1px $shadow-key-penumbra-opacity,
                        0px 8px 10px 1px $shadow-key-penumbra-opacity,
                        0px 9px 12px 1px $shadow-key-penumbra-opacity,
                        0px 10px 14px 1px $shadow-key-penumbra-opacity,
                        0px 11px 15px 1px $shadow-key-penumbra-opacity,
                        0px 12px 17px 2px $shadow-key-penumbra-opacity,
                        0px 13px 19px 2px $shadow-key-penumbra-opacity,
                        0px 14px 21px 2px $shadow-key-penumbra-opacity,
                        0px 15px 22px 2px $shadow-key-penumbra-opacity,
                        0px 16px 24px 2px $shadow-key-penumbra-opacity,
                        0px 17px 26px 2px $shadow-key-penumbra-opacity,
                        0px 18px 28px 2px $shadow-key-penumbra-opacity,
                        0px 19px 29px 2px $shadow-key-penumbra-opacity,
                        0px 20px 31px 3px $shadow-key-penumbra-opacity,
                        0px 21px 33px 3px $shadow-key-penumbra-opacity,
                        0px 22px 35px 3px $shadow-key-penumbra-opacity,
                        0px 23px 36px 3px $shadow-key-penumbra-opacity,
                        0px 24px 38px 3px $shadow-key-penumbra-opacity

$shadow-key-ambient := 0px 0px 0px 0px $shadow-key-ambient-opacity,
                      0px 1px 3px 0px $shadow-key-ambient-opacity,
                      0px 1px 5px 0px $shadow-key-ambient-opacity,
                      0px 1px 8px 0px $shadow-key-ambient-opacity,
                      0px 1px 10px 0px $shadow-key-ambient-opacity,
                      0px 1px 14px 0px $shadow-key-ambient-opacity,
                      0px 1px 18px 0px $shadow-key-ambient-opacity,
                      0px 2px 16px 1px $shadow-key-ambient-opacity,
                      0px 3px 14px 2px $shadow-key-ambient-opacity,
                      0px 3px 16px 2px $shadow-key-ambient-opacity,
                      0px 4px 18px 3px $shadow-key-ambient-opacity,
                      0px 4px 20px 3px $shadow-key-ambient-opacity,
                      0px 5px 22px 4px $shadow-key-ambient-opacity,
                      0px 5px 24px 4px $shadow-key-ambient-opacity,
                      0px 5px 26px 4px $shadow-key-ambient-opacity,
                      0px 6px 28px 5px $shadow-key-ambient-opacity,
                      0px 6px 30px 5px $shadow-key-ambient-opacity,
                      0px 6px 32px 5px $shadow-key-ambient-opacity,
                      0px 7px 34px 6px $shadow-key-ambient-opacity,
                      0px 7px 36px 6px $shadow-key-ambient-opacity,
                      0px 8px 38px 7px $shadow-key-ambient-opacity,
                      0px 8px 40px 7px $shadow-key-ambient-opacity,
                      0px 8px 42px 7px $shadow-key-ambient-opacity,
                      0px 9px 44px 8px $shadow-key-ambient-opacity,
                      0px 9px 46px 8px $shadow-key-ambient-opacity

// MIXINS
elevation($z, important = false)
  box-shadow: $shadow-key-umbra[$z],
              $shadow-key-penumbra[$z],
              $shadow-key-ambient[$z] (important ? !important : )

elevationTransition($duration = 280ms, $easing = cubic-bezier(.4, 0, .2, 1))
  transition: box-shadow $duration $easing
  will-change: box-shadow


// Mixins
import Colorable from '../../mixins/colorable'
import DatePickerTable from './mixins/date-picker-table'
import Themeable from '../../mixins/themeable'

// Utils
import { pad, createNativeLocaleFormatter } from './util'

export default {
  name: 'v-date-picker-month-table',

  mixins: [
    Colorable,
    DatePickerTable,
    Themeable
  ],

  computed: {
    formatter () {
      return this.format || createNativeLocaleFormatter(this.locale, { month: 'short', timeZone: 'UTC' }, { start: 5, length: 2 })
    }
  },

  methods: {
    calculateTableDate (delta) {
      return `${parseInt(this.tableDate, 10) + Math.sign(delta || 1)}`
    },
    genTBody () {
      const children = []
      const cols = Array(3).fill(null)
      const rows = 12 / cols.length

      for (let row = 0; row < rows; row++) {
        const tds = cols.map((_, col) => {
          const month = row * cols.length + col
          return this.$createElement('td', {
            key: month
          }, [
            this.genButton(`${this.displayedYear}-${pad(month + 1)}`, false)
          ])
        })

        children.push(this.$createElement('tr', {
          key: row
        }, tds))
      }

      return this.$createElement('tbody', children)
    }
  },

  render () {
    return this.genTable('v-date-picker-table v-date-picker-table--month', [
      this.genTBody()
    ])
  }
}


firefox()
  @-moz-document url-prefix()
    {block}


exports[`VTextField.js should not display counter when set to false 2`] = `

exports[`VTextField.js should not display counter when set to false/undefined/null 2`] = `


// Styles
import '../../stylus/components/_range-sliders.styl'

// Extensions
import VSlider from '../VSlider'

import {
  createRange,
  deepEqual
} from '../../util/helpers'

export default {
  name: 'v-range-slider',

  extends: VSlider,

  data: vm => ({
    activeThumb: null,
    lazyValue: !vm.value.length
      ? [0, 0]
      : vm.value
  }),

  props: {
    value: {
      type: Array,
      default: () => ([])
    }
  },

  computed: {
    classes () {
      return Object.assign({}, {
        'v-input--range-slider': true
      }, VSlider.computed.classes.call(this))
    },
    internalValue: {
      get () {
        return this.lazyValue
      },
      set (val) {
        const { min, max } = this
        // Round value to ensure the
        // entire slider range can
        // be selected with step
        let value = val.map(v => this.roundValue(Math.min(Math.max(v, min), max)))

        // Switch values if range and wrong order
        if (value[0] > value[1] || value[1] < value[0]) {
          if (this.activeThumb !== null) this.activeThumb = this.activeThumb === 1 ? 0 : 1
          value = [value[1], value[0]]
        }

        this.lazyValue = value
        if (!deepEqual(value, this.value)) this.$emit('input', value)

        this.validate()
      }
    },
    inputWidth () {
      return this.internalValue.map(v => (
        this.roundValue(v) - this.min) / (this.max - this.min) * 100
      )
    },
    isDirty () {
      return this.internalValue.some(v => v !== this.min) || this.alwaysDirty
    },
    trackFillStyles () {
      const styles = VSlider.computed.trackFillStyles.call(this)
      const fillPercent = Math.abs(this.inputWidth[0] - this.inputWidth[1])

      styles.width = `calc(${fillPercent}% - ${this.trackPadding}px)`
      styles[this.$vuetify.rtl ? 'right' : 'left'] = `${this.inputWidth[0]}%`

      return styles
    },
    trackPadding () {
      if (this.isDirty ||
        this.internalValue[0]
      ) return 0

      return VSlider.computed.trackPadding.call(this)
    }
  },

  methods: {
    getIndexOfClosestValue (arr, v) {
      if (Math.abs(arr[0] - v) < Math.abs(arr[1] - v)) return 0
      else return 1
    },
    genInput () {
      return createRange(2).map(i => {
        const input = VSlider.methods.genInput.call(this)

        input.data.attrs.value = this.internalValue[i]

        input.data.on.focus = e => {
          this.activeThumb = i
          VSlider.methods.onFocus.call(this, e)
        }

        return input
      })
    },
    genChildren () {
      return [
        this.genInput(),
        this.genTrackContainer(),
        this.genSteps(),
        createRange(2).map(i => {
          const value = this.internalValue[i]
          const onDrag = e => {
            this.isActive = true
            this.activeThumb = i
            this.onThumbMouseDown(e)
          }
          const valueWidth = this.inputWidth[i]
          const isActive = (this.isFocused || this.isActive) && this.activeThumb === i

          return this.genThumbContainer(value, valueWidth, isActive, onDrag)
        })
      ]
    },
    onSliderClick (e) {
      if (!this.isActive) {
        this.isFocused = true
        this.onMouseMove(e, true)
        this.$emit('change', this.internalValue)
      }
    },
    onMouseMove (e, trackClick = false) {
      const { value, isInsideTrack } = this.parseMouseMove(e)

      if (isInsideTrack) {
        if (trackClick) this.activeThumb = this.getIndexOfClosestValue(this.internalValue, value)

        this.setInternalValue(value)
      }
    },
    onKeyDown (e) {
      const value = this.parseKeyDown(e, this.internalValue[this.activeThumb])

      if (value == null) return

      this.setInternalValue(value)
    },
    setInternalValue (value) {
      this.internalValue = this.internalValue.map((v, i) => {
        if (i === this.activeThumb) return value
        else return Number(v)
      })
    }
  }
}


// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`VChip.vue should be removable 1`] = `

        cancel

`;

exports[`VChip.vue should be removable 2`] = `

        cancel

`;

exports[`VChip.vue should have a v-chip class 1`] = `

`;


@import '../bootstrap'
@import '../theme'

/* Themes */

v-picker-title($material)
  background: $material.picker.title

theme(v-picker-title, "v-picker__title")

v-picker-body($material)
  background: $material.picker.body

theme(v-picker-body, 'v-picker__body')

.v-picker
  border-radius: $card-border-radius
  contain: layout style
  display: inline-flex
  flex-direction: column
  vertical-align: top

.v-picker--full-width
  display: flex

.v-picker__title
  color: #fff
  border-top-left-radius: $card-border-radius
  border-top-right-radius: $card-border-radius
  padding: 16px

.v-picker__title__btn
  transition: $primary-transition

  &.active
    opacity: 1

  &:not(.active)
    opacity: .6
    cursor: pointer

    &:hover
      opacity: 1

.v-picker__body
  height: auto
  overflow: hidden
  position: relative

  flex: 1 0 auto
  display: flex
  flex-direction: column
  align-items: center

  > div
    width: 100%

    &.fade-transition-leave-active
      position: absolute

.v-picker--landscape
  .v-picker__title
    border-top-right-radius: 0
    border-bottom-right-radius: 0
    width: 170px
    position: absolute
    top: 0
    left: 0
    height: 100%
    z-index: 1

  .v-picker__body,
  .v-picker__actions
    margin-left: 170px


import type { ValidateFunction } from 'ajv';

import type {
	JoinPathPattern,
	Method,
	MethodOf,
	OperationParams,
	OperationResult,
	PathPattern,
	UrlParams,
} from '@rocket.chat/rest-typings';
import type { IUser, IMethodConnection } from '@rocket.chat/core-typings';

import { ITwoFactorOptions } from '../../2fa/server/code';

type SuccessResult<T> = {
	statusCode: 200;
	body: T extends object ? { success: true } & T : T;
};

type FailureResult<T, TStack = undefined, TErrorType = undefined, TErrorDetails = undefined> = {
	statusCode: 400;
	body: T extends object
		? { success: false } & T
		: {
				success: false;
				error: T;
				stack: TStack;
				errorType: TErrorType;
				details: TErrorDetails;
		  } & (undefined extends TErrorType ? {} : { errorType: TErrorType }) &
				(undefined extends TErrorDetails ? {} : { details: TErrorDetails extends string ? unknown : TErrorDetails });
};

type UnauthorizedResult<T> = {
	statusCode: 403;
	body: {
		success: false;
		error: T | 'unauthorized';
	};
};

type NotFoundResult = {
	statusCode: 404;
	body: {
		success: false;
		error: string;
	};
};

export type NonEnterpriseTwoFactorOptions = {
	authRequired: true;
	forceTwoFactorAuthenticationForNonEnterprise: true;
	twoFactorRequired: true;
	permissionsRequired?: string[];
	twoFactorOptions: ITwoFactorOptions;
};

type Options =
	| {
			permissionsRequired?: string[];
			authRequired?: boolean;
			forceTwoFactorAuthenticationForNonEnterprise?: boolean;
	  }
	| {
			authRequired: true;
			twoFactorRequired: true;
			twoFactorOptions?: ITwoFactorOptions;
	  };

type Request = {
	method: 'GET' | 'POST' | 'PUT' | 'DELETE';
	url: string;
	headers: Record<string, string>;
	body: any;
};

type PartialThis = {
	readonly request: Request & { query: Record<string, string> };
};

type ActionThis<TMethod extends Method, TPathPattern extends PathPattern, TOptions> = {
	urlParams: UrlParams<TPathPattern>;
	// TODO make it unsafe
	readonly queryParams: TMethod extends 'GET' ? Partial<OperationParams<TMethod, TPathPattern>> : Record<string, string>;
	// TODO make it unsafe
	readonly bodyParams: TMethod extends 'GET' ? Record<string, unknown> : Partial<OperationParams<TMethod, TPathPattern>>;
	readonly request: Request;
	requestParams(): OperationParams<TMethod, TPathPattern>;
	getLoggedInUser(): IUser | undefined;
	getPaginationItems(): {
		readonly offset: number;
		readonly count: number;
	};
	parseJsonQuery(): {
		sort: Record<string, unknown>;
		fields: Record<string, unknown>;
		query: Record<string, unknown>;
	};
	/* @deprecated */
	getUserFromParams(): IUser;
} & (TOptions extends { authRequired: true }
	? {
			readonly user: IUser;
			readonly userId: string;
	  }
	: {
			readonly user: null;
			readonly userId: null;
	  });

export type ResultFor<TMethod extends Method, TPathPattern extends PathPattern> =
	| SuccessResult<OperationResult<TMethod, TPathPattern>>
	| FailureResult<unknown, unknown, unknown, unknown>
	| UnauthorizedResult<unknown>
	| NotFoundResult;

type Action<TMethod extends Method, TPathPattern extends PathPattern, TOptions> =
	| ((this: ActionThis<TMethod, TPathPattern, TOptions>) => Promise<ResultFor<TMethod, TPathPattern>>)
	| ((this: ActionThis<TMethod, TPathPattern, TOptions>) => ResultFor<TMethod, TPathPattern>);

type Operation<TMethod extends Method, TPathPattern extends PathPattern, TEndpointOptions> =
	| Action<TMethod, TPathPattern, TEndpointOptions>
	| ({
			action: Action<TMethod, TPathPattern, TEndpointOptions>;
	  } & { twoFactorRequired: boolean });

type Operations<TPathPattern extends PathPattern, TOptions extends Options = {}> = {
	[M in MethodOf<TPathPattern> as Lowercase<M>]: Operation<Uppercase<M>, TPathPattern, TOptions>;
};

declare class APIClass<TBasePath extends string = '/'> {
	fieldSeparator(fieldSeparator: unknown): void;

	limitedUserFieldsToExclude(fields: { [x: string]: unknown }, limitedUserFieldsToExclude: unknown): { [x: string]: unknown };

	limitedUserFieldsToExcludeIfIsPrivilegedUser(
		fields: { [x: string]: unknown },
		limitedUserFieldsToExcludeIfIsPrivilegedUser: unknown,
	): { [x: string]: unknown };

	processTwoFactor({
		userId,
		request,
		invocation,
		options,
		connection,
	}: {
		userId: string;
		request: Request;
		invocation: { twoFactorChecked: boolean };
		options?: Options;
		connection: IMethodConnection;
	}): void;

	addRoute<TSubPathPattern extends string>(
		subpath: TSubPathPattern,
		operations: Operations<JoinPathPattern<TBasePath, TSubPathPattern>>,
	): void;

	addRoute<TSubPathPattern extends string, TPathPattern extends JoinPathPattern<TBasePath, TSubPathPattern>>(
		subpaths: TSubPathPattern[],
		operations: Operations<TPathPattern>,
	): void;

	addRoute<TSubPathPattern extends string, TOptions extends Options>(
		subpath: TSubPathPattern,
		options: TOptions,
		operations: Operations<JoinPathPattern<TBasePath, TSubPathPattern>, TOptions>,
	): void;

	addRoute<TSubPathPattern extends string, TPathPattern extends JoinPathPattern<TBasePath, TSubPathPattern>, TOptions extends Options>(
		subpaths: TSubPathPattern[],
		options: TOptions,
		operations: Operations<TPathPattern, TOptions>,
	): void;

	addAuthMethod(func: (this: PartialThis, ...args: any[]) => any): void;

	success<T>(result: T): SuccessResult<T>;

	success(): SuccessResult<void>;

	failure<T, TErrorType extends string, TStack extends string, TErrorDetails>(
		result: T,
		errorType?: TErrorType,
		stack?: TStack,
		error?: { details: TErrorDetails },
	): FailureResult<T, TErrorType, TStack, TErrorDetails>;

	failure<T>(result: T): FailureResult<T>;

	failure(): FailureResult<void>;

	notFound(msg?: string): NotFoundResult;

	unauthorized<T>(msg?: T): UnauthorizedResult<T>;

	defaultFieldsToExclude: {
		joinCode: 0;
		members: 0;
		importIds: 0;
		e2e: 0;
	};
}

export declare const API: {
	v1: APIClass<'/v1'>;
	default: APIClass;
	helperMethods: Map<string, (...args: any[]) => unknown>;
};

export declare const defaultRateLimiterOptions: {
	numRequestsAllowed: number;
	intervalTimeInMS: number;
};


import { Meteor } from 'meteor/meteor';
import { Blaze } from 'meteor/blaze';
import { Template } from 'meteor/templating';

import { t, getUserPreference } from '../../../utils';
import './modal.html';
import { handleError } from '../../../../client/lib/utils/handleError';

let modalStack = [];

const createModal = (config = {}, fn, onCancel) => {
	config.confirmButtonText = config.confirmButtonText || (config.type === 'error' ? t('Ok') : t('Send'));
	config.cancelButtonText = config.cancelButtonText || t('Cancel');
	config.closeOnConfirm = config.closeOnConfirm == null ? true : config.closeOnConfirm;
	config.showConfirmButton = config.showConfirmButton == null ? true : config.showConfirmButton;
	config.showFooter = config.showConfirmButton === true || config.showCancelButton === true;
	config.confirmOnEnter = config.confirmOnEnter == null ? true : config.confirmOnEnter;
	config.closeOnEscape = config.closeOnEscape == null ? true : config.closeOnEscape;

	if (config.type === 'input') {
		config.input = true;
		config.type = false;

		if (!config.inputType) {
			config.inputType = 'text';
		}
	}

	let renderedModal;
	let timer;

	const instance = {
		...config,

		render: () => {
			if (renderedModal) {
				renderedModal.firstNode().style.display = '';
				return;
			}

			renderedModal = Blaze.renderWithData(Template.rc_modal, instance, document.body);

			if (config.timer) {
				timer = setTimeout(() => {
					instance.close();
				}, config.timer);
			}
		},

		hide: () => {
			if (renderedModal) {
				renderedModal.firstNode().style.display = 'none';
			}

			if (timer) {
				clearTimeout(timer);
				timer = undefined;
			}
		},

		destroy: () => {
			if (renderedModal) {
				Blaze.remove(renderedModal);
				renderedModal = undefined;
			}

			if (timer) {
				clearTimeout(timer);
				timer = undefined;
			}
		},

		close: () => {
			if (onCancel) {
				onCancel.call(instance);
			}
			instance.destroy();
			modalStack = modalStack.filter((modal) => modal !== instance);
			if (modalStack.length) {
				modalStack[modalStack.length - 1].render();
			}
		},

		confirm: (value) => {
			config.closeOnConfirm && instance.close();

			if (fn) {
				fn.call(instance, value);
				return;
			}

			instance.close();
		},

		cancel: () => {
			instance.close();
		},

		showInputError: (text) => {
			const errorEl = document.querySelector('.rc-modal__content-error');
			errorEl.innerHTML = text;
			errorEl.style.display = 'block';
		},
	};

	return instance;
};

export const modal = {
	open: (config = {}, fn, onCancel) => {
		modalStack.forEach((instance) => {
			instance.destroy();
		});
		modalStack = [];

		const instance = createModal(config, fn, onCancel);

		if (config.dontAskAgain) {
			const dontAskAgainList = getUserPreference(Meteor.userId(), 'dontAskAgainList');

			if (dontAskAgainList && dontAskAgainList.some((dontAsk) => dontAsk.action === config.dontAskAgain.action)) {
				instance.confirm(true);
				return;
			}
		}

		instance.render();
		modalStack.push(instance);
	},
	push: (config = {}, fn, onCancel) => {
		const instance = createModal(config, fn, onCancel);

		modalStack.forEach((instance) => {
			instance.hide();
		});
		instance.render();
		modalStack.push(instance);
		return instance;
	},
	cancel: () => {
		if (modalStack.length) {
			modalStack[modalStack.length - 1].cancel();
		}
	},
	close: () => {
		if (modalStack.length) {
			modalStack[modalStack.length - 1].close();
		}
	},
	confirm: (value) => {
		if (modalStack.length) {
			modalStack[modalStack.length - 1].confirm(value);
		}
	},
	showInputError: (text) => {
		if (modalStack.length) {
			modalStack[modalStack.length - 1].showInputError(text);
		}
	},
	onKeyDown: (event) => {
		if (!modalStack.length) {
			return;
		}
		const instance = modalStack[modalStack.length - 1];
		if (event.key === 'Escape') {
			event.preventDefault();
			event.stopPropagation();

			instance.close();
		}

		if (instance && instance.confirmOnEnter && event.key === 'Enter') {
			event.preventDefault();
			event.stopPropagation();

			if (instance.input) {
				return instance.confirm($('.js-modal-input').val());
			}

			instance.confirm(true);
		}
	},
};

Template.rc_modal.helpers({
	showFooter() {
		const { showCancelButton, showConfirmButton } = this;
		return showCancelButton || showConfirmButton;
	},
	hasAction() {
		return !!this.action;
	},
	type() {
		return this.type && `rc-modal__content-icon rc-modal__content-icon--modal-${this.type}`;
	},
	modalIcon() {
		switch (this.type) {
			case 'success':
				return 'checkmark-circled';
			case 'error':
				return 'circle-cross';
			case 'info':
				return 'info-circled';
			default:
				return `modal-${this.type}`;
		}
	},
});

Template.rc_modal.onRendered(function () {
	this.oldFocus = document.activeElement;
	if (this.data.input) {
		$('.js-modal-input', this.firstNode).focus();
	} else if (this.data.showConfirmButton && this.data.confirmOnEnter) {
		$('.js-confirm', this.firstNode).focus();
	}
	if (this.data.onRendered) {
		this.data.onRendered();
	}
	this.data.closeOnEscape && document.addEventListener('keydown', modal.onKeyDown);
});

Template.rc_modal.onDestroyed(function () {
	this.oldFocus && this.oldFocus.focus();
	document.removeEventListener('keydown', modal.onKeyDown);
});

Template.rc_modal.events({
	'click .js-action'(event, instance) {
		!this.action || this.action.call(instance.data.data, event, instance);
		event.stopPropagation();
		this.close();
	},
	'click .js-input-action'(e, instance) {
		!this.inputAction || this.inputAction.call(instance.data.data, e, instance);
		e.stopPropagation();
	},
	'click .js-close'(e) {
		e.preventDefault();
		e.stopPropagation();
		this.cancel();
	},
	'click .js-confirm'(event, instance) {
		event.stopPropagation();
		const { dontAskAgain } = instance.data;
		if (dontAskAgain && document.getElementById('dont-ask-me-again').checked) {
			const dontAskAgainObject = {
				action: dontAskAgain.action,
				label: dontAskAgain.label,
			};

			let dontAskAgainList = getUserPreference(Meteor.userId(), 'dontAskAgainList');
			if (dontAskAgainList) {
				dontAskAgainList.push(dontAskAgainObject);
			} else {
				dontAskAgainList = [dontAskAgainObject];
			}

			Meteor.call('saveUserPreferences', { dontAskAgainList }, function (error) {
				if (error) {
					return handleError(error);
				}
			});
		}

		if (instance.data.input) {
			this.confirm(document.getElementsByClassName('js-modal-input')[0].value);
			return;
		}

		this.confirm(true);
	},
	'click .rc-modal-wrapper'(event, instance) {
		if (instance.data.allowOutsideClick === false) {
			return false;
		}

		if (event.currentTarget === event.target) {
			event.stopPropagation();
			this.close();
		}
	},
});


import { debounce } from 'underscore';

import { api } from '../../../../server/sdk/api';
import { ILicense } from '../../../../server/sdk/types/ILicense';
import { ServiceClassInternal } from '../../../../server/sdk/types/ServiceClass';
import { guestPermissions } from '../../authorization/lib/guestPermissions';
import { resetEnterprisePermissions } from '../../authorization/server/resetEnterprisePermissions';
import { getModules, hasLicense, isEnterprise, onModule, onValidateLicenses } from './license';

export class LicenseService extends ServiceClassInternal implements ILicense {
	protected name = 'license';

	constructor() {
		super();

		onValidateLicenses((): void => {
			if (!isEnterprise()) {
				return;
			}

			api.broadcast('authorization.guestPermissions', guestPermissions);
			resetEnterprisePermissions();
		});

		onModule((licenseModule) => {
			api.broadcast('license.module', licenseModule);
		});

		/**
		 * The main idea is if there is no scalability module enabled,
		 * then we should not allow more than one service per environment.
		 * So we list the services and nodes, and if there is more than
		 * one, we inform the service that it should be disabled.
		 */
		const shutdownServices = debounce(async () => {
			if (hasLicense('scalability')) {
				return;
			}

			const services: {
				name: string;
				nodes: string[];
			}[] = await api.call('$node.services');

			// Filter only the duplicated services
			const duplicated = services.filter((service) => {
				return service.name !== '$node' && service.nodes.length > 1;
			});

			if (!duplicated.length) {
				return;
			}

			const brokers: Record<string, string[]> = Object.fromEntries(
				duplicated.map((service) => {
					// remove the first node from the list
					const [, ...nodes] = service.nodes;
					return [service.name, nodes];
				}),
			);

			const duplicatedServicesNames = duplicated.map((service) => service.name);

			// send shutdown signal to the duplicated services
			api.broadcastToServices(duplicatedServicesNames, 'shutdown', brokers);
		}, 1000);

		this.onEvent('$services.changed', async ({ localService }) => {
			// since this is an internal service, we can ignore this event if the service that triggered is a local service.
			// this will also prevent race conditions if a license is not in place when this process is starting up.
			if (localService) {
				return;
			}
			shutdownServices();
		});
	}

	async started(): Promise<void> {
		if (!isEnterprise()) {
			return;
		}

		api.broadcast('authorization.guestPermissions', guestPermissions);
		resetEnterprisePermissions();
	}

	hasLicense(feature: string): boolean {
		return hasLicense(feature);
	}

	isEnterprise(): boolean {
		return isEnterprise();
	}

	getModules(): string[] {
		return getModules();
	}

	getGuestPermissions(): string[] {
		return guestPermissions;
	}
}


var ism = db.isMaster();
if (!ism.ismaster) {
    rs.initiate(
        {
            _id: 'rs0',
            members: [
                {
                    _id: 0,
                    host: 'localhost:27017'
                }
            ]
        });
}


twoFactorRequired: true;
permissionsRequired?: string[];
twoFactorOptions: ITwoFactorOptions;
};
type Options =
| {
permissionsRequired?: string[];
authRequired?: boolean;

keep keep keep keep keep replace keep keep keep


import { VideoAttachmentProps } from '@rocket.chat/core-typings';
import { Box } from '@rocket.chat/fuselage';
import { useMediaUrl } from '@rocket.chat/ui-contexts';
import React, { FC } from 'react';

import MarkdownText from '../../../MarkdownText';
import Attachment from '../Attachment';
import { useCollapse } from '../hooks/useCollapse';

export const VideoAttachment: FC<VideoAttachmentProps> = ({
	title,
	video_url: url,
	video_type: type,
	collapsed: collapsedDefault = false,
	video_size: size,
	description,
	title_link: link,
	title_link_download: hasDownload,
}) => {
	const [collapsed, collapse] = useCollapse(collapsedDefault);
	const getURL = useMediaUrl();

	return (
		<Attachment>
			<Attachment.Row>
				<Attachment.Title>{title}</Attachment.Title>
				{size && <Attachment.Size size={size} />}
				{collapse}
				{hasDownload && link && <Attachment.Download title={title} href={getURL(link)} />}
			</Attachment.Row>
			{!collapsed && (
				<Attachment.Content width='full'>
					<Box is='video' width='full' controls preload='metadata'>
						<source src={getURL(url)} type={type} />
					</Box>
					{description && (
						<Attachment.Details is='figcaption'>
							<MarkdownText parseEmoji variant='inline' content={description} />
						</Attachment.Details>
					)}
				</Attachment.Content>
			)}
		</Attachment>
	);
};


/* eslint-disable complexity */
import { IMessage } from '@rocket.chat/core-typings';
import React, { FC, memo } from 'react';

import { isE2EEMessage } from '../../../../../lib/isE2EEMessage';
import MessageBodyRender from '../../../../components/Message/MessageBodyRender';
import { useMessageActions } from '../../contexts/MessageContext';
import EncryptedMessageRender from './EncryptedMessageRender';

const MessageRender: FC<{ message: IMessage; isThreadPreview?: boolean }> = ({ message, isThreadPreview }) => {
	const {
		actions: { openRoom, openUserCard },
	} = useMessageActions();

	const isEncryptedMessage = isE2EEMessage(message);

	return (
		<>
			{!isEncryptedMessage && !message.blocks && message.md && (
				<MessageBodyRender
					onUserMentionClick={openUserCard}
					onChannelMentionClick={openRoom}
					mentions={message?.mentions || []}
					channels={message?.channels || []}
					tokens={message.md}
					isThreadPreview={isThreadPreview}
				/>
			)}

			{!isEncryptedMessage && !message.blocks && !message.md && message.msg}

			{isEncryptedMessage && <EncryptedMessageRender message={message} />}
		</>
	);
};

export default memo(MessageRender);


import { Template } from 'meteor/templating';

import { roomCoordinator } from '../../../../../client/lib/rooms/roomCoordinator';

Template.roomSearch.helpers({
	roomIcon() {
		if (this.type === 'u') {
			return 'icon-at';
		}
		if (this.type === 'r') {
			return roomCoordinator.getIcon(this);
		}
	},
	userStatus() {
		if (this.type === 'u') {
			return `status-${this.status}`;
		}
	},
});


method: 'GET' | 'POST' | 'PUT' | 'DELETE';

keep keep keep replace keep keep keep keep keep

}
) & {
validateParams?: ValidateFunction;
};


import { slashCommands } from '../../utils/lib/slashCommand';

slashCommands.add(
	'unarchive',
	undefined,
	{
		description: 'Unarchive',
		params: '#channel',
		permission: 'unarchive-room',
	},
	undefined,
	false,
	undefined,
	undefined,
);


import './MethodInvocationOverride';
import './startup/settings';
import './methods/checkCodesRemaining';
import './methods/disable';
import './methods/enable';
import './methods/regenerateCodes';
import './methods/validateTempToken';
import './loginHandler';


type ActionThis<TMethod extends Method, TPathPattern extends PathPattern, TOptions> = {

readonly queryParams: TMethod extends 'GET' ? Partial<OperationParams<TMethod, TPathPattern>> : Record<string, string>;

? TOptions extends { validateParams: ValidateFunction<infer T> }


import { Serialized } from '@rocket.chat/core-typings';
import type { MatchPathPattern, Method, OperationParams, OperationResult, PathFor } from '@rocket.chat/rest-typings';
import { useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts';
import { useCallback } from 'react';

export const useEndpointActionExperimental = <TMethod extends Method, TPath extends PathFor<TMethod>>(
	method: TMethod,
	path: TPath,
	successMessage?: string,
): ((
	params: Serialized<OperationParams<TMethod, MatchPathPattern<TPath>>>,
) => Promise<Serialized<OperationResult<TMethod, MatchPathPattern<TPath>>>>) => {
	const sendData = useEndpoint(method, path);
	const dispatchToastMessage = useToastMessageDispatch();

	return useCallback(
		async (params) => {
			try {
				const data = await sendData(params);

				if (successMessage) {
					dispatchToastMessage({ type: 'success', message: successMessage });
				}

				return data;
			} catch (error) {
				dispatchToastMessage({ type: 'error', message: String(error) });
				// return { success: false };
				throw error;
			}
		},
		[dispatchToastMessage, sendData, successMessage],
	);
};


import { useTranslation } from '@rocket.chat/ui-contexts';
import { useCallback } from 'react';

export const useItemsPerPageLabel = (): (() => string) => {
	const t = useTranslation();
	return useCallback(() => t('Items_per_page:'), [t]);
};


import { Match, check } from 'meteor/check';
import { IUser, IVoipExtensionWithAgentInfo } from '@rocket.chat/core-typings';

import { API } from '../../api';
import { Users } from '../../../../models/server/raw/index';
import { hasPermission } from '../../../../authorization/server/index';
import { LivechatVoip } from '../../../../../server/sdk';
import { logger } from './logger';

function filter(
	array: IVoipExtensionWithAgentInfo[],
	{ queues, extension, agentId, status }: { queues?: string[]; extension?: string; agentId?: string; status?: string },
): IVoipExtensionWithAgentInfo[] {
	const defaultFunc = (): boolean => true;
	return array.filter((item) => {
		const queuesCond = queues && Array.isArray(queues) ? (): boolean => item.queues?.some((q) => queues.includes(q)) || false : defaultFunc;
		const extensionCond = extension?.trim() ? (): boolean => item?.extension === extension : defaultFunc;
		const agentIdCond = agentId?.trim() ? (): boolean => item?.userId === agentId : defaultFunc;
		const statusCond = status?.trim() ? (): boolean => item?.state === status : defaultFunc;

		return queuesCond() && extensionCond() && agentIdCond() && statusCond();
	});
}

function paginate<T>(array: T[], count = 10, offset = 0): T[] {
	return array.slice(offset, offset + count);
}

const isUserAndExtensionParams = (p: any): p is { userId: string; extension: string } => p.userId && p.extension;
const isUserIdndTypeParams = (p: any): p is { userId: string; type: 'free' | 'allocated' | 'available' } => p.userId && p.type;

API.v1.addRoute(
	'omnichannel/agent/extension',
	{ authRequired: true },
	{
		// Get the extensions associated with the agent passed as request params.
		async get() {
			if (!hasPermission(this.userId, 'view-agent-extension-association')) {
				return API.v1.unauthorized();
			}
			check(
				this.requestParams(),
				Match.ObjectIncluding({
					username: String,
				}),
			);
			const { username } = this.requestParams();
			const user = await Users.findOneByAgentUsername(username, {
				projection: { _id: 1 },
			});
			if (!user) {
				return API.v1.notFound('User not found');
			}
			const extension = await Users.getVoipExtensionByUserId(user._id, {
				projection: {
					_id: 1,
					username: 1,
					extension: 1,
				},
			});
			if (!extension) {
				return API.v1.notFound('Extension not found');
			}
			return API.v1.success({ extension });
		},

		// Create agent-extension association.
		async post() {
			if (!hasPermission(this.userId, 'manage-agent-extension-association')) {
				return API.v1.unauthorized();
			}
			check(
				this.bodyParams,
				Match.OneOf(
					Match.ObjectIncluding({
						username: String,
						extension: String,
					}),
					Match.ObjectIncluding({
						userId: String,
						extension: String,
					}),
				),
			);

			const { extension } = this.bodyParams;
			let user: IUser | null = null;

			if (!isUserAndExtensionParams(this.bodyParams)) {
				if (!this.bodyParams.username) {
					return API.v1.notFound();
				}
				user = await Users.findOneByAgentUsername(this.bodyParams.username, {
					projection: {
						_id: 1,
						username: 1,
					},
				});
			} else {
				if (!this.bodyParams.userId) {
					return API.v1.notFound();
				}
				user = await Users.findOneAgentById(this.bodyParams.userId, {
					projection: {
						_id: 1,
						username: 1,
					},
				});
			}

			if (!user) {
				return API.v1.notFound();
			}

			try {
				logger.debug(`Setting extension ${extension} for agent with id ${user._id}`);
				await Users.setExtension(user._id, extension);
				return API.v1.success();
			} catch (e) {
				logger.error({ msg: 'Extension already in use' });
				return API.v1.failure(`extension already in use ${extension}`);
			}
		},

		async delete() {
			if (!hasPermission(this.userId, 'manage-agent-extension-association')) {
				return API.v1.unauthorized();
			}
			check(
				this.requestParams(),
				Match.ObjectIncluding({
					username: String,
				}),
			);
			const { username } = this.requestParams();
			const user = await Users.findOneByAgentUsername(username, {
				projection: {
					_id: 1,
					username: 1,
					extension: 1,
				},
			});
			if (!user) {
				return API.v1.notFound();
			}
			if (!user.extension) {
				logger.debug(`User ${user._id} is not associated with any extension. Skipping`);
				return API.v1.success();
			}

			logger.debug(`Removing extension association for user ${user._id} (extension was ${user.extension})`);
			await Users.unsetExtension(user._id);
			return API.v1.success();
		},
	},
);

// Get free extensions
API.v1.addRoute(
	'omnichannel/extension',
	{ authRequired: true, permissionsRequired: ['manage-agent-extension-association'] },
	{
		async get() {
			check(
				this.queryParams,
				Match.OneOf(
					Match.ObjectIncluding({
						type: Match.OneOf('free', 'allocated', 'available'),
						userId: String,
					}),
					Match.ObjectIncluding({
						type: Match.OneOf('free', 'allocated', 'available'),
						username: String,
					}),
				),
			);
			const { type } = this.queryParams;
			switch ((type as string).toLowerCase()) {
				case 'free': {
					const extensions = await LivechatVoip.getFreeExtensions();
					if (!extensions) {
						return API.v1.failure('Error in finding free extensons');
					}
					return API.v1.success({ extensions });
				}
				case 'allocated': {
					const extensions = await LivechatVoip.getExtensionAllocationDetails();
					if (!extensions) {
						return API.v1.failure('Error in allocated extensions');
					}
					return API.v1.success({ extensions: extensions.map((e) => e.extension) });
				}
				case 'available': {
					let user: IUser | null = null;
					if (!isUserIdndTypeParams(this.queryParams)) {
						user = await Users.findOneByAgentUsername(this.queryParams.username, {
							projection: { _id: 1, extension: 1 },
						});
					} else {
						user = await Users.findOneAgentById(this.queryParams.userId, {
							projection: { _id: 1, extension: 1 },
						});
					}

					const freeExt = await LivechatVoip.getFreeExtensions();
					const extensions = user?.extension ? [user.extension, ...freeExt] : freeExt;
					return API.v1.success({ extensions });
				}
				default:
					return API.v1.notFound(`${type} not found `);
			}
		},
	},
);

API.v1.addRoute(
	'omnichannel/extensions',
	{ authRequired: true, permissionsRequired: ['manage-agent-extension-association'] },
	{
		async get() {
			const { offset, count } = this.getPaginationItems();
			const { status, agentId, queues, extension } = this.requestParams();

			check(status, Match.Maybe(String));
			check(agentId, Match.Maybe(String));
			check(queues, Match.Maybe([String]));
			check(extension, Match.Maybe(String));

			const extensions = await LivechatVoip.getExtensionListWithAgentData();
			const filteredExts = filter(extensions, { status, agentId, queues, extension });

			// paginating in memory as Asterisk doesn't provide pagination for commands
			return API.v1.success({
				extensions: paginate(filteredExts, count, offset),
				offset,
				count,
				total: filteredExts.length,
			});
		},
	},
);

API.v1.addRoute(
	'omnichannel/agents/available',
	{ authRequired: true, permissionsRequired: ['manage-agent-extension-association'] },
	{
		async get() {
			const { offset, count } = this.getPaginationItems();
			const { sort } = this.parseJsonQuery();
			const { text, includeExtension = '' } = this.queryParams;

			const { agents, total } = await LivechatVoip.getAvailableAgents(includeExtension, text, count, offset, sort);

			return API.v1.success({
				agents,
				offset,
				count,
				total,
			});
		},
	},
);


readonly bodyParams: TMethod extends 'GET' ? Record<string, unknown> : Partial<OperationParams<TMethod, TPathPattern>>;

: TOptions extends { validateParams: ValidateFunction<infer T> }


import type { UpdateWriteOpResult } from 'mongodb';
import type { IInvite } from '@rocket.chat/core-typings';

import { BaseRaw } from './BaseRaw';

type T = IInvite;

export class InvitesRaw extends BaseRaw<T> {
	findOneByUserRoomMaxUsesAndExpiration(userId: string, rid: string, maxUses: number, daysToExpire: number): Promise<T | null> {
		return this.findOne({
			rid,
			userId,
			days: daysToExpire,
			maxUses,
			...(daysToExpire > 0 ? { expires: { $gt: new Date() } } : {}),
			...(maxUses > 0 ? { uses: { $lt: maxUses } } : {}),
		});
	}

	increaseUsageById(_id: string, uses = 1): Promise<UpdateWriteOpResult> {
		return this.updateOne(
			{ _id },
			{
				$inc: {
					uses,
				},
			},
		);
	}
}


import React, { FC } from 'react';

import AutoCompleteTagsMultiple from './AutoCompleteTagsMultiple';

const CurrentChatTags: FC<{ value: Array<string>; handler: () => void }> = ({ value, handler }) => (
	<AutoCompleteTagsMultiple onChange={handler} value={value} />
);

export default CurrentChatTags;


import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Session } from 'meteor/session';
import { Template } from 'meteor/templating';

import { handleError } from '../../../../client/lib/utils/handleError';
import { settings } from '../../../settings/client';

const getMedia = () => navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
const createAndConnect = (url) => {
	if (!('WebSocket' in window)) {
		// eslint-disable-line no-negated-in-lhs
		return false;
	}

	const ws = new WebSocket(url);
	ws.onerror = (evt) => console.error(`Error: ${evt.data}`);
	return ws;
};
const sendMessageToWebSocket = (message, ws) => {
	if (ws != null) {
		if (ws.readyState === 1) {
			ws.send(message);
		}
	}
};
export const call = (...args) =>
	new Promise(function (resolve, reject) {
		Meteor.call(...args, function (err, result) {
			if (err) {
				handleError(err);
				reject(err);
			}
			resolve(result);
		});
	});

const delay = (time) => new Promise((resolve) => setTimeout(resolve, time));

const waitForStreamStatus = async (id, status) => {
	const streamActive = new Promise(async (resolve) => {
		// eslint-disable-next-line no-constant-condition
		while (true) {
			const currentStatus = await call('livestreamStreamStatus', { streamId: id }); // eslint-disable-line no-await-in-loop
			if (currentStatus === status) {
				return resolve(status);
			}
			await delay(1500); // eslint-disable-line no-await-in-loop
		}
	});
	await streamActive;
};
const waitForBroadcastStatus = async (id, status) => {
	const broadcastActive = new Promise(async (resolve) => {
		// eslint-disable-next-line no-constant-condition
		while (true) {
			const currentStatus = await call('getBroadcastStatus', { broadcastId: id }); // eslint-disable-line no-await-in-loop
			if (currentStatus === status) {
				return resolve(status);
			}
			await delay(1500); // eslint-disable-line no-await-in-loop
		}
	});
	await broadcastActive;
};

Template.broadcastView.helpers({
	broadcastSource() {
		return Template.instance().mediaStream.get() ? window.URL.createObjectURL(Template.instance().mediaStream.get()) : '';
	},
	mediaRecorder() {
		Template.instance().mediaRecorder.get();
	},
});

Template.broadcastView.onCreated(async function () {
	const connection = createAndConnect(`${settings.get('Broadcasting_media_server_url')}/${this.data.id}`);
	this.mediaStream = new ReactiveVar(null);
	this.mediaRecorder = new ReactiveVar(null);
	this.connection = new ReactiveVar(connection);
});
Template.broadcastView.onDestroyed(function () {
	if (this.connection.get()) {
		this.connection.get().close();
	}
	if (this.mediaRecorder.get()) {
		this.mediaRecorder.get().stop();
		this.mediaRecorder.set(null);
	}
	if (this.mediaStream.get()) {
		this.mediaStream
			.get()
			.getTracks()
			.map((track) => track.stop());
		this.mediaStream.set(null);
	}
});
Template.broadcastView.onRendered(async function () {
	navigator.getMedia = getMedia();
	if (!navigator.getMedia) {
		return alert('getUserMedia() is not supported in your browser!');
	}
	const localMediaStream = await new Promise((resolve, reject) => navigator.getMedia({ video: true, audio: true }, resolve, reject));

	const connection = this.connection.get();

	this.mediaStream.set(localMediaStream);
	let options = { mimeType: 'video/webm;codecs=vp9' };
	if (!MediaRecorder.isTypeSupported(options.mimeType)) {
		options = { mimeType: 'video/webm;codecs=vp8' };
		if (!MediaRecorder.isTypeSupported(options.mimeType)) {
			options = { mimeType: 'video/webm' };
			if (!MediaRecorder.isTypeSupported(options.mimeType)) {
				options = { mimeType: '' };
			}
		}
	}
	try {
		const mediaRecorder = new MediaRecorder(localMediaStream, options);
		mediaRecorder.ondataavailable = (event) => {
			if (!(event.data || event.data.size > 0)) {
				return;
			}
			sendMessageToWebSocket(event.data, connection);
		};
		mediaRecorder.start(100); // collect 100ms of data
		this.mediaRecorder.set(mediaRecorder);

		await waitForStreamStatus(this.data.stream.id, 'active');
		await call('setLivestreamStatus', { broadcastId: this.data.broadcast.id, status: 'testing' });
		await waitForBroadcastStatus(this.data.broadcast.id, 'testing');
		document.querySelector('.streaming-popup').dispatchEvent(new Event('broadcastStreamReady'));
	} catch (e) {
		console.log(e);
	}
});

Template.broadcastView.events({
	async 'startStreaming .streaming-popup'(e, i) {
		await call('setLivestreamStatus', { broadcastId: i.data.broadcast.id, status: 'live' });
		document.querySelector('.streaming-popup').dispatchEvent(new Event('broadcastStream'));
		await call('saveRoomSettings', Session.get('openedRoom'), 'streamingOptions', {
			id: i.data.broadcast.id,
			url: `https://www.youtube.com/embed/${i.data.broadcast.id}`,
			thumbnail: `https://img.youtube.com/vi/${i.data.broadcast.id}/0.jpg`,
		});
	},
	async 'stopStreaming .streaming-popup'(e, i) {
		await call('setBroadcastStatus', { broadcastId: i.data.broadcast.id, status: 'complete' });
		await call('saveRoomSettings', Session.get('openedRoom'), 'streamingOptions', {}, (err) => {
			if (err) {
				return handleError(err);
			}
			i.editing.set(false);
			i.streamingOptions.set({});
		});
		if (i.mediaRecorder.get()) {
			i.mediaRecorder.get().stop();
			i.mediaRecorder.set(null);
		}
		if (i.mediaStream.get()) {
			i.mediaStream
				.get()
				.getTracks()
				.map((track) => track.stop());
			i.mediaStream.set(null);
		}
	},
});


api.enforceRateLimit(objectForRateLimitMatch, this.request, this.response, this.userId);

if (shouldVerifyPermissions && (!this.userId || !hasAllPermission(this.userId, _options.permissionsRequired))) {

throw new Meteor.Error('error-unauthorized', 'User does not have the permissions required for this action', {

if (_options.validateParams && _options.validateParams(this.request.method === 'GET' ? this.queryParams : this.bodyParams)) {

throw new Meteor.Error('error-invalid-params', _options.validateParams.errors?.map((error) => error.message).join('\n '));


import { SidebarSection } from '@rocket.chat/fuselage';
import React, { memo } from 'react';

import OmnichannelSection from '../sections/OmnichannelSection';
import SideBarItemTemplateWithData from './SideBarItemTemplateWithData';

const sections = {
	Omnichannel: OmnichannelSection,
};

const Row = ({ data, item }) => {
	const { extended, t, SideBarItemTemplate, AvatarTemplate, openedRoom, sidebarViewMode } = data;

	if (typeof item === 'string') {
		const Section = sections[item];
		return Section ? (
			<Section aria-level='1' />
		) : (
			<SidebarSection aria-level='1'>
				<SidebarSection.Title>{t(item)}</SidebarSection.Title>
			</SidebarSection>
		);
	}
	return (
		<SideBarItemTemplateWithData
			sidebarViewMode={sidebarViewMode}
			selected={item.rid === openedRoom}
			t={t}
			room={item}
			extended={extended}
			SideBarItemTemplate={SideBarItemTemplate}
			AvatarTemplate={AvatarTemplate}
		/>
	);
};

export default memo(Row);


import { IRoom, IUser } from '@rocket.chat/core-typings';
import { css } from '@rocket.chat/css-in-js';
import { Field, TextInput, ButtonGroup, Button, Box, Icon, Callout, FieldGroup } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useToastMessageDispatch, useUserRoom, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts';
import React, { useState, useEffect, useContext, FC, MouseEventHandler } from 'react';

import { validateEmail } from '../../../../../lib/emailValidator';
import UserAutoCompleteMultiple from '../../../../components/UserAutoCompleteMultiple';
import { useForm } from '../../../../hooks/useForm';
import { roomCoordinator } from '../../../../lib/rooms/roomCoordinator';
import { SelectedMessageContext, useCountSelected } from '../../MessageList/contexts/SelectedMessagesContext';
import { useMessages } from '../../MessageList/hooks/useMessages';

type MailExportFormValues = {
	dateFrom: string;
	dateTo: string;
	toUsers: IUser['username'][];
	additionalEmails: string;
	subject: string;
};

type MailExportFormProps = { onCancel: MouseEventHandler<HTMLOrSVGElement>; rid: IRoom['_id'] };

const clickable = css`
	cursor: pointer;
`;

const MailExportForm: FC<MailExportFormProps> = ({ onCancel, rid }) => {
	const { selectedMessageStore } = useContext(SelectedMessageContext);

	const t = useTranslation();
	const room = useUserRoom(rid);
	const roomName = room?.t && roomCoordinator.getRoomName(room.t, room);

	const [errorMessage, setErrorMessage] = useState<string>();

	const messages = selectedMessageStore.getSelectedMessages();
	const count = useCountSelected();

	const messageList = useMessages({ rid });

	const { values, handlers } = useForm({
		dateFrom: '',
		dateTo: '',
		toUsers: [],
		additionalEmails: '',
		subject: t('Mail_Messages_Subject', roomName),
	});

	const dispatchToastMessage = useToastMessageDispatch();

	const { toUsers, additionalEmails, subject } = values as MailExportFormValues;

	const clearSelection = useMutableCallback(() => {
		selectedMessageStore.clearStore();
	});

	useEffect(() => {
		selectedMessageStore.setIsSelecting(true);
		return (): void => {
			selectedMessageStore.reset();
		};
	}, [selectedMessageStore]);

	// TODO: chapter day frontend -  after 5.0 remove
	useEffect(() => {
		const $root = $(`#chat-window-${rid}`);

		$('.messages-box', $root).addClass('selectable');

		const handler = function (this: any): void {
			selectedMessageStore.toggle(this.id);
			this.classList.toggle('selected');
		};

		$('.messages-box .message', $root).on('click', handler);

		return (): void => {
			$('.messages-box', $root).removeClass('selectable');
			$('.messages-box .message', $root).off('click', handler).filter('.selected').removeClass('selected');
		};
	}, [rid, messageList, selectedMessageStore]);

	const { handleToUsers, handleAdditionalEmails, handleSubject } = handlers;

	const onChangeUsers = useMutableCallback((value, action) => {
		if (!action) {
			if (toUsers.includes(value)) {
				return;
			}
			return handleToUsers([...toUsers, value]);
		}
		handleToUsers(toUsers.filter((current) => current !== value));
	});

	const roomsExport = useEndpoint('POST', 'rooms.export');

	const handleSubmit = async (): Promise<void> => {
		if (toUsers.length === 0 && additionalEmails === '') {
			setErrorMessage(t('Mail_Message_Missing_to'));
			return;
		}
		if (additionalEmails !== '' && !validateEmail(additionalEmails)) {
			setErrorMessage(t('Mail_Message_Invalid_emails', additionalEmails));
			return;
		}
		if (messages.length === 0) {
			setErrorMessage(t('Mail_Message_No_messages_selected_select_all'));
			return;
		}
		setErrorMessage(undefined);

		try {
			await roomsExport({
				rid,
				type: 'email',
				toUsers,
				toEmails: additionalEmails.split(','),
				subject,
				messages,
			});

			dispatchToastMessage({
				type: 'success',
				message: t('Your_email_has_been_queued_for_sending'),
			});
		} catch (error) {
			dispatchToastMessage({
				type: 'error',
				message: error as string | Error,
			});
		}
	};

	return (
		<FieldGroup>
			<Field>
				<Callout onClick={clearSelection} title={t('Messages_selected')} type={count > 0 ? 'success' : 'info'}>
					<p>{`${count} Messages selected`}</p>
					{count > 0 && (
						<Box is='p' className={clickable}>
							{t('Click_here_to_clear_the_selection')}
						</Box>
					)}
					{count === 0 && <Box is='p'>{t('Click_the_messages_you_would_like_to_send_by_email')}</Box>}
				</Callout>
			</Field>
			<Field>
				<Field.Label>{t('To_users')}</Field.Label>
				<Field.Row>
					<UserAutoCompleteMultiple value={toUsers} onChange={onChangeUsers} />
				</Field.Row>
			</Field>
			<Field>
				<Field.Label>{t('To_additional_emails')}</Field.Label>
				<Field.Row>
					<TextInput
						placeholder={t('Email_Placeholder_any')}
						value={additionalEmails}
						onChange={handleAdditionalEmails}
						addon={<Icon name='mail' size='x20' />}
					/>
				</Field.Row>
			</Field>
			<Field>
				<Field.Label>{t('Subject')}</Field.Label>
				<Field.Row>
					<TextInput value={subject} onChange={handleSubject} addon={<Icon name='edit' size='x20' />} />
				</Field.Row>
			</Field>

			{errorMessage && <Callout type={'danger'}>{errorMessage}</Callout>}

			<ButtonGroup stretch mb='x12'>
				<Button onClick={onCancel}>{t('Cancel')}</Button>
				<Button primary onClick={(): Promise<void> => handleSubmit()}>
					{t('Send')}
				</Button>
			</ButtonGroup>
		</FieldGroup>
	);
};

export default MailExportForm;


Departmanı değiştirmek istediğinden emin misin?

Şu anda çevrimiçi değiliz. Lütfen mesaj bırakın.


return this.persistent || !this.hideOverlay

@import '../bootstrap'

.v-overlay
  position: fixed
  top: 0
  left: 0
  right: 0
  bottom: 0
  pointer-events: none
  // The overlay has a dynamically set
  // z-index, we want the transition
  // timing to affect its changing
  // https://github.com/vuetifyjs/vuetify/issues/2146
  transition: .3s $transition.swing
  // This is the standard index
  z-index: 5

  &--absolute
    position: absolute

  &:before
    background-color: rgba(33, 33, 33, 1)
    bottom: 0
    content: ''
    height: 100%
    left: 0
    opacity: 0
    position: absolute
    right: 0
    top: 0
    transition: inherit
    // Delay the transition to avoid a
    // rendering bug that is visible
    // within Edge and Firefox
    // https://github.com/vuetifyjs/vuetify/issues/2146
    transition-delay: 150ms
    width: 100%

  &--active
    pointer-events: auto
    touch-action: none

    &:before
      opacity: .46


import '../../stylus/components/_subheaders.styl'

import Themeable from '../../mixins/themeable'

/* @vue/component */
export default {
  name: 'v-subheader',

  functional: true,

  mixins: [Themeable],

  props: {
    inset: Boolean
  },

  render (h, { data, children, props }) {
    data.staticClass = (`v-subheader ${data.staticClass || ''}`).trim()

    if (props.inset) data.staticClass += ' v-subheader--inset'
    if (props.light) data.staticClass += ' theme--light'
    if (props.dark) data.staticClass += ' theme--dark'

    return h('div', data, children)
  }
}


import { VNodeDirective } from 'vue/types/vnode'

interface ScrollHTMLElement extends HTMLElement {
  _onScroll?: {
    callback: EventListenerOrEventListenerObject
    options: boolean | AddEventListenerOptions
    target: EventTarget
  }
}

interface ScrollVNodeDirective extends VNodeDirective {
  arg: string
  value: EventListenerOrEventListenerObject
  options?: boolean | AddEventListenerOptions
}

function inserted (el: ScrollHTMLElement, binding: ScrollVNodeDirective) {
  const callback = binding.value
  const options = binding.options || { passive: true }
  let target = binding.arg ? document.querySelector(binding.arg) : window
  if (!target) return

  target.addEventListener('scroll', callback, options)

  el._onScroll = {
    callback,
    options,
    target
  }
}

function unbind (el: ScrollHTMLElement) {
  if (!el._onScroll) return

  const { callback, options, target } = el._onScroll

  target.removeEventListener('scroll', callback, options)
  delete el._onScroll
}

export default {
  name: 'scroll',
  inserted,
  unbind
}


@import '../bootstrap'
@import '../theme'

.v-input--radio-group
  .v-radio
    margin-bottom: 8px

  &__input
    display: flex
    width: 100%

    & > .v-label
      padding-bottom: 8px

  &--row
    .v-input--radio-group__input
      flex-direction: row
      flex-wrap: wrap

  &--column
    .v-input--radio-group__input
      flex-direction: column