<|start_filename|>src/components/datepicker/YearView.js<|end_filename|>
import Btn from './../button/Btn'

export default {
  components: { Btn },
  props: {
    year: Number,
    iconControlLeft: String,
    iconControlRight: String,
  },
  computed: {
    rows() {
      const rows = []
      const yearGroupStart = this.year - (this.year % 20)
      for (let i = 0; i < 4; i++) {
        rows.push([])
        for (let j = 0; j < 5; j++) {
          rows[i].push(yearGroupStart + i * 5 + j)
        }
      }
      return rows
    },
    yearStr() {
      const start = this.year - (this.year % 20)
      return `${start} ~ ${start + 19}`
    },
  },
  methods: {
    getBtnClass(year) {
      if (year === this.year) {
        return 'primary'
      } else {
        return 'default'
      }
    },
    goPrevYear() {
      this.$emit('year-change', this.year - 20)
    },
    goNextYear() {
      this.$emit('year-change', this.year + 20)
    },
    changeView(year) {
      this.$emit('year-change', year)
      this.$emit('view-change', 'm')
    },
  },
}


<|start_filename|>test/specs/array.utils.spec.js<|end_filename|>
import * as utils from '../../src/utils/array.utils'
import _ from 'lodash'
import $ from 'jquery'

describe('array.utils', () => {
  describe('#spliceIfExist', () => {
    it('should be able to handle non array object', () => {
      const test = null
      utils.spliceIfExist(test, undefined)
      expect(test).to.be.null
    })

    it('should be able to handle non in array object', () => {
      const arr = [0, 1, 2]
      utils.spliceIfExist(arr, 3)
      expect(arr.length).to.equal(3)
      expect(arr[0]).to.equal(0)
      expect(arr[1]).to.equal(1)
      expect(arr[2]).to.equal(2)
    })

    it('should be able to splice if exist', () => {
      const arr = [0, 1, 2]
      utils.spliceIfExist(arr, 1)
      expect(arr.length).to.equal(2)
      expect(arr[0]).to.equal(0)
      expect(arr[1]).to.equal(2)
    })
  })

  describe('#range', () => {
    it('should be able to get range array', () => {
      const b = utils.range(5)
      expect(_.isEqual(b, [0, 1, 2, 3, 4])).to.be.true
    })

    it('should be able to get range array with start', () => {
      const b = utils.range(5, 3, 1)
      expect(_.isEqual(b, [3, 4])).to.be.true
    })

    it('should be able to get range array with step', () => {
      const b = utils.range(5, 0, 2)
      expect(_.isEqual(b, [0, 2, 4])).to.be.true
    })
  })

  describe('#nodeListToArray', () => {
    it('should be able to convert node list to array', () => {
      $('<div class="cls">').appendTo('body')
      $('<div class="cls">').appendTo('body')
      $('<div class="cls">').appendTo('body')
      const a = document.querySelectorAll('.cls')
      expect(a.length).to.equal(3)
      expect(Array.isArray(a)).to.be.false
      const b = utils.nodeListToArray(a)
      expect(b.length).to.equal(3)
      expect(Array.isArray(b)).to.be.true
      expect(a[0]).to.equal(b[0])
      expect(a[1]).to.equal(b[1])
      expect(a[2]).to.equal(b[2])
      $('<div class="cls">').remove()
    })

    it('should be able ok if no param present', () => {
      const b = utils.nodeListToArray()
      expect(Array.isArray(b)).to.be.true
      expect(b.length).to.equal(0)
    })
  })

  describe('#onlyUnique', () => {
    it('should be able to uniq array', () => {
      const b = [0, 1, 2, 3, 3, 4, 4, 5].filter(utils.onlyUnique)
      expect(_.isEqual(b, [0, 1, 2, 3, 4, 5])).to.be.true
    })
  })
})


<|start_filename|>test/specs/ScrollSpy.spec.js<|end_filename|>
import $ from 'jquery'
import { sleep, createVm, destroyVm } from '../utils'

const expectActive = ($nav, hash) => {
  expect($nav.find('li.active').length).to.equal(1)
  expect($nav.find('li.active > a').attr('href')).to.equal(hash)
}

const expectDropdownActive = ($nav, hash) => {
  expect($nav.find('li.active').length).to.equal(2)
  expect($nav.find('li.dropdown.active').length).to.equal(1)
  expect($nav.find('li.dropdown.active > ul > li.active').length).to.equal(1)
  expect(
    $nav.find('li.dropdown.active > ul > li.active > a').attr('href')
  ).to.equal(hash)
}

describe('ScrollSpy', () => {
  let vm

  beforeEach(() => {
    vm = createVm(
      `
  <section>
    <nav class="navbar navbar-default navbar-static"  v-scrollspy:scrollspy-example>
      <div class="container-fluid">
        <div class="navbar-header">
          <button class="collapsed navbar-toggle" type="button" @click="show=!show">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a role="button" class="navbar-brand">Project Name</a>
        </div>
        <collapse class="navbar-collapse" v-model="show">
          <ul class="nav navbar-nav">
            <li class="active"><a href="#vue">@vue</a></li>
            <li class=""><a href="#bootstrap">@bootstrap</a></li>
            <dropdown tag="li">
              <a class="dropdown-toggle" role="button">Dropdown <span class="caret"></span></a>
              <template slot="dropdown">
                <li><a href="#one">one</a></li>
                <li><a href="#two">two</a></li>
                <li role="separator" class="divider"></li>
                <li><a href="#three">three</a></li>
              </template>
            </dropdown>
          </ul>
        </collapse>
      </div>
    </nav>
    <div id="scrollspy-example" ref="target" style="position: relative;height: 200px;width: 729px;margin-top: 10px;overflow: auto;">
      <h4 id="vue">@vue</h4>
      <p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
      <h4 id="bootstrap">@bootstrap</h4>
      <p>Veniam marfa mustache skateboard, adipisicing fugiat velit pitchfork beard. Freegan beard aliqua cupidatat mcsweeney's vero. Cupidatat four loko nisi, ea helvetica nulla carles. Tattooed cosby sweater food truck, mcsweeney's quis non freegan vinyl. Lo-fi wes anderson +1 sartorial. Carles non aesthetic exercitation quis gentrify. Brooklyn adipisicing craft beer vice keytar deserunt.</p>
      <h4 id="one">one</h4>
      <p>Occaecat commodo aliqua delectus. Fap craft beer deserunt skateboard ea. Lomo bicycle rights adipisicing banh mi, velit ea sunt next level locavore single-origin coffee in magna veniam. High life id vinyl, echo park consequat quis aliquip banh mi pitchfork. Vero VHS est adipisicing. Consectetur nisi DIY minim messenger bag. Cred ex in, sustainable delectus consectetur fanny pack iphone.</p>
      <h4 id="two">two</h4>
      <p>In incididunt echo park, officia deserunt mcsweeney's proident master cleanse thundercats sapiente veniam. Excepteur VHS elit, proident shoreditch +1 biodiesel laborum craft beer. Single-origin coffee wayfarers irure four loko, cupidatat terry richardson master cleanse. Assumenda you probably haven't heard of them art party fanny pack, tattooed nulla cardigan tempor ad. Proident wolf nesciunt sartorial keffiyeh eu banh mi sustainable. Elit wolf voluptate, lo-fi ea portland before they sold out four loko. Locavore enim nostrud mlkshk brooklyn nesciunt.</p>
      <h4 id="three">three</h4>
      <p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
    </div>
  </section>
    `,
      { show: false }
    )
  })

  afterEach(() => {
    destroyVm(vm)
  })

  it('should be able to toggle active class', async () => {
    // console.log(window.innerWidth, window.innerHeight)
    await vm.$nextTick()
    const _$el = $(vm.$el)
    const nav = _$el.find('nav')
    expectActive(nav, '#vue')
    const scrollEl = _$el.find('#scrollspy-example').get(0)
    scrollEl.scrollTop = _$el.find('#vue').get(0).offsetTop
    await sleep(100)
    expectActive(nav, '#vue')
    scrollEl.scrollTop = _$el.find('#bootstrap').get(0).offsetTop
    await sleep(100)
    expectActive(nav, '#bootstrap')
  })

  it('should be able to toggle dropdown active class', async () => {
    await vm.$nextTick()
    const _$el = $(vm.$el)
    const nav = _$el.find('nav')
    const scrollEl = _$el.find('#scrollspy-example').get(0)
    scrollEl.scrollTop = _$el.find('#one').get(0).offsetTop
    await sleep(100)
    expectDropdownActive(nav, '#one')
    scrollEl.scrollTop = _$el.find('#two').get(0).offsetTop
    await sleep(100)
    expectDropdownActive(nav, '#two')
    scrollEl.scrollTop = _$el.find('#three').get(0).offsetTop
    await sleep(100)
    expectDropdownActive(nav, '#three')
  })

  it('should be able to refresh on target height changed', async () => {
    await vm.$nextTick()
    const _$el = $(vm.$el)
    const nav = _$el.find('nav')
    const scrollEl = _$el.find('#scrollspy-example').get(0)
    scrollEl.scrollTop = _$el.find('#two').get(0).offsetTop
    await sleep(100)
    expectDropdownActive(nav, '#two')
    _$el.find('#one').css('height', 200)
    scrollEl.scrollTop = 0
    await sleep(100)
    scrollEl.scrollTop = _$el.find('#two').get(0).offsetTop
    await sleep(100)
    expectDropdownActive(nav, '#two')
  })

  it('should be able to append to body', async () => {
    vm = createVm(`
<section style="height: 5000px;">
  <ul class="nav" v-scrollspy style="height: 200px">
    <li><a href="#1">1</a></li>
    <li><a href="#2">2</a></li>
    <li><a href="#3">3</a></li>
  </ul>
  <div id="1" style="height: 200px">1</div>
  <div id="2" style="height: 500px">2</div>
  <div id="3" style="height: 300px">3</div>
</section>`)
    await vm.$nextTick()
    const $el = $(vm.$el)
    const $nav = $el.find('.nav')
    expect($nav.find('li.active').length).to.equal(0)
    window.scrollTo(0, $el.find('#1').get(0).offsetTop)
    await sleep(100)
    expectActive($nav, '#1')
    window.scrollTo(0, $el.find('#2').get(0).offsetTop)
    await sleep(100)
    expectActive($nav, '#2')
    window.scrollTo(0, $el.find('#3').get(0).offsetTop)
    await sleep(100)
    expectActive($nav, '#3')
    window.scrollTo(0, 0)
    await sleep(100)
    expect($nav.find('li.active').length).to.equal(0)
  })

  it('should be able to handle invalid target', async () => {
    vm = createVm(
      `
<section style="height: 5000px">
  <ul class="nav" v-scrollspy:test="opts" style="height: 200px">
    <li><a href="#1">{{msg}}</a></li>
  </ul>
</section>
    `,
      {
        opts: {},
        msg: 'test',
      }
    )
    await vm.$nextTick()
    vm.opts = { offset: 100 }
    await vm.$nextTick()
    vm.msg = '12345'
    await vm.$nextTick()
    // no err should be throw here
  })
})


<|start_filename|>src/utils/string.utils.js<|end_filename|>
export function pad(value, num) {
  value = value + ''
  for (let i = num - value.length; i > 0; i--) {
    value = '0' + value
  }
  return value
}


<|start_filename|>src/components/typeahead/Typeahead.js<|end_filename|>
import { request } from '../../utils/http.utils'
import { isString } from '../../utils/object.utils'
import {
  on,
  off,
  ensureElementMatchesFunction,
  EVENTS,
  getElementBySelectorOrRef,
} from '../../utils/dom.utils'
import Dropdown from '../dropdown/Dropdown.js'

export default {
  components: { Dropdown },
  props: {
    value: {
      required: true,
    },
    data: Array,
    itemKey: String,
    appendToBody: {
      type: Boolean,
      default: false,
    },
    ignoreCase: {
      type: Boolean,
      default: true,
    },
    matchStart: {
      type: Boolean,
      default: false,
    },
    forceSelect: {
      type: Boolean,
      default: false,
    },
    forceClear: {
      type: Boolean,
      default: false,
    },
    limit: {
      type: Number,
      default: 10,
    },
    asyncSrc: String,
    asyncKey: String,
    asyncFunction: Function,
    debounce: {
      type: Number,
      default: 200,
    },
    openOnFocus: {
      type: Boolean,
      default: true,
    },
    openOnEmpty: {
      type: Boolean,
      default: false,
    },
    target: {
      required: true,
    },
    preselect: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      inputEl: null,
      items: [],
      activeIndex: 0,
      timeoutID: 0,
      elements: [],
      open: false,
      dropdownMenuEl: null,
    }
  },
  computed: {
    regexOptions() {
      let options = ''
      if (this.ignoreCase) {
        options += 'i'
      }
      if (!this.matchStart) {
        options += 'g'
      }
      return options
    },
  },
  mounted() {
    ensureElementMatchesFunction()
    this.$nextTick(() => {
      this.initInputElByTarget(this.target)
      this.initListeners()
      this.dropdownMenuEl =
        this.$refs.dropdown.$el.querySelector('.dropdown-menu')
      // set input text if v-model not empty
      if (this.value) {
        this.setInputTextByValue(this.value)
      }
    })
  },
  beforeDestroy() {
    this.removeListeners()
  },
  watch: {
    target(el) {
      this.removeListeners()
      this.initInputElByTarget(el)
      this.initListeners()
    },
    value(value) {
      this.setInputTextByValue(value)
    },
    activeIndex(index) {
      index >= 0 && this.$emit('selected-item-changed', index)
    },
  },
  methods: {
    setInputTextByValue(value) {
      if (isString(value)) {
        // direct
        this.inputEl.value = value
      } else if (value) {
        // is object
        this.inputEl.value = this.itemKey ? value[this.itemKey] : value
      } else if (value === null) {
        // is null or undefined or something else not valid
        this.inputEl.value = ''
      }
    },
    hasEmptySlot() {
      return !!this.$slots.empty || !!this.$scopedSlots.empty
    },
    initInputElByTarget(target) {
      if (!target) {
        return
      }
      this.inputEl = getElementBySelectorOrRef(target)
    },
    initListeners() {
      if (this.inputEl) {
        this.elements = [this.inputEl]
        on(this.inputEl, EVENTS.FOCUS, this.inputFocused)
        on(this.inputEl, EVENTS.BLUR, this.inputBlured)
        on(this.inputEl, EVENTS.INPUT, this.inputChanged)
        on(this.inputEl, EVENTS.KEY_DOWN, this.inputKeyPressed)
      }
    },
    removeListeners() {
      this.elements = []
      if (this.inputEl) {
        off(this.inputEl, EVENTS.FOCUS, this.inputFocused)
        off(this.inputEl, EVENTS.BLUR, this.inputBlured)
        off(this.inputEl, EVENTS.INPUT, this.inputChanged)
        off(this.inputEl, EVENTS.KEY_DOWN, this.inputKeyPressed)
      }
    },
    prepareItems(data, disableFilters = false) {
      if (disableFilters) {
        this.items = data.slice(0, this.limit)
        return
      }
      this.items = []
      this.activeIndex = this.preselect ? 0 : -1
      for (let i = 0, l = data.length; i < l; i++) {
        const item = data[i]
        let key = this.itemKey ? item[this.itemKey] : item
        key = key.toString()
        let index = -1
        if (this.ignoreCase) {
          index = key.toLowerCase().indexOf(this.inputEl.value.toLowerCase())
        } else {
          index = key.indexOf(this.inputEl.value)
        }
        if (this.matchStart ? index === 0 : index >= 0) {
          this.items.push(item)
        }
        if (this.items.length >= this.limit) {
          break
        }
      }
    },
    fetchItems(value, debounce) {
      clearTimeout(this.timeoutID)
      if (value === '' && !this.openOnEmpty) {
        this.open = false
      } else if (this.data) {
        this.prepareItems(this.data)
        this.open = this.hasEmptySlot() || Boolean(this.items.length)
      } else if (this.asyncSrc) {
        this.timeoutID = setTimeout(() => {
          this.$emit('loading')
          request(this.asyncSrc + encodeURIComponent(value))
            .then((data) => {
              if (this.inputEl.matches(':focus')) {
                this.prepareItems(
                  this.asyncKey ? data[this.asyncKey] : data,
                  true
                )
                this.open = this.hasEmptySlot() || Boolean(this.items.length)
              }
              this.$emit('loaded')
            })
            .catch((err) => {
              console.error(err)
              this.$emit('loaded-error')
            })
        }, debounce)
      } else if (this.asyncFunction) {
        const cb = (data) => {
          if (this.inputEl.matches(':focus')) {
            this.prepareItems(data, true)
            this.open = this.hasEmptySlot() || Boolean(this.items.length)
          }
          this.$emit('loaded')
        }
        this.timeoutID = setTimeout(() => {
          this.$emit('loading')
          this.asyncFunction(value, cb)
        }, debounce)
      }
    },
    inputChanged() {
      const value = this.inputEl.value
      this.fetchItems(value, this.debounce)
      this.$emit('input', this.forceSelect ? undefined : value)
    },
    inputFocused() {
      if (this.openOnFocus) {
        const value = this.inputEl.value
        this.fetchItems(value, 0)
      }
    },
    inputBlured() {
      if (!this.dropdownMenuEl.matches(':hover')) {
        this.open = false
      }
      if (this.inputEl && this.forceClear) {
        this.$nextTick(() => {
          if (typeof this.value === 'undefined') {
            this.inputEl.value = ''
          }
        })
      }
    },
    inputKeyPressed(event) {
      event.stopPropagation()
      if (this.open) {
        switch (event.keyCode) {
          case 13:
            if (this.activeIndex >= 0) {
              this.selectItem(this.items[this.activeIndex])
            } else {
              this.open = false
            }
            event.preventDefault()
            break
          case 27:
            this.open = false
            break
          case 38:
            this.activeIndex = this.activeIndex > 0 ? this.activeIndex - 1 : 0
            break
          case 40: {
            const maxIndex = this.items.length - 1
            this.activeIndex =
              this.activeIndex < maxIndex ? this.activeIndex + 1 : maxIndex
            break
          }
        }
      }
    },
    selectItem(item) {
      this.$emit('input', item)
      this.open = false
    },
    highlight(item) {
      const value = this.itemKey ? item[this.itemKey] : item
      const inputValue = this.inputEl.value.replace(
        /[-[\]{}()*+?.,\\^$|#\s]/g,
        '\\$&'
      )
      return value.replace(
        new RegExp(`${inputValue}`, this.regexOptions),
        '<b>$&</b>'
      )
    },
  },
}


<|start_filename|>test/specs/Pagination.spec.js<|end_filename|>
import $ from 'jquery'
import { createVm, destroyVm, triggerEvent } from '../utils'

describe('Pagination', () => {
  let vm

  afterEach(() => {
    destroyVm(vm)
  })

  it('should be able to hide boundary links', async () => {
    vm = createVm(
      '<div><pagination v-model="currentPage" :total-page="totalPage"/></div>',
      {
        totalPage: 18,
        currentPage: 1,
      }
    )
    await vm.$nextTick()
    const pagination = vm.$el.querySelector('.pagination')
    const first = pagination.querySelector('[aria-label="First"]')
    const last = pagination.querySelector('[aria-label="Last"]')
    expect(first).to.not.exist
    expect(last).to.not.exist
  })

  it('should be able to show boundary links', async () => {
    vm = createVm(
      '<div><pagination v-model="currentPage" :total-page="totalPage" boundary-links/></div>',
      {
        totalPage: 18,
        currentPage: 1,
      }
    )
    const pagination = vm.$el.querySelector('.pagination')
    const first = pagination.querySelector('[aria-label="First"]')
    const last = pagination.querySelector('[aria-label="Last"]')
    expect(first).to.exist
    expect(last).to.exist
  })

  it('should be able to hide direction links', async () => {
    vm = createVm(
      `<section>
    <pagination v-model="currentPage" :total-page="totalPage"/>
    <pagination v-model="currentPage" :total-page="totalPage" :direction-links="false"/>
  </section>`,
      {
        totalPage: 18,
        currentPage: 1,
      }
    )
    await vm.$nextTick()
    const pagination = vm.$el.querySelectorAll('ul.pagination')[1]
    const pre = pagination.querySelector('[aria-label="Previous"]')
    const next = pagination.querySelector('[aria-label="Next"]')
    expect(pre).to.not.exist
    expect(next).to.not.exist
  })

  it('should be able to show direction links', async () => {
    vm = createVm(
      `<section>
    <pagination v-model="currentPage" :total-page="totalPage"/>
    <pagination v-model="currentPage" :total-page="totalPage" :direction-links="false"/>
  </section>`,
      {
        totalPage: 18,
        currentPage: 1,
      }
    )
    await vm.$nextTick()
    const pagination = vm.$el.querySelectorAll('ul.pagination')[0]
    const pre = pagination.querySelector('[aria-label="Previous"]')
    const next = pagination.querySelector('[aria-label="Next"]')
    expect(pre).to.exist
    expect(next).to.exist
  })

  it('should be able to change current page', async () => {
    vm = createVm(
      '<div><pagination v-model="currentPage" :total-page="totalPage"/></div>',
      {
        totalPage: 18,
        currentPage: 1,
      }
    )
    vm.currentPage = 1
    await vm.$nextTick()
    let pagination = vm.$el.querySelector('ul.pagination')
    let currentPageElement = pagination.querySelector('li.active a')
    expect(Number(currentPageElement.text)).to.equal(vm.currentPage)
    vm.currentPage = 6
    await vm.$nextTick()
    pagination = vm.$el.querySelector('ul.pagination')
    currentPageElement = pagination.querySelector('li.active a')
    expect(Number(currentPageElement.text)).to.equal(vm.currentPage)
  })

  it('should be able to change size', async () => {
    vm = createVm(
      `<section>
    <pagination v-model="currentPage" :total-page="totalPage" size="lg"/>
    <pagination v-model="currentPage" :total-page="totalPage"/>
    <pagination v-model="currentPage" :total-page="totalPage" size="sm"/>
  </section>`,
      {
        totalPage: 18,
        currentPage: 1,
      }
    )
    await vm.$nextTick()
    expect(vm.$el.querySelectorAll('.pagination')[0].className).to.equal(
      'pagination pagination-lg'
    )
    expect(vm.$el.querySelectorAll('.pagination')[1].className).to.equal(
      'pagination'
    )
    expect(vm.$el.querySelectorAll('.pagination')[2].className).to.equal(
      'pagination pagination-sm'
    )
  })

  it('should be able to change alignment', async () => {
    vm = createVm(
      `<section>
    <pagination v-model="currentPage" :total-page="totalPage"/>
    <pagination v-model="currentPage" :total-page="totalPage" align="center"/>
    <pagination v-model="currentPage" :total-page="totalPage" align="right"/>
  </section>`,
      {
        totalPage: 18,
        currentPage: 1,
      }
    )
    await vm.$nextTick()
    expect(vm.$el.querySelectorAll('nav')[0].className).to.equal('')
    expect(vm.$el.querySelectorAll('nav')[1].className).to.equal('text-center')
    expect(vm.$el.querySelectorAll('nav')[2].className).to.equal('text-right')
  })

  it('should be able to change total page', async () => {
    vm = createVm(
      '<div><pagination v-model="currentPage" :total-page="totalPage" boundary-links/></div>',
      {
        totalPage: 18,
        currentPage: 1,
      }
    )
    await vm.$nextTick()
    const pagination = vm.$el.querySelector('ul.pagination')
    const lastBtn = pagination.querySelector('[aria-label="Last"]')
    triggerEvent(lastBtn, 'click')
    await vm.$nextTick()
    let activeBtn = pagination.querySelector('.active a')
    expect(Number(activeBtn.text)).to.equal(vm.totalPage)
    vm.totalPage = 100
    await vm.$nextTick()
    triggerEvent(lastBtn, 'click')
    await vm.$nextTick()
    activeBtn = pagination.querySelector('.active a')
    expect(Number(activeBtn.text)).to.equal(vm.totalPage)
  })

  it('should be able to go to first page', async () => {
    vm = createVm(
      '<div><pagination v-model="currentPage" :total-page="totalPage" boundary-links/></div>',
      {
        totalPage: 18,
        currentPage: 1,
      }
    )
    vm.currentPage = 18
    await vm.$nextTick()
    const pagination = vm.$el.querySelector('ul.pagination')
    const lastBtn = pagination.querySelector('[aria-label="Last"]')
    triggerEvent(lastBtn, 'click')
    await vm.$nextTick()
    let activeBtn = pagination.querySelector('.active a')
    expect(Number(activeBtn.text)).to.equal(vm.totalPage)
    const firstBtn = pagination.querySelector('[aria-label="First"]')
    triggerEvent(firstBtn, 'click')
    await vm.$nextTick()
    activeBtn = pagination.querySelector('.active a')
    expect(vm.currentPage).to.equal(1)
    expect(Number(activeBtn.text)).to.equal(vm.currentPage)
  })

  it('last group has max size item', async () => {
    vm = createVm(
      '<div><pagination v-model="currentPage" :total-page="totalPage"/></div>',
      {
        totalPage: 18,
        currentPage: 1,
      }
    )
    vm.currentPage = 18
    await vm.$nextTick()
    let pagination = vm.$el.querySelector('ul.pagination')
    let startBtn = $(pagination).find('a:not([aria-label])').get(0)
    expect(Number(startBtn.text)).to.equal(vm.totalPage - 5 + 1)
    vm.currentPage = 12
    await vm.$nextTick()
    pagination = vm.$el.querySelector('ul.pagination')
    startBtn = $(pagination).find('a:not([aria-label])').get(0)
    expect(Number(startBtn.text)).to.equal(vm.currentPage - 5 + 1)
  })

  it('should be go to next group', async () => {
    vm = createVm(
      '<div><pagination v-model="currentPage" :total-page="totalPage"/></div>',
      {
        totalPage: 18,
        currentPage: 1,
      }
    )
    vm.totalPage = 13
    await vm.$nextTick()
    let pagination = vm.$el.querySelector('ul.pagination')
    const nextGroupBtn = pagination.querySelector('[aria-label="Next group"]')
    let startBtn = $(pagination).find('a:not([aria-label])').get(0)
    expect(Number(startBtn.text)).to.equal(1)
    triggerEvent(nextGroupBtn, 'click')
    await vm.$nextTick()
    pagination = vm.$el.querySelector('ul.pagination')
    startBtn = $(pagination).find('a:not([aria-label])').get(0)
    expect(Number(startBtn.text)).to.equal(1 + 5)
    triggerEvent(nextGroupBtn, 'click')
    await vm.$nextTick()
    pagination = vm.$el.querySelector('ul.pagination')
    startBtn = $(pagination).find('a:not([aria-label])').get(0)
    expect(Number(startBtn.text)).to.equal(vm.totalPage - 5 + 1)
  })

  it('should be go to perv group', async () => {
    vm = createVm(
      '<div><pagination v-model="currentPage" :total-page="totalPage"/></div>',
      {
        totalPage: 18,
        currentPage: 1,
      }
    )
    vm.totalPage = 13
    vm.currentPage = 12
    await vm.$nextTick()
    let pagination = vm.$el.querySelector('ul.pagination')
    const prevGroupBtn = pagination.querySelector(
      '[aria-label="Previous group"]'
    )
    let startBtn = $(pagination).find('a:not([aria-label])').get(0)
    expect(Number(startBtn.text)).to.equal(vm.totalPage - 5 + 1)
    triggerEvent(prevGroupBtn, 'click')
    await vm.$nextTick()
    pagination = vm.$el.querySelector('ul.pagination')
    startBtn = $(pagination).find('a:not([aria-label])').get(0)
    expect(Number(startBtn.text)).to.equal(vm.totalPage - 5 * 2 + 1)
    triggerEvent(prevGroupBtn, 'click')
    await vm.$nextTick()
    pagination = vm.$el.querySelector('ul.pagination')
    startBtn = $(pagination).find('a:not([aria-label])').get(0)
    expect(Number(startBtn.text)).to.equal(1)
  })

  it('should be able to disable component', async () => {
    vm = createVm(
      '<div><pagination v-model="currentPage" :total-page="totalPage" disabled/></div>',
      {
        totalPage: 18,
        currentPage: 1,
      }
    )
    await vm.$nextTick()
    const pagination = vm.$el.querySelector('ul.pagination')
    // all btns has disabled class
    expect(pagination.querySelectorAll('li').length).to.equal(
      pagination.querySelectorAll('.disabled').length
    )
    expect(pagination.querySelector('.active > a').textContent).to.equal('1')
    // all btns function are disabled
    const btns = pagination.querySelectorAll('li > a')
    for (let i = 0; i < btns.length; i++) {
      btns[i].click()
      await vm.$nextTick()
      expect(pagination.querySelector('.active > a').textContent).to.equal('1')
    }
  })
})


<|start_filename|>src/components/popover/Popover.js<|end_filename|>
import { TRIGGERS } from '../../utils/dom.utils'
import popupMixin from '../../mixins/popup.mixin'

export default {
  mixins: [popupMixin],
  data() {
    return {
      name: 'popover',
    }
  },
  render(h) {
    return h(this.tag, [
      this.$slots.default,
      h(
        'div',
        {
          style: {
            display: 'block',
          },
          ref: 'popup',
          on: {
            mouseleave: this.hideOnLeave,
          },
        },
        [
          h('div', { class: 'arrow' }),
          h(
            'h3',
            {
              class: 'popover-title',
              directives: [{ name: 'show', value: this.title }],
            },
            this.title
          ),
          h('div', { class: 'popover-content' }, [
            this.content || this.$slots.popover,
          ]),
        ]
      ),
    ])
  },
  props: {
    title: {
      type: String,
      default: '',
    },
    content: {
      type: String,
      default: '',
    },
    trigger: {
      type: String,
      default: TRIGGERS.OUTSIDE_CLICK,
    },
  },
  computed: {
    allContent() {
      return this.title + this.content
    },
  },
  methods: {
    isNotEmpty() {
      return this.title || this.content || this.$slots.popover
    },
  },
}


<|start_filename|>test/specs/Alert.spec.js<|end_filename|>
import $ from 'jquery'
import { createVm, destroyVm, sleep, triggerEvent } from '../utils'

const DEFAULT_ALERT_CLASS = 'alert-info'

describe('Alert', () => {
  let vm

  afterEach(() => {
    destroyVm(vm)
  })

  const getDefaultAlertLength = ($el) => {
    return $el.find(`.${DEFAULT_ALERT_CLASS}`).length
  }

  it('should be able to add alert with no type', () => {
    vm = createVm('<alert>{{ msg }}</alert>', {
      msg: 'This is a alert!',
    })
    const $alert = $(vm.$el)
    expect($alert.hasClass('alert')).to.be.true
    expect($alert.hasClass(DEFAULT_ALERT_CLASS)).to.be.true
  })

  it('should be able to dismiss alerts', async () => {
    vm = createVm(
      `
  <section>
    <alert type="warning" v-if="show" dismissible @dismissed="show = false">
      <b>Warning!</b> Better check yourself, you're not looking too good.
    </alert>
  </section>
    `,
      {
        show: true,
      }
    )
    const _$el = $(vm.$el)
    const $alert = _$el.find('.alert')
    expect($alert.length).to.equal(1)
    const closeBtn = $alert.find('button.close').get(0)
    triggerEvent(closeBtn, 'click')
    await vm.$nextTick()
    expect(_$el.find('.alert').length).to.equal(0)
  })

  it('should be able to add dismissible alerts', async () => {
    vm = createVm(
      `
      <section>
    <alert type="warning" v-if="show" dismissible @dismissed="show = false">
      <b>Warning!</b> Better check yourself, you're not looking too good.
    </alert>
    <alert v-for="(item, index) in alerts" dismissible :key="item.key" @dismissed="alerts.splice(index, 1)">
      <b>Heads up!</b> This alert needs your attention, but it's not super important.
    </alert>
    <hr/>
    <btn type="primary" @click="addDismissibleAlert()">Add Dismissible Alert</btn>
  </section>
    `,
      {
        show: true,
        alerts: [],
      },
      {
        methods: {
          addDismissibleAlert() {
            this.alerts.push({ key: new Date().getTime() })
          },
        },
      }
    )
    const _$el = $(vm.$el)
    const alertInstancesBefore = getDefaultAlertLength(_$el)
    const addAlertBtn = _$el.find('.btn-primary').get(0)
    triggerEvent(addAlertBtn, 'click')
    await vm.$nextTick()
    const alertInstancesAfter = getDefaultAlertLength(_$el)
    expect(alertInstancesAfter).to.equal(alertInstancesBefore + 1)
  })

  it('should be able to add auto dismiss alerts', async () => {
    vm = createVm(
      `
      <section>
    <alert v-for="(item, index) in alerts" :duration="duration" :key="item.key" @dismissed="alerts.splice(index, 1)">
      This alert <b>will dismiss after {{duration}}ms</b>.
    </alert>
    <hr/>
    <btn type="primary" @click="addAutoDismissAlert()">Add Auto Dismiss Alert</btn>
  </section>
    `,
      {
        alerts: [],
        duration: 2000,
      },
      {
        methods: {
          addAutoDismissAlert() {
            this.alerts.push({ key: new Date().getTime() })
          },
        },
      }
    )
    const _$el = $(vm.$el)
    vm.duration = 1000
    await vm.$nextTick()
    const alertInstancesBefore = getDefaultAlertLength(_$el)
    const addAlertBtn = _$el.find('.btn-primary').get(0)
    triggerEvent(addAlertBtn, 'click')
    await vm.$nextTick()
    const alertInstancesAfter = getDefaultAlertLength(_$el)
    expect(alertInstancesAfter).to.equal(alertInstancesBefore + 1)
    await sleep(vm.duration + 200)
    const alertInstancesAfterDelay = getDefaultAlertLength(_$el)
    expect(alertInstancesAfterDelay).to.equal(alertInstancesBefore)
  })
})


<|start_filename|>src/components/modal/Modal.js<|end_filename|>
import Local from '../../mixins/locale.mixin'
import Btn from './../button/Btn'
import {
  EVENTS,
  on,
  off,
  removeFromDom,
  toggleBodyOverflow,
  addClass,
  removeClass,
  getComputedStyle,
  getOpenModals,
  getOpenModalNum,
} from '../../utils/dom.utils'
import { isFunction, isPromiseSupported } from '../../utils/object.utils'

const IN = 'in'

export default {
  mixins: [Local],
  components: { Btn },
  props: {
    value: {
      type: Boolean,
      default: false,
    },
    title: String,
    size: String,
    backdrop: {
      type: Boolean,
      default: true,
    },
    footer: {
      type: Boolean,
      default: true,
    },
    header: {
      type: Boolean,
      default: true,
    },
    cancelText: String,
    cancelType: {
      type: String,
      default: 'default',
    },
    okText: String,
    okType: {
      type: String,
      default: 'primary',
    },
    dismissBtn: {
      type: Boolean,
      default: true,
    },
    transition: {
      type: Number,
      default: 150,
    },
    autoFocus: {
      type: Boolean,
      default: false,
    },
    keyboard: {
      type: Boolean,
      default: true,
    },
    beforeClose: Function,
    zOffset: {
      type: Number,
      default: 20,
    },
    appendToBody: {
      type: Boolean,
      default: false,
    },
    displayStyle: {
      type: String,
      default: 'block',
    },
  },
  data() {
    return {
      msg: '',
    }
  },
  computed: {
    modalSizeClass() {
      return {
        [`modal-${this.size}`]: Boolean(this.size),
      }
    },
  },
  watch: {
    value(v) {
      this.$toggle(v)
    },
  },
  mounted() {
    removeFromDom(this.$refs.backdrop)
    on(window, EVENTS.MOUSE_DOWN, this.suppressBackgroundClose)
    on(window, EVENTS.KEY_UP, this.onKeyPress)
    if (this.value) {
      this.$toggle(true)
    }
  },
  beforeDestroy() {
    clearTimeout(this.timeoutId)
    removeFromDom(this.$refs.backdrop)
    removeFromDom(this.$el)
    if (getOpenModalNum() === 0) {
      toggleBodyOverflow(true)
    }
    off(window, EVENTS.MOUSE_DOWN, this.suppressBackgroundClose)
    off(window, EVENTS.MOUSE_UP, this.unsuppressBackgroundClose)
    off(window, EVENTS.KEY_UP, this.onKeyPress)
  },
  methods: {
    onKeyPress(event) {
      if (this.keyboard && this.value && event.keyCode === 27) {
        const thisModal = this.$refs.backdrop
        let thisZIndex = thisModal.style.zIndex
        thisZIndex =
          thisZIndex && thisZIndex !== 'auto' ? parseInt(thisZIndex) : 0
        // Find out if this modal is the top most one.
        const modals = getOpenModals()
        const modalsLength = modals.length
        for (let i = 0; i < modalsLength; i++) {
          if (modals[i] !== thisModal) {
            let zIndex = modals[i].style.zIndex
            zIndex = zIndex && zIndex !== 'auto' ? parseInt(zIndex) : 0
            // if any existing modal has higher zIndex, ignore
            if (zIndex > thisZIndex) {
              return
            }
          }
        }
        this.toggle(false)
      }
    },
    toggle(show, msg) {
      let shouldClose = true
      if (isFunction(this.beforeClose)) {
        shouldClose = this.beforeClose(msg)
      }

      if (isPromiseSupported()) {
        // Skip the hiding when beforeClose returning falsely value or returned Promise resolves to falsely value
        // Use Promise.resolve to accept both Boolean values and Promises
        Promise.resolve(shouldClose).then((shouldClose) => {
          // Skip the hiding while show===false
          if (!show && shouldClose) {
            this.msg = msg
            this.$emit('input', show)
          }
        })
      } else {
        // Fallback to old version if promise is not supported
        // skip the hiding while show===false & beforeClose returning falsely value
        if (!show && !shouldClose) {
          return
        }

        this.msg = msg
        this.$emit('input', show)
      }
    },
    $toggle(show) {
      const modal = this.$el
      const backdrop = this.$refs.backdrop
      clearTimeout(this.timeoutId)
      if (show) {
        // If two modals share the same v-if condition the calculated z-index is incorrect,
        // resulting in popover misbehaviour.
        // solved by adding a nextTick.
        // https://github.com/uiv-lib/uiv/issues/342
        this.$nextTick(() => {
          const alreadyOpenModalNum = getOpenModalNum()
          document.body.appendChild(backdrop)
          if (this.appendToBody) {
            document.body.appendChild(modal)
          }
          modal.style.display = this.displayStyle
          modal.scrollTop = 0
          backdrop.offsetHeight // force repaint
          toggleBodyOverflow(false)
          addClass(backdrop, IN)
          addClass(modal, IN)
          // fix z-index for nested modals
          // no need to calculate if no modal is already open
          if (alreadyOpenModalNum > 0) {
            const modalBaseZ = parseInt(getComputedStyle(modal).zIndex) || 1050 // 1050 is default modal z-Index
            const backdropBaseZ =
              parseInt(getComputedStyle(backdrop).zIndex) || 1040 // 1040 is default backdrop z-Index
            const offset = alreadyOpenModalNum * this.zOffset
            modal.style.zIndex = `${modalBaseZ + offset}`
            backdrop.style.zIndex = `${backdropBaseZ + offset}`
          }
          // z-index fix end
          this.timeoutId = setTimeout(() => {
            if (this.autoFocus) {
              const btn = this.$el.querySelector('[data-action="auto-focus"]')
              if (btn) {
                btn.focus()
                /* START.TESTS_ONLY */
                /* istanbul ignore next */
                btn.setAttribute('data-focused', 'true')
                /* END.TESTS_ONLY */
              }
            }
            this.$emit('show')
            this.timeoutId = 0
          }, this.transition)
        })
      } else {
        removeClass(backdrop, IN)
        removeClass(modal, IN)
        this.timeoutId = setTimeout(() => {
          modal.style.display = 'none'
          removeFromDom(backdrop)
          if (this.appendToBody) {
            removeFromDom(modal)
          }
          if (getOpenModalNum() === 0) {
            toggleBodyOverflow(true)
          }
          this.$emit('hide', this.msg || 'dismiss')
          this.msg = ''
          this.timeoutId = 0
          // restore z-index for nested modals
          modal.style.zIndex = ''
          backdrop.style.zIndex = ''
          // z-index fix end
        }, this.transition)
      }
    },
    suppressBackgroundClose(event) {
      if (event && event.target === this.$el) {
        return
      }
      this.isCloseSuppressed = true
      on(window, 'mouseup', this.unsuppressBackgroundClose)
    },
    unsuppressBackgroundClose() {
      if (this.isCloseSuppressed) {
        off(window, 'mouseup', this.unsuppressBackgroundClose)
        setTimeout(() => {
          this.isCloseSuppressed = false
        }, 1)
      }
    },
    backdropClicked(event) {
      if (this.backdrop && !this.isCloseSuppressed) {
        this.toggle(false)
      }
    },
  },
}


<|start_filename|>src/components/alert/Alert.js<|end_filename|>
export default {
  props: {
    dismissible: {
      type: Boolean,
      default: false,
    },
    duration: {
      type: Number,
      default: 0,
    },
    type: {
      type: String,
      default: 'info',
    },
  },
  data() {
    return {
      timeout: 0,
    }
  },
  computed: {
    alertClass() {
      return {
        alert: true,
        [`alert-${this.type}`]: Boolean(this.type),
        'alert-dismissible': this.dismissible,
      }
    },
  },
  methods: {
    closeAlert() {
      clearTimeout(this.timeout)
      this.$emit('dismissed')
    },
  },
  mounted() {
    if (this.duration > 0) {
      this.timeout = setTimeout(this.closeAlert, this.duration)
    }
  },
  destroyed() {
    clearTimeout(this.timeout)
  },
}


<|start_filename|>test/specs/locale.spec.js<|end_filename|>
import * as locale from '../../src/locale/index'

describe('locale', () => {
  it('should fetch correct value for given key', () => {
    locale.i18n()
    locale.use()
    expect(locale.t('uiv.datePicker.month1')).to.equal('January')
  })

  it('should fetch empty value for not exist given key', () => {
    expect(locale.t('uiv.datePicker.month13')).not.exist
    expect(locale.t('312.123')).to.equal('')
  })
})


<|start_filename|>src/services/notification/Notification.js<|end_filename|>
import { removeFromDom } from '../../utils/dom.utils'
import { spliceIfExist } from '../../utils/array.utils'
import {
  isFunction,
  isExist,
  isString,
  isPromiseSupported,
  assign,
  hasOwnProperty,
} from '../../utils/object.utils'
import Notification from '../../components/notification/Notification.vue'
import { PLACEMENTS } from '../../constants/notification.constants'
import Vue from 'vue'

const queues = {
  [PLACEMENTS.TOP_LEFT]: [],
  [PLACEMENTS.TOP_RIGHT]: [],
  [PLACEMENTS.BOTTOM_LEFT]: [],
  [PLACEMENTS.BOTTOM_RIGHT]: [],
}

const destroy = (queue, instance) => {
  // console.log('destroyNotification')
  removeFromDom(instance.$el)
  instance.$destroy()
  spliceIfExist(queue, instance)
}

const init = (options, cb, resolve = null, reject = null) => {
  const placement = options.placement
  const queue = queues[placement]
  // check if placement is valid
  if (!isExist(queue)) {
    return
  }
  /* istanbul ignore else */
  // `error` alias of `danger`
  if (options.type === 'error') {
    options.type = 'danger'
  }
  const instance = new Vue({
    extends: Notification,
    propsData: assign({}, { queue, placement }, options, {
      cb(msg) {
        destroy(queue, instance)
        if (isFunction(cb)) {
          cb(msg)
        } else if (resolve && reject) {
          resolve(msg)
        }
      },
    }),
  })
  instance.$mount()
  document.body.appendChild(instance.$el)
  queue.push(instance)
}

// eslint-disable-next-line default-param-last
const _notify = (options = {}, cb) => {
  // simplify usage: pass string as option.content
  if (isString(options)) {
    options = {
      content: options,
    }
  }
  // set default placement as top-right
  if (!isExist(options.placement)) {
    options.placement = PLACEMENTS.TOP_RIGHT
  }
  if (isPromiseSupported()) {
    return new Promise((resolve, reject) => {
      init(options, cb, resolve, reject)
    })
  } else {
    init(options, cb)
  }
}

function _notify2(type, args) {
  if (isString(args)) {
    _notify({
      content: args,
      type,
    })
  } else {
    _notify(
      assign({}, args, {
        type,
      })
    )
  }
}

const notify = Object.defineProperties(_notify, {
  success: {
    configurable: false,
    writable: false,
    value(args) {
      _notify2('success', args)
    },
  },
  info: {
    configurable: false,
    writable: false,
    value(args) {
      _notify2('info', args)
    },
  },
  warning: {
    configurable: false,
    writable: false,
    value(args) {
      _notify2('warning', args)
    },
  },
  danger: {
    configurable: false,
    writable: false,
    value(args) {
      _notify2('danger', args)
    },
  },
  error: {
    configurable: false,
    writable: false,
    value(args) {
      _notify2('danger', args)
    },
  },
  dismissAll: {
    configurable: false,
    writable: false,
    value() {
      for (const key in queues) {
        /* istanbul ignore else */
        if (hasOwnProperty(queues, key)) {
          queues[key].forEach((instance) => {
            instance.onDismissed()
          })
        }
      }
    },
  },
})

export default { notify }


<|start_filename|>src/locale/lang/tr-TR.js<|end_filename|>
export default {
  uiv: {
    datePicker: {
      clear: 'Temizle',
      today: 'Bugün',
      month: 'Ay',
      month1: 'Ocak',
      month2: 'Şubat',
      month3: 'Mart',
      month4: 'Nisan',
      month5: 'Mayıs',
      month6: 'Haziran',
      month7: 'Temmuz',
      month8: 'Ağustos',
      month9: 'Eylül',
      month10: 'Ekim',
      month11: 'Kasım',
      month12: 'Aralık',
      year: 'Yıl',
      week1: 'Pzt',
      week2: 'Sal',
      week3: 'Çar',
      week4: 'Per',
      week5: 'Cum',
      week6: 'Cmt',
      week7: 'Paz',
    },
    timePicker: {
      am: 'Öğleden Önce',
      pm: 'Öğleden Sonra',
    },
    modal: {
      cancel: 'İptal',
      ok: 'Tamam',
    },
  },
}


<|start_filename|>src/locale/lang/pt-BR.js<|end_filename|>
export default {
  uiv: {
    datePicker: {
      clear: 'Limpar',
      today: 'Hoje',
      month: 'Mês',
      month1: 'Janeiro',
      month2: 'Fevereiro',
      month3: 'Março',
      month4: 'Abril',
      month5: 'Maio',
      month6: 'Junho',
      month7: 'Julho',
      month8: 'Agosto',
      month9: 'Setembro',
      month10: 'Outubro',
      month11: 'Novembro',
      month12: 'Dezembro',
      year: 'Ano',
      week1: 'Seg',
      week2: 'Ter',
      week3: 'Qua',
      week4: 'Qui',
      week5: 'Sex',
      week6: 'Sáb',
      week7: 'Dom',
    },
    timePicker: {
      am: 'manhã',
      pm: 'noite',
    },
    modal: {
      cancel: 'Cancelar',
      ok: 'OK',
    },
  },
}


<|start_filename|>rollup/rollup.base.js<|end_filename|>
const fs = require('fs')
const path = require('path')
const vue = require('rollup-plugin-vue')
const resolve = require('@rollup/plugin-node-resolve').nodeResolve
const commonjs = require('@rollup/plugin-commonjs')
const buble = require('@rollup/plugin-buble')
const alias = require('@rollup/plugin-alias')
const strip = require('rollup-plugin-strip-code')

const dist = path.join(__dirname, '..', 'dist')

// Ensure dist directory exists
if (!fs.existsSync(dist)) {
  fs.mkdirSync(dist)
}

module.exports = {
  external: ['vue'],
  plugins: [
    alias({
      entries: [
        { find: /^vue$/, replacement: 'vue/dist/vue.esm.js' }
      ]
    }),
    vue(),
    resolve(),
    commonjs(),
    buble(),
    strip({
      start_comment: 'START.TESTS_ONLY',
      end_comment: 'END.TESTS_ONLY'
    })
  ]
}


<|start_filename|>src/index-browser.js<|end_filename|>
import { install } from './install'

// auto install
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue, window.__uiv_options || {})
}

export * from './index'


<|start_filename|>src/locale/lang/es-ES.js<|end_filename|>
export default {
  uiv: {
    datePicker: {
      clear: 'Limpiar',
      today: 'Hoy',
      month: 'Mes',
      month1: 'Enero',
      month2: 'Febrero',
      month3: 'Marzo',
      month4: 'Abril',
      month5: 'Mayo',
      month6: 'Junio',
      month7: 'Julio',
      month8: 'Agosto',
      month9: 'Septiembre',
      month10: 'Octubre',
      month11: 'Noviembre',
      month12: 'Diciembre',
      year: 'Año',
      week1: 'Lunes',
      week2: 'Martes',
      week3: 'Miercoles',
      week4: 'Jueves',
      week5: 'Viernes',
      week6: 'Sabado',
      week7: 'Domingo',
    },
    timePicker: {
      am: 'AM',
      pm: 'PM',
    },
    modal: {
      cancel: 'Cancelar',
      ok: 'OK',
    },
  },
}


<|start_filename|>src/components/navbar/Navbar.js<|end_filename|>
import Collapse from '../collapse/Collapse.js'

export default {
  components: { Collapse },
  props: {
    value: Boolean,
    fluid: {
      type: Boolean,
      default: true,
    },
    fixedTop: Boolean,
    fixedBottom: Boolean,
    staticTop: Boolean,
    inverse: Boolean,
  },
  data() {
    return {
      show: false,
    }
  },
  computed: {
    navClasses() {
      return {
        navbar: true,
        'navbar-default': !this.inverse,
        'navbar-inverse': this.inverse,
        'navbar-static-top': this.staticTop,
        'navbar-fixed-bottom': this.fixedBottom,
        'navbar-fixed-top': this.fixedTop,
      }
    },
  },
  mounted() {
    this.show = !!this.value
  },
  watch: {
    value(v) {
      this.show = v
    },
  },
  methods: {
    toggle() {
      this.show = !this.show
      this.$emit('input', this.show)
    },
  },
}


<|start_filename|>src/utils/object.utils.js<|end_filename|>
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill
export function assign(target, varArgs) {
  if (target === null || target === undefined) {
    throw new TypeError('Cannot convert undefined or null to object')
  }
  const to = Object(target)
  for (let index = 1; index < arguments.length; index++) {
    const nextSource = arguments[index]
    if (nextSource !== null && nextSource !== undefined) {
      for (const nextKey in nextSource) {
        // Avoid bugs when hasOwnProperty is shadowed
        /* istanbul ignore else */
        if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
          to[nextKey] = nextSource[nextKey]
        }
      }
    }
  }
  return to
}

export function isExist(obj) {
  return typeof obj !== 'undefined' && obj !== null
}

export function isFunction(obj) {
  return typeof obj === 'function'
}

export function isNumber(obj) {
  return typeof obj === 'number'
}

export function isString(obj) {
  return typeof obj === 'string'
}

export function isBoolean(obj) {
  return typeof obj === 'boolean'
}

export function isPromiseSupported() {
  return typeof window !== 'undefined' && isExist(window.Promise)
}

export function hasOwnProperty(o, k) {
  return Object.prototype.hasOwnProperty.call(o, k)
}


<|start_filename|>rollup/rollup.test.js<|end_filename|>
const replace = require('@rollup/plugin-replace')
const buble = require('@rollup/plugin-buble')
const vue = require('rollup-plugin-vue')
const resolve = require('@rollup/plugin-node-resolve').nodeResolve
const commonjs = require('@rollup/plugin-commonjs')
const json = require('@rollup/plugin-json')
const alias = require('@rollup/plugin-alias')
const istanbul = require('rollup-plugin-istanbul')
const path = require('path')

const { name } = require('../package.json')

module.exports = {
  plugins: [
    replace({
      'process.env.NODE_ENV': JSON.stringify('test')
    }),
    alias({
      entries: [
        { find: /^vue$/, replacement: 'vue/dist/vue.esm.js' }
      ]
    }),
    json(),
    vue(),
    resolve(),
    commonjs(),
    buble({
      objectAssign: 'Object.assign',
      transforms: {
        asyncAwait: false
      }
    }),
    istanbul({
      exclude: [
        'test/**/*.js',
        'test/**/*.json',
        'node_modules/**/*.js',
        'node_modules/**/*.mjs',
        'src/**/*.vue'
      ]
    })
  ],
  output: {
    format: 'iife', // Helps prevent naming collisions.
    name: name, // Required for 'iife' format.
    sourcemap: 'inline' // Sensible for testing.
  }
  // input: 'test/index.js',
  // output: {
  //   format: 'es',
  //   file: path.join(__dirname, '..', 'dist', `${name}.test.js`),
  //   sourcemap: true
  // }
}


<|start_filename|>test/specs/Notification.spec.js<|end_filename|>
import $ from 'jquery'
import { createVm, destroyVm, sleep, transition } from '../utils'
import Notification from '../../src/services/notification/Notification'

const OFFSET = '15px'

function baseVm() {
  return createVm(
    `<section>
    <btn @click="notify" type="primary">Simplest Notification</btn>
    <btn @click="notify2" type="primary">No Auto-dismiss Notification</btn>
  </section>`,
    {},
    {
      methods: {
        // example with callback
        // pass a String as the notification content
        notify() {
          this.$notify('This is a simple notify msg.', () => {
            // callback after dismissed
            console.log('dismissed')
          })
        },
        // example with Promise and options
        notify2() {
          this.$notify({
            title: 'Title',
            content: 'This notification will not dismiss automatically.',
            duration: 0,
          }).then(() => {
            // resolve after dismissed
            console.log('dismissed')
          })
        },
      },
    }
  )
}

function typesVm() {
  return createVm(
    `<section>
    <btn @click="info" type="info">Info</btn>
    <btn @click="success" type="success">Success</btn>
    <btn @click="warning" type="warning">Warning</btn>
    <btn @click="danger" type="danger">Danger</btn>
  </section>`,
    {},
    {
      methods: {
        info() {
          this.$notify({
            type: 'info',
            title: 'Heads up!',
            content:
              "This alert needs your attention, but it's not super important.",
          })
        },
        success() {
          this.$notify({
            type: 'success',
            title: 'Well done!',
            content: 'You successfully read this important alert message.',
          })
        },
        warning() {
          // simple warning with content only
          this.$notify.warning(
            "Better check yourself, you're not looking too good."
          )
        },
        danger() {
          // error msg with title and content (other options available too)
          // `error` is an alias of `danger` (both of them works)
          this.$notify.error({
            title: 'Oh snap!',
            content: 'Change a few things up and try submitting again.',
          })
        },
      },
    }
  )
}

function placementVm() {
  return createVm(
    `<section>
    <btn @click="notify('top-right')" type="primary">Top Right (Default)</btn>
    <btn @click="notify('bottom-right')" type="primary">Bottom Right</btn>
    <btn @click="notify('bottom-left')" type="primary">Bottom Left</btn>
    <btn @click="notify('top-left')" type="primary">Top Left</btn>
  </section>`,
    {},
    {
      methods: {
        notify(placement) {
          this.$notify({
            placement, // equal to `placement: placement` in ES6
            title: 'Title',
            content: `This is a notify msg at ${placement}.`,
          })
        },
      },
    }
  )
}

describe('Notification', () => {
  let vm
  let spy
  let savedLog

  beforeEach(() => {
    savedLog = console.log
    console.log = function () {
      return true
    }

    spy = sinon.spy(console, 'log')
  })

  afterEach(() => {
    console.log.restore()
    console.log = savedLog
    destroyVm(vm)
    $('.alert').remove()
  })

  it('should be able to use notification', async () => {
    vm = baseVm()
    await vm.$nextTick()
    const trigger = vm.$el.querySelectorAll('.btn')[0]
    trigger.click()
    await sleep(transition)
    await vm.$nextTick()
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    expect(alert.className).to.contain('alert-info')
    expect(alert.className).to.contain('alert-dismissible')
    expect(alert.className).to.contain('fade')
    expect(alert.className).to.contain('in')
    expect(alert.querySelector('.media-heading')).not.exist
    expect(alert.querySelector('.media-body > div').textContent).to.equal(
      'This is a simple notify msg.'
    )
    alert.querySelector('button.close').click()
    await sleep(transition)
    await vm.$nextTick()
    expect(document.querySelector('.alert')).not.exist
    sinon.assert.calledWith(spy, 'dismissed')
  })

  it('should be able to use no auto-dismiss notification', async () => {
    vm = baseVm()
    await vm.$nextTick()
    const trigger = vm.$el.querySelectorAll('.btn')[1]
    trigger.click()
    await sleep(transition)
    await vm.$nextTick()
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    expect(alert.className).to.contain('alert-info')
    expect(alert.className).to.contain('alert-dismissible')
    expect(alert.className).to.contain('fade')
    expect(alert.className).to.contain('in')
    expect(alert.querySelector('.media-heading').textContent).to.equal('Title')
    expect(alert.querySelectorAll('.media-body > div')[1].textContent).to.equal(
      'This notification will not dismiss automatically.'
    )
    await sleep(5000 + 1000)
    expect(document.querySelector('.alert')).to.exist
    alert.querySelector('button.close').click()
    await sleep(transition)
    await vm.$nextTick()
    expect(document.querySelector('.alert')).not.exist
    sinon.assert.calledWith(spy, 'dismissed')
  }).timeout(5000 + 3000)

  it('should be able to use `type=info` notification', async () => {
    vm = typesVm()
    await vm.$nextTick()
    const trigger = vm.$el.querySelectorAll('.btn')[0]
    trigger.click()
    await sleep(transition)
    await vm.$nextTick()
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    expect(alert.className).to.contain('alert-info')
    expect(alert.querySelectorAll('.media-left > .glyphicon').length).to.equal(
      1
    )
    expect(alert.querySelectorAll('.media-left > .glyphicon-info-sign')).to
      .exist
    alert.querySelector('button.close').click()
    await sleep(transition)
    await vm.$nextTick()
    expect(document.querySelector('.alert')).not.exist
  })

  it('should be able to use `type=success` notification', async () => {
    vm = typesVm()
    await vm.$nextTick()
    const trigger = vm.$el.querySelectorAll('.btn')[1]
    trigger.click()
    await sleep(transition)
    await vm.$nextTick()
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    expect(alert.className).to.contain('alert-success')
    expect(alert.querySelectorAll('.media-left > .glyphicon').length).to.equal(
      1
    )
    expect(alert.querySelectorAll('.media-left > .glyphicon-ok-sign')).to.exist
    alert.querySelector('button.close').click()
    await sleep(transition)
    await vm.$nextTick()
    expect(document.querySelector('.alert')).not.exist
  })

  it('should be able to use `type=warning` notification', async () => {
    vm = typesVm()
    await vm.$nextTick()
    const trigger = vm.$el.querySelectorAll('.btn')[2]
    trigger.click()
    await sleep(transition)
    await vm.$nextTick()
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    expect(alert.className).to.contain('alert-warning')
    expect(alert.querySelectorAll('.media-left > .glyphicon').length).to.equal(
      1
    )
    expect(alert.querySelectorAll('.media-left > .glyphicon-info-sign')).to
      .exist
    alert.querySelector('button.close').click()
    await sleep(transition)
    await vm.$nextTick()
    expect(document.querySelector('.alert')).not.exist
  })

  it('should be able to use `type=danger` notification', async () => {
    vm = typesVm()
    await vm.$nextTick()
    const trigger = vm.$el.querySelectorAll('.btn')[3]
    trigger.click()
    await sleep(transition)
    await vm.$nextTick()
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    expect(alert.className).to.contain('alert-danger')
    expect(alert.querySelectorAll('.media-left > .glyphicon').length).to.equal(
      1
    )
    expect(alert.querySelectorAll('.media-left > .glyphicon-remove-sign')).to
      .exist
    alert.querySelector('button.close').click()
    await sleep(transition)
    await vm.$nextTick()
    expect(document.querySelector('.alert')).not.exist
  })

  it('should be able to use `placement=top-right` notification', async () => {
    vm = placementVm()
    await vm.$nextTick()
    const trigger = vm.$el.querySelectorAll('.btn')[0]
    trigger.click()
    await sleep(transition)
    await vm.$nextTick()
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    expect(alert.style.top).to.equal(OFFSET)
    expect(alert.style.right).to.equal(OFFSET)
    expect(alert.style.bottom).to.equal('')
    expect(alert.style.left).to.equal('')
    alert.querySelector('button.close').click()
    await sleep(transition)
    await vm.$nextTick()
    expect(document.querySelector('.alert')).not.exist
  })

  it('should be able to use `placement=bottom-right` notification', async () => {
    vm = placementVm()
    await vm.$nextTick()
    const trigger = vm.$el.querySelectorAll('.btn')[1]
    trigger.click()
    await sleep(transition)
    await vm.$nextTick()
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    expect(alert.style.top).to.equal('')
    expect(alert.style.right).to.equal(OFFSET)
    expect(alert.style.bottom).to.equal(OFFSET)
    expect(alert.style.left).to.equal('')
    alert.querySelector('button.close').click()
    await sleep(transition)
    await vm.$nextTick()
    expect(document.querySelector('.alert')).not.exist
  })

  it('should be able to use `placement=bottom-left` notification', async () => {
    vm = placementVm()
    await vm.$nextTick()
    const trigger = vm.$el.querySelectorAll('.btn')[2]
    trigger.click()
    await sleep(transition)
    await vm.$nextTick()
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    expect(alert.style.top).to.equal('')
    expect(alert.style.right).to.equal('')
    expect(alert.style.bottom).to.equal(OFFSET)
    expect(alert.style.left).to.equal(OFFSET)
    alert.querySelector('button.close').click()
    await sleep(transition)
    await vm.$nextTick()
    expect(document.querySelector('.alert')).not.exist
  })

  it('should be able to use `placement=top-left` notification', async () => {
    vm = placementVm()
    await vm.$nextTick()
    const trigger = vm.$el.querySelectorAll('.btn')[3]
    trigger.click()
    await sleep(transition)
    await vm.$nextTick()
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    expect(alert.style.top).to.equal(OFFSET)
    expect(alert.style.right).to.equal('')
    expect(alert.style.bottom).to.equal('')
    expect(alert.style.left).to.equal(OFFSET)
    alert.querySelector('button.close').click()
    await sleep(transition)
    await vm.$nextTick()
    expect(document.querySelector('.alert')).not.exist
  })

  it('should be able to use `dismissible=false` notification', async () => {
    vm = createVm(
      '<div><btn @click="notify" type="primary">Notification Without Dismiss Button</btn></div>',
      {},
      {
        methods: {
          notify() {
            this.$notify({
              title: 'Title',
              content: 'This is a notification without dismiss btn.',
              dismissible: false,
            })
          },
        },
      }
    )
    await vm.$nextTick()
    const trigger = vm.$el.querySelectorAll('.btn')[0]
    trigger.click()
    await vm.$nextTick()
    trigger.click()
    await sleep(transition)
    await vm.$nextTick()
    const alert = document.querySelectorAll('.alert')
    expect(alert.length).to.equal(2)
    expect(alert[0].querySelector('button.close')).not.exist
    expect(alert[1].querySelector('button.close')).not.exist
    await sleep(5000 + 1000)
    await vm.$nextTick()
    expect(document.querySelector('.alert')).not.exist
  }).timeout(5000 + 3000)

  it('should be able to use without options and callback', async () => {
    Notification.notify(undefined)
    await sleep(transition)
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    alert.querySelector('button.close').click()
    await sleep(transition)
    expect(document.querySelector('.alert')).not.exist
  })

  it('should be able to use without Promise', async () => {
    // mute Promise
    const savedPromise = window.Promise
    window.Promise = null
    // alert
    Notification.notify({ title: 'test' })
    // restore Promise
    window.Promise = savedPromise
    await sleep(transition)
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    alert.querySelector('button.close').click()
    await sleep(transition)
    expect(document.querySelector('.alert')).not.exist
  })

  it('should be able to avoid invalid placement', async () => {
    Notification.notify({ placement: 'top-bottom' }) // invalid
    await sleep(transition)
    expect(document.querySelector('.alert')).not.exist
  })

  it('should be able to use custom icon', async () => {
    Notification.notify({ title: 'test', icon: 'fa fa-check' })
    await sleep(transition)
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    expect(alert.querySelectorAll('.media-left > .fa').length).to.equal(1)
    expect(alert.querySelectorAll('.media-left > .fa-check')).to.exist
    alert.querySelector('button.close').click()
    await sleep(transition)
    expect(document.querySelector('.alert')).not.exist
  })

  it('should be able to disable icon with types', async () => {
    Notification.notify({ title: 'test', icon: '', type: 'danger' })
    await sleep(transition)
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    expect(alert.querySelector('.media-left')).not.exist
    alert.querySelector('button.close').click()
    await sleep(transition)
    expect(document.querySelector('.alert')).not.exist
  })

  it('should be able to work with `custom-class`', async () => {
    Notification.notify({
      title: 'test',
      type: 'danger',
      customClass: 'test-class',
    })
    await sleep(transition)
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    expect(alert.className).to.contain('test-class')
    expect(alert.className).to.contain('alert-danger')
    alert.querySelector('button.close').click()
    await sleep(transition)
    expect(document.querySelector('.alert')).not.exist
  })

  it('should not be able to use HTML content if html=false', async () => {
    Notification.notify({
      title: 'test',
      content: '<a href="#" id="test-a">test</a>',
    })
    await sleep(transition)
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    expect(alert.querySelector('#test-a')).not.exist
    alert.querySelector('button.close').click()
    await sleep(transition)
    expect(document.querySelector('.alert')).not.exist
  })

  it('should be able to use HTML content', async () => {
    Notification.notify({
      title: 'test',
      html: true,
      content: '<a href="#" id="test-a">test</a>',
    })
    await sleep(transition)
    const alert = document.querySelector('.alert')
    expect(alert).to.exist
    expect(alert.querySelector('#test-a')).to.exist
    alert.querySelector('button.close').click()
    await sleep(transition)
    expect(document.querySelector('.alert')).not.exist
  })
})


<|start_filename|>src/components/carousel/Carousel.js<|end_filename|>
import { isExist } from '../../utils/object.utils'

export default {
  props: {
    value: Number,
    indicators: {
      type: Boolean,
      default: true,
    },
    controls: {
      type: Boolean,
      default: true,
    },
    interval: {
      type: Number,
      default: 5000,
    },
    iconControlLeft: {
      type: String,
      default: 'glyphicon glyphicon-chevron-left',
    },
    iconControlRight: {
      type: String,
      default: 'glyphicon glyphicon-chevron-right',
    },
  },
  data() {
    return {
      slides: [],
      activeIndex: 0, // Make v-model not required
      timeoutId: 0,
      intervalId: 0,
    }
  },
  watch: {
    interval() {
      this.startInterval()
    },
    value(index, oldValue) {
      this.run(index, oldValue)
      this.activeIndex = index
    },
  },
  mounted() {
    if (isExist(this.value)) {
      this.activeIndex = this.value
    }
    if (this.slides.length > 0) {
      this.$select(this.activeIndex)
    }
    this.startInterval()
  },
  beforeDestroy() {
    this.stopInterval()
  },
  methods: {
    run(newIndex, oldIndex) {
      const currentActiveIndex = oldIndex || 0
      let direction
      if (newIndex > currentActiveIndex) {
        direction = ['next', 'left']
      } else {
        direction = ['prev', 'right']
      }
      this.slides[newIndex].slideClass[direction[0]] = true
      this.$nextTick(() => {
        this.slides[newIndex].$el.offsetHeight
        this.slides.forEach((slide, i) => {
          if (i === currentActiveIndex) {
            slide.slideClass.active = true
            slide.slideClass[direction[1]] = true
          } else if (i === newIndex) {
            slide.slideClass[direction[1]] = true
          }
        })
        this.timeoutId = setTimeout(() => {
          this.$select(newIndex)
          this.$emit('change', newIndex)
          this.timeoutId = 0
        }, 600)
      })
    },
    startInterval() {
      this.stopInterval()
      if (this.interval > 0) {
        this.intervalId = setInterval(() => {
          this.next()
        }, this.interval)
      }
    },
    stopInterval() {
      clearInterval(this.intervalId)
      this.intervalId = 0
    },
    resetAllSlideClass() {
      this.slides.forEach((slide) => {
        slide.slideClass.active = false
        slide.slideClass.left = false
        slide.slideClass.right = false
        slide.slideClass.next = false
        slide.slideClass.prev = false
      })
    },
    $select(index) {
      this.resetAllSlideClass()
      this.slides[index].slideClass.active = true
    },
    select(index) {
      if (this.timeoutId !== 0 || index === this.activeIndex) {
        return
      }
      if (isExist(this.value)) {
        this.$emit('input', index)
      } else {
        this.run(index, this.activeIndex)
        this.activeIndex = index
      }
    },
    prev() {
      this.select(
        this.activeIndex === 0 ? this.slides.length - 1 : this.activeIndex - 1
      )
    },
    next() {
      this.select(
        this.activeIndex === this.slides.length - 1 ? 0 : this.activeIndex + 1
      )
    },
  },
}


<|start_filename|>test/specs/Popover.spec.js<|end_filename|>
import $ from 'jquery'
import { createVm, destroyVm, triggerEvent, sleep } from '../utils'

describe('Popover', () => {
  let vm

  beforeEach(() => {
    $('body')
      .css('display', 'flex')
      .css('justify-content', 'center')
      .css('align-items', 'center')
      .css('padding-top', '200px')
  })

  afterEach(() => {
    destroyVm(vm)
    $('.popover').remove()
    $('body').attr('style', '')
  })

  it('should be ok to render if no trigger present', async () => {
    vm = createVm(
      '<popover v-model="show" title="123"><button data-role="trigger"></button></popover>',
      {
        show: true,
      }
    )
    await vm.$nextTick()
  })

  it('should clear all timeouts before destroy', async () => {
    vm = createVm(
      '<popover ref="popover" v-model="show" title="123"></popover>',
      { show: false }
    )
    vm.$refs.popover.hideTimeoutId = 1
    vm.$refs.popover.showTimeoutId = 2
    vm.$refs.popover.transitionTimeoutId = 3
    vm.$refs.popover.autoTimeoutId = 4
    await vm.$nextTick()
    vm.$refs.popover.$destroy()
    await vm.$nextTick()
    expect(vm.$refs.popover.hideTimeoutId).to.equal(0)
    expect(vm.$refs.popover.showTimeoutId).to.equal(0)
    expect(vm.$refs.popover.transitionTimeoutId).to.equal(0)
    expect(vm.$refs.popover.autoTimeoutId).to.equal(0)
  })

  it('should be able to show popover on init', async () => {
    vm = createVm(
      '<popover v-model="show" title="123"><button data-role="trigger"></button></popover>',
      {
        show: true,
      }
    )
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
  })

  it('should hide popover while enable set to false', async () => {
    vm = createVm(
      '<popover v-model="show" title="123" :enable="enable"><button data-role="trigger"></button></popover>',
      {
        show: true,
        enable: true,
      }
    )
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    vm.enable = false
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should hide popover while all content become empty', async () => {
    vm = createVm(
      '<popover v-model="show" :title="title" :content="content"><button data-role="trigger"></button></popover>',
      {
        show: true,
        content: '123',
        title: '321',
      }
    )
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    vm.content = ''
    vm.title = ''
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to use custom string append-to', async () => {
    vm = createVm(
      `<div id="test">
<popover append-to="#test" v-model="show" title="123">
<button data-role="trigger"></button>
</popover>
</div>`,
      {
        show: true,
      }
    )
    await sleep(300)
    expect(document.querySelectorAll('#test .popover').length).to.equal(1)
  })

  it('should be able to use custom element append-to', async () => {
    vm = createVm(
      `<div id="test" ref="el">
<popover v-if="appendTo" :append-to="appendTo" v-model="show" title="123">
<button data-role="trigger"></button>
</popover>
</div>`,
      {
        show: true,
        appendTo: null,
      }
    )
    await vm.$nextTick()
    vm.appendTo = vm.$refs.el
    await vm.$nextTick()
    await sleep(300)
    expect(document.querySelectorAll('#test .popover').length).to.equal(1)
  })

  it('should be able to use custom component append-to', async () => {
    vm = createVm(
      `<div id="test" ref="el">
<popover v-if="appendTo" :append-to="appendTo" v-model="show" title="123">
<button data-role="trigger"></button>
</popover>
</div>`,
      {
        show: true,
        appendTo: null,
      }
    )
    await vm.$nextTick()
    vm.appendTo = vm
    await vm.$nextTick()
    await sleep(300)
    expect(document.querySelectorAll('#test .popover').length).to.equal(1)
  })

  it('should be able to use popover directive', async () => {
    vm = createVm('<btn v-popover="msg"></btn>', {
      msg: { title: 'title', content: 'content' },
    })
    await vm.$nextTick()
    const trigger = vm.$el
    triggerEvent(trigger, 'click')
    await sleep(300)
    let popover = document.querySelector('.popover')
    expect(popover).to.exist
    expect(popover.querySelector('.popover-title').innerText).to.equal('title')
    expect(popover.querySelector('.popover-content').innerText).to.equal(
      'content'
    )
    triggerEvent(trigger, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    // this should work
    vm.msg = { title: 'title2', content: 'content2' }
    await vm.$nextTick()
    await vm.$nextTick()
    triggerEvent(trigger, 'click')
    await sleep(300)
    popover = document.querySelector('.popover')
    expect(popover).to.exist
    expect(popover.querySelector('.popover-title').innerText).to.equal('title2')
    expect(popover.querySelector('.popover-content').innerText).to.equal(
      'content2'
    )
    triggerEvent(trigger, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    // this should not work
    vm.$set(vm.msg, 'title', 'title3')
    await vm.$nextTick()
    triggerEvent(trigger, 'click')
    await sleep(300)
    popover = document.querySelector('.popover')
    expect(popover).to.exist
    expect(popover.querySelector('.popover-title').innerText).to.equal('title2')
    expect(popover.querySelector('.popover-content').innerText).to.equal(
      'content2'
    )
  })

  it('directive with invalid modifiers should be ok', async () => {
    // invalid modifier should be ok
    vm = createVm('<btn v-popover.test1.test2="msg"></btn>', {
      msg: { title: 'title', content: 'content' },
    })
    await vm.$nextTick()
    const trigger = vm.$el
    triggerEvent(trigger, 'click')
    await sleep(300)
    const popover = document.querySelector('.popover')
    expect(popover).to.exist
    expect(popover.querySelector('.popover-title').innerText).to.equal('title')
    expect(popover.querySelector('.popover-content').innerText).to.equal(
      'content'
    )
    triggerEvent(trigger, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should not show popover with no title and content', async () => {
    vm = createVm(
      '<popover v-model="show"><button data-role="trigger"></button></popover>',
      {
        show: true,
      }
    )
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to use custom target', async () => {
    vm = createVm(
      '<div><button ref="btn" type="button">btn</button><popover :target="btn" trigger="focus" title="123"></popover></div>',
      { btn: null },
      {
        mounted() {
          this.btn = this.$refs.btn
        },
      }
    )
    await vm.$nextTick()
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    triggerEvent(vm.btn, 'focus')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
  })

  it('should be able to show popover on click', async () => {
    vm = createVm(`
<div>
  <btn type="primary" id="btn">Popover</btn>
  <popover title="Title" target="#btn">
    <template slot="popover">
      <h1>Hello world!</h1>
    </template>
  </popover>
</div>
    `)
    await vm.$nextTick()
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    triggerEvent(vm.$el.querySelector('button'), 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    triggerEvent(vm.$el.querySelector('button'), 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to change trigger to manual', async () => {
    vm = createVm(
      `
  <section>
    <popover title="Title" trigger="manual" v-model="show">
      <btn>You Can't Trigger Popover Here...</btn>
      <template slot="popover">
        <p>Popover content</p>
      </template>
    </popover>
    <hr/>
    <btn type="primary" @click="show = !show">Toggle Popover</btn>
  </section>
    `,
      { show: false }
    )
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    vm.show = true
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    vm.show = false
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able change trigger to hover-focus', async () => {
    vm = createVm(
      '<btn v-popover.hover-focus="{title:\'Title\', content:\'Popover content\'}" type="primary">Hover-Focus</btn>'
    )
    await vm.$nextTick()
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    // matches don't work in here
    const savedMatches = Element.prototype.matches
    Element.prototype.matches = () => true
    const trigger = vm.$el
    triggerEvent(trigger, 'focus')
    await sleep(300)
    Element.prototype.matches = savedMatches
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    triggerEvent(trigger, 'blur')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to change trigger to click', async () => {
    vm = createVm(
      '<btn v-popover.click="{title:\'Title\', content:\'Popover content\'}" type="primary">Click</btn>'
    )
    await vm.$nextTick()
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    const trigger = vm.$el
    triggerEvent(trigger, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    triggerEvent(trigger, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to change trigger to hover', async () => {
    vm = createVm(
      '<btn v-popover.hover="{title:\'Title\', content:\'Popover content\'}" type="primary">Hover</btn>'
    )
    await vm.$nextTick()
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    const trigger = vm.$el
    triggerEvent(trigger, 'mouseenter')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    triggerEvent(trigger, 'mouseleave')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to toggle correctly on fast click', async () => {
    vm = createVm(
      '<btn v-popover.click="{title:\'Title\', content:\'Popover content\'}" type="primary">Click</btn>'
    )
    const button = vm.$el
    await vm.$nextTick()
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    triggerEvent(button, 'click')
    triggerEvent(button, 'click')
    triggerEvent(button, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    triggerEvent(button, 'click')
    triggerEvent(button, 'click')
    triggerEvent(button, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to change trigger to outside-click', async () => {
    vm = createVm(
      '<btn v-popover="{title:\'Title\', content:\'Popover content\'}" type="primary">Outside-Click (Default)</btn>'
    )
    const button = vm.$el
    await vm.$nextTick()
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    triggerEvent(button, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    document.body.click()
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to disable', async () => {
    vm = createVm(`
<popover title="Title" :enable="false">
  <btn type="primary">Disabled Popover</btn>
  <template slot="popover">
    <h1>Hello world!</h1>
  </template>
</popover>
    `)
    await vm.$nextTick()
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    triggerEvent(vm.$el.querySelector('button'), 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to hide title', async () => {
    vm = createVm(
      '<btn v-popover="{content:\'Popover without a title\'}" type="primary">Popover</btn>'
    )
    await vm.$nextTick()
    const trigger = vm.$el
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    triggerEvent(trigger, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    expect(
      document.querySelector('.popover .popover-title').style.display
    ).to.equal('none')
    triggerEvent(trigger, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to change placement to top', async () => {
    vm = createVm(
      '<btn v-popover.top="{title:\'Title\', content:\'Popover on top\'}" type="primary">Top</btn>'
    )
    await vm.$nextTick()
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    const trigger = vm.$el
    triggerEvent(trigger, 'click')
    await sleep(300)
    const popover = document.querySelector('.popover')
    expect(popover).to.exist
    expect(popover.className).to.contain('top')
    triggerEvent(trigger, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to change placement to bottom', async () => {
    vm = createVm(
      '<btn v-popover.bottom="{title:\'Title\', content:\'Popover on bottom\'}" type="primary">Bottom</btn>'
    )
    await vm.$nextTick()
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    const trigger = vm.$el
    triggerEvent(trigger, 'click')
    await sleep(300)
    const popover = document.querySelector('.popover')
    expect(popover).to.exist
    expect(popover.className).to.contain('bottom')
    triggerEvent(trigger, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to change placement to left', async () => {
    vm = createVm(
      '<btn v-popover.left="{title:\'Title\', content:\'Popover on left\'}" type="primary">Left</btn>'
    )
    await vm.$nextTick()
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    const trigger = vm.$el
    triggerEvent(trigger, 'click')
    await sleep(300)
    const popover = document.querySelector('.popover')
    expect(popover).to.exist
    expect(popover.className).to.contain('left')
    triggerEvent(trigger, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to change placement to right', async () => {
    vm = createVm(
      '<btn v-popover.right="{title:\'Title\', content:\'Popover on right\'}" type="primary">Right</btn>'
    )
    await vm.$nextTick()
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    const trigger = vm.$el
    triggerEvent(trigger, 'click')
    await sleep(300)
    const popover = document.querySelector('.popover')
    expect(popover).to.exist
    expect(popover.className).to.contain('right')
    triggerEvent(trigger, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to change trigger in runtime', async () => {
    vm = createVm(
      '<popover title="123" :trigger="trigger"><button data-role="trigger"></button></popover>',
      {
        trigger: 'focus',
      }
    )
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    await vm.$nextTick()
    const trigger = vm.$el.querySelector('button')
    triggerEvent(trigger, 'focus')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    vm.trigger = 'click'
    await vm.$nextTick()
    triggerEvent(trigger, 'blur')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    triggerEvent(trigger, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to change content in runtime', async () => {
    vm = createVm(
      '<popover :content="msg" trigger="click"><btn>123</btn></popover>',
      {
        msg: 'text',
      }
    )
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    await vm.$nextTick()
    vm.msg = 'text2'
    await vm.$nextTick()
    await vm.$nextTick()
    const trigger = vm.$el.querySelector('button')
    triggerEvent(trigger, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    expect(document.querySelector('.popover-content').innerText).to.equal(
      'text2'
    )
    const topBefore = document.querySelector('.popover').style.top
    vm.msg = `
This is a very very long text. This is a very very long text. This is a very very long text
This is a very very long text. This is a very very long text. This is a very very long text
This is a very very long text. This is a very very long text. This is a very very long text
This is a very very long text. This is a very very long text. This is a very very long text
This is a very very long text. This is a very very long text. This is a very very long text
    `
    await vm.$nextTick()
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    expect(document.querySelector('.popover-content').innerText).to.contain(
      'This is a very very long text'
    )
    await vm.$nextTick()
    const topAfter = document.querySelector('.popover').style.top
    expect(topAfter).not.equal(topBefore)
    triggerEvent(trigger, 'click')
    await sleep(300)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to show/hide on specified delay', async function () {
    vm = createVm(
      '<popover :showDelay="300" :hideDelay="400" trigger="hover" title="123"><button></button></popover>'
    )
    await vm.$nextTick()
    const trigger = vm.$el.querySelector('button')
    triggerEvent(trigger, 'mouseenter')
    await sleep(150)
    // not shown yet
    expect(document.querySelectorAll('.popover').length).to.equal(0)
    await sleep(150)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    triggerEvent(trigger, 'mouseleave')
    await sleep(300)
    // not hidden yet
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    await sleep(400)
    expect(document.querySelectorAll('.popover').length).to.equal(0)
  })

  it('should be able to show even when hideDelay < showDelay < transition ', async function () {
    vm = createVm(
      '<popover :hideDelay="1" :showDelay="100" :transition="500" trigger="hover" title="123"><button></button></popover>'
    )
    await vm.$nextTick()
    const trigger = vm.$el.querySelector('button')
    triggerEvent(trigger, 'mouseenter')
    await sleep(200)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
    triggerEvent(trigger, 'mouseleave')
    await sleep(200)
    triggerEvent(trigger, 'mouseenter')
    await sleep(600)
    expect(document.querySelectorAll('.popover').length).to.equal(1)
  })
})


<|start_filename|>test/index.js<|end_filename|>
import './setup'
import './specs/object.utils.spec'
import './specs/array.utils.spec'
import './specs/dom.utils.spec'
import './specs/http.utils.spec'
import './specs/date.utils.spec'
import './specs/install.spec'
import './specs/locale.spec'
import './specs/Alert.spec'
import './specs/Affix.spec'
import './specs/Breadcrumbs.spec'
import './specs/Btn.spec'
import './specs/BtnGroup.spec'
import './specs/Carousel.spec'
import './specs/Collapse.spec'
import './specs/DatePicker.spec'
import './specs/Dropdown.spec'
import './specs/MessageBox.spec'
import './specs/Modal.spec'
import './specs/MultiSelect.spec'
import './specs/Navbar.spec'
import './specs/Notification.spec'
import './specs/Pagination.spec'
import './specs/Popover.spec'
import './specs/ProgressBar.spec'
import './specs/scroll.spec'
import './specs/ScrollSpy.spec'
import './specs/Tabs.spec'
import './specs/TimePicker.spec'
import './specs/Tooltip.spec'
import './specs/Typeahead.spec'


<|start_filename|>test/specs/scroll.spec.js<|end_filename|>
import scroll from '../../src/directives/scroll'
import { createVm, destroyVm } from '../utils'

const HANDLER = '_uiv_scroll_handler'

describe('scroll directive', () => {
  let vm

  afterEach(() => {
    destroyVm(vm)
  })

  it('should be able to bind scroll function', async () => {
    vm = createVm(
      '<div v-scroll="onScroll"></div>',
      {
        msg: 'hello',
      },
      {
        directives: { scroll },
        methods: {
          onScroll() {
            // TODO
          },
        },
      }
    )
    await vm.$nextTick()
    expect(vm.$el[HANDLER]).to.equal(vm.onScroll)
  })

  it('should not bind non-function value', async () => {
    vm = createVm(
      '<div v-scroll="msg"></div>',
      {
        msg: 'hello',
      },
      {
        directives: { scroll },
      }
    )
    await vm.$nextTick()
    expect(vm.$el[HANDLER]).not.exist
  })

  it('should be able to get with fail callback', async () => {
    vm = createVm(
      '<div v-scroll="msg"></div>',
      {
        msg: 'hello',
      },
      {
        directives: { scroll },
      }
    )
    await vm.$nextTick()
    expect(vm.$el[HANDLER]).not.exist
    vm.msg = 'test'
    await vm.$nextTick()
    expect(vm.$el[HANDLER]).not.exist
  })
})


<|start_filename|>test/utils.js<|end_filename|>
import $ from 'jquery'
import Vue from 'vue'

export const transition = 300

export const keyCodes = {
  esc: 27,
  tab: 9,
  enter: 13,
  space: 32,
  up: 38,
  left: 37,
  right: 39,
  down: 40,
  delete: [8, 46],
}

export const triggerEvent = (elm, name, evtProps = {}, ...opts) => {
  let eventName
  let evt
  if (/^mouse|click/.test(name)) {
    eventName = 'MouseEvents'
  } else if (/^key/.test(name)) {
    eventName = 'KeyboardEvent'
    evt = new KeyboardEvent(name, evtProps)
  } else {
    eventName = 'HTMLEvents'
  }
  if (!evt) {
    evt = document.createEvent(eventName)
    evt.initEvent(name, ...opts)
    for (let k in evtProps) {
      evt[k] = evtProps[k]
    }
  }
  elm.dispatchEvent ? elm.dispatchEvent(evt) : elm.fireEvent('on' + name, evt)
  return elm
}

export const triggerKey = (el, key, type = 'down') => {
  if (document.createEventObject) {
    let eventObj = document.createEventObject()
    eventObj.keyCode = key
    el.fireEvent(`onkey${type}`, eventObj)
    eventObj.keyCode = key
  } else if (document.createEvent) {
    let eventObj = document.createEvent('Events')
    eventObj.initEvent(`key${type}`, true, true)
    eventObj.which = key
    eventObj.keyCode = key
    el.dispatchEvent(eventObj)
  }
}

export const sleep = (time) => {
  return new Promise((resolve) => {
    setTimeout(resolve, time)
  })
}

export const createVm = (template, _data, _options) => {
  $('<div id="app"><div id="mount"></div></div>').appendTo('body')
  const res = Vue.compile(template)
  return new Vue({
    data() {
      return {
        ..._data,
      }
    },
    ..._options,
    render: res.render,
    staticRenderFns: res.staticRenderFns,
  }).$mount('#mount')
}

export const destroyVm = (vm) => {
  if (!vm) {
    return
  }
  $(vm.$el).remove()
  $('#app').remove()
  vm.$destroy()
}


<|start_filename|>src/install.js<|end_filename|>
import * as components from './components'
import * as directives from './directives'
import * as services from './services'
import locale from './locale'

const install = (Vue, options = {}) => {
  // Setup language, en-US for default
  locale.use(options.locale)
  locale.i18n(options.i18n)
  // Register components
  Object.keys(components).forEach((key) => {
    const _key = options.prefix ? options.prefix + key : key
    Vue.component(_key, components[key])
  })
  // Register directives
  Object.keys(directives).forEach((key) => {
    const _key = options.prefix ? options.prefix + '-' + key : key
    Vue.directive(_key, directives[key])
  })
  // Register services
  Object.keys(services).forEach((key) => {
    const service = services[key]
    Object.keys(service).forEach((serviceKey) => {
      const _key = options.prefix
        ? options.prefix + '_' + serviceKey
        : serviceKey
      Vue.prototype['$' + _key] = service[serviceKey]
    })
  })
}

export { install }


<|start_filename|>rollup/rollup.umd.js<|end_filename|>
const path = require('path')
const uglify = require('rollup-plugin-uglify').uglify
const filesize = require('rollup-plugin-filesize')
const _ = require('lodash')

const baseConfig = require('./rollup.base')
const { name } = require('../package.json')

module.exports = _.merge({}, baseConfig, {
  input: path.join(__dirname, '..', 'src', 'index-browser.js'),
  plugins: [
    uglify(),
    filesize()
  ],
  output: {
    format: 'umd',
    name: 'uiv',
    file: path.join(__dirname, '..', 'dist', `${name}.min.js`),
    sourcemap: true,
    globals: {
      'vue': 'Vue'
    }
  }
})


<|start_filename|>src/components/notification/Notification.js<|end_filename|>
import { addClass, removeClass } from '../../utils/dom.utils'
import { isString } from '../../utils/object.utils'
import Alert from '../alert/Alert.vue'
import { TYPES, PLACEMENTS } from '../../constants/notification.constants'

const IN_CLASS = 'in'
const ICON = 'glyphicon'
const WIDTH = 300
const TRANSITION_DURATION = 300

export default {
  components: { Alert },
  props: {
    title: String,
    content: String,
    html: {
      type: Boolean,
      default: false,
    },
    duration: {
      type: Number,
      default: 5000,
    },
    dismissible: {
      type: Boolean,
      default: true,
    },
    type: String,
    placement: String,
    icon: String,
    customClass: null,
    cb: {
      type: Function,
      required: true,
    },
    queue: {
      type: Array,
      required: true,
    },
    offsetY: {
      type: Number,
      default: 15,
    },
    offsetX: {
      type: Number,
      default: 15,
    },
    offset: {
      type: Number,
      default: 15,
    },
  },
  data() {
    return {
      height: 0,
      top: 0,
      horizontal:
        this.placement === PLACEMENTS.TOP_LEFT ||
        this.placement === PLACEMENTS.BOTTOM_LEFT
          ? 'left'
          : 'right',
      vertical:
        this.placement === PLACEMENTS.TOP_LEFT ||
        this.placement === PLACEMENTS.TOP_RIGHT
          ? 'top'
          : 'bottom',
    }
  },
  created() {
    // get prev notifications total height in the queue
    this.top = this.getTotalHeightOfQueue(this.queue)
  },
  mounted() {
    const el = this.$el
    el.style[this.vertical] = this.top + 'px'
    this.$nextTick(() => {
      el.style[this.horizontal] = `-${WIDTH}px`
      this.height = el.offsetHeight
      el.style[this.horizontal] = `${this.offsetX}px`
      addClass(el, IN_CLASS)
    })
  },
  computed: {
    styles() {
      const queue = this.queue
      const thisIndex = queue.indexOf(this)
      return {
        position: 'fixed',
        [this.vertical]: `${this.getTotalHeightOfQueue(queue, thisIndex)}px`,
        width: `${WIDTH}px`,
        transition: `all ${TRANSITION_DURATION / 1000}s ease-in-out`,
      }
    },
    icons() {
      if (isString(this.icon)) {
        return this.icon
      }
      switch (this.type) {
        case TYPES.INFO:
        case TYPES.WARNING:
          return `${ICON} ${ICON}-info-sign`
        case TYPES.SUCCESS:
          return `${ICON} ${ICON}-ok-sign`
        case TYPES.DANGER:
          return `${ICON} ${ICON}-remove-sign`
        default:
          return null
      }
    },
  },
  methods: {
    getTotalHeightOfQueue(queue, lastIndex = queue.length) {
      let totalHeight = this.offsetY
      for (let i = 0; i < lastIndex; i++) {
        totalHeight += queue[i].height + this.offset
      }
      return totalHeight
    },
    onDismissed() {
      removeClass(this.$el, IN_CLASS)
      setTimeout(this.cb, TRANSITION_DURATION)
    },
  },
}


<|start_filename|>src/components/index.js<|end_filename|>
export { default as Carousel } from './carousel/Carousel.vue'
export { default as Slide } from './carousel/Slide.vue'
export { default as Collapse } from './collapse/Collapse.js'
export { default as Dropdown } from './dropdown/Dropdown.js'
export { default as Modal } from './modal/Modal.vue'
export { default as Tab } from './tabs/Tab.vue'
export { default as Tabs } from './tabs/Tabs.vue'
export { default as DatePicker } from './datepicker/DatePicker.vue'
export { default as Affix } from './affix/Affix.vue'
export { default as Alert } from './alert/Alert.vue'
export { default as Pagination } from './pagination/Pagination.vue'
export { default as Tooltip } from './tooltip/Tooltip.js'
export { default as Popover } from './popover/Popover.js'
export { default as TimePicker } from './timepicker/TimePicker.vue'
export { default as Typeahead } from './typeahead/Typeahead.vue'
export { default as ProgressBar } from './progressbar/ProgressBar'
export { default as ProgressBarStack } from './progressbar/ProgressBarStack'
export { default as Breadcrumbs } from './breadcrumbs/Breadcrumbs'
export { default as BreadcrumbItem } from './breadcrumbs/BreadcrumbItem'
export { default as Btn } from './button/Btn'
export { default as BtnGroup } from './button/BtnGroup'
export { default as BtnToolbar } from './button/BtnToolbar'
export { default as MultiSelect } from './select/MultiSelect.vue'
export { default as Navbar } from './navbar/Navbar.vue'
export { default as NavbarNav } from './navbar/NavbarNav'
export { default as NavbarForm } from './navbar/NavbarForm'
export { default as NavbarText } from './navbar/NavbarText'


<|start_filename|>rollup/rollup.esm-single.js<|end_filename|>
const path = require('path')
const _ = require('lodash')

const baseConfig = require('./rollup.base')

const files = [
  'services/notification/Notification.js',
  'services/messagebox/MessageBox.js',
  'directives/popover/popover.js',
  'directives/scrollspy/scrollspy.js',
  'directives/tooltip/tooltip.js',
  'components/affix/Affix.vue',
  'components/alert/Alert.vue',
  'components/breadcrumbs/BreadcrumbItem.js',
  'components/breadcrumbs/Breadcrumbs.js',
  'components/button/Btn.js',
  'components/button/BtnGroup.js',
  'components/button/BtnToolbar.js',
  'components/carousel/Carousel.vue',
  'components/carousel/Slide.vue',
  'components/collapse/Collapse.js',
  'components/datepicker/DatePicker.vue',
  'components/dropdown/Dropdown.js',
  'components/modal/Modal.vue',
  'components/navbar/Navbar.vue',
  'components/navbar/NavbarForm.js',
  'components/navbar/NavbarNav.js',
  'components/navbar/NavbarText.js',
  'components/pagination/Pagination.vue',
  'components/popover/Popover.js',
  'components/progressbar/ProgressBar.js',
  'components/progressbar/ProgressBarStack.js',
  'components/select/MultiSelect.vue',
  'components/tabs/Tab.vue',
  'components/tabs/Tabs.vue',
  'components/timepicker/TimePicker.vue',
  'components/tooltip/Tooltip.js',
  'components/typeahead/Typeahead.vue'
]

module.exports = files.map(file => {
  const prefix = file.startsWith('directives') ? 'v_' : ''
  return _.merge({}, baseConfig, {
    input: path.join(__dirname, '..', 'src', file),
    output: [
      {
        format: 'es',
        file: path.join(__dirname, '..', 'dist', `${prefix + file.split('/').pop().split('.')[0]}.js`),
        sourcemap: true
      }
    ]
  })
})


<|start_filename|>rollup/rollup.cjs.js<|end_filename|>
const path = require('path')
const _ = require('lodash')

const baseConfig = require('./rollup.base')
const { name } = require('../package.json')

module.exports = _.merge({}, baseConfig, {
  input: path.join(__dirname, '..', 'src', 'index.js'),
  output: {
    format: 'cjs',
    file: path.join(__dirname, '..', 'dist', `${name}.common.js`),
    sourcemap: true
  }
})


<|start_filename|>src/components/datepicker/MonthView.js<|end_filename|>
import Locale from '../../mixins/locale.mixin'
import Btn from './../button/Btn'
import { isExist } from '../../utils/object.utils'

export default {
  components: { Btn },
  mixins: [Locale],
  props: {
    month: Number,
    year: Number,
    iconControlLeft: String,
    iconControlRight: String,
  },
  data() {
    return {
      rows: [],
    }
  },
  mounted() {
    for (let i = 0; i < 4; i++) {
      this.rows.push([])
      for (let j = 0; j < 3; j++) {
        this.rows[i].push(i * 3 + j + 1)
      }
    }
  },
  methods: {
    tCell(cell) {
      return this.t(`uiv.datePicker.month${cell}`)
    },
    getBtnClass(month) {
      if (month === this.month) {
        return 'primary'
      } else {
        return 'default'
      }
    },
    goPrevYear() {
      this.$emit('year-change', this.year - 1)
    },
    goNextYear() {
      this.$emit('year-change', this.year + 1)
    },
    changeView(monthIndex) {
      if (isExist(monthIndex)) {
        this.$emit('month-change', monthIndex)
        this.$emit('view-change', 'd')
      } else {
        this.$emit('view-change', 'y')
      }
    },
  },
}


<|start_filename|>test/setup.js<|end_filename|>
import Vue from 'vue'
import * as uiv from '../src/install'

Vue.config.productionTip = false
// simulate router-link
Vue.component('RouterLink', {
  template: '<a href="#router-link"><slot></slot></a>',
})
Vue.use(uiv)


<|start_filename|>src/locale/lang/nb-NO.js<|end_filename|>
export default {
  uiv: {
    datePicker: {
      clear: 'Nullstill',
      today: 'I dag',
      month: 'Måned',
      month1: 'januar',
      month2: 'februar',
      month3: 'mars',
      month4: 'april',
      month5: 'mai',
      month6: 'juni',
      month7: 'juli',
      month8: 'august',
      month9: 'september',
      month10: 'oktober',
      month11: 'november',
      month12: 'desember',
      year: 'År',
      week1: 'man',
      week2: 'tir',
      week3: 'ons',
      week4: 'tor',
      week5: 'fre',
      week6: 'lør',
      week7: 'søn',
    },
    timePicker: {
      am: 'AM',
      pm: 'PM',
    },
    modal: {
      cancel: 'Avbryt',
      ok: 'OK',
    },
  },
}


<|start_filename|>test/specs/Affix.spec.js<|end_filename|>
import $ from 'jquery'
import { sleep, createVm, destroyVm } from '../utils'

describe('Affix', () => {
  let vm
  let $el

  beforeEach(() => {
    vm = createVm(
      `
<div style="height: 2000px">
  <affix :offset="offset">
    <ul>
      <li>Test1</li>
      <li>Test2</li>
      <li>Test3</li>
    </ul>
  </affix>
</div>`,
      {
        offset: 0,
      }
    )
    $el = $(vm.$el)
  })

  afterEach(() => {
    destroyVm(vm)
    window.scrollTo(0, 0)
  })

  it('should be able to toggle affix class', async () => {
    expect($el.find('.affix').length).to.equal(0)
    window.scrollTo(0, 200)
    await sleep(200)
    expect($el.find('.affix').length).to.equal(1)
    window.scrollTo(0, 500)
    await sleep(200)
    expect($el.find('.affix').length).to.equal(1)
    window.scrollTo(0, 0)
    await sleep(200)
    expect($el.find('.affix').length).to.equal(0)
  })

  it('should not toggle affix class if element is hidden', async () => {
    const $body = $('html, body')
    $body.css('height', '9999px')
    $el.css('display', 'none')
    await vm.$nextTick()
    expect($el.find('.affix').length).to.equal(0)
    window.scrollTo(0, 500)
    await sleep(200)
    expect($el.find('.affix').length).to.equal(0)
    window.scrollTo(0, 0)
    await sleep(200)
    expect($el.find('.affix').length).to.equal(0)
    $body.css('height', '')
  })

  it('should be able to use offset', async () => {
    vm.offset = 50
    await vm.$nextTick()
    window.scrollTo(0, 500)
    await sleep(200)
    expect($el.find('.affix').get(0).style.top).to.equal('50px')
  })
})


<|start_filename|>src/components/dropdown/Dropdown.js<|end_filename|>
import {
  setDropdownPosition,
  on,
  off,
  EVENTS,
  focus,
} from '../../utils/dom.utils'
import { isBoolean } from '../../utils/object.utils'

const DEFAULT_TAG = 'div'

export default {
  render(h) {
    return h(
      this.tag,
      {
        class: {
          'btn-group': this.tag === DEFAULT_TAG,
          dropdown: !this.dropup,
          dropup: this.dropup,
          open: this.show,
        },
      },
      [
        this.$slots.default,
        h(
          'ul',
          {
            class: {
              'dropdown-menu': true,
              'dropdown-menu-right': this.menuRight,
            },
            ref: 'dropdown',
          },
          [this.$slots.dropdown]
        ),
      ]
    )
  },
  props: {
    tag: {
      type: String,
      default: DEFAULT_TAG,
    },
    appendToBody: {
      type: Boolean,
      default: false,
    },
    value: Boolean,
    dropup: {
      type: Boolean,
      default: false,
    },
    menuRight: {
      type: Boolean,
      default: false,
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    notCloseElements: Array,
    positionElement: null,
  },
  data() {
    return {
      show: false,
      triggerEl: undefined,
    }
  },
  watch: {
    value(v) {
      this.toggle(v)
    },
  },
  mounted() {
    this.initTrigger()
    if (this.triggerEl) {
      on(this.triggerEl, EVENTS.CLICK, this.toggle)
      on(this.triggerEl, EVENTS.KEY_DOWN, this.onKeyPress)
    }
    on(this.$refs.dropdown, EVENTS.KEY_DOWN, this.onKeyPress)
    on(window, EVENTS.CLICK, this.windowClicked)
    on(window, EVENTS.TOUCH_END, this.windowClicked)
    if (this.value) {
      this.toggle(true)
    }
  },
  beforeDestroy() {
    this.removeDropdownFromBody()
    if (this.triggerEl) {
      off(this.triggerEl, EVENTS.CLICK, this.toggle)
      off(this.triggerEl, EVENTS.KEY_DOWN, this.onKeyPress)
    }
    off(this.$refs.dropdown, EVENTS.KEY_DOWN, this.onKeyPress)
    off(window, EVENTS.CLICK, this.windowClicked)
    off(window, EVENTS.TOUCH_END, this.windowClicked)
  },
  methods: {
    getFocusItem() {
      const dropdownEl = this.$refs.dropdown
      /* START.TESTS_ONLY */
      /* istanbul ignore else */
      if (typeof window.__karma__ !== 'undefined') {
        return dropdownEl.querySelector('li > a[focus=true]')
      }
      /* END.TESTS_ONLY */
      /* istanbul ignore next */
      return dropdownEl.querySelector('li > a:focus')
    },
    onKeyPress(event) {
      if (this.show) {
        const dropdownEl = this.$refs.dropdown
        const keyCode = event.keyCode
        if (keyCode === 27) {
          // esc
          this.toggle(false)
          this.triggerEl && this.triggerEl.focus()
        } else if (keyCode === 13) {
          // enter
          const currentFocus = this.getFocusItem()
          currentFocus && currentFocus.click()
        } else if (keyCode === 38 || keyCode === 40) {
          // up || down
          event.preventDefault()
          event.stopPropagation()
          const currentFocus = this.getFocusItem()
          const items = dropdownEl.querySelectorAll('li:not(.disabled) > a')
          if (!currentFocus) {
            focus(items[0])
          } else {
            for (let i = 0; i < items.length; i++) {
              if (currentFocus === items[i]) {
                if (keyCode === 38 && i < items.length > 0) {
                  focus(items[i - 1])
                } else if (keyCode === 40 && i < items.length - 1) {
                  focus(items[i + 1])
                }
                break
              }
            }
          }
        }
      }
    },
    initTrigger() {
      const trigger =
        this.$el.querySelector('[data-role="trigger"]') ||
        this.$el.querySelector('.dropdown-toggle') ||
        this.$el.firstChild
      this.triggerEl =
        trigger && trigger !== this.$refs.dropdown ? trigger : null
    },
    toggle(show) {
      if (this.disabled) {
        return
      }
      if (isBoolean(show)) {
        this.show = show
      } else {
        this.show = !this.show
      }
      if (this.appendToBody) {
        this.show ? this.appendDropdownToBody() : this.removeDropdownFromBody()
      }
      this.$emit('input', this.show)
    },
    windowClicked(event) {
      const target = event.target
      if (this.show && target) {
        let targetInNotCloseElements = false
        if (this.notCloseElements) {
          for (let i = 0, l = this.notCloseElements.length; i < l; i++) {
            const isTargetInElement = this.notCloseElements[i].contains(target)
            let shouldBreak = isTargetInElement
            /* istanbul ignore else */
            if (this.appendToBody) {
              const isTargetInDropdown = this.$refs.dropdown.contains(target)
              const isElInElements =
                this.notCloseElements.indexOf(this.$el) >= 0
              shouldBreak =
                isTargetInElement || (isTargetInDropdown && isElInElements)
            }
            if (shouldBreak) {
              targetInNotCloseElements = true
              break
            }
          }
        }
        const targetInDropdownBody = this.$refs.dropdown.contains(target)
        const targetInTrigger =
          this.$el.contains(target) && !targetInDropdownBody
        // normally, a dropdown select event is handled by @click that trigger after @touchend
        // then @touchend event have to be ignore in this case
        const targetInDropdownAndIsTouchEvent =
          targetInDropdownBody && event.type === 'touchend'
        if (
          !targetInTrigger &&
          !targetInNotCloseElements &&
          !targetInDropdownAndIsTouchEvent
        ) {
          this.toggle(false)
        }
      }
    },
    appendDropdownToBody() {
      try {
        const el = this.$refs.dropdown
        el.style.display = 'block'
        document.body.appendChild(el)
        const positionElement = this.positionElement || this.$el
        setDropdownPosition(el, positionElement, this)
      } catch (e) {
        // Silent
      }
    },
    removeDropdownFromBody() {
      try {
        const el = this.$refs.dropdown
        el.removeAttribute('style')
        this.$el.appendChild(el)
      } catch (e) {
        // Silent
      }
    },
  },
}


<|start_filename|>src/directives/index.js<|end_filename|>
export { default as tooltip } from './tooltip/tooltip'
export { default as popover } from './popover/popover'
export { default as scrollspy } from './scrollspy/scrollspy'


<|start_filename|>test/specs/Btn.spec.js<|end_filename|>
import $ from 'jquery'
import { createVm, destroyVm, triggerEvent } from '../utils'

describe('Btn', () => {
  let vm

  afterEach(() => {
    destroyVm(vm)
  })

  it('should be able to render btn types', () => {
    vm = createVm(`<div>
<btn>Default</btn>
<btn type="primary">Primary</btn>
<btn type="success">Success</btn>
<btn type="info">Info</btn>
<btn type="warning">Warning</btn>
<btn type="danger">Danger</btn>
<btn type="link">Link</btn>
</div>`)
    const $cont = $(vm.$el)
    const $btns = $('button.btn', $cont)
    expect($btns.length).to.equal(7)
    // all render as type=button
    const $btnsType = $('[type=button]', $cont)
    expect($btnsType.length).to.equal(7)
    // classnames
    expect($btns.get(0).className).to.contain('btn-default')
    expect($btns.get(1).className).to.contain('btn-primary')
    expect($btns.get(2).className).to.contain('btn-success')
    expect($btns.get(3).className).to.contain('btn-info')
    expect($btns.get(4).className).to.contain('btn-warning')
    expect($btns.get(5).className).to.contain('btn-danger')
    expect($btns.get(6).className).to.contain('btn-link')
  })

  it('should be able to render link btn', () => {
    vm = createVm(`<div><h4>Native links</h4>
<btn href="#">Default</btn>
<btn href="#" type="primary">Primary</btn>
<h4>Vue Router links</h4>
<btn to="/">Default</btn>
<btn to="/" type="primary">Primary</btn></div>`)
    const $btnLinks = $(vm.$el)
    expect($('button', $btnLinks).length).to.equal(0)
    expect($('a.btn', $btnLinks).length).to.equal(4)
    // native links
    const $btn = $('.btn', $btnLinks)
    const $btn0 = $($btn.get(0))
    expect($btn0.attr('class')).to.contain('btn-default')
    expect($btn0.attr('href')).to.equal('#')
    expect($btn0.attr('role')).to.equal('button')
    const $btn1 = $($btn.get(1))
    expect($btn1.attr('class')).to.contain('btn-primary')
    expect($btn1.attr('href')).to.equal('#')
    expect($btn1.attr('role')).to.equal('button')
    // router links
    const $btn2 = $($btn.get(2))
    expect($btn2.attr('class')).to.contain('btn-default')
    expect($btn2.attr('href')).to.equal('#router-link')
    expect($btn2.attr('role')).to.equal('button')
    const $btn3 = $($btn.get(3))
    expect($btn3.attr('class')).to.contain('btn-primary')
    expect($btn3.attr('href')).to.equal('#router-link')
    expect($btn3.attr('role')).to.equal('button')
  })

  it('should be able to render different size btn', () => {
    vm = createVm(`<div>
  <btn size="lg" type="primary">Large button</btn>
  <btn size="lg">Large button</btn>
  <btn type="primary">Default button</btn>
  <btn>Default button</btn>
  <btn size="sm" type="primary">Small button</btn>
  <btn size="sm">Small button</btn>
  <btn size="xs" type="primary">Extra small button</btn>
  <btn size="xs">Extra small button</btn>
</div>`)
    const $btnSizes = $(vm.$el)
    const $btn = $('.btn', $btnSizes)
    expect($btn.length).to.equal(8)
    expect($btn.get(0).className).to.contain('btn-lg')
    expect($btn.get(1).className).to.contain('btn-lg')
    expect($btn.get(4).className).to.contain('btn-sm')
    expect($btn.get(5).className).to.contain('btn-sm')
    expect($btn.get(6).className).to.contain('btn-xs')
    expect($btn.get(7).className).to.contain('btn-xs')
  })

  it('should be able to render block level btn', () => {
    vm =
      createVm(`<div><btn block size="lg" type="primary">Block level button</btn>
<btn block size="lg">Block level button</btn></div>`)
    const $btnBlock = $(vm.$el)
    expect($btnBlock.find('.btn.btn-block').length).to.equal(2)
  })

  it('should be able to render active btn', () => {
    vm = createVm(`<div><h4>Buttons</h4>
<btn active type="primary">Primary button</btn>
<btn active>Button</btn>
<h4>Links</h4>
<btn active href="#" type="primary">Primary button</btn>
<btn active to="/">Button</btn></div>`)
    const $btnActive = $(vm.$el)
    expect($btnActive.find('.btn.active').length).to.equal(4)
    expect($btnActive.find('button.active').length).to.equal(2)
    expect($btnActive.find('a.active').length).to.equal(2)
  })

  it('should be able to render disabled btn', () => {
    vm = createVm(`<div><h4>Buttons</h4>
<btn disabled type="primary">Primary button</btn>
<btn disabled>Button</btn>
<h4>Links</h4>
<btn disabled href="#" type="primary">Primary button</btn>
<btn disabled to="/">Button</btn></div>`)
    const $btnDisabled = $(vm.$el)
    expect($btnDisabled.find('.btn').length).to.equal(4)
    expect($btnDisabled.find('.btn.disabled').length).to.equal(4)
    expect($btnDisabled.find('.btn[disabled]').length).to.equal(2)
  })

  it('should not response on disabled link btn click', async () => {
    vm = createVm(`<div><h4>Buttons</h4>
<btn disabled type="primary">Primary button</btn>
<btn disabled>Button</btn>
<h4>Links</h4>
<btn disabled href="#" type="primary">Primary button</btn>
<btn disabled to="/">Button</btn></div>`)
    const $btnDisabled = $(vm.$el)
    expect(window.location.hash).to.equal('')
    const btn = $btnDisabled.find('a.btn').get(0)
    triggerEvent(btn, 'click')
    await vm.$nextTick()
    expect(window.location.hash).to.equal('')
  })

  it('should emit click event', async () => {
    vm = createVm(
      '<btn @click="onClick">{{ msg }}</btn>',
      {
        msg: 'test',
      },
      {
        methods: {
          onClick() {
            this.msg = 'clicked'
          },
        },
      }
    )
    await vm.$nextTick()
    expect(vm.msg).to.equal('test')
    triggerEvent(vm.$el, 'click')
    await vm.$nextTick()
    expect(vm.msg).to.equal('clicked')
  })

  it('should not emit click event while disabled', async () => {
    vm = createVm(
      '<btn disabled @click="onClick">{{ msg }}</btn>',
      {
        msg: 'test',
      },
      {
        methods: {
          onClick() {
            this.msg = 'clicked'
          },
        },
      }
    )
    await vm.$nextTick()
    expect(vm.msg).to.equal('test')
    triggerEvent(vm.$el, 'click')
    await vm.$nextTick()
    expect(vm.msg).to.equal('test')
  })

  it('should be able to render checkbox btn', async () => {
    vm = createVm(
      `  <section>
    <btn-group>
      <btn input-type="checkbox" input-value="1" v-model="model">Checkbox 1</btn>
      <btn input-type="checkbox" input-value="2" v-model="model">Checkbox 2</btn>
      <btn input-type="checkbox" input-value="3" v-model="model">Checkbox 3</btn>
      <btn input-type="checkbox" input-value="4" v-model="model" disabled>Checkbox 4 (Disabled)</btn>
    </btn-group>
    <hr/>
    <alert>Selected: {{model}}</alert>
  </section>`,
      {
        model: ['1'],
      }
    )
    const $btnInputCheckbox = $(vm.$el)
    await vm.$nextTick()
    expect($btnInputCheckbox.find('label.btn').length).to.equal(4)
    expect(
      $btnInputCheckbox.find('label.btn > input[type=checkbox]').length
    ).to.equal(4)
    // first one should be actived by default
    expect($btnInputCheckbox.find('label.btn.active').length).to.equal(1)
    expect(
      $btnInputCheckbox.find('label.btn > input[type=checkbox]').get(0).checked
    ).to.be.true
    expect($btnInputCheckbox.find('label.btn').get(0).className).to.contain(
      'active'
    )
    // last one shoubd be disabled by default
    expect($btnInputCheckbox.find('label.btn.disabled').length).to.equal(1)
    expect(
      $btnInputCheckbox.find('label.btn.disabled > input[disabled]').length
    ).to.equal(1)
    expect($btnInputCheckbox.find('label.btn').get(3).className).to.contain(
      'disabled'
    )
  })

  it('should be able to select checkbox btn', async () => {
    vm = createVm(
      `  <section>
    <btn-group>
      <btn input-type="checkbox" input-value="1" v-model="model">Checkbox 1</btn>
      <btn input-type="checkbox" input-value="2" v-model="model">Checkbox 2</btn>
      <btn input-type="checkbox" input-value="3" v-model="model">Checkbox 3</btn>
      <btn input-type="checkbox" input-value="4" v-model="model" disabled>Checkbox 4 (Disabled)</btn>
    </btn-group>
    <hr/>
    <alert>Selected: {{model}}</alert>
  </section>`,
      {
        model: ['1'],
      }
    )
    const $el = $(vm.$el)
    await vm.$nextTick()
    // phantomjs won't response on label click
    triggerEvent($el.find('label.btn > input').get(1), 'change')
    await vm.$nextTick()
    // active second one
    expect($el.find('label.btn.active').length).to.equal(2)
    expect($el.find('label.btn > input[type=checkbox]').get(0).checked).to.be
      .true
    expect($el.find('label.btn > input[type=checkbox]').get(1).checked).to.be
      .true
    expect($el.find('label.btn > input[type=checkbox]').get(3).checked).to.be
      .false
    expect($el.find('label.btn').get(1).className).to.contain('active')
    // model change
    expect(vm.model.length).to.equal(2)
    expect(vm.model.indexOf('1')).to.be.at.least(0)
    expect(vm.model.indexOf('2')).to.be.at.least(0)
  })

  it('should be able to un-select checkbox btn', async () => {
    vm = createVm(
      `  <section>
    <btn-group>
      <btn input-type="checkbox" input-value="1" v-model="model">Checkbox 1</btn>
      <btn input-type="checkbox" input-value="2" v-model="model">Checkbox 2</btn>
      <btn input-type="checkbox" input-value="3" v-model="model">Checkbox 3</btn>
      <btn input-type="checkbox" input-value="4" v-model="model" disabled>Checkbox 4 (Disabled)</btn>
    </btn-group>
    <hr/>
    <alert>Selected: {{model}}</alert>
  </section>`,
      {
        model: ['1'],
      }
    )
    const $el = $(vm.$el)
    await vm.$nextTick()
    // phantomjs won't response on label click
    triggerEvent($el.find('label.btn > input').get(0), 'change')
    await vm.$nextTick()
    expect($el.find('label.btn.active').length).to.equal(0)
    // model change
    expect(vm.model.length).to.equal(0)
  })

  it('should be able to render radio btn', async () => {
    vm = createVm(
      `  <section>
    <btn-group>
      <btn input-type="radio" input-value="1" v-model="model">Radio 1</btn>
      <btn input-type="radio" input-value="2" v-model="model">Radio 2</btn>
      <btn input-type="radio" input-value="3" v-model="model">Radio 3</btn>
      <btn input-type="radio" input-value="4" v-model="model" disabled>Radio 4 (Disabled)</btn>
    </btn-group>
    <hr/>
    <alert>Selected: {{model}}</alert>
  </section>`,
      {
        model: '1',
      }
    )
    const $btnInputRadio = $(vm.$el)
    await vm.$nextTick()
    expect($btnInputRadio.find('label.btn').length).to.equal(4)
    expect(
      $btnInputRadio.find('label.btn > input[type=radio]').length
    ).to.equal(4)
    // active first one
    expect($btnInputRadio.find('label.btn.active').length).to.equal(1)
    expect($btnInputRadio.find('label.btn > input[type=radio]').get(0).checked)
      .to.be.true
    expect($btnInputRadio.find('label.btn').get(0).className).to.contain(
      'active'
    )
    // disabled last one
    expect($btnInputRadio.find('label.btn.disabled').length).to.equal(1)
    expect(
      $btnInputRadio.find('label.btn.disabled > input[disabled]').length
    ).to.equal(1)
    expect($btnInputRadio.find('label.btn').get(3).className).to.contain(
      'disabled'
    )
  })

  it('should be able to select radio btn', async () => {
    vm = createVm(
      `  <section>
    <btn-group>
      <btn input-type="radio" input-value="1" v-model="model">Radio 1</btn>
      <btn input-type="radio" input-value="2" v-model="model">Radio 2</btn>
      <btn input-type="radio" input-value="3" v-model="model">Radio 3</btn>
      <btn input-type="radio" input-value="4" v-model="model" disabled>Radio 4 (Disabled)</btn>
    </btn-group>
    <hr/>
    <alert>Selected: {{model}}</alert>
  </section>`,
      {
        model: '1',
      }
    )
    const $el = $(vm.$el)
    await vm.$nextTick()
    // phantomjs won't response on label click
    triggerEvent($el.find('label.btn > input').get(1), 'change')
    await vm.$nextTick()
    // active second one
    expect($el.find('label.btn.active').length).to.equal(1)
    expect($el.find('label.btn > input[type=radio]').get(0).checked).to.be.false
    expect($el.find('label.btn > input[type=radio]').get(1).checked).to.be.true
    expect($el.find('label.btn > input[type=radio]').get(3).checked).to.be.false
    expect($el.find('label.btn').get(1).className).to.contain('active')
    // model change
    expect(vm.model).to.equal('2')
  })

  it('should not be able to un-select radio btn', async () => {
    vm = createVm(
      `  <section>
    <btn-group>
      <btn input-type="radio" input-value="1" v-model="model">Radio 1</btn>
      <btn input-type="radio" input-value="2" v-model="model">Radio 2</btn>
      <btn input-type="radio" input-value="3" v-model="model">Radio 3</btn>
      <btn input-type="radio" input-value="4" v-model="model" disabled>Radio 4 (Disabled)</btn>
    </btn-group>
    <hr/>
    <alert>Selected: {{model}}</alert>
  </section>`,
      {
        model: '1',
      }
    )
    const $el = $(vm.$el)
    await vm.$nextTick()
    // phantomjs won't response on label click
    triggerEvent($el.find('label.btn > input').get(0), 'change')
    await vm.$nextTick()
    // active second one
    expect($el.find('label.btn.active').length).to.equal(1)
    expect($el.find('label.btn > input[type=radio]').get(0).checked).to.be.true
    expect($el.find('label.btn > input[type=radio]').get(1).checked).to.be.false
    expect($el.find('label.btn > input[type=radio]').get(3).checked).to.be.false
    expect($el.find('label.btn').get(0).className).to.contain('active')
    // model change
    expect(vm.model).to.equal('1')
  })

  it('should be able to render as justified', async () => {
    vm = createVm('<btn justified>test</btn>')
    await vm.$nextTick()
    expect(vm.$el.className).to.contain('btn-group')
    const btn = vm.$el.querySelector('.btn')
    expect(btn).to.exist
    expect(btn.innerText).to.equal('test')
  })
})


<|start_filename|>src/mixins/locale.mixin.js<|end_filename|>
import { t } from '../locale'
import { assign } from '../utils/object.utils'

export default {
  methods: {
    t() {
      const args = []
      for (let i = 0; i < arguments.length; ++i) {
        args.push(arguments[i])
      }
      args[1] = assign({}, { $$locale: this.locale }, args[1])
      return t.apply(this, args)
    },
  },
  props: {
    locale: Object,
  },
}


<|start_filename|>src/services/messagebox/MessageBox.js<|end_filename|>
import { TYPES } from '../../constants/messagebox.constants'
import { removeFromDom } from '../../utils/dom.utils'
import { spliceIfExist } from '../../utils/array.utils'
import {
  isFunction,
  isExist,
  isString,
  isPromiseSupported,
  assign,
} from '../../utils/object.utils'
import MessageBox from '../../components/messagebox/MessageBox.vue'
import Vue from 'vue'

const queue = []

const destroy = (instance) => {
  // console.log('destroyModal')
  removeFromDom(instance.$el)
  instance.$destroy()
  spliceIfExist(queue, instance)
}

// handel cancel or ok for confirm & prompt
const shallResolve = (type, msg) => {
  if (type === TYPES.CONFIRM) {
    // is confirm
    return msg === 'ok'
  } else {
    // is prompt
    return isExist(msg) && isString(msg.value)
  }
}

const init = function (type, options, cb, resolve = null, reject = null) {
  const i18n = this.$i18n
  const instance = new Vue({
    extends: MessageBox,
    i18n,
    propsData: assign({}, { type }, options, {
      cb(msg) {
        destroy(instance)
        if (isFunction(cb)) {
          if (type === TYPES.CONFIRM) {
            shallResolve(type, msg) ? cb(null, msg) : cb(msg)
          } else if (type === TYPES.PROMPT) {
            shallResolve(type, msg) ? cb(null, msg.value) : cb(msg)
          } else {
            cb(msg)
          }
        } else if (resolve && reject) {
          if (type === TYPES.CONFIRM) {
            shallResolve(type, msg) ? resolve(msg) : reject(msg)
          } else if (type === TYPES.PROMPT) {
            shallResolve(type, msg) ? resolve(msg.value) : reject(msg)
          } else {
            resolve(msg)
          }
        }
      },
    }),
  })
  instance.$mount()
  document.body.appendChild(instance.$el)
  instance.show = true
  queue.push(instance)
}

// eslint-disable-next-line default-param-last
const initModal = function (type, options = {}, cb) {
  if (isPromiseSupported()) {
    return new Promise((resolve, reject) => {
      init.apply(this, [type, options, cb, resolve, reject])
    })
  } else {
    init.apply(this, [type, options, cb])
  }
}

const alert = function (options, cb) {
  return initModal.apply(this, [TYPES.ALERT, options, cb])
}

const confirm = function (options, cb) {
  return initModal.apply(this, [TYPES.CONFIRM, options, cb])
}

const prompt = function (options, cb) {
  return initModal.apply(this, [TYPES.PROMPT, options, cb])
}

export default { alert, confirm, prompt }


<|start_filename|>test/specs/Dropdown.spec.js<|end_filename|>
import { createVm, destroyVm, triggerEvent, keyCodes } from '../utils'
import $ from 'jquery'

function appendToBodyVm() {
  return createVm(`<div><dropdown append-to-body>
  <btn class="dropdown-toggle">Dropdown <span class="caret"></span></btn>
  <template slot="dropdown">
    <li><a role="button">Action</a></li>
    <li><a role="button">Another action</a></li>
    <li><a role="button">Something else here</a></li>
    <li role="separator" class="divider"></li>
    <li><a role="button">Separated link</a></li>
  </template>
</dropdown>
<!-- dropdown with append-to-body + menu-right -->
<dropdown append-to-body menu-right>
  <btn class="dropdown-toggle">Menu-Right <span class="caret"></span></btn>
  <template slot="dropdown">
    <li><a role="button">Action</a></li>
    <li><a role="button">Another action</a></li>
    <li><a role="button">Something else here</a></li>
    <li role="separator" class="divider"></li>
    <li><a role="button">Separated link</a></li>
  </template>
</dropdown>
<!-- dropdown with append-to-body + dropup -->
<dropdown append-to-body dropup>
  <btn class="dropdown-toggle">Dropup <span class="caret"></span></btn>
  <template slot="dropdown">
    <li><a role="button">Action</a></li>
    <li><a role="button">Another action</a></li>
    <li><a role="button">Something else here</a></li>
    <li role="separator" class="divider"></li>
    <li><a role="button">Separated link</a></li>
  </template>
</dropdown></div>`)
}

function baseVm() {
  return createVm(`<div><dropdown ref="dropdown">
  <btn type="primary" class="dropdown-toggle">Dropdown <span class="caret"></span></btn>
  <template slot="dropdown">
    <li><a role="button">Action</a></li>
    <li><a role="button">Another action</a></li>
    <li><a role="button">Something else here</a></li>
    <li role="separator" class="divider"></li>
    <li><a role="button">Separated link</a></li>
  </template>
</dropdown>
<dropdown>
  <btn type="info">Split Button</btn>
  <btn type="info" class="dropdown-toggle"><span class="caret"></span></btn>
  <template slot="dropdown">
    <li><a role="button">Action</a></li>
    <li><a role="button">Another action</a></li>
    <li><a role="button">Something else here</a></li>
    <li role="separator" class="divider"></li>
    <li><a role="button">Separated link</a></li>
  </template>
</dropdown></div>`)
}

function assertKeyboardNav(trigger, dropdown, index, keyCode, called = true) {
  const $dropdown = $(dropdown)
  const spy = sinon.spy($dropdown.find('li > a').get(index), 'focus')
  triggerEvent(trigger, 'keydown', { keyCode: keyCode })
  if (called) {
    sinon.assert.called(spy)
  } else {
    sinon.assert.notCalled(spy)
  }
  $dropdown.find('li > a').removeAttr('focus')
  $dropdown.find('li > a').eq(index).attr('focus', true)
  $dropdown.find('li > a').get(index).focus.restore()
}

describe('Dropdown', () => {
  let vm

  afterEach(() => {
    destroyVm(vm)
  })

  it('should be able to open dropdown on trigger click', async () => {
    vm = baseVm()
    const dropdown = vm.$el.querySelector('.dropdown')
    const trigger = dropdown.querySelector('button')
    expect(dropdown.tagName.toLowerCase()).to.equal('div')
    expect(dropdown.className).to.not.contain('open')
    triggerEvent(trigger, 'click')
    await vm.$nextTick()
    expect(dropdown.className).to.contain('open')
  })

  it('should be able to close dropdown using keyboard esc', async () => {
    vm = baseVm()
    const dropdown = vm.$el.querySelector('.dropdown')
    const trigger = dropdown.querySelector('button')
    expect(dropdown.tagName.toLowerCase()).to.equal('div')
    expect(dropdown.className).to.not.contain('open')
    triggerEvent(trigger, 'click')
    await vm.$nextTick()
    expect(dropdown.className).to.contain('open')
    triggerEvent(trigger, 'keydown', { keyCode: keyCodes.esc })
    await vm.$nextTick()
    expect(dropdown.className).to.not.contain('open')
  })

  it('should be able to navigate between items using keyboard up & down', async () => {
    vm = baseVm()
    const dropdown = vm.$el.querySelector('.dropdown')
    const trigger = dropdown.querySelector('button')
    expect(dropdown.tagName.toLowerCase()).to.equal('div')
    expect(dropdown.className).to.not.contain('open')
    triggerEvent(trigger, 'click')
    await vm.$nextTick()
    expect(dropdown.className).to.contain('open')
    assertKeyboardNav(trigger, dropdown, 0, keyCodes.down)
    assertKeyboardNav(trigger, dropdown, 1, keyCodes.down)
    assertKeyboardNav(trigger, dropdown, 0, keyCodes.up)
    assertKeyboardNav(trigger, dropdown, 0, keyCodes.up, false)
    assertKeyboardNav(trigger, dropdown, 1, keyCodes.down)
    assertKeyboardNav(trigger, dropdown, 2, keyCodes.down)
    assertKeyboardNav(trigger, dropdown, 3, keyCodes.down)
    assertKeyboardNav(trigger, dropdown, 3, keyCodes.down, false)
  })

  it('should not be able to navigate between items using keyboard up & down when dropdown is not open', async () => {
    vm = baseVm()
    const dropdown = vm.$el.querySelector('.dropdown')
    const trigger = dropdown.querySelector('button')
    expect(dropdown.tagName.toLowerCase()).to.equal('div')
    expect(dropdown.className).to.not.contain('open')
    assertKeyboardNav(trigger, dropdown, 0, keyCodes.down, false)
  })

  it('should be able to navigate between items and select with keyboard enter', async () => {
    vm = baseVm()
    const dropdown = vm.$el.querySelector('.dropdown')
    const trigger = dropdown.querySelector('button')
    expect(dropdown.tagName.toLowerCase()).to.equal('div')
    expect(dropdown.className).to.not.contain('open')
    triggerEvent(trigger, 'click')
    await vm.$nextTick()
    expect(dropdown.className).to.contain('open')
    assertKeyboardNav(trigger, dropdown, 0, keyCodes.down)
    const spy = sinon.spy($(dropdown).find('li > a').get(0), 'click')
    triggerEvent(trigger, 'keydown', { keyCode: keyCodes.enter })
    sinon.assert.called(spy)
  })

  it('should ignore other keys when open', async () => {
    vm = baseVm()
    const dropdown = vm.$el.querySelector('.dropdown')
    const trigger = dropdown.querySelector('button')
    expect(dropdown.tagName.toLowerCase()).to.equal('div')
    expect(dropdown.className).to.not.contain('open')
    triggerEvent(trigger, 'click')
    await vm.$nextTick()
    expect(dropdown.className).to.contain('open')
    assertKeyboardNav(trigger, dropdown, 0, keyCodes.down)
    assertKeyboardNav(trigger, dropdown, 1, keyCodes.left, false)
  })

  it('should be able to close dropdown on trigger click', async () => {
    vm = baseVm()
    const dropdown = vm.$el.querySelector('.dropdown')
    const trigger = dropdown.querySelector('button')
    expect(dropdown.tagName.toLowerCase()).to.equal('div')
    expect(dropdown.className).to.not.contain('open')
    triggerEvent(trigger, 'click')
    await vm.$nextTick()
    expect(dropdown.className).to.contain('open')
    triggerEvent(trigger, 'click')
    await vm.$nextTick()
    expect(dropdown.className).to.not.contain('open')
  })

  it('should be able to close dropdown on window click', async () => {
    vm = baseVm()
    const dropdown = vm.$el.querySelector('.dropdown')
    const trigger = dropdown.querySelector('button')
    expect(dropdown.tagName.toLowerCase()).to.equal('div')
    expect(dropdown.className).to.not.contain('open')
    triggerEvent(trigger, 'click')
    await vm.$nextTick()
    expect(dropdown.className).to.contain('open')
    // Simulate a window click
    vm.$refs.dropdown.windowClicked({ target: document.body })
    await vm.$nextTick()
    expect(dropdown.className).to.not.contain('open')
  })

  it('should not close dropdown on self click if not-close-elements contains component ref and with append-to-body', async () => {
    vm = createVm(
      `<dropdown v-model="show" ref="test" append-to-body :not-close-elements="eles">
  <button class="btn btn-default dropdown-toggle" type="button">
    <span>Dropdown 1</span><span class="caret"></span>
  </button>
  <template slot="dropdown">
    <li ref="li1"><a href="#">Action</a></li>
    <li ref="li2"><a href="#">Action2</a></li>
  </template>
</dropdown>`,
      {
        show: true,
        eles: [],
      },
      {
        mounted() {
          this.eles.push(this.$refs.li1)
        },
      }
    )
    await vm.$nextTick()
    const dropdown = vm.$el
    expect(dropdown.tagName.toLowerCase()).to.equal('div')
    expect(dropdown.className).to.contain('open')
    // Simulate a window click
    vm.$refs.test.windowClicked({ target: vm.$refs.li1 })
    await vm.$nextTick()
    expect(dropdown.className).to.contain('open')
    // Simulate a window click
    vm.$refs.test.windowClicked({ target: document.body })
    await vm.$nextTick()
    expect(dropdown.className).to.not.contain('open')
    vm.show = true
    await vm.$nextTick()
    // Simulate a window click
    vm.$refs.test.windowClicked({ target: vm.$refs.li2 })
    await vm.$nextTick()
    expect(dropdown.className).to.not.contain('open')
  })

  it('should be able to open dropdown append to body on trigger click', async () => {
    vm = appendToBodyVm()
    const dropdown = vm.$el.querySelector('.dropdown')
    const trigger = dropdown.querySelector('button')
    expect(dropdown.className).to.not.contain('open')
    expect(dropdown.querySelector('.dropdown-menu')).to.exist
    triggerEvent(trigger, 'click')
    await vm.$nextTick()
    expect(dropdown.className).to.contain('open')
    expect(dropdown.querySelector('.dropdown-menu')).not.exist
    triggerEvent(trigger, 'click')
    await vm.$nextTick()
    expect(dropdown.className).not.contain('open')
    expect(dropdown.querySelector('.dropdown-menu')).to.exist
  })

  it('should be able to use dropup style', async () => {
    vm = createVm(`<div><dropdown dropup>
  <btn class="dropdown-toggle">Dropup <span class="caret"></span></btn>
  <template slot="dropdown">
    <li><a role="button">Action</a></li>
    <li><a role="button">Another action</a></li>
    <li><a role="button">Something else here</a></li>
    <li role="separator" class="divider"></li>
    <li><a role="button">Separated link</a></li>
  </template>
</dropdown></div>`)
    await vm.$nextTick()
    const dropup = vm.$el.querySelector('.dropup')
    expect(dropup.className).to.not.contain('open')
    expect(dropup.querySelector('.dropdown-menu')).to.exist
  })

  it('should be able to use menu-right style', async () => {
    vm = createVm(`<div><dropdown menu-right>
  <btn class="dropdown-toggle">Menu-Right <span class="caret"></span></btn>
  <template slot="dropdown">
    <li><a role="button">Action</a></li>
    <li><a role="button">Another action</a></li>
    <li><a role="button">Something else here</a></li>
    <li role="separator" class="divider"></li>
    <li><a role="button">Separated link</a></li>
  </template>
</dropdown></div>`)
    await vm.$nextTick()
    const menuRight = vm.$el.querySelector('.dropdown')
    expect(menuRight.querySelector('.dropdown-menu').className).to.contain(
      'dropdown-menu-right'
    )
  })

  it('should be able to open dropdown append to body & menu-right on trigger click', async () => {
    vm = appendToBodyVm()
    const dropdown = vm.$el.querySelectorAll('.dropdown')[1]
    const trigger = dropdown.querySelector('button')
    expect(dropdown.className).to.not.contain('open')
    expect(dropdown.querySelector('.dropdown-menu-right')).to.exist
    triggerEvent(trigger, 'click')
    await vm.$nextTick()
    expect(dropdown.className).to.contain('open')
    expect(dropdown.querySelector('.dropdown-menu-right')).not.exist
    triggerEvent(trigger, 'click')
    await vm.$nextTick()
    expect(dropdown.className).not.contain('open')
    expect(dropdown.querySelector('.dropdown-menu-right')).to.exist
  })

  it('should be able to open dropdown append to body & dropup on trigger click', async () => {
    vm = appendToBodyVm()
    const dropdown = vm.$el.querySelector('.dropup')
    const trigger = dropdown.querySelector('button')
    expect(dropdown.className).to.not.contain('open')
    expect(dropdown.querySelector('.dropdown-menu')).to.exist
    triggerEvent(trigger, 'click')
    await vm.$nextTick()
    expect(dropdown.className).to.contain('open')
    expect(dropdown.querySelector('.dropdown-menu')).not.exist
    triggerEvent(trigger, 'click')
    await vm.$nextTick()
    expect(dropdown.className).not.contain('open')
    expect(dropdown.querySelector('.dropdown-menu')).to.exist
  })

  it('should be able to open dropdown on init', async () => {
    vm = createVm(
      '<dropdown v-model="show"><button class="btn btn-default dropdown-toggle" type="button"><span>Dropdown 1</span><span class="caret"></span></button><template slot="dropdown"><li><a href="#">Action</a></li></template></dropdown>',
      {
        show: true,
      }
    )
    await vm.$nextTick()
    const dropdown = vm.$el
    expect(dropdown.className).to.contain('open')
  })

  it('should be able to disable dropdown', async () => {
    vm = createVm(
      '<dropdown v-model="show" disabled><button class="btn btn-default dropdown-toggle" type="button"><span>Dropdown 1</span><span class="caret"></span></button><template slot="dropdown"><li><a href="#">Action</a></li></template></dropdown>',
      {
        show: true,
      }
    )
    await vm.$nextTick()
    const dropdown = vm.$el
    expect(dropdown.className).not.contain('open')
  })

  it('should be able to init with no trigger', async () => {
    vm = createVm('<dropdown/>')
    await vm.$nextTick()
  })
})


<|start_filename|>test/specs/object.utils.spec.js<|end_filename|>
import * as utils from '../../src/utils/object.utils'

describe('object.utils', () => {
  describe('#assign', () => {
    it('should be able to merge objects', () => {
      expect(utils.assign({}, { a: 1 }, { a: 2, b: 3 })).to.deep.equal({
        a: 2,
        b: 3,
      })
    })

    it('should be able to handle null target', () => {
      expect(utils.assign.bind(this, null, {})).to.throw(
        'Cannot convert undefined or null to object'
      )
    })

    it('should be able to handle null or undefined source', () => {
      expect(
        utils.assign({}, { a: 1 }, null, undefined, { a: 2, b: 3 })
      ).to.deep.equal({ a: 2, b: 3 })
    })

    it('should be able to avoid prototype properties', () => {
      // eslint-disable-next-line no-new-func
      const a = new Function()
      a.prototype.b = 1
      a.c = 2
      expect(utils.assign({}, a)).to.deep.equal({ c: 2 })
    })
  })

  describe('#isExist ', () => {
    it('should be able return false if null', () => {
      expect(utils.isExist(null)).to.be.false
    })

    it('should be able return false if undefined', () => {
      expect(utils.isExist(undefined)).to.be.false
    })

    it('should be able return true if others', () => {
      expect(utils.isExist(0)).to.be.true
      expect(utils.isExist(false)).to.be.true
      expect(utils.isExist([])).to.be.true
      expect(utils.isExist({})).to.be.true
      expect(utils.isExist('')).to.be.true
    })
  })

  describe('#isFunction ', () => {
    it('should be able return false if not function', () => {
      expect(utils.isFunction(null)).to.be.false
    })

    it('should be able return true if function', () => {
      expect(utils.isFunction(() => 0)).to.be.true
    })
  })

  describe('#isNumber  ', () => {
    it('should be able return false if not number', () => {
      expect(utils.isNumber(null)).to.be.false
    })

    it('should be able return true if number', () => {
      expect(utils.isNumber(0)).to.be.true
    })
  })

  describe('#isString', () => {
    it('should be able return false if not string', () => {
      expect(utils.isString(null)).to.be.false
    })

    it('should be able return true if string', () => {
      expect(utils.isString('')).to.be.true
    })
  })

  describe('#isBoolean', () => {
    it('should be able return false if not boolean', () => {
      expect(utils.isBoolean(null)).to.be.false
    })

    it('should be able return true if boolean', () => {
      expect(utils.isBoolean(false)).to.be.true
    })
  })

  describe('#isPromiseSupported ', () => {
    it('should be able return true in chrome', () => {
      expect(utils.isPromiseSupported()).to.be.true
    })

    it('should be able return false if window.Promise is not valid', () => {
      const saved = window.Promise
      window.Promise = undefined
      expect(utils.isPromiseSupported()).to.be.false
      window.Promise = saved
    })
  })
})


<|start_filename|>src/utils/date.utils.js<|end_filename|>
import { pad } from './string.utils'

const monthNames = [
  'January',
  'February',
  'March',
  'April',
  'May',
  'June',
  'July',
  'August',
  'September',
  'October',
  'November',
  'December',
]

/**
 * Get total days number in a month.
 * because we're using 0 as the day so that it returns the last day
 * of the last month, so you have to add 1 to the month number
 * so it returns the correct amount of days.
 * https://stackoverflow.com/questions/1184334/get-number-days-in-a-specified-month-using-javascript
 * @param month 0-based
 * @param year
 * @returns {number}
 */
export function daysInMonth(month, year) {
  return new Date(year, month + 1, 0).getDate()
}

export function stringify(date, format) {
  try {
    const year = date.getFullYear()
    const month = date.getMonth() + 1
    const day = date.getDate()
    const monthName = monthNames[month - 1]
    return format
      .replace(/yyyy/g, year)
      .replace(/MMMM/g, monthName)
      .replace(/MMM/g, monthName.substring(0, 3))
      .replace(/MM/g, pad(month, 2))
      .replace(/dd/g, pad(day, 2))
      .replace(/yy/g, year)
      .replace(/M(?!a)/g, month)
      .replace(/d/g, day)
  } catch (e) {
    return ''
  }
}

export function convertDateToUTC(date) {
  return new Date(
    date.getUTCFullYear(),
    date.getUTCMonth(),
    date.getUTCDate(),
    date.getUTCHours(),
    date.getUTCMinutes(),
    date.getUTCSeconds()
  )
}

/**
 * https://stackoverflow.com/questions/6117814/get-week-of-year-in-javascript-like-in-php
 * For a given date, get the ISO week number
 * Based on information at:
 *    http://www.merlyn.demon.co.uk/weekcalc.htm#WNR
 *
 * Algorithm is to find nearest thursday, it's year
 * is the year of the week number. Then get weeks
 * between that date and the first day of that year.
 *
 * Note that dates in one year can be weeks of previous
 * or next year, overlap is up to 3 days.
 *
 * e.g. 2014/12/29 is Monday in week  1 of 2015
 *      2012/1/1   is Sunday in week 52 of 2011
 *
 * @param {number} d.year year of date
 * @param {number} d.month month of date
 * @param {number} d.date date of date
 * @returns {number}
 */
export function getWeekNumber(d) {
  // Copy date so don't modify original
  const _d = new Date(Date.UTC(d.year, d.month, d.date))
  // Set to nearest Thursday: current date + 4 - current day number
  // Make Sunday's day number 7
  _d.setUTCDate(_d.getUTCDate() + 4 - (_d.getUTCDay() || 7))
  // Get first day of year
  const yearStart = new Date(Date.UTC(_d.getUTCFullYear(), 0, 1))
  // Calculate full weeks to nearest Thursday
  return Math.ceil(((_d - yearStart) / 86400000 + 1) / 7)
}


<|start_filename|>src/components/affix/Affix.js<|end_filename|>
import scroll from './../../directives/scroll'

export default {
  directives: {
    scroll,
  },
  props: {
    offset: {
      type: Number,
      default: 0,
    },
  },
  data() {
    return {
      affixed: false,
    }
  },
  computed: {
    classes() {
      return {
        affix: this.affixed,
      }
    },
    styles() {
      return {
        top: this.affixed ? this.offset + 'px' : null,
      }
    },
  },
  methods: {
    // from https://github.com/ant-design/ant-design/blob/master/components/affix/index.jsx#L20
    onScroll() {
      // if is hidden don't calculate anything
      if (
        !(
          this.$el.offsetWidth ||
          this.$el.offsetHeight ||
          this.$el.getClientRects().length
        )
      ) {
        return
      }
      // get window scroll and element position to detect if have to be normal or affixed
      const scroll = {}
      const element = {}
      const rect = this.$el.getBoundingClientRect()
      const body = document.body
      const types = ['Top', 'Left']
      types.forEach((type) => {
        const t = type.toLowerCase()
        scroll[t] = window['page' + (type === 'Top' ? 'Y' : 'X') + 'Offset']
        element[t] =
          scroll[t] +
          rect[t] -
          (this.$el['client' + type] || body['client' + type] || 0)
      })
      const fix = scroll.top > element.top - this.offset
      if (this.affixed !== fix) {
        this.affixed = fix
        this.$emit(this.affixed ? 'affix' : 'unfix')
        this.$nextTick(() => {
          this.$emit(this.affixed ? 'affixed' : 'unfixed')
        })
      }
    },
  },
}


<|start_filename|>src/components/tabs/Tabs.js<|end_filename|>
import Dropdown from '../dropdown/Dropdown.js'
import { PortalTarget } from 'portal-vue'
import {
  isNumber,
  isFunction,
  isExist,
  isString,
  assign,
  hasOwnProperty,
} from '../../utils/object.utils'

const BEFORE_CHANGE_EVENT = 'before-change'

export default {
  components: { Dropdown, PortalTarget },
  props: {
    value: {
      type: Number,
      validator: (v) => v >= 0,
    },
    transition: {
      type: Number,
      default: 150,
    },
    justified: Boolean,
    pills: Boolean,
    stacked: Boolean,
    customNavClass: null,
    customContentClass: null,
  },
  data() {
    return {
      tabs: [],
      activeIndex: 0, // Make v-model not required
    }
  },
  watch: {
    value: {
      immediate: true,
      handler(value) {
        if (isNumber(value)) {
          this.activeIndex = value
          this.selectCurrent()
        }
      },
    },
    tabs(tabs) {
      tabs.forEach((tab, index) => {
        tab.transition = this.transition
        if (index === this.activeIndex) {
          tab.show()
        }
      })
      this.selectCurrent()
    },
  },
  computed: {
    navClasses() {
      const tabClasses = {
        nav: true,
        'nav-justified': this.justified,
        'nav-tabs': !this.pills,
        'nav-pills': this.pills,
        'nav-stacked': this.stacked && this.pills,
      }
      const customNavClass = this.customNavClass
      if (isExist(customNavClass)) {
        if (isString(customNavClass)) {
          return assign({}, tabClasses, {
            [customNavClass]: true,
          })
        } else {
          return assign({}, tabClasses, customNavClass)
        }
      } else {
        return tabClasses
      }
    },
    contentClasses() {
      const contentClasses = {
        'tab-content': true,
      }
      const customContentClass = this.customContentClass
      if (isExist(customContentClass)) {
        if (isString(customContentClass)) {
          return assign({}, contentClasses, {
            [customContentClass]: true,
          })
        } else {
          return assign({}, contentClasses, customContentClass)
        }
      } else {
        return contentClasses
      }
    },
    groupedTabs() {
      let tabs = []
      const hash = {}
      this.tabs.forEach((tab) => {
        if (tab.group) {
          if (hasOwnProperty(hash, tab.group)) {
            tabs[hash[tab.group]].tabs.push(tab)
          } else {
            tabs.push({
              tabs: [tab],
              group: tab.group,
            })
            hash[tab.group] = tabs.length - 1
          }
          if (tab.active) {
            tabs[hash[tab.group]].active = true
          }
          if (tab.pullRight) {
            tabs[hash[tab.group]].pullRight = true
          }
        } else {
          tabs.push(tab)
        }
      })
      tabs = tabs.map((tab) => {
        if (Array.isArray(tab.tabs)) {
          tab.hidden =
            tab.tabs.filter((v) => v.hidden).length === tab.tabs.length
        }
        return tab
      })
      return tabs
    },
  },
  methods: {
    getTabClasses(tab, isSubTab = false) {
      const defaultClasses = {
        active: tab.active,
        disabled: tab.disabled,
        'pull-right': tab.pullRight && !isSubTab,
      }

      // return with new classes added to tab
      return assign(defaultClasses, tab.tabClasses)
    },
    selectCurrent() {
      let found = false
      this.tabs.forEach((tab, index) => {
        if (index === this.activeIndex) {
          found = !tab.active
          tab.active = true
        } else {
          tab.active = false
        }
      })
      if (found) {
        this.$emit('change', this.activeIndex)
      }
    },
    selectValidate(index) {
      if (isFunction(this.$listeners[BEFORE_CHANGE_EVENT])) {
        this.$emit(BEFORE_CHANGE_EVENT, this.activeIndex, index, (result) => {
          if (!isExist(result)) {
            this.$select(index)
          }
        })
      } else {
        this.$select(index)
      }
    },
    select(index) {
      if (!this.tabs[index].disabled && index !== this.activeIndex) {
        this.selectValidate(index)
      }
    },
    $select(index) {
      if (isNumber(this.value)) {
        this.$emit('input', index)
      } else {
        this.activeIndex = index
        this.selectCurrent()
      }
    },
  },
}


<|start_filename|>src/components/datepicker/DateView.js<|end_filename|>
import Locale from '../../mixins/locale.mixin'
import Btn from './../button/Btn'
import { daysInMonth, getWeekNumber } from '../../utils/date.utils'
import { isExist, isFunction } from '../../utils/object.utils'

export default {
  mixins: [Locale],
  props: {
    month: Number,
    year: Number,
    date: Date,
    today: Date,
    limit: Object,
    weekStartsWith: Number,
    iconControlLeft: String,
    iconControlRight: String,
    dateClass: Function,
    yearMonthFormatter: Function,
    weekNumbers: Boolean,
  },
  components: { Btn },
  computed: {
    weekDays() {
      const days = []
      let firstDay = this.weekStartsWith
      while (days.length < 7) {
        days.push(firstDay++)
        if (firstDay > 6) {
          firstDay = 0
        }
      }
      return days
    },
    yearMonthStr() {
      if (this.yearMonthFormatter) {
        return this.yearMonthFormatter(this.year, this.month)
      } else {
        return isExist(this.month)
          ? `${this.year} ${this.t(`uiv.datePicker.month${this.month + 1}`)}`
          : this.year
      }
    },
    monthDayRows() {
      const rows = []
      const firstDay = new Date(this.year, this.month, 1)
      const prevMonthLastDate = new Date(this.year, this.month, 0).getDate()
      const startIndex = firstDay.getDay()
      // console.log(startIndex)
      const daysNum = daysInMonth(this.month, this.year)
      let weekOffset = 0
      if (this.weekStartsWith > startIndex) {
        weekOffset = 7 - this.weekStartsWith
      } else {
        weekOffset = 0 - this.weekStartsWith
      }
      // console.log(prevMonthLastDate, startIndex, daysNum)
      for (let i = 0; i < 6; i++) {
        rows.push([])
        for (let j = 0 - weekOffset; j < 7 - weekOffset; j++) {
          const currentIndex = i * 7 + j
          const date = { year: this.year, disabled: false }
          // date in and not in current month
          if (currentIndex < startIndex) {
            date.date = prevMonthLastDate - startIndex + currentIndex + 1
            if (this.month > 0) {
              date.month = this.month - 1
            } else {
              date.month = 11
              date.year--
            }
          } else if (currentIndex < startIndex + daysNum) {
            date.date = currentIndex - startIndex + 1
            date.month = this.month
          } else {
            date.date = currentIndex - startIndex - daysNum + 1
            if (this.month < 11) {
              date.month = this.month + 1
            } else {
              date.month = 0
              date.year++
            }
          }
          // process limit dates
          const dateObj = new Date(date.year, date.month, date.date)
          let afterFrom = true
          let beforeTo = true
          if (this.limit && this.limit.from) {
            afterFrom = dateObj >= this.limit.from
          }
          if (this.limit && this.limit.to) {
            beforeTo = dateObj < this.limit.to
          }
          date.disabled = !afterFrom || !beforeTo
          if (isFunction(this.dateClass)) {
            date.classes = this.dateClass(dateObj, {
              currentMonth: this.month,
              currentYear: this.year,
            })
          } else {
            date.classes = ''
          }
          rows[i].push(date)
        }
      }
      return rows
    },
  },
  methods: {
    getWeekNumber,
    tWeekName(index) {
      return this.t(`uiv.datePicker.week${index}`)
    },
    getBtnType(date) {
      if (
        this.date &&
        date.date === this.date.getDate() &&
        date.month === this.date.getMonth() &&
        date.year === this.date.getFullYear()
      ) {
        return 'primary'
      } else if (
        date.date === this.today.getDate() &&
        date.month === this.today.getMonth() &&
        date.year === this.today.getFullYear()
      ) {
        return 'info'
      } else {
        return 'default'
      }
    },
    select(date) {
      this.$emit('date-change', date)
    },
    goPrevMonth() {
      let month = this.month
      let year = this.year
      if (this.month > 0) {
        month--
      } else {
        month = 11
        year--
        this.$emit('year-change', year)
      }
      this.$emit('month-change', month)
    },
    goNextMonth() {
      let month = this.month
      let year = this.year
      if (this.month < 11) {
        month++
      } else {
        month = 0
        year++
        this.$emit('year-change', year)
      }
      this.$emit('month-change', month)
    },
    changeView() {
      this.$emit('view-change', 'm')
    },
  },
}


<|start_filename|>src/components/carousel/Slide.js<|end_filename|>
import { spliceIfExist } from '../../utils/array.utils'

export default {
  data() {
    return {
      slideClass: {
        active: false,
        prev: false,
        next: false,
        left: false,
        right: false,
      },
    }
  },
  created() {
    try {
      this.$parent.slides.push(this)
    } catch (e) {
      throw new Error('Slide parent must be Carousel.')
    }
  },
  beforeDestroy() {
    const slides = this.$parent && this.$parent.slides
    spliceIfExist(slides, this)
  },
}
