325 lines
6.6 KiB
JavaScript

const inBrowser = typeof window !== 'undefined' && window !== null;
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() {
return this.intersectionRatio > 0;
},
}
);
}
return true;
}
return false;
}
export const hasIntersectionObserver = checkIntersectionObserver();
export const modeType = {
event: 'event',
observer: 'observer',
};
// CustomEvent polyfill
const CustomEvent = (function () {
if (!inBrowser) return;
if (typeof window.CustomEvent === 'function') return window.CustomEvent;
function CustomEvent(event, params) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
const 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 getBestSelectionFromSrcset(el, scale) {
if (el.tagName !== 'IMG' || !el.getAttribute('data-srcset')) return;
let options = el.getAttribute('data-srcset');
const container = el.parentNode;
const containerWidth = container.offsetWidth * scale;
let spaceIndex;
let tmpSrc;
let tmpWidth;
options = options.trim().split(',');
const result = 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
);
}
return [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 lastRun = 0;
return function (...args) {
if (timeout) {
return;
}
const elapsed = Date.now() - lastRun;
const runCallback = () => {
lastRun = Date.now();
timeout = false;
action.apply(this, args);
};
if (elapsed >= delay) {
runCallback();
} else {
timeout = setTimeout(runCallback, delay);
}
};
}
function testSupportsPassive() {
if (!inBrowser) return;
let support = false;
try {
const opts = Object.defineProperty({}, 'passive', {
// eslint-disable-next-line getter-return
get() {
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,
passive: true,
});
} else {
el.addEventListener(type, func, capture);
}
},
off(el, type, func, capture = false) {
el.removeEventListener(type, func, capture);
},
};
const loadImageAsync = (item, resolve, reject) => {
const 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 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,
find,
noop,
_,
isObject,
throttle,
supportWebp,
getDPR,
scrollParent,
loadImageAsync,
getBestSelectionFromSrcset,
};