import { test } from '@/test'
import { colorToInt, intToHex, getContrast, getLuma } from '@/util/colorUtils'
import * as transformSRGB from '@/util/color/transformSRGB'
import * as transformCIELAB from '@/util/color/transformCIELAB'

test('colorToInt', () => {
  it('should convert a hex string to a number', () => {
    expect(colorToInt('#123456')).toBe(0x123456)
    expect(colorToInt('#abc')).toBe(0xaabbcc)
    expect(colorToInt('876543')).toBe(0x876543)
    expect(colorToInt('669')).toBe(0x666699)
    expect(colorToInt('fff')).toBe(0xffffff)
  })

  it('should keep existing numbers', () => {
    expect(colorToInt(0xabcdef)).toBe(0xabcdef)
  })

  it('should reject invalid formats', async () => {
    expect(() => colorToInt([])).toThrow('Colors can only be numbers or strings, recieved Array instead')
    expect(() => colorToInt(() => {})).toThrow('Colors can only be numbers or strings, recieved Function instead')

    colorToInt(-1)
    colorToInt('#1000000')
    colorToInt('#13')
    colorToInt('#6')
    colorToInt('red')

    expect(`Colors cannot be negative: '-1'`).toHaveBeenTipped()
    expect(`'#1000000' is not a valid rgb color`).toHaveBeenTipped()
    expect(`'#13' is not a valid rgb color`).toHaveBeenTipped()
    expect(`'#6' is not a valid rgb color`).toHaveBeenTipped()
    expect(`'red' is not a valid rgb color`).toHaveBeenTipped()
  })
})

// TODO
describe('intToHex', () => {
  it('should convert', () => {
    expect(intToHex(0)).toBe('#000000')
    expect(intToHex(0xffffff)).toBe('#ffffff')
  })
})

describe('transformSRGB', () => {
  it('should convert sRGB to XYZ', () => {
    expect(transformSRGB.toXYZ(0)).toEqual([0, 0, 0])
    expect(transformSRGB.toXYZ(0xffffff)).toEqual([0.9505, 1, 1.0890])
    expect(transformSRGB.toXYZ(0xfcfbf4)).toEqualCloseTo([0.909712, 0.962215, 0.993659], 6)
    expect(transformSRGB.toXYZ(0x45a081)).toEqualCloseTo([0.189875, 0.279918, 0.251711], 6)
    expect(transformSRGB.toXYZ(0x191995)).toEqualCloseTo([0.061733, 0.030719, 0.287013], 6)
    expect(transformSRGB.toXYZ(0xcd6600)).toEqualCloseTo([0.299282, 0.224819, 0.027620], 6)
  })

  it('should convert XYZ to sRGB', () => {
    expect(transformSRGB.fromXYZ([0, 0, 0])).toBe(0)
    expect(transformSRGB.fromXYZ([0.9505, 1, 1.0890])).toBe(0xffffff)
    expect(transformSRGB.fromXYZ([0.909712, 0.962215, 0.993659])).toBe(0xfcfbf4)
    expect(transformSRGB.fromXYZ([0.189875, 0.279918, 0.251711])).toBe(0x45a081)
    expect(transformSRGB.fromXYZ([0.061733, 0.030719, 0.287013])).toBe(0x191995)
    expect(transformSRGB.fromXYZ([0.299282, 0.224819, 0.027620])).toBe(0xcd6600)
  })
})

describe('transformCIELAB', () => {
  it('should convert LAB to XYZ', () => {
    expect(transformCIELAB.toXYZ([0, 0, 0])).toEqual([0, 0, 0])
    expect(transformCIELAB.toXYZ([100, 0.0053, -0.0104])).toEqualCloseTo([0.9505, 1, 1.0890], 4)
    expect(transformCIELAB.toXYZ([98.5202, -0.8731, 3.4542])).toEqualCloseTo([0.909713, 0.962215, 0.99366], 6)
    expect(transformCIELAB.toXYZ([59.8813, -34.7853, 8.0829])).toEqualCloseTo([0.189875, 0.279918, 0.251711], 6)
    expect(transformCIELAB.toXYZ([20.3296, 44.3917, -65.5991])).toEqualCloseTo([0.061733, 0.030719, 0.287014], 6)
    expect(transformCIELAB.toXYZ([54.5346, 36.1321, 62.8465])).toEqualCloseTo([0.299282, 0.224819, 0.027620], 6)
  })

  it('should convert XYZ to LAB', () => {
    expect(transformCIELAB.fromXYZ([0, 0, 0])).toEqual([0, 0, 0])
    expect(transformCIELAB.fromXYZ([0.9505, 1, 1.0890])).toEqualCloseTo([100, 0.0053, -0.0104], 4)
    expect(transformCIELAB.fromXYZ([0.909712, 0.962215, 0.993659])).toEqualCloseTo([98.5202, -0.8731, 3.4542], 4)
    expect(transformCIELAB.fromXYZ([0.189875, 0.279918, 0.251711])).toEqualCloseTo([59.8813, -34.7853, 8.0829], 4)
    expect(transformCIELAB.fromXYZ([0.061733, 0.030719, 0.287014])).toEqualCloseTo([20.3296, 44.3917, -65.5991], 4)
    expect(transformCIELAB.fromXYZ([0.299282, 0.224819, 0.027620])).toEqualCloseTo([54.5346, 36.1321, 62.8465], 4)
  })
})

expect.extend({
  toEqualCloseTo (received, expected, precision = 3) {
    const getType = item => item.constructor.name.toLowerCase()

    function round(obj) {
      switch (getType(obj)) {
        case 'array':
          return obj.map(round)

        case 'object':
          return Object.keys(obj).reduce((acc, key) => {
            acc[key] = round(obj[key])
            return acc
          }, {})

        case 'number':
          return +obj.toFixed(precision)

        default:
          return obj
      }
    }

    expect(round(received)).toEqual(expected)

    return { pass: true }
  },
})


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

// Extensions
import { BaseWindowItem } from '../VWindow/VWindowItem'

// Mixins
import {
  factory as GroupableFactory
} from '../../mixins/groupable'
import { deprecate } from '../../util/console'

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

  mixins: [
    BaseWindowItem,
    GroupableFactory('windowGroup', 'v-tab-item', 'v-tabs-items')
  ],

  props: {
    id: String
  },

  render (h) {
    const render = BaseWindowItem.options.render.call(this, h)

    // For backwards compatibility with v1.2
    if (this.id) {
      deprecate('id', 'value', this)

      render.data.domProps = render.data.domProps || {}
      render.data.domProps.id = this.id
    }

    return render
  }
}


import {
  createSimpleTransition,
  createJavaScriptTransition
} from '../../util/helpers'

import ExpandTransitionGenerator from './expand-transition'

// Component specific transitions
export const VBottomSheetTransition = createSimpleTransition('bottom-sheet-transition')
export const VCarouselTransition = createSimpleTransition('carousel-transition')
export const VCarouselReverseTransition = createSimpleTransition('carousel-reverse-transition')
export const VTabTransition = createSimpleTransition('tab-transition')
export const VTabReverseTransition = createSimpleTransition('tab-reverse-transition')
export const VMenuTransition = createSimpleTransition('menu-transition')
export const VFabTransition = createSimpleTransition('fab-transition', 'center center', 'out-in')

// Generic transitions
export const VDialogTransition = createSimpleTransition('dialog-transition')
export const VDialogBottomTransition = createSimpleTransition('dialog-bottom-transition')
export const VFadeTransition = createSimpleTransition('fade-transition')
export const VScaleTransition = createSimpleTransition('scale-transition')
export const VScrollXTransition = createSimpleTransition('scroll-x-transition')
export const VScrollXReverseTransition = createSimpleTransition('scroll-x-reverse-transition')
export const VScrollYTransition = createSimpleTransition('scroll-y-transition')
export const VScrollYReverseTransition = createSimpleTransition('scroll-y-reverse-transition')
export const VSlideXTransition = createSimpleTransition('slide-x-transition')
export const VSlideXReverseTransition = createSimpleTransition('slide-x-reverse-transition')
export const VSlideYTransition = createSimpleTransition('slide-y-transition')
export const VSlideYReverseTransition = createSimpleTransition('slide-y-reverse-transition')

// JavaScript transitions
export const VExpandTransition = createJavaScriptTransition('expand-transition', ExpandTransitionGenerator())
export const VRowExpandTransition = createJavaScriptTransition('row-expand-transition', ExpandTransitionGenerator('datatable__expand-col--expanded'))

export default {
  $_vuetify_subcomponents: {
    VBottomSheetTransition,
    VCarouselTransition,
    VCarouselReverseTransition,
    VDialogTransition,
    VDialogBottomTransition,
    VFabTransition,
    VFadeTransition,
    VMenuTransition,
    VScaleTransition,
    VScrollXTransition,
    VScrollXReverseTransition,
    VScrollYTransition,
    VScrollYReverseTransition,
    VSlideXTransition,
    VSlideXReverseTransition,
    VSlideYTransition,
    VSlideYReverseTransition,
    VTabReverseTransition,
    VTabTransition,
    VExpandTransition,
    VRowExpandTransition
  }
}


import VCounter from './VCounter'

export { VCounter }
export default VCounter


import '../../stylus/components/_grid.styl'

import Grid from './grid'

export default Grid('container')


keep keep replace replace replace replace replace replace replace replace keep keep keep

// Extensions
import { BaseWindow } from '../VWindow/VWindow'

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

  extends: BaseWindow,

  provide () {
    return {
      windowGroup: this
    }
  },

  inject: {
    registerItems: {
      default: null
    },
    tabProxy: {
      default: null
    },
    unregisterItems: {
      default: null
    }
  },

  props: {
    cycle: Boolean
  },

  watch: {
    internalValue (val) {
      /* istanbul ignore else */
      if (this.tabProxy) this.tabProxy(val)
    }
  },

  created () {
    this.registerItems && this.registerItems(this.changeModel)
  },

  beforeDestroy () {
    this.unregisterItems && this.unregisterItems()
  },

  methods: {
    changeModel (val) {
      this.internalValue = val
    },
    // For backwards compatability with v1.2
    getValue (item, i) {
      if (item.id) return item.id

      return BaseWindow.options.methods.getValue.call(this, item, i)
    },
    next () {
      if (!this.cycle && this.internalIndex === this.items.length - 1) {
        return
      }

      BaseWindow.options.methods.next.call(this)
    },
    prev () {
      if (!this.cycle && this.internalIndex === 0) {
        return
      }

      BaseWindow.options.methods.prev.call(this)
    }
  }
}


import Vue from 'vue'

/* @vue/component */
export default Vue.extend({
  name: 'returnable',

  props: {
    returnValue: null as any
  },

  data: () => ({
    isActive: false,
    originalValue: null as any
  }),

  watch: {
    isActive (val) {
      if (val) {
        this.originalValue = this.returnValue
      } else {
        this.$emit('update:returnValue', this.originalValue)
      }
    }
  },

  methods: {
    save (value: any) {
      this.originalValue = value
      this.isActive = false
    }
  }
})


import Vue from 'vue'

Vue.component('breakpoint', {
  created () {
    const name: string = this.$vuetify.breakpoint.name
    const numbers: number[] = [
      this.$vuetify.breakpoint.height,
      this.$vuetify.breakpoint.width
    ]
    const booleans: boolean[] = [
      this.$vuetify.breakpoint.lg,
      this.$vuetify.breakpoint.lgAndDown,
      this.$vuetify.breakpoint.lgAndUp,
      this.$vuetify.breakpoint.lgOnly,
      this.$vuetify.breakpoint.md,
      this.$vuetify.breakpoint.mdAndDown,
      this.$vuetify.breakpoint.mdAndUp,
      this.$vuetify.breakpoint.mdOnly,
      this.$vuetify.breakpoint.sm,
      this.$vuetify.breakpoint.smAndDown,
      this.$vuetify.breakpoint.smAndUp,
      this.$vuetify.breakpoint.smOnly,
      this.$vuetify.breakpoint.xl,
      this.$vuetify.breakpoint.xlOnly,
      this.$vuetify.breakpoint.xs,
      this.$vuetify.breakpoint.xsOnly
    ]
  }
})

Vue.component('theme', {
  created () {
    // Can't do this, some components rely on the pre-defined values
    // this.$vuetify.theme = { primary: 123 }

    Object.assign(this.$vuetify.theme, {
      primary: 123
    })

    this.$vuetify.theme = {
      ...this.$vuetify.theme,
      primary: 132
    }
  }
})


import Vue from 'vue'

/**
 * Delayable
 *
 * @mixin
 *
 * Changes the open or close delay time for elements
 */
export default Vue.extend({
  name: 'delayable',

  props: {
    openDelay: {
      type: [Number, String],
      default: 0
    },
    closeDelay: {
      type: [Number, String],
      default: 0
    }
  },

  data: () => ({
    openTimeout: undefined as number | undefined,
    closeTimeout: undefined as number | undefined
  }),

  methods: {
    /**
     * Clear any pending delay timers from executing
     */
    clearDelay (): void {
      clearTimeout(this.openTimeout)
      clearTimeout(this.closeTimeout)
    },
    /**
     * Runs callback after a specified delay
     */
    runDelay (type: 'open' | 'close', cb: () => void): void {
      this.clearDelay()

      const delay = parseInt((this as any)[`${type}Delay`], 10)

      ;(this as any)[`${type}Timeout`] = setTimeout(cb, delay)
    }
  }
})


return BaseWindow.options.methods.getValue.call(this, item, i)

if (!this.cycle && this.internalIndex === this.items.length - 1) {

return VWindow.options.methods.getValue.call(this, item, i)


// Styles
import '../../stylus/components/_autocompletes.styl'

// Extensions
import VSelect, { defaultMenuProps as VSelectMenuProps } from '../VSelect/VSelect'
import VTextField from '../VTextField/VTextField'

// Utils
import { keyCodes } from '../../util/helpers'

const defaultMenuProps = {
  ...VSelectMenuProps,
  offsetY: true,
  offsetOverflow: true,
  transition: false
}

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

  extends: VSelect,

  props: {
    allowOverflow: {
      type: Boolean,
      default: true
    },
    browserAutocomplete: {
      type: String,
      default: 'off'
    },
    filter: {
      type: Function,
      default: (item, queryText, itemText) => {
        const hasValue = val => val != null ? val : ''

        const text = hasValue(itemText)
        const query = hasValue(queryText)

        return text.toString()
          .toLowerCase()
          .indexOf(query.toString().toLowerCase()) > -1
      }
    },
    hideNoData: Boolean,
    noFilter: Boolean,
    searchInput: {
      default: undefined
    },
    menuProps: {
      type: VSelect.props.menuProps.type,
      default: () => defaultMenuProps
    }
  },

  data: vm => ({
    attrsInput: null,
    lazySearch: vm.searchInput
  }),

  computed: {
    classes () {
      return Object.assign({}, VSelect.computed.classes.call(this), {
        'v-autocomplete': true,
        'v-autocomplete--is-selecting-index': this.selectedIndex > -1
      })
    },
    computedItems () {
      return this.filteredItems
    },
    displayedItemsCount () {
      return this.hideSelected
        ? this.filteredItems.length - this.selectedItems.length
        : this.filteredItems.length
    },
    /**
     * The range of the current input text
     *
     * @return {Number}
     */
    currentRange () {
      if (this.selectedItem == null) return 0

      return this.getText(this.selectedItem).toString().length
    },
    filteredItems () {
      if (!this.isSearching || this.noFilter) return this.allItems

      return this.allItems.filter(i => this.filter(i, this.internalSearch, this.getText(i)))
    },
    internalSearch: {
      get () {
        return this.lazySearch
      },
      set (val) {
        this.lazySearch = val

        this.$emit('update:searchInput', val)
      }
    },
    isAnyValueAllowed () {
      return false
    },
    isDirty () {
      return this.searchIsDirty || this.selectedItems.length > 0
    },
    isSearching () {
      if (this.multiple) return this.searchIsDirty

      return (
        this.searchIsDirty &&
        this.internalSearch !== this.getText(this.selectedItem)
      )
    },
    menuCanShow () {
      if (!this.isFocused) return false

      return (this.displayedItemsCount > 0) || !this.hideNoData
    },
    $_menuProps () {
      const props = VSelect.computed.$_menuProps.call(this)
      props.contentClass = `v-autocomplete__content ${props.contentClass || ''}`.trim()
      return {
        ...defaultMenuProps,
        ...props
      }
    },
    searchIsDirty () {
      return this.internalSearch != null &&
        this.internalSearch !== ''
    },
    selectedItem () {
      if (this.multiple) return null

      return this.selectedItems.find(i => {
        return this.valueComparator(this.getValue(i), this.getValue(this.internalValue))
      })
    },
    listData () {
      const data = VSelect.computed.listData.call(this)

      Object.assign(data.props, {
        items: this.virtualizedItems,
        noFilter: (
          this.noFilter ||
          !this.isSearching ||
          !this.filteredItems.length
        ),
        searchInput: this.internalSearch
      })

      return data
    }
  },

  watch: {
    filteredItems (val) {
      this.onFilteredItemsChanged(val)
    },
    internalValue () {
      this.setSearch()
    },
    isFocused (val) {
      if (val) {
        this.$refs.input &&
          this.$refs.input.select()
      } else {
        this.updateSelf()
      }
    },
    isMenuActive (val) {
      if (val || !this.hasSlot) return

      this.lazySearch = null
    },
    items (val, oldVal) {
      // If we are focused, the menu
      // is not active, hide no data is enabled,
      // and items change
      // User is probably async loading
      // items, try to activate the menu
      if (
        !(oldVal && oldVal.length) &&
        this.hideNoData &&
        this.isFocused &&
        !this.isMenuActive &&
        val.length
      ) this.activateMenu()
    },
    searchInput (val) {
      this.lazySearch = val
    },
    internalSearch (val) {
      this.onInternalSearchChanged(val)
    }
  },

  created () {
    this.setSearch()
  },

  methods: {
    onFilteredItemsChanged (val) {
      this.setMenuIndex(-1)

      this.$nextTick(() => {
        this.setMenuIndex(val.length === 1 ? 0 : -1)
      })
    },
    onInternalSearchChanged (val) {
      this.updateMenuDimensions()
    },
    updateMenuDimensions () {
      if (this.isMenuActive &&
        this.$refs.menu
      ) {
        this.$refs.menu.updateDimensions()
      }
    },
    changeSelectedIndex (keyCode) {
      // Do not allow changing of selectedIndex
      // when search is dirty
      if (this.searchIsDirty) return

      if (![
        keyCodes.backspace,
        keyCodes.left,
        keyCodes.right,
        keyCodes.delete
      ].includes(keyCode)) return

      const indexes = this.selectedItems.length - 1

      if (keyCode === keyCodes.left) {
        this.selectedIndex = this.selectedIndex === -1
          ? indexes
          : this.selectedIndex - 1
      } else if (keyCode === keyCodes.right) {
        this.selectedIndex = this.selectedIndex >= indexes
          ? -1
          : this.selectedIndex + 1
      } else if (this.selectedIndex === -1) {
        this.selectedIndex = indexes
        return
      }

      const currentItem = this.selectedItems[this.selectedIndex]

      if ([
        keyCodes.backspace,
        keyCodes.delete
      ].includes(keyCode) &&
        !this.getDisabled(currentItem)
      ) {
        const newIndex = this.selectedIndex === indexes
          ? this.selectedIndex - 1
          : this.selectedItems[this.selectedIndex + 1]
            ? this.selectedIndex
            : -1

        if (newIndex === -1) {
          this.internalValue = this.multiple ? [] : undefined
        } else {
          this.selectItem(currentItem)
        }

        this.selectedIndex = newIndex
      }
    },
    clearableCallback () {
      this.internalSearch = undefined

      VSelect.methods.clearableCallback.call(this)
    },
    genInput () {
      const input = VTextField.methods.genInput.call(this)

      input.data.attrs.role = 'combobox'
      input.data.domProps.value = this.internalSearch

      return input
    },
    genSelections () {
      return this.hasSlot || this.multiple
        ? VSelect.methods.genSelections.call(this)
        : []
    },
    onClick () {
      if (this.isDisabled) return

      this.selectedIndex > -1
        ? (this.selectedIndex = -1)
        : this.onFocus()

      this.activateMenu()
    },
    onEnterDown () {
      // Avoid invoking this method
      // will cause updateSelf to
      // be called emptying search
    },
    onInput (e) {
      if (this.selectedIndex > -1) return

      // If typing and menu is not currently active
      if (e.target.value) {
        this.activateMenu()
        if (!this.isAnyValueAllowed) this.setMenuIndex(0)
      }

      this.mask && this.resetSelections(e.target)
      this.internalSearch = e.target.value
      this.badInput = e.target.validity && e.target.validity.badInput
    },
    onKeyDown (e) {
      const keyCode = e.keyCode

      VSelect.methods.onKeyDown.call(this, e)

      // The ordering is important here
      // allows new value to be updated
      // and then moves the index to the
      // proper location
      this.changeSelectedIndex(keyCode)
    },
    onTabDown (e) {
      VSelect.methods.onTabDown.call(this, e)
      this.updateSelf()
    },
    selectItem (item) {
      VSelect.methods.selectItem.call(this, item)

      this.setSearch()
    },
    setSelectedItems () {
      VSelect.methods.setSelectedItems.call(this)

      // #4273 Don't replace if searching
      // #4403 Don't replace if focused
      if (!this.isFocused) this.setSearch()
    },
    setSearch () {
      // Wait for nextTick so selectedItem
      // has had time to update
      this.$nextTick(() => {
        this.internalSearch = (
          !this.selectedItem ||
          this.multiple ||
          this.hasSlot
        )
          ? null
          : this.getText(this.selectedItem)
      })
    },
    setValue () {
      this.internalValue = this.internalSearch
      this.$emit('change', this.internalSearch)
    },
    updateSelf () {
      this.updateAutocomplete()
    },
    updateAutocomplete () {
      if (!this.searchIsDirty &&
        !this.internalValue
      ) return

      if (!this.valueComparator(
        this.internalSearch,
        this.getValue(this.internalValue)
      )) {
        this.setSearch()
      }
    }
  }
}


import { test } from '@/test'
import {
  VBreadcrumbs,
  VBreadcrumbsItem
} from '@/components/VBreadcrumbs'
import Vue from 'vue'

test('VBreadcrumbs.js', ({ mount, compileToFunctions }) => {
  it('should have breadcrumbs classes', () => {
    const wrapper = mount(VBreadcrumbs)

    expect(wrapper.hasClass('v-breadcrumbs')).toBe(true)
    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should render items without slot', () => {
    const { render } = compileToFunctions(`
      <v-breadcrumbs :items="items"></v-breadcrumbs>
    `)

    const component = Vue.component('test', {
      components: {
        VBreadcrumbs, VBreadcrumbsItem
      },
      data: () => ({
        items: [
          { text: 'a' },
          { text: 'b' },
          { text: 'c' },
          { text: 'd' }
        ]
      }),
      render
    })
    const wrapper = mount(component)

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

  it('should use slot to render items if present', () => {
    const { render } = compileToFunctions(`
      <v-breadcrumbs :items="items">
        <v-breadcrumbs-item slot="item" slot-scope="props" :key="props.item.text">
          {{ props.item.text.toUpperCase() }}
        </v-breadcrumbs-item>
      </v-breadcrumbs>
    `)
    const component = Vue.component('test', {
      components: {
        VBreadcrumbs, VBreadcrumbsItem
      },
      data: () => ({
        items: [
          { text: 'a' },
          { text: 'b' },
          { text: 'c' },
          { text: 'd' }
        ]
      }),
      render
    })
    const wrapper = mount(component)

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

  it('should use a custom divider slot', () => {
    const { render } = compileToFunctions(`
      <v-breadcrumbs :items="items">
        <template slot="divider">/divider/</template>
      </v-breadcrumbs>
    `)
    const component = Vue.component('test', {
      components: {
        VBreadcrumbs, VBreadcrumbsItem
      },
      data: () => ({
        items: [
          { text: 'a' },
          { text: 'b' },
          { text: 'c' },
          { text: 'd' }
        ]
      }),
      render
    })
    const wrapper = mount(component)

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

  it('should show deprecation notice when using justify-end and justify-start', () => {
    const wrapper = mount(VBreadcrumbs, {
      propsData: {
        justifyEnd: true,
        justifyCenter: true
      },
      slots: {
        default: [{ render: h => h('div') }]
      }
    })

    expect(wrapper.find('.justify-end').length).not.toBe(0)
    expect(wrapper.find('.justify-center').length).not.toBe(0)
    expect(wrapper.html()).toMatchSnapshot()
    expect(`'justify-end' is deprecated, use 'class="justify-end"' instead`).toHaveBeenTipped()
    expect(`'justify-center' is deprecated, use 'class="justify-center"' instead`).toHaveBeenTipped()
    expect(`'default slot' is deprecated, use ':items and scoped slot "item"' instead`).toHaveBeenTipped()
  })

  // TODO: this always passes in jest, needs to be e2e
  it.skip('should remove dividers when items change', async () => {
    const component = Vue.component('test', {
      data: () => ({ items: 3 }),
      render (h) {
        return h(VBreadcrumbs, Array.from(Array(this.items), (_, i) => (
          h(VBreadcrumbsItem, { key: i }, [i])
        )))
      }
    })

    const wrapper = mount(component)
    expect(wrapper.html()).toMatchSnapshot()

    wrapper.setData({ items: 1 })
    await wrapper.vm.$nextTick()
    expect(wrapper.html()).toMatchSnapshot()
  })
})


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

exports[`VSubheader.js should be dark 1`] = `

`;

exports[`VSubheader.js should be inset 1`] = `

`;

exports[`VSubheader.js should be light 1`] = `

`;

exports[`VSubheader.js should have custom class 1`] = `

`;


if (!this.cycle && this.internalIndex === 0) {

keep keep keep keep replace keep keep keep keep


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

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

/* @vue/component */
export default {
  name: 'v-date-picker-date-table',

  mixins: [
    Colorable,
    DatePickerTable,
    Themeable
  ],

  props: {
    events: {
      type: [Array, Object, Function],
      default: () => null
    },
    eventColor: {
      type: [String, Function, Object],
      default: 'warning'
    },
    firstDayOfWeek: {
      type: [String, Number],
      default: 0
    },
    weekdayFormat: {
      type: Function,
      default: null
    }
  },

  computed: {
    formatter () {
      return this.format || createNativeLocaleFormatter(this.locale, { day: 'numeric', timeZone: 'UTC' }, { start: 8, length: 2 })
    },
    weekdayFormatter () {
      return this.weekdayFormat || createNativeLocaleFormatter(this.locale, { weekday: 'narrow', timeZone: 'UTC' })
    },
    weekDays () {
      const first = parseInt(this.firstDayOfWeek, 10)

      return this.weekdayFormatter
        ? createRange(7).map(i => this.weekdayFormatter(`2017-01-${first + i + 15}`)) // 2017-01-15 is Sunday
        : createRange(7).map(i => ['S', 'M', 'T', 'W', 'T', 'F', 'S'][(i + first) % 7])
    }
  },

  methods: {
    calculateTableDate (delta) {
      return monthChange(this.tableDate, Math.sign(delta || 1))
    },
    genTHead () {
      const days = this.weekDays.map(day => this.$createElement('th', day))
      return this.$createElement('thead', this.genTR(days))
    },
    genEvent (date) {
      let eventColor
      if (typeof this.eventColor === 'string') {
        eventColor = this.eventColor
      } else if (typeof this.eventColor === 'function') {
        eventColor = this.eventColor(date)
      } else {
        eventColor = this.eventColor[date]
      }
      return this.$createElement('div', this.setBackgroundColor(eventColor || this.color || 'accent', {
        staticClass: 'v-date-picker-table__event'
      }))
    },
    // Returns number of the days from the firstDayOfWeek to the first day of the current month
    weekDaysBeforeFirstDayOfTheMonth () {
      const firstDayOfTheMonth = new Date(`${this.displayedYear}-${pad(this.displayedMonth + 1)}-01T00:00:00+00:00`)
      const weekDay = firstDayOfTheMonth.getUTCDay()
      return (weekDay - parseInt(this.firstDayOfWeek) + 7) % 7
    },
    isEvent (date) {
      if (Array.isArray(this.events)) {
        return this.events.indexOf(date) > -1
      } else if (this.events instanceof Function) {
        return this.events(date)
      } else {
        return false
      }
    },
    genTBody () {
      const children = []
      const daysInMonth = new Date(this.displayedYear, this.displayedMonth + 1, 0).getDate()
      let rows = []
      let day = this.weekDaysBeforeFirstDayOfTheMonth()

      while (day--) rows.push(this.$createElement('td'))
      for (day = 1; day <= daysInMonth; day++) {
        const date = `${this.displayedYear}-${pad(this.displayedMonth + 1)}-${pad(day)}`

        rows.push(this.$createElement('td', [
          this.genButton(date, true),
          this.isEvent(date) ? this.genEvent(date) : null
        ]))

        if (rows.length % 7 === 0) {
          children.push(this.genTR(rows))
          rows = []
        }
      }

      if (rows.length) {
        children.push(this.genTR(rows))
      }

      return this.$createElement('tbody', children)
    },
    genTR (children) {
      return [this.$createElement('tr', children)]
    }
  },

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


.ma-auto
  margin-top: auto !important
  margin-right: auto !important
  margin-bottom: auto !important
  margin-left: auto !important

.my-auto
  margin-top: auto !important
  margin-bottom: auto !important

.mx-auto
  margin-left: auto !important
  margin-right: auto !important

.mt-auto
  margin-top: auto

.mr-auto
  margin-right: auto

.mb-auto
  margin-bottom: auto

.ml-auto
  margin-left: auto

$i = 0
for $level, $spacer in $spacers
  .ma-{$i}
    margin: $spacer.y $spacer.x !important
  .my-{$i}
    margin-top: $spacer.y !important
    margin-bottom: $spacer.y !important
  .mx-{$i}
    margin-left: $spacer.x !important
    margin-right: $spacer.x !important
  .mt-{$i}
    margin-top: $spacer.y !important
  .mr-{$i}
    margin-right: $spacer.x !important
  .mb-{$i}
    margin-bottom: $spacer.y !important
  .ml-{$i}
    margin-left: $spacer.x !important

  .pa-{$i}
    padding: $spacer.y $spacer.x !important
  .py-{$i}
    padding-top: $spacer.y !important
    padding-bottom: $spacer.y !important
  .px-{$i}
    padding-left: $spacer.x !important
    padding-right: $spacer.x !important
  .pt-{$i}
    padding-top: $spacer.y !important
  .pr-{$i}
    padding-right: $spacer.x !important
  .pb-{$i}
    padding-bottom: $spacer.y !important
  .pl-{$i}
    padding-left: $spacer.x !important

  $i = $i + 1


import { test } from '@/test'
import VTabItem from '@/components/VTabs/VTabItem'
import VTabsItems from '@/components/VTabs/VTabsItems'

test('VTabsItems', ({ mount, shallow }) => {
  it('should pass internal value changes to tabProxy', async () => {
    const tabProxy = jest.fn()
    const wrapper = mount(VTabsItems, {
      provide: { tabProxy }
    })

    wrapper.vm.internalValue = 'foo'

    await wrapper.vm.$nextTick()

    expect(tabProxy).toBeCalledWith('foo')
  })

  it('should conditionally skip next and prev', async () => {
    const wrapper = mount(VTabsItems, {
      propsData: {
        value: 2
      },
      slots: {
        default: [
          VTabItem,
          VTabItem,
          VTabItem
        ]
      }
    })

    await wrapper.vm.$nextTick()
    expect(wrapper.vm.internalIndex).toBe(2)

    wrapper.vm.next()
    expect(wrapper.vm.internalIndex).toBe(2)

    wrapper.setProps({ value: 0 })

    expect(wrapper.vm.internalIndex).toBe(0)

    wrapper.vm.prev()
    expect(wrapper.vm.internalIndex).toBe(0)

    wrapper.setProps({ cycle: true })

    wrapper.vm.prev()
    expect(wrapper.vm.internalIndex).toBe(2)

    wrapper.vm.next()
    expect(wrapper.vm.internalIndex).toBe(0)
  })
})


'use strict'

module.exports = {
  'no-render-string-reference': {
    create: require('./eslint-rules/no-render-string-reference')
  }
}


import Vue, { VueConstructor } from 'vue'

/* eslint-disable-next-line no-use-before-define */
export type Toggleable<T extends string = 'value'> = VueConstructor<Vue & { isActive: boolean } & Record<T, any>>

export function factory<T extends string = 'value'> (prop?: T, event?: string): Toggleable<T>
export function factory (prop = 'value', event = 'input') {
  return Vue.extend({
    name: 'toggleable',

    model: { prop, event },

    props: {
      [prop]: { required: false }
    },

    data () {
      return {
        isActive: !!this[prop]
      }
    },

    watch: {
      [prop] (val) {
        this.isActive = !!val
      },
      isActive (val) {
        !!val !== this[prop] && this.$emit(event, val)
      }
    }
  })
}

/* eslint-disable-next-line no-redeclare */
const Toggleable = factory()

export default Toggleable


import VSystemBar from './VSystemBar'

export { VSystemBar }
export default VSystemBar


// Extensions
import { BaseWindowItem } from '../VWindow/VWindowItem'

// Mixins
import {
  factory as GroupableFactory
} from '../../mixins/groupable'
import { deprecate } from '../../util/console'

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

  mixins: [
    BaseWindowItem,
    GroupableFactory('windowGroup', 'v-tab-item', 'v-tabs-items')
  ],

  props: {
    id: String
  },

  render (h) {
    const render = BaseWindowItem.options.render.call(this, h)

    // For backwards compatibility with v1.2
    if (this.id) {
      deprecate('id', 'value', this)

      render.data.domProps = render.data.domProps || {}
      render.data.domProps.id = this.id
    }

    return render
  }
}


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

v-bottom-nav($material)
  background-color: $material.cards

theme(v-bottom-nav, "v-bottom-nav")

.v-item-group.v-bottom-nav
  bottom: 0
  box-shadow: 0 3px 14px 2px rgba(#000, .12)
  display: flex
  left: 0
  justify-content: center
  transform: translate(0, 60px)
  transition: all .4s $transition.swing
  width: 100%

  &--absolute
    position: absolute

  &--active
    transform: translate(0, 0)

  &--fixed
    position: fixed
    z-index: 4

  .v-btn
    background: transparent !important
    border-radius: 0
    box-shadow: none !important
    font-weight: 400
    height: 100%
    margin: 0
    max-width: 168px
    min-width: 80px
    padding: 8px 12px 10px
    text-transform: none
    opacity: .5
    width: 100%
    // https://github.com/vuetifyjs/vuetify/issues/4643
    flex-shrink: 1

    .v-btn__content
      flex-direction: column-reverse
      font-size: 12px
      white-space: nowrap
      will-change: font-size

      i.v-icon
        color: inherit
        margin-bottom: 4px
        transition: all .4s cubic-bezier(.25, .8, .50, 1)

      span
        line-height: 1

    &--active
      opacity: 1
      padding-top: 6px

      &:before
        background-color: transparent

      .v-btn__content
        font-size: 14px

        .v-icon
          transform: none

    &:not(.v-btn--active)
      filter: grayscale(100%)

  &--shift
    .v-btn__content
      font-size: 14px

    .v-btn
      transition: all .3s
      min-width: 56px
      max-width: 96px

      &--active
        min-width: 96px
        max-width: 168px

.v-bottom-nav--shift
  .v-btn:not(.v-btn--active) .v-btn__content
    .v-icon
      transform: scale(1, 1) translate(0, 8px)

    span
      color: transparent


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

exports[`VDialog.js should render a disabled component and match snapshot 1`] = `

`;

exports[`VDialog.js should render a fullscreen component and match snapshot 1`] = `

`;

exports[`VDialog.js should render a lazy component and match snapshot 1`] = `

`;

exports[`VDialog.js should render a persistent component and match snapshot 1`] = `

`;

exports[`VDialog.js should render a scrollable component and match snapshot 1`] = `

`;

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

`;

exports[`VDialog.js should render component with custom origin and match snapshot 1`] = `

`;

exports[`VDialog.js should render component with custom transition and match snapshot 1`] = `

`;

exports[`VDialog.js should render component with custom width (max-width) and match snapshot 1`] = `

`;

exports[`VDialog.js should render component with custom width and match snapshot 1`] = `

`;


import VTimeline from './VTimeline'
import VTimelineItem from './VTimelineItem'

export { VTimeline, VTimelineItem }

export default {
  $_vuetify_subcomponents: {
    VTimeline,
    VTimelineItem
  }
}


import VTabItem from '@/components/VTabs/VTabItem'

const contentWarning = '[Vuetify] The v-tab-item component must be used inside a v-tabs-items'

it('should not wrap component in transition if false is used', () => {

keep keep keep replace keep keep keep keep

const contentWarning = '[Vuetify] The v-window-item component must be used inside a v-window'

// Extensions
import { BaseWindow } from '../VWindow/VWindow'

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

  extends: BaseWindow,

  provide () {
    return {
      windowGroup: this
    }
  },

  inject: {
    registerItems: {
      default: null
    },
    tabProxy: {
      default: null
    },
    unregisterItems: {
      default: null
    }
  },

  props: {
    cycle: Boolean
  },

  watch: {
    internalValue (val) {
      /* istanbul ignore else */
      if (this.tabProxy) this.tabProxy(val)
    }
  },

  created () {
    this.registerItems && this.registerItems(this.changeModel)
  },

  beforeDestroy () {
    this.unregisterItems && this.unregisterItems()
  },

  methods: {
    changeModel (val) {
      this.internalValue = val
    },
    // For backwards compatability with v1.2
    getValue (item, i) {
      if (item.id) return item.id

      return BaseWindow.options.methods.getValue.call(this, item, i)
    },
    next () {
      if (!this.cycle && this.internalIndex === this.items.length - 1) {
        return
      }

      BaseWindow.options.methods.next.call(this)
    },
    prev () {
      if (!this.cycle && this.internalIndex === 0) {
        return
      }

      BaseWindow.options.methods.prev.call(this)
    }
  }
}


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

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

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

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

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

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

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

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

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

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

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

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

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

  it('should render component with custom origin and match snapshot', () => {
    const wrapper = mount(VDialog, {
      propsData: {
        origin: 'top right'
      }
    })

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

  it('should render component with custom width (max-width) and match snapshot', () => {
    const wrapper = mount(VDialog, {
      propsData: {
        maxWidth: 100
      }
    })

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

  it('should render component with custom width and match snapshot', () => {
    const wrapper = mount(VDialog, {
      propsData: {
        width: '50%'
      }
    })

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

  it('should render component with custom transition and match snapshot', () => {
    const wrapper = mount(VDialog, {
      propsData: {
        transition: 'fade-transition'
      }
    })

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

  it('should open dialog on activator click', async () => {
    const input = jest.fn()
    const wrapper = mount(VDialog, {
      slots: {
        activator: [compileToFunctions('activator')]
      }
    })

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

    expect(wrapper.vm.isActive).toBe(false)
    wrapper.find('.v-dialog__activator')[0].trigger('click')
    expect(wrapper.vm.isActive).toBe(true)
    await wrapper.vm.$nextTick()
    expect(input).toBeCalledWith(true)

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

  it('not should open disabed dialog on activator click', async () => {
    const input = jest.fn()
    const wrapper = mount(VDialog, {
      propsData: {
        disabled: true
      },
      slots: {
        activator: [compileToFunctions('activator')]
      }
    })

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

    expect(wrapper.vm.isActive).toBe(false)
    wrapper.find('.v-dialog__activator')[0].trigger('click')
    expect(wrapper.vm.isActive).toBe(false)
    await wrapper.vm.$nextTick()
    expect(input).not.toBeCalled()

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

  it('not change state on v-model update', async () => {
    const wrapper = mount(VDialog, {
      propsData: {
        value: false
      },
      slots: {
        activator: [compileToFunctions('activator')]
      }
    })

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

    wrapper.setProps({
      value: true
    })
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.isActive).toBe(true)

    wrapper.setProps({
      value: false
    })
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.isActive).toBe(false)

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

  it('should emit keydown event', async () => {
    const keydown = jest.fn()
    const component = {
      render: h => h(VDialog, {
        props: {
          value: true
        },
        on: {
          keydown
        }
      })
    }
    const wrapper = mount(component)

    window.dispatchEvent(new Event('keydown'))
    expect(keydown).toBeCalled()

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


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

v-text-field($material)
  .v-input__slot:before
    border-color: $material.input-bottom-line

  &:not(.v-input--has-state)
    .v-input__slot:hover:before
      border-color: $material.text.primary

  &.v-input--is-disabled
    .v-input__slot:before
      border-image: repeating-linear-gradient(
        to right,
        $material.text.disabled 0px,
        $material.text.disabled 2px,
        transparent 2px,
        transparent 4px
      ) 1 repeat

  &__prefix,
  &__suffix
    color: $material.text.secondary

  &--solo
    .v-input__slot
      border-radius: 2px
      background: $material.cards

  &--solo-inverted.v-text-field--solo
    .v-input__slot
      background: rgba($material.inputs.solo-inverted)

    &.v-input--is-focused
      .v-input__slot
        background: rgba($material.inputs.solo-inverted-focused)

        .v-label,
        input
          color: $material.inputs.solo-inverted-focused-text

  &--box
    .v-input__slot
      background: $material.text-fields.box

    &:not(.v-input--is-focused)
      .v-input__slot:hover
        background: $material.text-fields.box-hover

  &--outline
    .v-input__slot
      border: 2px solid $material.text.secondary

    &:not(.v-input--is-focused)
      .v-input__slot:hover
        border: 2px solid $material.text.primary

v-text-field-rtl()
  .v-label
    transform-origin: top right

  .v-counter
    margin-left: 0
    margin-right: 8px

  &--enclosed
    .v-input__append-outer
      margin-left: 0
      margin-right: 16px

    .v-input__prepend-outer
      margin-left: 16px
      margin-right: 0

  &--reverse
    input
      text-align: left

    .v-label
      transform-origin: top left

  &__prefix
    text-align: left
    padding-right: 0
    padding-left: 4px

  &__suffix
    padding-left: 0
    padding-right: 4px

  &--reverse
    .v-text-field__prefix
      text-align: right
      padding-left: 0
      padding-right: 4px

    .v-text-field__suffix
      padding-left: 0
      padding-right: 4px

theme(v-text-field, "v-text-field")
rtl(v-text-field-rtl, "v-text-field")

.v-text-field
  padding-top: $text-field-active-label-height
  margin-top: $input-top-spacing - $text-field-active-label-height

  input
    flex: 1 1 auto
    line-height: 20px
    padding: 8px 0 8px
    max-width: 100%
    min-width: 0px
    width: 100%

  .v-input__prepend-inner,
  .v-input__append-inner
    align-self: flex-start
    display: inline-flex
    margin-top: 4px
    line-height: 1
    user-select: none

  .v-input__prepend-inner
    margin-right: auto
    padding-right: 4px

  .v-input__append-inner
    margin-left: auto
    padding-left: 4px

  .v-counter
    margin-left: 8px
    white-space: nowrap

  .v-label
    max-width: 90%
    overflow: hidden
    text-overflow: ellipsis
    top: 6px // equal to the margin top of icons
    transform-origin: top left
    white-space: nowrap
    pointer-events: none

    &--active
      max-width: 100%
      transform: translateY(-18px) scale(.75)

  & > .v-input__control > .v-input__slot
    cursor: text
    transition: background $primary-transition

    &:before,
    &:after
      bottom: -1px // Needed for browser autocomplete styles
      content: ''
      left: 0
      position: absolute
      transition: $primary-transition
      width: 100%

    &:before
      border-style: solid
      border-width: thin 0 0 0

    &:after
      border-color: currentColor
      border-style: solid
      border-width: thin 0 thin 0
      transform: scaleX(0)

  &__details
    display: flex
    flex: 1 0 auto
    max-width: 100%
    overflow: hidden

  &__prefix,
  &__suffix
    align-self: center
    cursor: default

  &__prefix
    width: 16px
    text-align: right
    padding-right: 4px

  &__suffix
    padding-left: 4px
    white-space: nowrap

  &--reverse
    .v-text-field__prefix
      text-align: left
      padding-right: 0
      padding-left: 4px

    .v-text-field__suffix
      padding-left: 0
      padding-right: 4px

  &__slot
    display: flex
    flex: 1 1 auto
    position: relative

  &--box,
  &--outline
    position: relative

    .v-input__slot
      align-items: stretch

    input
      margin-top: 22px

    &.v-text-field--single-line
      input
        margin-top: 12px

    .v-label
      top: 18px

      &--active
        transform: translateY(-6px) scale(.75)

    .v-input__slot
      min-height: 56px

  &--box
    .v-input__slot
      border-top-left-radius: 4px
      border-top-right-radius: 4px

      &:before
        border-style: solid
        border-width: thin 0 thin 0

  &.v-text-field--enclosed
    margin: 0
    padding: 0

    &:not(.v-text-field--box)
      .v-progress-linear__background
        display: none

    .v-input__prepend-outer,
    .v-input__prepend-inner,
    .v-input__append-inner,
    .v-input__append-outer
      margin-top: 16px

    .v-text-field__details,
    .v-input__slot
      padding: 0 12px

    .v-text-field__details
      margin-bottom: 8px

  &.v-text-field--full-width
    &.v-input
      margin-bottom: 0
      margin-top: 0

    .v-label
      top: calc(50% - 10px)

    .v-input__control
      padding: 12px 0

    .v-input__prepend-outer,
    .v-input__append-outer
      margin-top: 4px

    .v-input__append-inner
      align-self: center
      margin-top: 0

  &--reverse
    input
      text-align: right

    .v-label
      transform-origin: top right

    .v-input__slot,
    .v-text-field__slot
      flex-direction: row-reverse

  &--solo,
  &--outline,
  &--full-width
    .v-input__slot
      &:before,
      &:after
        display: none

  &--outline
    margin-bottom: 16px
    transition: border $primary-transition

    .v-input__slot
      background: transparent !important
      border-radius: 4px

    .v-input__prepend-outer,
    .v-input__append-outer
      margin-top: 18px // 2px for border

    &.v-input--is-focused
      .v-input__slot
        border: 2px solid currentColor
        transition: border $primary-transition

  &.v-text-field--solo
    .v-label
      top: calc(50% - 10px)

    .v-input__control
      min-height: 48px
      padding: 0

    &:not(.v-text-field--solo-flat)
      .v-input__slot
        elevation(2)

    .v-text-field__slot
      align-items: center

    .v-input__append-inner,
    .v-input__prepend-inner
      align-self: center
      margin-top: 0

    .v-input__prepend-outer,
    .v-input__append-outer
      margin-top: 12px

  &.v-input--is-focused
    .v-input__slot:after
      transform: scaleX(1)

  &.v-input--has-state
    .v-input__slot:before
      border-color: currentColor


.blockquote
  padding: $spacers.three.y 0 $spacers.three.y $spacers.four.x
  font-size: 18px
  font-weight: 300


import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';

import type { INodeUi } from '@/Interface';

import { useWorkflowsStore } from '@/stores/workflows.store';

function getAutoCompletableNodeNames(nodes: INodeUi[]) {

import { escapeMappingString } from '@/utils/mappingUtils';

<template>

			<n8n-input
				ref="inputField"
				v-model="variableFilter"
				:placeholder="$locale.baseText('variableSelector.variableFilter')"
				size="small"
				type="text"
			></n8n-input>

			<VariableSelectorItem
				v-for="option in currentResults"
				:key="option.key"
				:item="option"
				:extend-all="extendAll"
				:redact-values="redactValues"
				@itemSelected="forwardItemSelected"
			></VariableSelectorItem>

</template>


/* eslint-disable @typescript-eslint/naming-convention */

import { isObjectLiteral } from '@/utils';
import { plainToInstance, instanceToPlain } from 'class-transformer';
import { validate } from 'class-validator';
import { ApplicationError, jsonParse } from 'n8n-workflow';

export class BaseFilter {
	protected static async toFilter(rawFilter: string, Filter: typeof BaseFilter) {
		const dto = jsonParse(rawFilter, { errorMessage: 'Failed to parse filter JSON' });

		if (!isObjectLiteral(dto)) throw new ApplicationError('Filter must be an object literal');

		const instance = plainToInstance(Filter, dto, {
			excludeExtraneousValues: true, // remove fields not in class
		});

		await instance.validate();

		return instanceToPlain(instance, {
			exposeUnsetFields: false, // remove in-class undefined fields
		});
	}

	private async validate() {
		const result = await validate(this);

		if (result.length > 0) throw new ApplicationError('Parsed filter does not fit the schema');
	}
}


import { get as execute } from './execute';
import { companyReportGetDescription as description } from './description';

export { description, execute };


const options: Completion[] = TOP_LEVEL_COMPLETIONS_IN_BOTH_MODES.map(addVarType);

keep keep keep keep keep replace keep keep keep

import { i18n } from '@/plugins/i18n';
import {
	autocompletableNodeNames,
	receivesNoBinaryData,
	longestCommonPrefix,
	setRank,
	prefixMatch,
	stripExcessParens,
	hasActiveNode,
	isCredentialsModalOpen,
} from './utils';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import { useExternalSecretsStore } from '@/stores/externalSecrets.ee.store';

/**
 * Completions offered at the dollar position: `$|`
 */
export function dollarCompletions(context: CompletionContext): CompletionResult | null {
	const word = context.matchBefore(/\$[^$]*/);

	if (!word) return null;

	if (word.from === word.to && !context.explicit) return null;

	let options = dollarOptions().map(stripExcessParens(context));

	const userInput = word.text;

	if (userInput !== '$') {
		options = options.filter((o) => prefixMatch(o.label, userInput));
	}

	if (options.length === 0) return null;

	return {
		from: word.to - userInput.length,
		options,
		filter: false,
		getMatch(completion: Completion) {
			const lcp = longestCommonPrefix(userInput, completion.label);

			return [0, lcp.length];
		},
	};
}

export function dollarOptions() {
	const rank = setRank(['$json', '$input']);
	const SKIP = new Set();
	const DOLLAR_FUNCTIONS = ['$jmespath', '$ifEmpty'];

	if (isCredentialsModalOpen()) {
		return useExternalSecretsStore().isEnterpriseExternalSecretsEnabled
			? [
					{
						label: '$secrets',
						type: 'keyword',
					},
					{
						label: '$vars',
						type: 'keyword',
					},
			  ]
			: [];
	}

	if (!hasActiveNode()) {
		return [];
	}

	if (receivesNoBinaryData()) SKIP.add('$binary');

	const keys = Object.keys(i18n.rootVars).sort((a, b) => a.localeCompare(b));

	return rank(keys)
		.filter((key) => !SKIP.has(key))
		.map((key) => {
			const isFunction = DOLLAR_FUNCTIONS.includes(key);

			const option: Completion = {
				label: isFunction ? key + '()' : key,
				type: isFunction ? 'function' : 'keyword',
			};

			const info = i18n.rootVars[key];

			if (info) option.info = info;

			return option;
		})
		.concat(
			autocompletableNodeNames().map((nodeName) => ({
				label: `$('${nodeName}')`,
				type: 'keyword',
				info: i18n.baseText('codeNodeEditor.completer.$()', { interpolate: { nodeName } }),
			})),
		);
}


Aggregate Dedupe Deduplicate Duplicates Limit Remove Slice Sort Split Unique JSON Transform Array List Object Item Map Format Nested Iterate Summarise Summarize Group Pivot Sum Count Min Max


import type {
	IExecuteFunctions,
	IDataObject,
	JsonObject,
	IHttpRequestMethods,
	IRequestOptions,
} from 'n8n-workflow';
import { NodeApiError, NodeOperationError } from 'n8n-workflow';

export async function uptimeRobotApiRequest(
	this: IExecuteFunctions,
	method: IHttpRequestMethods,
	resource: string,
	body: IDataObject = {},
	qs: IDataObject = {},
	uri?: string,
	option: IDataObject = {},
) {
	const credentials = await this.getCredentials('uptimeRobotApi');

	let options: IRequestOptions = {
		method,
		qs,
		form: {
			api_key: credentials.apiKey,
			...body,
		},
		uri: uri || `https://api.uptimerobot.com/v2${resource}`,
		json: true,
	};
	options = Object.assign({}, options, option);
	try {
		const responseData = await this.helpers.request(options);
		if (responseData.stat !== 'ok') {
			throw new NodeOperationError(this.getNode(), responseData as Error);
		}
		return responseData;
	} catch (error) {
		throw new NodeApiError(this.getNode(), error as JsonObject);
	}
}


import { useUsersStore } from '@/stores/users.store';
import type { RBACPermissionCheck, RolePermissionOptions } from '@/types/rbac';
import { ROLE } from '@/utils/userUtils';
import type { IRole } from '@/Interface';

export const hasRole: RBACPermissionCheck<RolePermissionOptions> = (checkRoles) => {
	const usersStore = useUsersStore();
	const currentUser = usersStore.currentUser;

	if (currentUser && checkRoles) {
		const userRole = currentUser.isDefaultUser ? ROLE.Default : currentUser.role;
		return checkRoles.includes(userRole as IRole);
	}

	return false;
};


const options: Completion[] = getAutoCompletableNodeNames(this.workflowsStore.allNodes).map(

import type { INodeProperties } from 'n8n-workflow';
import { getMappedResult, getMappedExpression } from '../mappingUtils';

const RLC_PARAM: INodeProperties = {
	displayName: 'Base',
	name: 'application',
	type: 'resourceLocator',
	default: {
		mode: 'url',
		value: '',
	},
	required: true,
	description: 'The Airtable Base in which to operate on',
	modes: [
		{
			displayName: 'ID',
			name: 'id',
			type: 'string',
			validation: [
				{
					type: 'regex',
					properties: {
						regex: '[a-zA-Z0-9]{2,}',
						errorMessage: 'Not a valid Airtable Base ID',
					},
				},
			],
			placeholder: 'appD3dfaeidke',
			url: '=https://airtable.com/{{$value}}',
		},
	],
};

const STRING_PARAM: INodeProperties = {
	displayName: 'Value',
	name: 'value',
	type: 'string',
	default: '',
	description: 'The string value to write in the property',
};

const SINGLE_DATA_PATH_PARAM: INodeProperties = {
	displayName: 'Name',
	name: 'name',
	type: 'string',
	default: 'propertyName',
	requiresDataPath: 'single',
	description:
		'Name of the property to write data to. Supports dot-notation. Example: "data.person[0].name"',
};

const MULTIPLE_DATA_PATH_PARAM: INodeProperties = {
	displayName: 'For Everything Except',
	name: 'exceptWhenMix',
	type: 'string',
	default: '',
	placeholder: 'e.g. id, country',
	hint: 'Enter the names of the input fields as text, separated by commas',
	displayOptions: {
		show: {
			resolve: ['mix'],
		},
	},
	requiresDataPath: 'multiple',
};

const JSON_PARAM: INodeProperties = {
	displayName: 'JSON Payload',
	name: 'payloadJson',
	type: 'json',
	default: '',
};

const BOOLEAN_PARAM: INodeProperties = {
	displayName: 'Value',
	name: 'value',
	type: 'boolean',
	default: false,
	description: 'The boolean value to write in the property',
};

const NUMBER_PARAM: INodeProperties = {
	displayName: 'Value',
	name: 'value',
	type: 'number',
	default: 0,
	description: 'The number value to write in the property',
};

describe('Mapping Utils', () => {
	describe('getMappedResult', () => {
		it('turns empty string into expression', () => {
			expect(getMappedResult(STRING_PARAM, '{{ $json["Readable date"] }}', '')).toEqual(
				'={{ $json["Readable date"] }}',
			);
		});

		it('keeps spaces when mapping data to fixed value', () => {
			expect(getMappedResult(STRING_PARAM, '{{ $json["Readable date"] }}', '  ')).toEqual(
				'=   {{ $json["Readable date"] }}',
			);
		});

		it('sets expression when mapping to an empty expression', () => {
			expect(getMappedResult(STRING_PARAM, '{{ $json["Readable date"] }}', '=')).toEqual(
				'={{ $json["Readable date"] }}',
			);
		});

		it('keeps spaces when mapping data to expression value', () => {
			expect(getMappedResult(STRING_PARAM, '{{ $json["Readable date"] }}', '=  ')).toEqual(
				'=   {{ $json["Readable date"] }}',
			);
		});

		it('appends to string fixed value and turns into expression', () => {
			expect(getMappedResult(STRING_PARAM, '{{ $json["Readable date"] }}', 'test')).toEqual(
				'=test {{ $json["Readable date"] }}',
			);
		});

		it('appends to json fixed value', () => {
			expect(getMappedResult(JSON_PARAM, '{{ $json["Readable date"] }}', 'test')).toEqual(
				'=test {{ $json["Readable date"] }}',
			);
		});

		it('replaces number value with expression', () => {
			expect(getMappedResult(NUMBER_PARAM, '{{ $json["Readable date"] }}', 0)).toEqual(
				'={{ $json["Readable date"] }}',
			);
		});

		it('replaces boolean value with expression', () => {
			expect(getMappedResult(BOOLEAN_PARAM, '{{ $json["Readable date"] }}', false)).toEqual(
				'={{ $json["Readable date"] }}',
			);
		});

		it('appends existing expression value', () => {
			expect(
				getMappedResult(STRING_PARAM, '{{ $json["Readable date"] }}', '={{$json.test}}'),
			).toEqual('={{$json.test}} {{ $json["Readable date"] }}');
		});

		it('sets data path, replacing if expecting single path', () => {
			expect(
				getMappedResult(SINGLE_DATA_PATH_PARAM, '{{ $json["Readable date"] }}', '={{$json.test}}'),
			).toEqual('["Readable date"]');

			expect(
				getMappedResult(SINGLE_DATA_PATH_PARAM, '{{ $json.path }}', '={{$json.test}}'),
			).toEqual('path');
		});

		it('appends to existing data path, if multiple', () => {
			expect(
				getMappedResult(MULTIPLE_DATA_PATH_PARAM, '{{ $json["Readable date"] }}', 'path'),
			).toEqual('path, ["Readable date"]');
		});

		it('replaces existing dadata path if multiple and is empty expression', () => {
			expect(getMappedResult(MULTIPLE_DATA_PATH_PARAM, '{{ $json.test }}', '=')).toEqual('test');
		});

		it('handles data when dragging from grand-parent nodes', () => {
			expect(
				getMappedResult(
					MULTIPLE_DATA_PATH_PARAM,
					'{{ $node["Schedule Trigger"].json["Day of week"] }}',
					'',
				),
			).toEqual('={{ $node["Schedule Trigger"].json["Day of week"] }}');

			expect(
				getMappedResult(
					SINGLE_DATA_PATH_PARAM,
					'{{ $node["Schedule Trigger"].json["Day of week"] }}',
					'=data',
				),
			).toEqual('=data {{ $node["Schedule Trigger"].json["Day of week"] }}');

			expect(
				getMappedResult(
					SINGLE_DATA_PATH_PARAM,
					'{{ $node["Schedule Trigger"].json["Day of week"] }}',
					'=   ',
				),
			).toEqual('=    {{ $node["Schedule Trigger"].json["Day of week"] }}');
		});

		it('handles RLC values', () => {
			expect(getMappedResult(RLC_PARAM, '{{ test }}', '')).toEqual('={{ test }}');
			expect(getMappedResult(RLC_PARAM, '{{ test }}', '=')).toEqual('={{ test }}');
			expect(getMappedResult(RLC_PARAM, '{{ test }}', '=test')).toEqual('=test {{ test }}');
		});
	});
	describe('getMappedExpression', () => {
		it('should generate a mapped expression with simple array path', () => {
			const input = {
				nodeName: 'nodeName',
				distanceFromActive: 2,
				path: ['sample', 'path'],
			};
			const result = getMappedExpression(input);
			expect(result).toBe("{{ $('nodeName').item.json.sample.path }}");
		});

		it('should generate a mapped expression with mixed array path', () => {
			const input = {
				nodeName: 'nodeName',
				distanceFromActive: 2,
				path: ['sample', 0, 'path'],
			};
			const result = getMappedExpression(input);
			expect(result).toBe("{{ $('nodeName').item.json.sample[0].path }}");
		});

		it('should generate a mapped expression with special characters in array path', () => {
			const input = {
				nodeName: 'nodeName',
				distanceFromActive: 2,
				path: ['sample', 'path with-space', 'path-with-hyphen'],
			};
			const result = getMappedExpression(input);
			expect(result).toBe(
				"{{ $('nodeName').item.json.sample['path with-space']['path-with-hyphen'] }}",
			);
		});

		it('should handle paths with special characters', () => {
			const input = {
				nodeName: 'nodeName',
				distanceFromActive: 2,
				path: [
					'sample',
					'"Execute"',
					'`Execute`',
					"'Execute'",
					'[Execute]',
					'{Execute}',
					'execute?',
					'test,',
					'test:',
					'path.',
				],
			};
			const result = getMappedExpression(input);
			expect(result).toBe(
				"{{ $('nodeName').item.json.sample['\"Execute\"']['`Execute`']['\\'Execute\\'']['[Execute]']['{Execute}']['execute?']['test,']['test:']['path.'] }}",
			);
		});

		it('should generate a mapped expression with various path elements', () => {
			const input = {
				nodeName: 'nodeName',
				distanceFromActive: 1,
				path: ['propertyName', 'capitalizedName', 'hyphen-prop'],
			};
			const result = getMappedExpression(input);
			expect(result).toBe("{{ $json.propertyName.capitalizedName['hyphen-prop'] }}");
		});

		it('should generate a mapped expression with a complex path', () => {
			const input = {
				nodeName: 'nodeName',
				distanceFromActive: 1,
				path: ['propertyName', 'capitalizedName', 'stringVal', 'some-value', 'capitalizedProp'],
			};
			const result = getMappedExpression(input);
			expect(result).toBe(
				"{{ $json.propertyName.capitalizedName.stringVal['some-value'].capitalizedProp }}",
			);
		});
	});
});


#!/bin/bash
set -e;

if [ -n "${POSTGRES_NON_ROOT_USER:-}" ] && [ -n "${POSTGRES_NON_ROOT_PASSWORD:-}" ]; then
	psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
		CREATE USER ${POSTGRES_NON_ROOT_USER} WITH PASSWORD '${POSTGRES_NON_ROOT_PASSWORD}';
		GRANT ALL PRIVILEGES ON DATABASE ${POSTGRES_DB} TO ${POSTGRES_NON_ROOT_USER};
	EOSQL
else
	echo "SETUP INFO: No Environment variables given!"
fi


import { useRootStore } from '@/stores/n8nRoot.store';

import { useNDVStore } from '@/stores/ndv.store';

import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';

// Node types that should not be displayed in variable selector

const SKIPPED_NODE_TYPES = [STICKY_NODE_TYPE];

import { defineComponent } from 'vue';
import { NODE_TYPES_EXCLUDED_FROM_AUTOCOMPLETION } from '../constants';
import { addVarType } from '../utils';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import type { INodeUi } from '@/Interface';
import { mapStores } from 'pinia';
import { useWorkflowsStore } from '@/stores/workflows.store';

function getAutoCompletableNodeNames(nodes: INodeUi[]) {
	return nodes
		.filter((node: INodeUi) => !NODE_TYPES_EXCLUDED_FROM_AUTOCOMPLETION.includes(node.type))
		.map((node: INodeUi) => node.name);
}

export const baseCompletions = defineComponent({
	computed: {
		...mapStores(useWorkflowsStore),
	},
	methods: {
		itemCompletions(context: CompletionContext): CompletionResult | null {
			const preCursor = context.matchBefore(/i\w*/);

			if (!preCursor || (preCursor.from === preCursor.to && !context.explicit)) return null;

			const options: Completion[] = [];

			if (this.mode === 'runOnceForEachItem') {
				options.push({
					label: 'item',
					info: this.$locale.baseText('codeNodeEditor.completer.$input.item'),
				});
			} else if (this.mode === 'runOnceForAllItems') {
				options.push({
					label: 'items',
					info: this.$locale.baseText('codeNodeEditor.completer.$input.all'),
				});
			}

			return {
				from: preCursor.from,
				options,
			};
		},

		/**
		 * - Complete `$` to `$execution $input $prevNode $runIndex $workflow $now $today
		 * $jmespath $ifEmpt $('nodeName')` in both modes.
		 * - Complete `$` to `$json $binary $itemIndex` in single-item mode.
		 */
		baseCompletions(context: CompletionContext): CompletionResult | null {
			const prefix = this.language === 'python' ? '_' : '$';
			const preCursor = context.matchBefore(new RegExp(`\\${prefix}\\w*`));

			if (!preCursor || (preCursor.from === preCursor.to && !context.explicit)) return null;

			const TOP_LEVEL_COMPLETIONS_IN_BOTH_MODES: Completion[] = [
				{
					label: `${prefix}execution`,
					info: this.$locale.baseText('codeNodeEditor.completer.$execution'),
				},
				{
					label: `${prefix}ifEmpty()`,
					info: this.$locale.baseText('codeNodeEditor.completer.$ifEmpty'),
				},
				{ label: `${prefix}input`, info: this.$locale.baseText('codeNodeEditor.completer.$input') },
				{
					label: `${prefix}prevNode`,
					info: this.$locale.baseText('codeNodeEditor.completer.$prevNode'),
				},
				{
					label: `${prefix}workflow`,
					info: this.$locale.baseText('codeNodeEditor.completer.$workflow'),
				},
				{
					label: `${prefix}vars`,
					info: this.$locale.baseText('codeNodeEditor.completer.$vars'),
				},
				{
					label: `${prefix}now`,
					info: this.$locale.baseText('codeNodeEditor.completer.$now'),
				},
				{
					label: `${prefix}today`,
					info: this.$locale.baseText('codeNodeEditor.completer.$today'),
				},
				{
					label: `${prefix}jmespath()`,
					info: this.$locale.baseText('codeNodeEditor.completer.$jmespath'),
				},
				{
					label: `${prefix}runIndex`,
					info: this.$locale.baseText('codeNodeEditor.completer.$runIndex'),
				},
			];

			const options: Completion[] = TOP_LEVEL_COMPLETIONS_IN_BOTH_MODES.map(addVarType);

			options.push(
				...getAutoCompletableNodeNames(this.workflowsStore.allNodes).map((nodeName) => {
					return {
						label: `${prefix}('${nodeName}')`,
						type: 'variable',
						info: this.$locale.baseText('codeNodeEditor.completer.$()', {
							interpolate: { nodeName },
						}),
					};
				}),
			);

			if (this.mode === 'runOnceForEachItem') {
				const TOP_LEVEL_COMPLETIONS_IN_SINGLE_ITEM_MODE = [
					{ label: `${prefix}json` },
					{ label: `${prefix}binary` },
					{
						label: `${prefix}itemIndex`,
						info: this.$locale.baseText('codeNodeEditor.completer.$itemIndex'),
					},
				];

				options.push(...TOP_LEVEL_COMPLETIONS_IN_SINGLE_ITEM_MODE.map(addVarType));
			}

			return {
				from: preCursor.from,
				options,
			};
		},

		/**
		 * Complete `$(` to `$('nodeName')`.
		 */
		nodeSelectorCompletions(context: CompletionContext): CompletionResult | null {
			const prefix = this.language === 'python' ? '_' : '$';
			const preCursor = context.matchBefore(new RegExp(`\\${prefix}\\(.*`));

			if (!preCursor || (preCursor.from === preCursor.to && !context.explicit)) return null;

			const options: Completion[] = getAutoCompletableNodeNames(this.workflowsStore.allNodes).map(
				(nodeName) => {
					return {
						label: `${prefix}('${nodeName}')`,
						type: 'variable',
						info: this.$locale.baseText('codeNodeEditor.completer.$()', {
							interpolate: { nodeName },
						}),
					};
				},
			);

			return {
				from: preCursor.from,
				options,
			};
		},
	},
});


import N8nCard from './Card.vue';

export default N8nCard;


import type { INodeProperties } from 'n8n-workflow';

import {
	dueDatePreSendAction,
	taskPostReceiceAction,
	taskUpdatePreSendAction,
} from '../GenericFunctions';

export const taskOperations: INodeProperties[] = [
	{
		displayName: 'Operation',
		name: 'operation',
		type: 'options',
		noDataExpression: true,
		displayOptions: {
			show: {
				resource: ['task'],
			},
		},
		options: [
			{
				name: 'Create',
				value: 'create',
				routing: {
					request: {
						method: 'POST',
						url: '=/contacts/{{$parameter.contactId}}/tasks',
					},
					output: {
						postReceive: [taskPostReceiceAction],
					},
				},
				action: 'Create a task',
			},
			{
				name: 'Delete',
				value: 'delete',
				routing: {
					request: {
						method: 'DELETE',
						url: '=/contacts/{{$parameter.contactId}}/tasks/{{$parameter.taskId}}',
					},
					output: {
						postReceive: [
							{
								type: 'set',
								properties: {
									value: '={{ { "success": true } }}',
								},
							},
						],
					},
				},
				action: 'Delete a task',
			},
			{
				name: 'Get',
				value: 'get',
				routing: {
					request: {
						method: 'GET',
						url: '=/contacts/{{$parameter.contactId}}/tasks/{{$parameter.taskId}}',
					},
					output: {
						postReceive: [taskPostReceiceAction],
					},
				},
				action: 'Get a task',
			},
			{
				name: 'Get Many',
				value: 'getAll',
				routing: {
					request: {
						method: 'GET',
						url: '=/contacts/{{$parameter.contactId}}/tasks',
					},
					output: {
						postReceive: [
							{
								type: 'rootProperty',
								properties: {
									property: 'tasks',
								},
							},
							taskPostReceiceAction,
						],
					},
				},
				action: 'Get many tasks',
			},
			{
				name: 'Update',
				value: 'update',
				routing: {
					request: {
						method: 'PUT',
						url: '=/contacts/{{$parameter.contactId}}/tasks/{{$parameter.taskId}}',
					},
					send: {
						preSend: [taskUpdatePreSendAction],
					},
					output: {
						postReceive: [taskPostReceiceAction],
					},
				},
				action: 'Update a task',
			},
		],
		default: 'create',
	},
];

const createProperties: INodeProperties[] = [
	{
		displayName: 'Contact ID',
		name: 'contactId',
		type: 'string',
		displayOptions: {
			show: {
				resource: ['task'],
				operation: ['create'],
			},
		},
		default: '',
		required: true,
		description: 'Contact the task belongs to',
	},
	{
		displayName: 'Title',
		name: 'title',
		type: 'string',
		required: true,
		default: '',
		displayOptions: {
			show: {
				resource: ['task'],
				operation: ['create'],
			},
		},
		routing: {
			send: {
				type: 'body',
				property: 'title',
			},
		},
	},
	{
		displayName: 'Due Date',
		name: 'dueDate',
		type: 'dateTime',
		required: true,
		default: '',
		displayOptions: {
			show: {
				resource: ['task'],
				operation: ['create'],
			},
		},
		routing: {
			send: {
				type: 'body',
				property: 'dueDate',
				preSend: [dueDatePreSendAction],
			},
		},
	},
	{
		displayName: 'Additional Fields',
		name: 'additionalFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: ['task'],
				operation: ['create'],
			},
		},
		options: [
			{
				// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options
				displayName: 'Assigned To',
				name: 'assignedTo',
				type: 'options',
				default: '',
				description:
					'Choose from the list, or specify an ID using an expression',
				typeOptions: {
					loadOptionsMethod: 'getUsers',
				},
				routing: {
					send: {
						type: 'body',
						property: 'assignedTo',
					},
				},
			},
			{
				displayName: 'Description',
				name: 'description',
				type: 'string',
				default: '',
				routing: {
					send: {
						type: 'body',
						property: 'description',
					},
				},
			},
			{
				displayName: 'Status',
				name: 'status',
				type: 'options',
				options: [
					{
						name: 'Incompleted',
						value: 'incompleted',
					},
					{
						name: 'Completed',
						value: 'completed',
					},
				],
				default: 'incompleted',
				routing: {
					send: {
						type: 'body',
						property: 'status',
					},
				},
			},
		],
	},
];

const deleteProperties: INodeProperties[] = [
	{
		displayName: 'Contact ID',
		name: 'contactId',
		type: 'string',
		displayOptions: {
			show: {
				resource: ['task'],
				operation: ['delete'],
			},
		},
		default: '',
		required: true,
		description: 'Contact the task belongs to',
	},
	{
		displayName: 'Task ID',
		name: 'taskId',
		type: 'string',
		required: true,
		displayOptions: {
			show: {
				resource: ['task'],
				operation: ['delete'],
			},
		},
		default: '',
	},
];

const getProperties: INodeProperties[] = [
	{
		displayName: 'Contact ID',
		name: 'contactId',
		type: 'string',
		displayOptions: {
			show: {
				resource: ['task'],
				operation: ['get'],
			},
		},
		default: '',
		required: true,
		description: 'Contact the task belongs to',
	},
	{
		displayName: 'Task ID',
		name: 'taskId',
		type: 'string',
		required: true,
		displayOptions: {
			show: {
				resource: ['task'],
				operation: ['get'],
			},
		},
		default: '',
	},
];

const getAllProperties: INodeProperties[] = [
	{
		displayName: 'Contact ID',
		name: 'contactId',
		type: 'string',
		displayOptions: {
			show: {
				resource: ['task'],
				operation: ['getAll'],
			},
		},
		default: '',
		required: true,
		description: 'Contact the task belongs to',
	},
	{
		displayName: 'Return All',
		name: 'returnAll',
		type: 'boolean',
		displayOptions: {
			show: {
				resource: ['task'],
				operation: ['getAll'],
			},
		},
		default: false,
		description: 'Whether to return all results or only up to a given limit',
	},
	{
		displayName: 'Limit',
		name: 'limit',
		type: 'number',
		displayOptions: {
			show: {
				resource: ['task'],
				operation: ['getAll'],
				returnAll: [false],
			},
		},
		typeOptions: {
			minValue: 1,
			maxValue: 100,
		},
		default: 20,
		routing: {
			send: {
				type: 'query',
				property: 'limit',
			},
		},
		description: 'Max number of results to return',
	},
];

const updateProperties: INodeProperties[] = [
	{
		displayName: 'Contact ID',
		name: 'contactId',
		type: 'string',
		displayOptions: {
			show: {
				resource: ['task'],
				operation: ['update'],
			},
		},
		default: '',
		required: true,
		description: 'Contact the task belongs to',
	},
	{
		displayName: 'Task ID',
		name: 'taskId',
		type: 'string',
		displayOptions: {
			show: {
				resource: ['task'],
				operation: ['update'],
			},
		},
		default: '',
		required: true,
	},
	{
		displayName: 'Update Fields',
		name: 'updateFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: ['task'],
				operation: ['update'],
			},
		},
		options: [
			{
				// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options
				displayName: 'Assigned To',
				name: 'assignedTo',
				type: 'options',
				default: '',
				description:
					'Choose from the list, or specify an ID using an expression',
				typeOptions: {
					loadOptionsMethod: 'getUsers',
				},
				routing: {
					send: {
						type: 'body',
						property: 'assignedTo',
					},
				},
			},
			{
				displayName: 'Description',
				name: 'description',
				type: 'string',
				default: '',
				routing: {
					send: {
						type: 'body',
						property: 'description',
					},
				},
			},
			{
				displayName: 'Due Date',
				name: 'dueDate',
				type: 'dateTime',
				default: '',
				routing: {
					send: {
						type: 'body',
						property: 'dueDate',
						preSend: [dueDatePreSendAction],
					},
				},
			},
			{
				displayName: 'Status',
				name: 'status',
				type: 'options',
				options: [
					{
						name: 'Incompleted',
						value: 'incompleted',
					},
					{
						name: 'Completed',
						value: 'completed',
					},
				],
				default: 'incompleted',
				routing: {
					send: {
						type: 'body',
						property: 'status',
					},
				},
			},
			{
				displayName: 'Title',
				name: 'title',
				type: 'string',
				default: '',
				routing: {
					send: {
						type: 'body',
						property: 'title',
					},
				},
			},
		],
	},
];

export const taskFields: INodeProperties[] = [
	...createProperties,
	...updateProperties,
	...deleteProperties,
	...getProperties,
	...getAllProperties,
];


import { BasePage } from './../base';

export class ChangePasswordModal extends BasePage {
	getters = {
		modalContainer: () => cy.getByTestId('changePassword-modal').last(),
		currentPasswordInput: () => cy.getByTestId('currentPassword').find('input').first(),
		newPasswordInputContainer: () => cy.getByTestId('password'),
		newPasswordInput: () => cy.getByTestId('password').find('input').first(),
		repeatPasswordInput: () => cy.getByTestId('password2').find('input').first(),
		changePasswordButton: () => cy.getByTestId('change-password-button'),
	};
}


const returnData: IVariableSelectorOption[] = [];

const jsonPropertyPrefix = useShort ? '$json' : `$('${nodeName}').item.json`;

const jsonDataOptions: IVariableSelectorOption[] = [];

for (const propertyName of Object.keys(outputData.json)) {

keep keep keep keep keep replace keep keep keep keep


import { ref } from 'vue';
import { debounce as _debounce } from 'lodash-es';

export interface DebounceOptions {
	debounceTime: number;
	trailing?: boolean;
}

export type DebouncedFunction<R = void> = (...args: unknown[]) => R;

export function useDebounce() {
	// Create a ref for the WeakMap to store debounced functions.
	const debounceCache = ref(new WeakMap<DebouncedFunction<unknown>, DebouncedFunction<unknown>>());

	const debounce = <T extends DebouncedFunction<ReturnType<T>>>(
		fn: T,
		options: DebounceOptions,
	): T => {
		const { trailing, debounceTime } = options;

		// Check if a debounced version of the function is already stored in the WeakMap.
		let debouncedFn = debounceCache.value.get(fn);

		// If a debounced version is not found, create one and store it in the WeakMap.
		if (debouncedFn === undefined) {
			debouncedFn = _debounce(
				async (...args: unknown[]) => {
					return fn(...args);
				},
				debounceTime,
				trailing ? { trailing } : { leading: true },
			);

			debounceCache.value.set(fn, debouncedFn);
		}

		return debouncedFn as T;
	};

	const callDebounced = <T extends DebouncedFunction<ReturnType<T>>>(
		fn: T,
		options: DebounceOptions,
		...inputParameters: Parameters<T>
	): ReturnType<T> => {
		const debouncedFn = debounce(fn, options);

		return debouncedFn(...inputParameters);
	};

	return {
		debounce,
		callDebounced,
	};
}


import { setFlagsFromString } from 'v8';
import { runInNewContext } from 'vm';

export const runGarbageCollector = () => {
	try {
		setFlagsFromString('--expose_gc');
		const gc = runInNewContext('gc'); // nocommit
		gc();
	} catch (error) {
		console.log(error);
	}
};

export const generateGarbageMemory = (sizeInMB: number, onHeap = true) => {
	const divider = onHeap ? 8 : 1;
	const size = Math.max(1, Math.floor((sizeInMB * 1024 * 1024) / divider));
	if (onHeap) {
		// arrays are allocated on the heap
		// size in this case is only an approximation...
		const array = Array(size);
		array.fill(0);
	} else {
		const array = new Uint8Array(size);
		array.fill(0);
	}
	// const used = process.memoryUsage().heapUsed / 1024 / 1024;
	// const external = process.memoryUsage().external / 1024 / 1024;
	// console.log(`heap: ${used} MB / external: ${external} MB`);
	return { ...process.memoryUsage() };
};


import type { IExecuteFunctions, IDataObject, INodeExecutionData } from 'n8n-workflow';

import { apiRequest, apiRequestAllItems } from '../../../transport';

export async function getAll(
	this: IExecuteFunctions,
	index: number,
): Promise<INodeExecutionData[]> {
	const returnAll = this.getNodeParameter('returnAll', index);
	const filters = this.getNodeParameter('filters', index);

	let qs = {} as IDataObject;
	const requestMethod = 'GET';
	const endpoint = 'customers';
	const body = {} as IDataObject;

	if (filters) {
		qs = filters;
		if (qs.businessName) {
			qs.business_name = qs.businessName;
		}
		if (qs.includeDisabled) {
			qs.include_disabled = qs.includeDisabled;
		}
	}

	if (!returnAll) {
		qs.per_page = this.getNodeParameter('limit', index);
	}

	let responseData;
	if (returnAll) {
		responseData = await apiRequestAllItems.call(this, requestMethod, endpoint, body, qs);
		return this.helpers.returnJsonArray(responseData);
	} else {
		responseData = await apiRequest.call(this, requestMethod, endpoint, body, qs);
		return this.helpers.returnJsonArray(responseData.customers as IDataObject[]);
	}
}


const binaryPropertyPrefix = useShort ? '$binary' : `$('${nodeName}').item.binary`;

for (const dataPropertyName of Object.keys(outputData.binary!)) {

keep keep keep replace keep keep keep keep keep


import type { INodeProperties } from 'n8n-workflow';

export const requesterGroupOperations: INodeProperties[] = [
	{
		displayName: 'Operation',
		name: 'operation',
		type: 'options',
		noDataExpression: true,
		displayOptions: {
			show: {
				resource: ['requesterGroup'],
			},
		},
		options: [
			{
				name: 'Create',
				value: 'create',
				description: 'Create a requester group',
				action: 'Create a requester group',
			},
			{
				name: 'Delete',
				value: 'delete',
				description: 'Delete a requester group',
				action: 'Delete a requester group',
			},
			{
				name: 'Get',
				value: 'get',
				description: 'Retrieve a requester group',
				action: 'Get a requester group',
			},
			{
				name: 'Get Many',
				value: 'getAll',
				description: 'Retrieve many requester groups',
				action: 'Get many requester groups',
			},
			{
				name: 'Update',
				value: 'update',
				description: 'Update a requester group',
				action: 'Update a requester group',
			},
		],
		default: 'create',
	},
];

export const requesterGroupFields: INodeProperties[] = [
	// ----------------------------------------
	//          requesterGroup: create
	// ----------------------------------------
	{
		displayName: 'Name',
		name: 'name',
		type: 'string',
		required: true,
		default: '',
		displayOptions: {
			show: {
				resource: ['requesterGroup'],
				operation: ['create'],
			},
		},
	},
	{
		displayName: 'Additional Fields',
		name: 'additionalFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: ['requesterGroup'],
				operation: ['create'],
			},
		},
		options: [
			{
				displayName: 'Description',
				name: 'description',
				type: 'string',
				default: '',
			},
		],
	},

	// ----------------------------------------
	//          requesterGroup: delete
	// ----------------------------------------
	{
		displayName: 'Requester Group ID',
		name: 'requesterGroupId',
		description: 'ID of the requester group to delete',
		type: 'string',
		required: true,
		default: '',
		displayOptions: {
			show: {
				resource: ['requesterGroup'],
				operation: ['delete'],
			},
		},
	},

	// ----------------------------------------
	//           requesterGroup: get
	// ----------------------------------------
	{
		displayName: 'Requester Group ID',
		name: 'requesterGroupId',
		description: 'ID of the requester group to retrieve',
		type: 'string',
		required: true,
		default: '',
		displayOptions: {
			show: {
				resource: ['requesterGroup'],
				operation: ['get'],
			},
		},
	},

	// ----------------------------------------
	//          requesterGroup: getAll
	// ----------------------------------------
	{
		displayName: 'Return All',
		name: 'returnAll',
		type: 'boolean',
		default: false,
		description: 'Whether to return all results or only up to a given limit',
		displayOptions: {
			show: {
				resource: ['requesterGroup'],
				operation: ['getAll'],
			},
		},
	},
	{
		displayName: 'Limit',
		name: 'limit',
		type: 'number',
		default: 50,
		description: 'Max number of results to return',
		typeOptions: {
			minValue: 1,
		},
		displayOptions: {
			show: {
				resource: ['requesterGroup'],
				operation: ['getAll'],
				returnAll: [false],
			},
		},
	},

	// ----------------------------------------
	//          requesterGroup: update
	// ----------------------------------------
	{
		displayName: 'Requester Group ID',
		name: 'requesterGroupId',
		description: 'ID of the requester group to update',
		type: 'string',
		required: true,
		default: '',
		displayOptions: {
			show: {
				resource: ['requesterGroup'],
				operation: ['update'],
			},
		},
	},
	{
		displayName: 'Update Fields',
		name: 'updateFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: ['requesterGroup'],
				operation: ['update'],
			},
		},
		options: [
			{
				displayName: 'Description',
				name: 'description',
				type: 'string',
				default: '',
				description: 'Description of the requester group',
			},
			{
				displayName: 'Name',
				name: 'name',
				type: 'string',
				default: '',
				description: 'Name of the requester group',
			},
		],
	},
];


import type { INodeProperties } from 'n8n-workflow';

import { activeCampaignDefaultGetAllProperties } from './GenericFunctions';

export const ecomOrderProductsOperations: INodeProperties[] = [
	{
		displayName: 'Operation',
		name: 'operation',
		type: 'options',
		noDataExpression: true,
		displayOptions: {
			show: {
				resource: ['ecommerceOrderProducts'],
			},
		},
		options: [
			{
				name: 'Get Many',
				value: 'getAll',
				description: 'Get data of many order products',
				action: 'Get many ecommerce orders',
			},
			{
				name: 'Get by Product ID',
				value: 'getByProductId',
				description: 'Get data of a ordered product',
				action: 'Get an e-commerce order product by product ID',
			},
			{
				name: 'Get by Order ID',
				value: 'getByOrderId',
				description: "Get data of an order's products",
				action: 'Get an e-commerce order product by order ID',
			},
		],
		default: 'getAll',
	},
];

export const ecomOrderProductsFields: INodeProperties[] = [
	// ----------------------------------
	//         ecommerceOrderProducts:getByOrderId
	// ----------------------------------
	{
		displayName: 'Order ID',
		name: 'orderId',
		type: 'number',
		default: 0,
		displayOptions: {
			show: {
				operation: ['getByOrderId'],
				resource: ['ecommerceOrderProducts'],
			},
		},
		description: "The ID of the order whose products you'd like returned",
	},

	// ----------------------------------
	//         ecommerceOrderProducts:getByProductId
	// ----------------------------------
	{
		displayName: 'Product ID',
		name: 'procuctId',
		type: 'number',
		default: 0,
		displayOptions: {
			show: {
				operation: ['getByProductId'],
				resource: ['ecommerceOrderProducts'],
			},
		},
		description: "The ID of the product you'd like returned",
	},

	// ----------------------------------
	//         ecommerceOrderProducts:getAll
	// ----------------------------------
	...activeCampaignDefaultGetAllProperties('ecommerceOrderProducts', 'getAll'),
];


import type { INodeProperties } from 'n8n-workflow';

export const folderOperations: INodeProperties[] = [
	{
		displayName: 'Operation',
		name: 'operation',
		type: 'options',
		noDataExpression: true,
		displayOptions: {
			show: {
				resource: ['folder'],
			},
		},
		options: [
			{
				name: 'Create',
				value: 'create',
				description: 'Create a folder',
				action: 'Create a folder',
			},
			{
				name: 'Delete',
				value: 'delete',
				description: 'Delete a folder',
				action: 'Delete a folder',
			},
			{
				name: 'Get Children',
				value: 'getChildren',
				description: 'Get items inside a folder',
				action: 'Get items in a folder',
			},
			{
				name: 'Rename',
				value: 'rename',
				description: 'Rename a folder',
				action: 'Rename a folder',
			},
			{
				name: 'Search',
				value: 'search',
				description: 'Search a folder',
				action: 'Search a folder',
			},
			{
				name: 'Share',
				value: 'share',
				description: 'Share a folder',
				action: 'Share a folder',
			},
		],
		default: 'getChildren',
	},
];

export const folderFields: INodeProperties[] = [
	/* -------------------------------------------------------------------------- */
	/*                                 folder:create                              */
	/* -------------------------------------------------------------------------- */
	{
		displayName: 'Name',
		name: 'name',
		required: true,
		type: 'string',
		placeholder: '/Pictures/2021',
		displayOptions: {
			show: {
				operation: ['create'],
				resource: ['folder'],
			},
		},
		default: '',
		description: 'The name or path of the folder',
	},
	{
		displayName: 'Options',
		name: 'options',
		type: 'collection',
		displayOptions: {
			show: {
				operation: ['create'],
				resource: ['folder'],
			},
		},
		default: {},
		placeholder: 'Add Field',
		options: [
			{
				displayName: 'Parent Folder ID',
				name: 'parentFolderId',
				type: 'string',
				default: '',
				description: 'ID of the folder you want to crate the new folder in',
			},
		],
	},
	/* -------------------------------------------------------------------------- */
	/*                                 folder:getChildren/delete                  */
	/* -------------------------------------------------------------------------- */
	{
		displayName: 'Folder ID',
		name: 'folderId',
		type: 'string',
		displayOptions: {
			show: {
				operation: ['delete', 'getChildren'],
				resource: ['folder'],
			},
		},
		default: '',
	},
	/* -------------------------------------------------------------------------- */
	/*                               folder:rename                                */
	/* -------------------------------------------------------------------------- */
	{
		displayName: 'Item ID',
		name: 'itemId',
		type: 'string',
		displayOptions: {
			show: {
				operation: ['rename'],
				resource: ['folder'],
			},
		},
		default: '',
		description: 'ID of the folder',
	},
	{
		displayName: 'New Name',
		name: 'newName',
		type: 'string',
		displayOptions: {
			show: {
				operation: ['rename'],
				resource: ['folder'],
			},
		},
		default: '',
		description: 'New name for folder',
	},
	/* -------------------------------------------------------------------------- */
	/*                                 folder:search                              */
	/* -------------------------------------------------------------------------- */
	{
		displayName: 'Query',
		name: 'query',
		type: 'string',
		displayOptions: {
			show: {
				operation: ['search'],
				resource: ['folder'],
			},
		},
		default: '',
		description:
			'The query text used to search for items. Values may be matched across several fields including filename, metadata, and file content.',
	},
	/* -------------------------------------------------------------------------- */
	/*                                 folder:share                               */
	/* -------------------------------------------------------------------------- */
	{
		displayName: 'Folder ID',
		name: 'folderId',
		type: 'string',
		displayOptions: {
			show: {
				operation: ['share'],
				resource: ['folder'],
			},
		},
		default: '',
		description: 'File ID',
	},
	{
		displayName: 'Type',
		name: 'type',
		type: 'options',
		options: [
			{
				name: 'View',
				value: 'view',
			},
			{
				name: 'Edit',
				value: 'edit',
			},
			{
				name: 'Embed',
				value: 'embed',
			},
		],
		displayOptions: {
			show: {
				operation: ['share'],
				resource: ['folder'],
			},
		},
		default: '',
		description: 'The type of sharing link to create',
	},
	{
		displayName: 'Scope',
		name: 'scope',
		type: 'options',
		options: [
			{
				name: 'Anonymous',
				value: 'anonymous',
			},
			{
				name: 'Organization',
				value: 'organization',
			},
		],
		displayOptions: {
			show: {
				operation: ['share'],
				resource: ['folder'],
			},
		},
		default: '',
		description: 'The type of sharing link to create',
	},
];


import type { Command, Undoable } from '@/models/history';
import { BulkCommand } from '@/models/history';
import { STORES } from '@/constants';
import type { HistoryState } from '@/Interface';
import { defineStore } from 'pinia';

const STACK_LIMIT = 100;

export const useHistoryStore = defineStore(STORES.HISTORY, {
	state: (): HistoryState => ({
		undoStack: [],
		redoStack: [],
		currentBulkAction: null,
		bulkInProgress: false,
	}),
	actions: {
		popUndoableToUndo(): Undoable | undefined {
			if (this.undoStack.length > 0) {
				return this.undoStack.pop();
			}

			return undefined;
		},
		pushCommandToUndo(undoable: Command, clearRedo = true): void {
			if (!this.bulkInProgress) {
				if (this.currentBulkAction) {
					const alreadyIn =
						this.currentBulkAction.commands.find((c) => c.isEqualTo(undoable)) !== undefined;
					if (!alreadyIn) {
						this.currentBulkAction.commands.push(undoable);
					}
				} else {
					this.undoStack.push(undoable);
				}
				this.checkUndoStackLimit();
				if (clearRedo) {
					this.clearRedoStack();
				}
			}
		},
		pushBulkCommandToUndo(undoable: BulkCommand, clearRedo = true): void {
			this.undoStack.push(undoable);
			this.checkUndoStackLimit();
			if (clearRedo) {
				this.clearRedoStack();
			}
		},
		checkUndoStackLimit() {
			if (this.undoStack.length > STACK_LIMIT) {
				this.undoStack.shift();
			}
		},
		checkRedoStackLimit() {
			if (this.redoStack.length > STACK_LIMIT) {
				this.redoStack.shift();
			}
		},
		clearUndoStack() {
			this.undoStack = [];
		},
		clearRedoStack() {
			this.redoStack = [];
		},
		reset() {
			this.clearRedoStack();
			this.clearUndoStack();
		},
		popUndoableToRedo(): Undoable | undefined {
			if (this.redoStack.length > 0) {
				return this.redoStack.pop();
			}
			return undefined;
		},
		pushUndoableToRedo(undoable: Undoable): void {
			this.redoStack.push(undoable);
			this.checkRedoStackLimit();
		},
		startRecordingUndo() {
			this.currentBulkAction = new BulkCommand([]);
		},
		stopRecordingUndo() {
			if (this.currentBulkAction && this.currentBulkAction.commands.length > 0) {
				this.undoStack.push(this.currentBulkAction);
				this.checkUndoStackLimit();
			}
			this.currentBulkAction = null;
		},
	},
});