| import throttle from 'throttle-debounce/debounce'; | 
| import { | 
|   isHtmlElement, | 
|   isFunction, | 
|   isUndefined, | 
|   isDefined | 
| } from 'element-ui/src/utils/types'; | 
| import { | 
|   getScrollContainer | 
| } from 'element-ui/src/utils/dom'; | 
|   | 
| const getStyleComputedProperty = (element, property) => { | 
|   if (element === window) { | 
|     element = document.documentElement; | 
|   } | 
|   | 
|   if (element.nodeType !== 1) { | 
|     return []; | 
|   } | 
|   // NOTE: 1 DOM access here | 
|   const css = window.getComputedStyle(element, null); | 
|   return property ? css[property] : css; | 
| }; | 
|   | 
| const entries = (obj) => { | 
|   return Object.keys(obj || {}) | 
|     .map(key => ([key, obj[key]])); | 
| }; | 
|   | 
| const getPositionSize = (el, prop) => { | 
|   return el === window || el === document | 
|     ? document.documentElement[prop] | 
|     : el[prop]; | 
| }; | 
|   | 
| const getOffsetHeight = el => { | 
|   return getPositionSize(el, 'offsetHeight'); | 
| }; | 
|   | 
| const getClientHeight = el => { | 
|   return getPositionSize(el, 'clientHeight'); | 
| }; | 
|   | 
| const scope = 'ElInfiniteScroll'; | 
| const attributes = { | 
|   delay: { | 
|     type: Number, | 
|     default: 200 | 
|   }, | 
|   distance: { | 
|     type: Number, | 
|     default: 0 | 
|   }, | 
|   disabled: { | 
|     type: Boolean, | 
|     default: false | 
|   }, | 
|   immediate: { | 
|     type: Boolean, | 
|     default: true | 
|   } | 
| }; | 
|   | 
| const getScrollOptions = (el, vm) => { | 
|   if (!isHtmlElement(el)) return {}; | 
|   | 
|   return entries(attributes).reduce((map, [key, option]) => { | 
|     const { type, default: defaultValue } = option; | 
|     let value = el.getAttribute(`infinite-scroll-${key}`); | 
|     value = isUndefined(vm[value]) ? value : vm[value]; | 
|     switch (type) { | 
|       case Number: | 
|         value = Number(value); | 
|         value = Number.isNaN(value) ? defaultValue : value; | 
|         break; | 
|       case Boolean: | 
|         value = isDefined(value) ? value === 'false' ? false : Boolean(value) : defaultValue; | 
|         break; | 
|       default: | 
|         value = type(value); | 
|     } | 
|     map[key] = value; | 
|     return map; | 
|   }, {}); | 
| }; | 
|   | 
| const getElementTop = el => el.getBoundingClientRect().top; | 
|   | 
| const handleScroll = function(cb) { | 
|   const { el, vm, container, observer } = this[scope]; | 
|   const { distance, disabled } = getScrollOptions(el, vm); | 
|   | 
|   if (disabled) return; | 
|   | 
|   const containerInfo = container.getBoundingClientRect(); | 
|   if (!containerInfo.width && !containerInfo.height) return; | 
|   | 
|   let shouldTrigger = false; | 
|   | 
|   if (container === el) { | 
|     // be aware of difference between clientHeight & offsetHeight & window.getComputedStyle().height | 
|     const scrollBottom = container.scrollTop + getClientHeight(container); | 
|     shouldTrigger = container.scrollHeight - scrollBottom <= distance; | 
|   } else { | 
|     const heightBelowTop = getOffsetHeight(el) + getElementTop(el) - getElementTop(container); | 
|     const offsetHeight = getOffsetHeight(container); | 
|     const borderBottom = Number.parseFloat(getStyleComputedProperty(container, 'borderBottomWidth')); | 
|     shouldTrigger = heightBelowTop - offsetHeight + borderBottom <= distance; | 
|   } | 
|   | 
|   if (shouldTrigger && isFunction(cb)) { | 
|     cb.call(vm); | 
|   } else if (observer) { | 
|     observer.disconnect(); | 
|     this[scope].observer = null; | 
|   } | 
|   | 
| }; | 
|   | 
| export default { | 
|   name: 'InfiniteScroll', | 
|   inserted(el, binding, vnode) { | 
|     const cb = binding.value; | 
|   | 
|     const vm = vnode.context; | 
|     // only include vertical scroll | 
|     const container = getScrollContainer(el, true); | 
|     const { delay, immediate } = getScrollOptions(el, vm); | 
|     const onScroll = throttle(delay, handleScroll.bind(el, cb)); | 
|   | 
|     el[scope] = { el, vm, container, onScroll }; | 
|   | 
|     if (container) { | 
|       container.addEventListener('scroll', onScroll); | 
|   | 
|       if (immediate) { | 
|         const observer = el[scope].observer = new MutationObserver(onScroll); | 
|         observer.observe(container, { childList: true, subtree: true }); | 
|         onScroll(); | 
|       } | 
|     } | 
|   }, | 
|   unbind(el) { | 
|     const { container, onScroll } = el[scope]; | 
|     if (container) { | 
|       container.removeEventListener('scroll', onScroll); | 
|     } | 
|   } | 
| }; |