import assign from 'assign-deep' const inBrowser = typeof window !== 'undefined' && window !== null export const hasIntersectionObserver = checkIntersectionObserver() function checkIntersectionObserver () { if (inBrowser && 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype) { // Minimal polyfill for Edge 15's lack of `isIntersecting` // See: https://github.com/w3c/IntersectionObserver/issues/211 if (!('isIntersecting' in window.IntersectionObserverEntry.prototype)) { Object.defineProperty(window.IntersectionObserverEntry.prototype, 'isIntersecting', { get: function () { return this.intersectionRatio > 0 } }) } return true } return false } export const modeType = { event: 'event', observer: 'observer' } // CustomEvent polyfill for IE const CustomEvent = (function () { if (!inBrowser) return // not IE if (typeof window.CustomEvent === 'function') return window.CustomEvent function CustomEvent (event, params) { params = params || { bubbles: false, cancelable: false, detail: undefined } var evt = document.createEvent('CustomEvent') evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail) return evt } CustomEvent.prototype = window.Event.prototype return CustomEvent })() function remove (arr, item) { if (!arr.length) return const index = arr.indexOf(item) if (index > -1) return arr.splice(index, 1) } function some (arr, fn) { let has = false for (let i = 0, len = arr.length; i < len; i++) { if (fn(arr[i])) { has = true break } } return has } function getBestSelectionFromSrcset (el, scale) { if (el.tagName !== 'IMG' || !el.getAttribute('data-srcset')) return let options = el.getAttribute('data-srcset') const result = [] const container = el.parentNode const containerWidth = container.offsetWidth * scale let spaceIndex let tmpSrc let tmpWidth options = options.trim().split(',') options.map(item => { item = item.trim() spaceIndex = item.lastIndexOf(' ') if (spaceIndex === -1) { tmpSrc = item tmpWidth = 999998 } else { tmpSrc = item.substr(0, spaceIndex) tmpWidth = parseInt(item.substr(spaceIndex + 1, item.length - spaceIndex - 2), 10) } result.push([tmpWidth, tmpSrc]) }) result.sort(function (a, b) { if (a[0] < b[0]) { return 1 } if (a[0] > b[0]) { return -1 } if (a[0] === b[0]) { if (b[1].indexOf('.webp', b[1].length - 5) !== -1) { return 1 } if (a[1].indexOf('.webp', a[1].length - 5) !== -1) { return -1 } } return 0 }) let bestSelectedSrc = '' let tmpOption for (let i = 0; i < result.length; i++) { tmpOption = result[i] bestSelectedSrc = tmpOption[1] const next = result[i + 1] if (next && next[0] < containerWidth) { bestSelectedSrc = tmpOption[1] break } else if (!next) { bestSelectedSrc = tmpOption[1] break } } return bestSelectedSrc } function find (arr, fn) { let item for (let i = 0, len = arr.length; i < len; i++) { if (fn(arr[i])) { item = arr[i] break } } return item } const getDPR = (scale = 1) => inBrowser ? (window.devicePixelRatio || scale) : scale function supportWebp () { if (!inBrowser) return false let support = true try { const elem = document.createElement('canvas') if (elem.getContext && elem.getContext('2d')) { support = elem.toDataURL('image/webp').indexOf('data:image/webp') === 0 } } catch (err) { support = false } return support } function throttle (action, delay) { let timeout = null let movement = null let lastRun = 0 let needRun = false return function () { needRun = true if (timeout) { return } let elapsed = Date.now() - lastRun let context = this let args = arguments let runCallback = function () { lastRun = Date.now() timeout = false action.apply(context, args) } if (elapsed >= delay) { runCallback() } else { timeout = setTimeout(runCallback, delay) } if (needRun) { clearTimeout(movement) movement = setTimeout(runCallback, 2 * delay) } } } function testSupportsPassive () { if (!inBrowser) return let support = false try { let opts = Object.defineProperty({}, 'passive', { get: function () { support = true } }) window.addEventListener('test', null, opts) } catch (e) {} return support } const supportsPassive = testSupportsPassive() const _ = { on (el, type, func, capture = false) { if (supportsPassive) { el.addEventListener(type, func, { capture: capture, passive: true }) } else { el.addEventListener(type, func, capture) } }, off (el, type, func, capture = false) { el.removeEventListener(type, func, capture) } } const loadImageAsync = (item, resolve, reject) => { let image = new Image() if (!item || !item.src) { const err = new Error('image src is required') return reject(err) } image.src = item.src if (item.cors) { image.crossOrigin = item.cors } image.onload = function () { resolve({ naturalHeight: image.naturalHeight, naturalWidth: image.naturalWidth, src: image.src }) } image.onerror = function (e) { reject(e) } } const style = (el, prop) => { return typeof getComputedStyle !== 'undefined' ? getComputedStyle(el, null).getPropertyValue(prop) : el.style[prop] } const overflow = (el) => { return style(el, 'overflow') + style(el, 'overflow-y') + style(el, 'overflow-x') } const scrollParent = (el) => { if (!inBrowser) return if (!(el instanceof HTMLElement)) { return window } let parent = el while (parent) { if (parent === document.body || parent === document.documentElement) { break } if (!parent.parentNode) { break } if (/(scroll|auto)/.test(overflow(parent))) { return parent } parent = parent.parentNode } return window } function isObject (obj) { return obj !== null && typeof obj === 'object' } function ObjectKeys (obj) { if (!(obj instanceof Object)) return [] if (Object.keys) { return Object.keys(obj) } else { let keys = [] for (let key in obj) { if (obj.hasOwnProperty(key)) { keys.push(key) } } return keys } } function ArrayFrom (arrLike) { let len = arrLike.length const list = [] for (let i = 0; i < len; i++) { list.push(arrLike[i]) } return list } function noop () {} class ImageCache { constructor ({ max }) { this.options = { max: max || 100 } this._caches = [] } has (key) { return this._caches.indexOf(key) > -1 } add (key) { if (this.has(key)) return this._caches.push(key) if (this._caches.length > this.options.max) { this.free() } } free () { this._caches.shift() } } export { ImageCache, inBrowser, CustomEvent, remove, some, find, assign, noop, ArrayFrom, _, isObject, throttle, supportWebp, getDPR, scrollParent, loadImageAsync, getBestSelectionFromSrcset, ObjectKeys }