mirror of
				https://github.com/crater-invoice/crater.git
				synced 2025-11-04 06:23:17 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			627 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			627 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import { ref, toRefs, computed, watch, nextTick } from 'vue'
 | 
						|
import normalize from './../utils/normalize'
 | 
						|
import isObject from './../utils/isObject'
 | 
						|
import isNullish from './../utils/isNullish'
 | 
						|
import arraysEqual from './../utils/arraysEqual'
 | 
						|
 | 
						|
export default function useOptions(props, context, dep) {
 | 
						|
  const {
 | 
						|
    options, mode, trackBy, limit, hideSelected, createTag, label,
 | 
						|
    appendNewTag, multipleLabel, object, loading, delay, resolveOnLoad,
 | 
						|
    minChars, filterResults, clearOnSearch, clearOnSelect, valueProp,
 | 
						|
    canDeselect, max, strict, closeOnSelect, groups: groupped, groupLabel,
 | 
						|
    groupOptions, groupHideEmpty, groupSelect,
 | 
						|
  } = toRefs(props)
 | 
						|
 | 
						|
  // ============ DEPENDENCIES ============
 | 
						|
 | 
						|
  const iv = dep.iv
 | 
						|
  const ev = dep.ev
 | 
						|
  const search = dep.search
 | 
						|
  const clearSearch = dep.clearSearch
 | 
						|
  const update = dep.update
 | 
						|
  const pointer = dep.pointer
 | 
						|
  const clearPointer = dep.clearPointer
 | 
						|
  const blur = dep.blur
 | 
						|
  const deactivate = dep.deactivate
 | 
						|
 | 
						|
  // ================ DATA ================
 | 
						|
 | 
						|
  // no export
 | 
						|
  // appendedOptions
 | 
						|
  const ap = ref([])
 | 
						|
 | 
						|
  // no export
 | 
						|
  // resolvedOptions
 | 
						|
  const ro = ref([])
 | 
						|
 | 
						|
  const resolving = ref(false)
 | 
						|
 | 
						|
  // ============== COMPUTED ==============
 | 
						|
 | 
						|
  // no export
 | 
						|
  // extendedOptions
 | 
						|
  const eo = computed(() => {
 | 
						|
    if (groupped.value) {
 | 
						|
      let groups = ro.value || /* istanbul ignore next */[]
 | 
						|
 | 
						|
      let eo = []
 | 
						|
 | 
						|
      groups.forEach((group) => {
 | 
						|
        optionsToArray(group[groupOptions.value]).forEach((option) => {
 | 
						|
          eo.push(Object.assign({}, option, group.disabled ? { disabled: true } : {}))
 | 
						|
        })
 | 
						|
      })
 | 
						|
 | 
						|
      return eo
 | 
						|
    } else {
 | 
						|
      let eo = optionsToArray(ro.value || [])
 | 
						|
 | 
						|
      if (ap.value.length) {
 | 
						|
        eo = eo.concat(ap.value)
 | 
						|
      }
 | 
						|
 | 
						|
      return eo
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  const fg = computed(() => {
 | 
						|
    if (!groupped.value) {
 | 
						|
      return []
 | 
						|
    }
 | 
						|
 | 
						|
    return filterGroups((ro.value || /* istanbul ignore next */[]).map((group) => {
 | 
						|
      const arrayOptions = optionsToArray(group[groupOptions.value])
 | 
						|
 | 
						|
      return {
 | 
						|
        ...group,
 | 
						|
        group: true,
 | 
						|
        [groupOptions.value]: filterOptions(arrayOptions, false).map(o => Object.assign({}, o, group.disabled ? { disabled: true } : {})),
 | 
						|
        __VISIBLE__: filterOptions(arrayOptions).map(o => Object.assign({}, o, group.disabled ? { disabled: true } : {})),
 | 
						|
      }
 | 
						|
      // Difference between __VISIBLE__ and {groupOptions}: visible does not contain selected options when hideSelected=true
 | 
						|
    }))
 | 
						|
  })
 | 
						|
 | 
						|
  // filteredOptions
 | 
						|
  const fo = computed(() => {
 | 
						|
    let options = eo.value
 | 
						|
 | 
						|
    if (createdTag.value.length) {
 | 
						|
      options = createdTag.value.concat(options)
 | 
						|
    }
 | 
						|
 | 
						|
    options = filterOptions(options)
 | 
						|
 | 
						|
    if (limit.value > 0) {
 | 
						|
      options = options.slice(0, limit.value)
 | 
						|
    }
 | 
						|
 | 
						|
    return options
 | 
						|
  })
 | 
						|
 | 
						|
  const hasSelected = computed(() => {
 | 
						|
    switch (mode.value) {
 | 
						|
      case 'single':
 | 
						|
        return !isNullish(iv.value[valueProp.value])
 | 
						|
 | 
						|
      case 'multiple':
 | 
						|
      case 'tags':
 | 
						|
        return !isNullish(iv.value) && iv.value.length > 0
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  const multipleLabelText = computed(() => {
 | 
						|
    return multipleLabel !== undefined && multipleLabel.value !== undefined
 | 
						|
      ? multipleLabel.value(iv.value)
 | 
						|
      : (iv.value && iv.value.length > 1 ? `${iv.value.length} options selected` : `1 option selected`)
 | 
						|
  })
 | 
						|
 | 
						|
  const noOptions = computed(() => {
 | 
						|
    return !eo.value.length && !resolving.value && !createdTag.value.length
 | 
						|
  })
 | 
						|
 | 
						|
 | 
						|
  const noResults = computed(() => {
 | 
						|
    return eo.value.length > 0 && fo.value.length == 0 && ((search.value && groupped.value) || !groupped.value)
 | 
						|
  })
 | 
						|
 | 
						|
  // no export
 | 
						|
  const createdTag = computed(() => {
 | 
						|
    if (createTag.value === false || !search.value) {
 | 
						|
      return []
 | 
						|
    }
 | 
						|
 | 
						|
    return getOptionByTrackBy(search.value) !== -1 ? [] : [{
 | 
						|
      [valueProp.value]: search.value,
 | 
						|
      [label.value]: search.value,
 | 
						|
      [trackBy.value]: search.value,
 | 
						|
    }]
 | 
						|
  })
 | 
						|
 | 
						|
  // no export
 | 
						|
  const nullValue = computed(() => {
 | 
						|
    switch (mode.value) {
 | 
						|
      case 'single':
 | 
						|
        return null
 | 
						|
 | 
						|
      case 'multiple':
 | 
						|
      case 'tags':
 | 
						|
        return []
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  const busy = computed(() => {
 | 
						|
    return loading.value || resolving.value
 | 
						|
  })
 | 
						|
 | 
						|
  // =============== METHODS ==============
 | 
						|
 | 
						|
  /**
 | 
						|
   * @param {array|object|string|number} option
 | 
						|
   */
 | 
						|
  const select = (option) => {
 | 
						|
    if (typeof option !== 'object') {
 | 
						|
      option = getOption(option)
 | 
						|
    }
 | 
						|
 | 
						|
    switch (mode.value) {
 | 
						|
      case 'single':
 | 
						|
        update(option)
 | 
						|
        break
 | 
						|
 | 
						|
      case 'multiple':
 | 
						|
      case 'tags':
 | 
						|
        update((iv.value).concat(option))
 | 
						|
        break
 | 
						|
    }
 | 
						|
 | 
						|
    context.emit('select', finalValue(option), option)
 | 
						|
  }
 | 
						|
 | 
						|
  const deselect = (option) => {
 | 
						|
    if (typeof option !== 'object') {
 | 
						|
      option = getOption(option)
 | 
						|
    }
 | 
						|
 | 
						|
    switch (mode.value) {
 | 
						|
      case 'single':
 | 
						|
        clear()
 | 
						|
        break
 | 
						|
 | 
						|
      case 'tags':
 | 
						|
      case 'multiple':
 | 
						|
        update(Array.isArray(option)
 | 
						|
          ? iv.value.filter(v => option.map(o => o[valueProp.value]).indexOf(v[valueProp.value]) === -1)
 | 
						|
          : iv.value.filter(v => v[valueProp.value] != option[valueProp.value]))
 | 
						|
        break
 | 
						|
    }
 | 
						|
 | 
						|
    context.emit('deselect', finalValue(option), option)
 | 
						|
  }
 | 
						|
 | 
						|
  // no export
 | 
						|
  const finalValue = (option) => {
 | 
						|
    return object.value ? option : option[valueProp.value]
 | 
						|
  }
 | 
						|
 | 
						|
  const remove = (option) => {
 | 
						|
    deselect(option)
 | 
						|
  }
 | 
						|
 | 
						|
  const handleTagRemove = (option, e) => {
 | 
						|
    if (e.button !== 0) {
 | 
						|
      e.preventDefault()
 | 
						|
      return
 | 
						|
    }
 | 
						|
 | 
						|
    remove(option)
 | 
						|
  }
 | 
						|
 | 
						|
  const clear = () => {
 | 
						|
    context.emit('clear')
 | 
						|
    update(nullValue.value)
 | 
						|
  }
 | 
						|
 | 
						|
  const isSelected = (option) => {
 | 
						|
    if (option.group !== undefined) {
 | 
						|
      return mode.value === 'single' ? false : areAllSelected(option[groupOptions.value]) && option[groupOptions.value].length
 | 
						|
    }
 | 
						|
 | 
						|
    switch (mode.value) {
 | 
						|
      case 'single':
 | 
						|
        return !isNullish(iv.value) && iv.value[valueProp.value] == option[valueProp.value]
 | 
						|
 | 
						|
      case 'tags':
 | 
						|
      case 'multiple':
 | 
						|
        return !isNullish(iv.value) && iv.value.map(o => o[valueProp.value]).indexOf(option[valueProp.value]) !== -1
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  const isDisabled = (option) => {
 | 
						|
    return option.disabled === true
 | 
						|
  }
 | 
						|
 | 
						|
  const isMax = () => {
 | 
						|
    if (max === undefined || max.value === -1 || (!hasSelected.value && max.value > 0)) {
 | 
						|
      return false
 | 
						|
    }
 | 
						|
 | 
						|
    return iv.value.length >= max.value
 | 
						|
  }
 | 
						|
 | 
						|
  const handleOptionClick = (option) => {
 | 
						|
    if (isDisabled(option)) {
 | 
						|
      return
 | 
						|
    }
 | 
						|
 | 
						|
    switch (mode.value) {
 | 
						|
      case 'single':
 | 
						|
        if (isSelected(option)) {
 | 
						|
          if (canDeselect.value) {
 | 
						|
            deselect(option)
 | 
						|
          }
 | 
						|
          return
 | 
						|
        }
 | 
						|
 | 
						|
        blur()
 | 
						|
        select(option)
 | 
						|
        break
 | 
						|
 | 
						|
      case 'multiple':
 | 
						|
        if (isSelected(option)) {
 | 
						|
          deselect(option)
 | 
						|
          return
 | 
						|
        }
 | 
						|
 | 
						|
        if (isMax()) {
 | 
						|
          return
 | 
						|
        }
 | 
						|
 | 
						|
        select(option)
 | 
						|
 | 
						|
        if (clearOnSelect.value) {
 | 
						|
          clearSearch()
 | 
						|
        }
 | 
						|
 | 
						|
        if (hideSelected.value) {
 | 
						|
          clearPointer()
 | 
						|
        }
 | 
						|
 | 
						|
        // If we need to close the dropdown on select we also need
 | 
						|
        // to blur the input, otherwise further searches will not
 | 
						|
        // display any options
 | 
						|
        if (closeOnSelect.value) {
 | 
						|
          blur()
 | 
						|
        }
 | 
						|
        break
 | 
						|
 | 
						|
      case 'tags':
 | 
						|
        if (isSelected(option)) {
 | 
						|
          deselect(option)
 | 
						|
          return
 | 
						|
        }
 | 
						|
 | 
						|
        if (isMax()) {
 | 
						|
          return
 | 
						|
        }
 | 
						|
 | 
						|
        if (getOption(option[valueProp.value]) === undefined && createTag.value) {
 | 
						|
          context.emit('tag', option[valueProp.value])
 | 
						|
 | 
						|
          if (appendNewTag.value) {
 | 
						|
            appendOption(option)
 | 
						|
          }
 | 
						|
 | 
						|
          clearSearch()
 | 
						|
        }
 | 
						|
 | 
						|
        if (clearOnSelect.value) {
 | 
						|
          clearSearch()
 | 
						|
        }
 | 
						|
 | 
						|
        select(option)
 | 
						|
 | 
						|
        if (hideSelected.value) {
 | 
						|
          clearPointer()
 | 
						|
        }
 | 
						|
 | 
						|
        // If we need to close the dropdown on select we also need
 | 
						|
        // to blur the input, otherwise further searches will not
 | 
						|
        // display any options
 | 
						|
        if (closeOnSelect.value) {
 | 
						|
          blur()
 | 
						|
        }
 | 
						|
        break
 | 
						|
    }
 | 
						|
 | 
						|
    if (closeOnSelect.value) {
 | 
						|
      deactivate()
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  const handleGroupClick = (group) => {
 | 
						|
    if (isDisabled(group) || mode.value === 'single' || !groupSelect.value) {
 | 
						|
      return
 | 
						|
    }
 | 
						|
 | 
						|
    switch (mode.value) {
 | 
						|
      case 'multiple':
 | 
						|
      case 'tags':
 | 
						|
        if (areAllEnabledSelected(group[groupOptions.value])) {
 | 
						|
          deselect(group[groupOptions.value])
 | 
						|
        } else {
 | 
						|
          select(group[groupOptions.value]
 | 
						|
            .filter(o => iv.value.map(v => v[valueProp.value]).indexOf(o[valueProp.value]) === -1)
 | 
						|
            .filter(o => !o.disabled)
 | 
						|
            .filter((o, k) => iv.value.length + 1 + k <= max.value || max.value === -1)
 | 
						|
          )
 | 
						|
        }
 | 
						|
        break
 | 
						|
    }
 | 
						|
 | 
						|
    if (closeOnSelect.value) {
 | 
						|
      deactivate()
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // no export
 | 
						|
  const areAllEnabledSelected = (options) => {
 | 
						|
    return options.find(o => !isSelected(o) && !o.disabled) === undefined
 | 
						|
  }
 | 
						|
 | 
						|
  // no export
 | 
						|
  const areAllSelected = (options) => {
 | 
						|
    return options.find(o => !isSelected(o)) === undefined
 | 
						|
  }
 | 
						|
 | 
						|
  const getOption = (val) => {
 | 
						|
    return eo.value[eo.value.map(o => String(o[valueProp.value])).indexOf(String(val))]
 | 
						|
  }
 | 
						|
 | 
						|
  // no export
 | 
						|
  const getOptionByTrackBy = (val, norm = true) => {
 | 
						|
    return eo.value.map(o => o[trackBy.value]).indexOf(val)
 | 
						|
  }
 | 
						|
 | 
						|
  // no export
 | 
						|
  const shouldHideOption = (option) => {
 | 
						|
    return ['tags', 'multiple'].indexOf(mode.value) !== -1 && hideSelected.value && isSelected(option)
 | 
						|
  }
 | 
						|
 | 
						|
  // no export
 | 
						|
  const appendOption = (option) => {
 | 
						|
    ap.value.push(option)
 | 
						|
  }
 | 
						|
 | 
						|
  // no export
 | 
						|
  const filterGroups = (groups) => {
 | 
						|
    // If the search has value we need to filter among
 | 
						|
    // he ones that are visible to the user to avoid
 | 
						|
    // displaying groups which technically have options
 | 
						|
    // based on search but that option is already selected.
 | 
						|
    return groupHideEmpty.value
 | 
						|
      ? groups.filter(g => search.value
 | 
						|
        ? g.__VISIBLE__.length
 | 
						|
        : g[groupOptions.value].length
 | 
						|
      )
 | 
						|
      : groups.filter(g => search.value ? g.__VISIBLE__.length : true)
 | 
						|
  }
 | 
						|
 | 
						|
  // no export
 | 
						|
  const filterOptions = (options, excludeHideSelected = true) => {
 | 
						|
    let fo = options
 | 
						|
 | 
						|
    if (search.value && filterResults.value) {
 | 
						|
      fo = fo.filter((option) => {
 | 
						|
        return normalize(option[trackBy.value], strict.value).indexOf(normalize(search.value, strict.value)) !== -1
 | 
						|
      })
 | 
						|
    }
 | 
						|
 | 
						|
    if (hideSelected.value && excludeHideSelected) {
 | 
						|
      fo = fo.filter((option) => !shouldHideOption(option))
 | 
						|
    }
 | 
						|
 | 
						|
    return fo
 | 
						|
  }
 | 
						|
 | 
						|
  // no export
 | 
						|
  const optionsToArray = (options) => {
 | 
						|
    let uo = options
 | 
						|
 | 
						|
    // Transforming an object to an array of objects
 | 
						|
    if (isObject(uo)) {
 | 
						|
      uo = Object.keys(uo).map((key) => {
 | 
						|
        let val = uo[key]
 | 
						|
 | 
						|
        return { [valueProp.value]: key, [trackBy.value]: val, [label.value]: val }
 | 
						|
      })
 | 
						|
    }
 | 
						|
 | 
						|
    // Transforming an plain arrays to an array of objects
 | 
						|
    uo = uo.map((val) => {
 | 
						|
      return typeof val === 'object' ? val : { [valueProp.value]: val, [trackBy.value]: val, [label.value]: val }
 | 
						|
    })
 | 
						|
 | 
						|
    return uo
 | 
						|
  }
 | 
						|
 | 
						|
  // no export
 | 
						|
  const initInternalValue = () => {
 | 
						|
    if (!isNullish(ev.value)) {
 | 
						|
      iv.value = makeInternal(ev.value)
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  const resolveOptions = (callback) => {
 | 
						|
    resolving.value = true
 | 
						|
 | 
						|
    options.value(search.value).then((response) => {
 | 
						|
      ro.value = response
 | 
						|
 | 
						|
      if (typeof callback == 'function') {
 | 
						|
        callback(response)
 | 
						|
      }
 | 
						|
 | 
						|
      resolving.value = false
 | 
						|
    })
 | 
						|
  }
 | 
						|
 | 
						|
  // no export
 | 
						|
  const refreshLabels = () => {
 | 
						|
    if (!hasSelected.value) {
 | 
						|
      return
 | 
						|
    }
 | 
						|
 | 
						|
    if (mode.value === 'single') {
 | 
						|
      let newLabel = getOption(iv.value[valueProp.value])[label.value]
 | 
						|
 | 
						|
      iv.value[label.value] = newLabel
 | 
						|
 | 
						|
      if (object.value) {
 | 
						|
        ev.value[label.value] = newLabel
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      iv.value.forEach((val, i) => {
 | 
						|
        let newLabel = getOption(iv.value[i][valueProp.value])[label.value]
 | 
						|
 | 
						|
        iv.value[i][label.value] = newLabel
 | 
						|
 | 
						|
        if (object.value) {
 | 
						|
          ev.value[i][label.value] = newLabel
 | 
						|
        }
 | 
						|
      })
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  const refreshOptions = (callback) => {
 | 
						|
    resolveOptions(callback)
 | 
						|
  }
 | 
						|
 | 
						|
  // no export
 | 
						|
  const makeInternal = (val) => {
 | 
						|
    if (isNullish(val)) {
 | 
						|
      return mode.value === 'single' ? {} : []
 | 
						|
    }
 | 
						|
 | 
						|
    if (object.value) {
 | 
						|
      return val
 | 
						|
    }
 | 
						|
 | 
						|
    // If external should be plain transform
 | 
						|
    // value object to plain values
 | 
						|
    return mode.value === 'single' ? getOption(val) || {} : val.filter(v => !!getOption(v)).map(v => getOption(v))
 | 
						|
  }
 | 
						|
 | 
						|
  // ================ HOOKS ===============
 | 
						|
 | 
						|
  if (mode.value !== 'single' && !isNullish(ev.value) && !Array.isArray(ev.value)) {
 | 
						|
    throw new Error(`v-model must be an array when using "${mode.value}" mode`)
 | 
						|
  }
 | 
						|
 | 
						|
  if (options && typeof options.value == 'function') {
 | 
						|
    if (resolveOnLoad.value) {
 | 
						|
      resolveOptions(initInternalValue)
 | 
						|
    } else if (object.value == true) {
 | 
						|
      initInternalValue()
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    ro.value = options.value
 | 
						|
 | 
						|
    initInternalValue()
 | 
						|
  }
 | 
						|
 | 
						|
  // ============== WATCHERS ==============
 | 
						|
 | 
						|
  if (delay.value > -1) {
 | 
						|
    watch(search, (query) => {
 | 
						|
      if (query.length < minChars.value) {
 | 
						|
        return
 | 
						|
      }
 | 
						|
 | 
						|
      resolving.value = true
 | 
						|
 | 
						|
      if (clearOnSearch.value) {
 | 
						|
        ro.value = []
 | 
						|
      }
 | 
						|
      setTimeout(() => {
 | 
						|
        if (query != search.value) {
 | 
						|
          return
 | 
						|
        }
 | 
						|
 | 
						|
        options.value(search.value).then((response) => {
 | 
						|
          if (query == search.value) {
 | 
						|
            ro.value = response
 | 
						|
            pointer.value = fo.value.filter(o => o.disabled !== true)[0] || null
 | 
						|
            resolving.value = false
 | 
						|
          }
 | 
						|
        })
 | 
						|
      }, delay.value)
 | 
						|
 | 
						|
    }, { flush: 'sync' })
 | 
						|
  }
 | 
						|
 | 
						|
  watch(ev, (newValue) => {
 | 
						|
    if (isNullish(newValue)) {
 | 
						|
      iv.value = makeInternal(newValue)
 | 
						|
      return
 | 
						|
    }
 | 
						|
 | 
						|
    switch (mode.value) {
 | 
						|
      case 'single':
 | 
						|
        if (object.value ? newValue[valueProp.value] != iv.value[valueProp.value] : newValue != iv.value[valueProp.value]) {
 | 
						|
          iv.value = makeInternal(newValue)
 | 
						|
        }
 | 
						|
        break
 | 
						|
 | 
						|
      case 'multiple':
 | 
						|
      case 'tags':
 | 
						|
        if (!arraysEqual(object.value ? newValue.map(o => o[valueProp.value]) : newValue, iv.value.map(o => o[valueProp.value]))) {
 | 
						|
          iv.value = makeInternal(newValue)
 | 
						|
        }
 | 
						|
        break
 | 
						|
    }
 | 
						|
  }, { deep: true })
 | 
						|
 | 
						|
  if (typeof props.options !== 'function') {
 | 
						|
    watch(options, (n, o) => {
 | 
						|
      ro.value = props.options
 | 
						|
 | 
						|
      if (!Object.keys(iv.value).length) {
 | 
						|
        initInternalValue()
 | 
						|
      }
 | 
						|
 | 
						|
      refreshLabels()
 | 
						|
    })
 | 
						|
  }
 | 
						|
 | 
						|
  return {
 | 
						|
    fo,
 | 
						|
    filteredOptions: fo,
 | 
						|
    hasSelected,
 | 
						|
    multipleLabelText,
 | 
						|
    eo,
 | 
						|
    extendedOptions: eo,
 | 
						|
    fg,
 | 
						|
    filteredGroups: fg,
 | 
						|
    noOptions,
 | 
						|
    noResults,
 | 
						|
    resolving,
 | 
						|
    busy,
 | 
						|
    select,
 | 
						|
    deselect,
 | 
						|
    remove,
 | 
						|
    clear,
 | 
						|
    isSelected,
 | 
						|
    isDisabled,
 | 
						|
    isMax,
 | 
						|
    getOption,
 | 
						|
    handleOptionClick,
 | 
						|
    handleGroupClick,
 | 
						|
    handleTagRemove,
 | 
						|
    refreshOptions,
 | 
						|
    resolveOptions,
 | 
						|
  }
 | 
						|
}
 |