diff --git a/.babelrc b/.babelrc
index 3ed94df..9bd07a7 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,5 +1,13 @@
{
"presets": [
- ["env", { "modules": false }]
+ [
+ "env",
+ {
+ "modules": false
+ }
+ ]
+ ],
+ "plugins": [
+ "external-helpers"
]
-}
+}
\ No newline at end of file
diff --git a/.circleci/config.yml b/.circleci/config.yml
index ca093f1..8c39cd3 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -7,7 +7,7 @@ jobs:
build:
docker:
# specify the version you desire here
- - image: circleci/node:7.10
+ - image: circleci/node:8.9.4
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
diff --git a/README.md b/README.md
index 396a267..f03004c 100644
--- a/README.md
+++ b/README.md
@@ -68,7 +68,7 @@ CDN: [https://unpkg.com/vue-lazyload/vue-lazyload.js](https://unpkg.com/vue-lazy
# Usage
-main.js
+main.js:
```javascript
@@ -94,6 +94,26 @@ new Vue({
})
```
+template:
+
+```html
+
+ -
+
+
+
+```
+
+use `v-lazy-container` work with raw HTML
+
+```html
+
+```
+
## Constructor Options
|key|description|default|options|
diff --git a/build.js b/build.js
index e0ce76b..847c881 100644
--- a/build.js
+++ b/build.js
@@ -16,10 +16,7 @@ async function build() {
const bundle = await rollup.rollup({
input: path.resolve(__dirname, 'src/index.js'),
plugins: [
- babel({
- exclude: 'node_modules/**',
- plugins: ['external-helpers']
- }),
+ babel({ runtimeHelpers: true }),
uglify()
]
})
diff --git a/karma.conf.js b/karma.conf.js
new file mode 100644
index 0000000..b1d671f
--- /dev/null
+++ b/karma.conf.js
@@ -0,0 +1,50 @@
+const DEBUG = !!process.env.DEBUG
+if (!DEBUG) process.env.CHROME_BIN = require('puppeteer').executablePath()
+
+module.exports = function (config) {
+ config.set({
+ basePath: '',
+ frameworks: ['mocha', 'chai'],
+ plugins: [
+ require('karma-mocha'),
+ require('karma-chai'),
+ require('karma-chrome-launcher'),
+ require('karma-rollup-preprocessor'),
+ // require('karma-coverage')
+ ],
+ files: [
+ { pattern: 'test/*-spec.js', watched: false }
+ ],
+ exclude: [],
+ preprocessors: {
+ 'test/*-spec.js': ['rollup']
+ },
+ // coverageReporter: {
+ // type: 'html',
+ // dir: 'coverage/'
+ // },
+ rollupPreprocessor: {
+ plugins: [
+ require('rollup-plugin-babel')(),
+ require('rollup-plugin-node-resolve')({
+ jsnext: true,
+ browser: true
+ }),
+ require('rollup-plugin-replace')({
+ 'process.env.NODE_ENV': JSON.stringify( 'production' )
+ })
+ ],
+ format: 'iife', // Helps prevent naming collisions.
+ name: 'lib', // Required for 'iife' format.
+ sourcemap: 'inline' // Sensible for testing.
+ },
+ reporters: ['progress'],
+ port: 9876,
+ colors: true,
+ logLevel: config.LOG_INFO,
+ autoWatch: true,
+ browsers: DEBUG ? ['Chrome'] : ['ChromeHeadless'],
+ singleRun: !DEBUG,
+ concurrency: Infinity
+ })
+}
diff --git a/package.json b/package.json
index a24fd49..6e64f6e 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,13 @@
{
"name": "vue-lazyload",
- "version": "1.1.4",
+ "version": "1.2.0",
"description": "Vue module for lazy-loading images in your vue.js applications.",
"main": "vue-lazyload.js",
"unpkg": "vue-lazyload.js",
"scripts": {
- "start": "node build",
- "compile": "babel --presets env -d lib/ src/",
- "test": "npm run compile && mocha --require babel-core/register"
+ "build": "node build",
+ "test": "karma start",
+ "test:debug": "cross-env DEBUG=true karma start"
},
"dependencies": {},
"repository": {
@@ -24,6 +24,11 @@
"bugs": {
"url": "https://github.com/hilongjw/vue-lazyload/issues"
},
+ "browserslist": [
+ "> 1%",
+ "last 2 versions",
+ "not ie <= 8"
+ ],
"license": "MIT",
"devDependencies": {
"babel-cli": "^6.26.0",
@@ -34,10 +39,21 @@
"babel-preset-stage-0": "^6.24.1",
"babel-register": "^6.26.0",
"chai": "^4.1.2",
+ "karma": "^1.7.1",
+ "karma-chai": "^0.1.0",
+ "karma-chrome-launcher": "^2.2.0",
+ "karma-coverage": "^1.1.1",
+ "karma-expect": "^1.1.3",
+ "karma-mocha": "^1.3.0",
+ "karma-rollup-preprocessor": "^5.0.2",
"mocha": "^4.0.1",
- "rollup": "^0.52.1",
- "rollup-plugin-babel": "^3.0.2",
- "rollup-plugin-uglify": "^2.0.1",
- "vue": "^2.5.9"
+ "puppeteer": "^0.13.0",
+ "rollup": "^0.51.1",
+ "rollup-plugin-babel": "^2.6.1",
+ "rollup-plugin-commonjs": "^8.2.6",
+ "rollup-plugin-node-resolve": "^3.0.2",
+ "rollup-plugin-replace": "^2.0.0",
+ "rollup-plugin-uglify": "^1.0.1",
+ "vue": "^2.5.13"
}
}
diff --git a/src/index.js b/src/index.js
index 168d917..05dba38 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,50 +1,73 @@
import Lazy from './lazy'
import LazyComponent from './lazy-component'
+import LazyContainer from './lazy-container'
import { assign } from './util'
export default {
- /**
- * install function
- * @param {Vue} Vue
- * @param {object} options lazyload options
- */
- install (Vue, options = {}) {
- const LazyClass = Lazy(Vue)
- const lazy = new LazyClass(options)
+ /*
+ * install function
+ * @param {Vue} Vue
+ * @param {object} options lazyload options
+ */
+ install (Vue, options = {}) {
+ const LazyClass = Lazy(Vue)
+ const lazy = new LazyClass(options)
+ const lazyContainer = new LazyContainer({ lazy })
- const isVueNext = Vue.version.split('.')[0] === '2'
+ const isVue2 = Vue.version.split('.')[0] === '2'
- Vue.prototype.$Lazyload = lazy
+ Vue.prototype.$Lazyload = lazy
- if (options.lazyComponent) {
- Vue.component('lazy-component', LazyComponent(lazy))
+ if (options.lazyComponent) {
+ Vue.component('lazy-component', LazyComponent(lazy))
+ }
+
+ if (isVue2) {
+ Vue.directive('lazy', {
+ bind: lazy.add.bind(lazy),
+ update: lazy.update.bind(lazy),
+ componentUpdated: lazy.lazyLoadHandler.bind(lazy),
+ unbind: lazy.remove.bind(lazy)
+ })
+ Vue.directive('lazy-container', {
+ bind: lazyContainer.bind.bind(lazyContainer),
+ update: lazyContainer.update.bind(lazyContainer),
+ unbind: lazyContainer.unbind.bind(lazyContainer)
+ })
+ } else {
+ Vue.directive('lazy', {
+ bind: lazy.lazyLoadHandler.bind(lazy),
+ update (newValue, oldValue) {
+ assign(this.vm.$refs, this.vm.$els)
+ lazy.add(this.el, {
+ modifiers: this.modifiers || {},
+ arg: this.arg,
+ value: newValue,
+ oldValue: oldValue
+ }, {
+ context: this.vm
+ })
+ },
+ unbind () {
+ lazy.remove(this.el)
}
+ })
- if (isVueNext) {
- Vue.directive('lazy', {
- bind: lazy.add.bind(lazy),
- update: lazy.update.bind(lazy),
- componentUpdated: lazy.lazyLoadHandler.bind(lazy),
- unbind : lazy.remove.bind(lazy)
- })
- } else {
- Vue.directive('lazy', {
- bind: lazy.lazyLoadHandler.bind(lazy),
- update (newValue, oldValue) {
- assign(this.vm.$refs, this.vm.$els)
- lazy.add(this.el, {
- modifiers: this.modifiers || {},
- arg: this.arg,
- value: newValue,
- oldValue: oldValue
- }, {
- context: this.vm
- })
- },
- unbind () {
- lazy.remove(this.el)
- }
- })
+ Vue.directive('lazy-container', {
+ update (newValue, oldValue) {
+ lazyContainer.update(this.el, {
+ modifiers: this.modifiers || {},
+ arg: this.arg,
+ value: newValue,
+ oldValue: oldValue
+ }, {
+ context: this.vm
+ })
+ },
+ unbind () {
+ lazyContainer.unbind(this.el)
}
+ })
}
-}
\ No newline at end of file
+ }
+}
diff --git a/src/lazy-component.js b/src/lazy-component.js
index 8643312..e503d8c 100644
--- a/src/lazy-component.js
+++ b/src/lazy-component.js
@@ -1,52 +1,52 @@
import { inBrowser } from './util'
export default (lazy) => {
- return {
- props: {
- tag: {
- type: String,
- default: 'div'
- }
+ return {
+ props: {
+ tag: {
+ type: String,
+ default: 'div'
+ }
+ },
+ render (h) {
+ if (this.show === false) {
+ return h(this.tag)
+ }
+ return h(this.tag, null, this.$slots.default)
+ },
+ data () {
+ return {
+ el: null,
+ state: {
+ loaded: false
},
- render (h) {
- if (this.show === false) {
- return h(this.tag)
- }
- return h(this.tag, null, this.$slots.default)
- },
- data () {
- return {
- el: null,
- state: {
- loaded: false
- },
- rect: {},
- show: false
- }
- },
- mounted () {
- this.el = this.$el
- lazy.addLazyBox(this)
- lazy.lazyLoadHandler()
- },
- beforeDestroy () {
- lazy.removeComponent(this)
- },
- methods: {
- getRect () {
- this.rect = this.$el.getBoundingClientRect()
- },
- checkInView () {
- this.getRect()
- return inBrowser &&
+ rect: {},
+ show: false
+ }
+ },
+ mounted () {
+ this.el = this.$el
+ lazy.addLazyBox(this)
+ lazy.lazyLoadHandler()
+ },
+ beforeDestroy () {
+ lazy.removeComponent(this)
+ },
+ methods: {
+ getRect () {
+ this.rect = this.$el.getBoundingClientRect()
+ },
+ checkInView () {
+ this.getRect()
+ return inBrowser &&
(this.rect.top < window.innerHeight * lazy.options.preLoad && this.rect.bottom > 0) &&
(this.rect.left < window.innerWidth * lazy.options.preLoad && this.rect.right > 0)
- },
- load () {
- this.show = true
- this.state.loaded = true
- this.$emit('show', this)
- }
- }
+ },
+ load () {
+ this.show = true
+ this.state.loaded = true
+ this.$emit('show', this)
+ }
}
+ }
}
diff --git a/src/lazy-container.js b/src/lazy-container.js
new file mode 100644
index 0000000..80b01bb
--- /dev/null
+++ b/src/lazy-container.js
@@ -0,0 +1,75 @@
+import {
+ assign,
+ find,
+ remove,
+ ArrayFrom
+} from './util'
+
+export default class LazyContainerMananger {
+ constructor ({ lazy }) {
+ this.lazy = lazy
+ lazy.lazyContainerMananger = this
+ this._queue = []
+ }
+
+ bind (el, binding, vnode) {
+ const container = new LazyContainer({ el, binding, vnode, lazy: this.lazy })
+ this._queue.push(container)
+ }
+
+ update (el, binding, vnode) {
+ const container = find(this._queue, item => item.el === el)
+ if (!container) return
+ container.update({ el, binding, vnode })
+ }
+
+ unbind (el, binding, vnode) {
+ const container = find(this._queue, item => item.el === el)
+ if (!container) return
+ container.clear()
+ remove(this._queue, container)
+ }
+}
+
+const defaultOptions = {
+ selector: 'img'
+}
+
+class LazyContainer {
+ constructor ({ el, binding, vnode, lazy }) {
+ this.el = null
+ this.vnode = vnode
+ this.binding = binding
+ this.options = {}
+ this.lazy = lazy
+
+ this._queue = []
+ this.update({ el, binding })
+ }
+
+ update ({ el, binding }) {
+ this.el = el
+ this.options = assign({}, defaultOptions, binding.value)
+ const imgs = this.getImgs()
+ imgs.forEach(el => {
+ this.lazy.add(el, Object.assign({}, this.binding, {
+ value: {
+ src: el.getAttribute('data-src')
+ }
+ }), this.vnode)
+ })
+ }
+
+ getImgs () {
+ return ArrayFrom(this.el.querySelectorAll(this.options.selector))
+ }
+
+ clear () {
+ const imgs = this.getImgs()
+ imgs.forEach(el => this.lazy.remove(el))
+
+ this.vnode = null
+ this.binding = null
+ this.lazy = null
+ }
+}
diff --git a/src/lazy.js b/src/lazy.js
index 7397be9..cb7d0f8 100644
--- a/src/lazy.js
+++ b/src/lazy.js
@@ -1,18 +1,19 @@
import {
- inBrowser,
- remove,
- some,
- find,
- _,
- throttle,
- supportWebp,
- getDPR,
- scrollParent,
- getBestSelectionFromSrcset,
- assign,
- isObject,
- hasIntersectionObserver,
- modeType
+ inBrowser,
+ CustomEvent,
+ remove,
+ some,
+ find,
+ _,
+ throttle,
+ supportWebp,
+ getDPR,
+ scrollParent,
+ getBestSelectionFromSrcset,
+ assign,
+ isObject,
+ hasIntersectionObserver,
+ modeType
} from './util'
import ReactiveListener from './listener'
@@ -33,23 +34,23 @@ export default function (Vue) {
this.TargetIndex = 0
this.TargetQueue = []
this.options = {
- silent: silent,
- dispatchEvent: !!dispatchEvent,
- throttleWait: throttleWait || 200,
- preLoad: preLoad || 1.3,
- preLoadTop: preLoadTop || 0,
- error: error || DEFAULT_URL,
- loading: loading || DEFAULT_URL,
- attempt: attempt || 3,
- scale: scale || getDPR(scale),
- ListenEvents: listenEvents || DEFAULT_EVENTS,
- hasbind: false,
- supportWebp: supportWebp(),
- filter: filter || {},
- adapter: adapter || {},
- observer: !!observer,
- observerOptions: observerOptions || DEFAULT_OBSERVER_OPTIONS
- }
+ silent: silent,
+ dispatchEvent: !!dispatchEvent,
+ throttleWait: throttleWait || 200,
+ preLoad: preLoad || 1.3,
+ preLoadTop: preLoadTop || 0,
+ error: error || DEFAULT_URL,
+ loading: loading || DEFAULT_URL,
+ attempt: attempt || 3,
+ scale: scale || getDPR(scale),
+ ListenEvents: listenEvents || DEFAULT_EVENTS,
+ hasbind: false,
+ supportWebp: supportWebp(),
+ filter: filter || {},
+ adapter: adapter || {},
+ observer: !!observer,
+ observerOptions: observerOptions || DEFAULT_OBSERVER_OPTIONS
+ }
this._initEvent()
this.lazyLoadHandler = throttle(this._lazyLoadHandler.bind(this), this.options.throttleWait)
@@ -57,106 +58,106 @@ export default function (Vue) {
this.setMode(this.options.observer ? modeType.observer : modeType.event)
}
- /**
- * update config
- * @param {Object} config params
- * @return
- */
+ /**
+ * update config
+ * @param {Object} config params
+ * @return
+ */
config (options = {}) {
assign(this.options, options)
}
- /**
- * output listener's load performance
- * @return {Array}
- */
+ /**
+ * output listener's load performance
+ * @return {Array}
+ */
performance () {
let list = []
this.ListenerQueue.map(item => {
- list.push(item.performance())
- })
+ list.push(item.performance())
+ })
return list
}
- /**
- * add lazy component to queue
- * @param {Vue} vm lazy component instance
- * @return
- */
+ /*
+ * add lazy component to queue
+ * @param {Vue} vm lazy component instance
+ * @return
+ */
addLazyBox (vm) {
this.ListenerQueue.push(vm)
if (inBrowser) {
- this._addListenerTarget(window)
- this._observer && this._observer.observe(vm.el)
- if (vm.$el && vm.$el.parentNode) {
- this._addListenerTarget(vm.$el.parentNode)
- }
+ this._addListenerTarget(window)
+ this._observer && this._observer.observe(vm.el)
+ if (vm.$el && vm.$el.parentNode) {
+ this._addListenerTarget(vm.$el.parentNode)
}
+ }
}
- /**
- * add image listener to queue
- * @param {DOM} el
- * @param {object} binding vue directive binding
- * @param {vnode} vnode vue directive vnode
- * @return
- */
+ /*
+ * add image listener to queue
+ * @param {DOM} el
+ * @param {object} binding vue directive binding
+ * @param {vnode} vnode vue directive vnode
+ * @return
+ */
add (el, binding, vnode) {
if (some(this.ListenerQueue, item => item.el === el)) {
- this.update(el, binding)
- return Vue.nextTick(this.lazyLoadHandler)
- }
+ this.update(el, binding)
+ return Vue.nextTick(this.lazyLoadHandler)
+ }
let { src, loading, error } = this._valueFormatter(binding.value)
Vue.nextTick(() => {
- src = getBestSelectionFromSrcset(el, this.options.scale) || src
- this._observer && this._observer.observe(el)
-
- const container = Object.keys(binding.modifiers)[0]
- let $parent
+ src = getBestSelectionFromSrcset(el, this.options.scale) || src
+ this._observer && this._observer.observe(el)
- if (container) {
- $parent = vnode.context.$refs[container]
- // if there is container passed in, try ref first, then fallback to getElementById to support the original usage
- $parent = $parent ? $parent.$el || $parent : document.getElementById(container)
- }
+ const container = Object.keys(binding.modifiers)[0]
+ let $parent
- if (!$parent) {
- $parent = scrollParent(el)
- }
+ if (container) {
+ $parent = vnode.context.$refs[container]
+ // if there is container passed in, try ref first, then fallback to getElementById to support the original usage
+ $parent = $parent ? $parent.$el || $parent : document.getElementById(container)
+ }
- const newListener = new ReactiveListener({
- bindType: binding.arg,
- $parent,
- el,
- loading,
- error,
- src,
- elRenderer: this._elRenderer.bind(this),
- options: this.options
- })
-
- this.ListenerQueue.push(newListener)
-
- if (inBrowser) {
- this._addListenerTarget(window)
- this._addListenerTarget($parent)
- }
+ if (!$parent) {
+ $parent = scrollParent(el)
+ }
- this.lazyLoadHandler()
- Vue.nextTick(() => this.lazyLoadHandler())
+ const newListener = new ReactiveListener({
+ bindType: binding.arg,
+ $parent,
+ el,
+ loading,
+ error,
+ src,
+ elRenderer: this._elRenderer.bind(this),
+ options: this.options
})
+
+ this.ListenerQueue.push(newListener)
+
+ if (inBrowser) {
+ this._addListenerTarget(window)
+ this._addListenerTarget($parent)
+ }
+
+ this.lazyLoadHandler()
+ Vue.nextTick(() => this.lazyLoadHandler())
+ })
}
- /**
- * update image src
- * @param {DOM} el
- * @param {object} vue directive binding
- * @return
- */
+ /**
+ * update image src
+ * @param {DOM} el
+ * @param {object} vue directive binding
+ * @return
+ */
update (el, binding) {
let { src, loading, error } = this._valueFormatter(binding.value)
src = getBestSelectionFromSrcset(el, this.options.scale) || src
@@ -164,234 +165,238 @@ export default function (Vue) {
const exist = find(this.ListenerQueue, item => item.el === el)
exist && exist.update({
- src,
- loading,
- error
- })
+ src,
+ loading,
+ error
+ })
this._observer && this._observer.observe(el)
this.lazyLoadHandler()
Vue.nextTick(() => this.lazyLoadHandler())
}
- /**
- * remove listener form list
- * @param {DOM} el
- * @return
- */
+ /**
+ * remove listener form list
+ * @param {DOM} el
+ * @return
+ */
remove (el) {
if (!el) return
this._observer && this._observer.unobserve(el)
const existItem = find(this.ListenerQueue, item => item.el === el)
if (existItem) {
- this._removeListenerTarget(existItem.$parent)
- this._removeListenerTarget(window)
- remove(this.ListenerQueue, existItem) && existItem.destroy()
- }
+ this._removeListenerTarget(existItem.$parent)
+ this._removeListenerTarget(window)
+ remove(this.ListenerQueue, existItem) && existItem.destroy()
+ }
}
- /**
- * remove lazy components form list
- * @param {Vue} vm Vue instance
- * @return
- */
+ /*
+ * remove lazy components form list
+ * @param {Vue} vm Vue instance
+ * @return
+ */
removeComponent (vm) {
if (!vm) return
remove(this.ListenerQueue, vm)
this._observer && this._observer.unobserve(vm.el)
if (vm.$parent && vm.$el.parentNode) {
- this._removeListenerTarget(vm.$el.parentNode)
- }
+ this._removeListenerTarget(vm.$el.parentNode)
+ }
this._removeListenerTarget(window)
}
setMode (mode) {
if (!hasIntersectionObserver && mode === modeType.observer) {
- mode = modeType.event
- }
+ mode = modeType.event
+ }
this.mode = mode // event or observer
if (mode === modeType.event) {
- if (this._observer) {
- this.ListenerQueue.forEach(listener => {
- this._observer.unobserve(listener.el)
- })
- this._observer = null
- }
-
- this.TargetQueue.forEach(target => {
- this._initListen(target.el, true)
- })
- } else {
- this.TargetQueue.forEach(target => {
- this._initListen(target.el, false)
- })
- this._initIntersectionObserver()
+ if (this._observer) {
+ this.ListenerQueue.forEach(listener => {
+ this._observer.unobserve(listener.el)
+ })
+ this._observer = null
}
+
+ this.TargetQueue.forEach(target => {
+ this._initListen(target.el, true)
+ })
+ } else {
+ this.TargetQueue.forEach(target => {
+ this._initListen(target.el, false)
+ })
+ this._initIntersectionObserver()
+ }
}
- /** ** Private functions ****/
+ /**** Private functions ****/
- /**
- * add listener target
- * @param {DOM} el listener target
- * @return
- */
+ /*
+ * add listener target
+ * @param {DOM} el listener target
+ * @return
+ */
_addListenerTarget (el) {
if (!el) return
let target = find(this.TargetQueue, target => target.el === el)
if (!target) {
- target = {
- el: el,
- id: ++this.TargetIndex,
- childrenCount: 1,
- listened: true
- }
- this.mode === modeType.event && this._initListen(target.el, true)
- this.TargetQueue.push(target)
- } else {
- target.childrenCount++
+ target = {
+ el: el,
+ id: ++this.TargetIndex,
+ childrenCount: 1,
+ listened: true
}
+ this.mode === modeType.event && this._initListen(target.el, true)
+ this.TargetQueue.push(target)
+ } else {
+ target.childrenCount++
+ }
return this.TargetIndex
}
- /**
- * remove listener target or reduce target childrenCount
- * @param {DOM} el or window
- * @return
- */
+ /*
+ * remove listener target or reduce target childrenCount
+ * @param {DOM} el or window
+ * @return
+ */
_removeListenerTarget (el) {
this.TargetQueue.forEach((target, index) => {
- if (target.el === el) {
- target.childrenCount--
- if (!target.childrenCount) {
- this._initListen(target.el, false)
- this.TargetQueue.splice(index, 1)
- target = null
- }
- }
- })
+ if (target.el === el) {
+ target.childrenCount--
+ if (!target.childrenCount) {
+ this._initListen(target.el, false)
+ this.TargetQueue.splice(index, 1)
+ target = null
+ }
+ }
+ })
}
- /**
- * add or remove eventlistener
- * @param {DOM} el DOM or Window
- * @param {boolean} start flag
- * @return
- */
+ /*
+ * add or remove eventlistener
+ * @param {DOM} el DOM or Window
+ * @param {boolean} start flag
+ * @return
+ */
_initListen (el, start) {
this.options.ListenEvents.forEach((evt) => _[start ? 'on' : 'off'](el, evt, this.lazyLoadHandler))
}
_initEvent () {
this.Event = {
- listeners: {
- loading: [],
- loaded: [],
- error: []
- }
+ listeners: {
+ loading: [],
+ loaded: [],
+ error: []
}
+ }
this.$on = (event, func) => {
- this.Event.listeners[event].push(func)
- }
+ this.Event.listeners[event].push(func)
+ }
this.$once = (event, func) => {
- const vm = this
- function on () {
- vm.$off(event, on)
- func.apply(vm, arguments)
- }
- this.$on(event, on)
+ const vm = this
+ function on () {
+ vm.$off(event, on)
+ func.apply(vm, arguments)
}
+ this.$on(event, on)
+ }
this.$off = (event, func) => {
- if (!func) {
- this.Event.listeners[event] = []
- return
- }
- remove(this.Event.listeners[event], func)
+ if (!func) {
+ this.Event.listeners[event] = []
+ return
}
+ remove(this.Event.listeners[event], func)
+ }
this.$emit = (event, context, inCache) => {
- this.Event.listeners[event].forEach(func => func(context, inCache))
- }
+ this.Event.listeners[event].forEach(func => func(context, inCache))
+ }
}
- /**
- * find nodes which in viewport and trigger load
- * @return
- */
+ /**
+ * find nodes which in viewport and trigger load
+ * @return
+ */
_lazyLoadHandler () {
- console.log(this)
let catIn = false
- this.ListenerQueue.forEach((listener, index)=> {
- if (listener.state.loaded) return
- catIn = listener.checkInView()
- catIn && (listener.load(() => this.ListenerQueue.splice(index, 1)))
+ this.ListenerQueue.forEach((listener, index) => {
+ if (listener.state.loaded) return
+ catIn = listener.checkInView()
+ if (!catIn) return
+ listener.load(() => {
+ if (!listener.error && listener.loaded) {
+ this.ListenerQueue.splice(index, 1)
+ }
})
+ })
}
- /**
- * init IntersectionObserver
- * set mode to observer
- * @return
- */
+ /**
+ * init IntersectionObserver
+ * set mode to observer
+ * @return
+ */
_initIntersectionObserver () {
if (!hasIntersectionObserver) return
this._observer = new IntersectionObserver(this._observerHandler.bind(this), this.options.observerOptions)
if (this.ListenerQueue.length) {
- this.ListenerQueue.forEach(listener => {
- this._observer.observe(listener.el)
- })
- }
+ this.ListenerQueue.forEach(listener => {
+ this._observer.observe(listener.el)
+ })
+ }
}
- /**
- * init IntersectionObserver
- * @return
- */
+ /**
+ * init IntersectionObserver
+ * @return
+ */
_observerHandler (entries, observer) {
entries.forEach(entry => {
- if (entry.isIntersecting) {
- this.ListenerQueue.forEach(listener => {
- if (listener.el === entry.target) {
- if (listener.state.loaded) return this._observer.unobserve(listener.el)
- listener.load()
- }
- })
+ if (entry.isIntersecting) {
+ this.ListenerQueue.forEach(listener => {
+ if (listener.el === entry.target) {
+ if (listener.state.loaded) return this._observer.unobserve(listener.el)
+ listener.load()
}
- })
+ })
+ }
+ })
}
- /**
- * set element attribute with image'url and state
- * @param {object} lazyload listener object
- * @param {string} state will be rendered
- * @param {bool} inCache is rendered from cache
- * @return
- */
+ /**
+ * set element attribute with image'url and state
+ * @param {object} lazyload listener object
+ * @param {string} state will be rendered
+ * @param {bool} inCache is rendered from cache
+ * @return
+ */
_elRenderer (listener, state, cache) {
if (!listener.el) return
const { el, bindType } = listener
let src
switch (state) {
- case 'loading':
- src = listener.loading
- break
- case 'error':
- src = listener.error
- break
- default:
- src = listener.src
- break
- }
+ case 'loading':
+ src = listener.loading
+ break
+ case 'error':
+ src = listener.error
+ break
+ default:
+ src = listener.src
+ break
+ }
if (bindType) {
- el.style[bindType] = 'url(' + src + ')'
- } else if (el.getAttribute('src') !== src) {
- el.setAttribute('src', src)
- }
+ el.style[bindType] = 'url(' + src + ')'
+ } else if (el.getAttribute('src') !== src) {
+ el.setAttribute('src', src)
+ }
el.setAttribute('lazy', state)
@@ -399,35 +404,35 @@ export default function (Vue) {
this.options.adapter[state] && this.options.adapter[state](listener, this.options)
if (this.options.dispatchEvent) {
- const event = new CustomEvent(state, {
- detail: listener
- })
- el.dispatchEvent(event)
- }
+ const event = new CustomEvent(state, {
+ detail: listener
+ })
+ el.dispatchEvent(event)
+ }
}
- /**
- * generate loading loaded error image url
- * @param {string} image's src
- * @return {object} image's loading, loaded, error url
- */
+ /**
+ * generate loading loaded error image url
+ * @param {string} image's src
+ * @return {object} image's loading, loaded, error url
+ */
_valueFormatter (value) {
let src = value
let loading = this.options.loading
let error = this.options.error
- // value is object
+ // value is object
if (isObject(value)) {
- if (!value.src && !this.options.silent) console.error('Vue Lazyload warning: miss src with ' + value)
- src = value.src
- loading = value.loading || this.options.loading
- error = value.error || this.options.error
- }
+ if (!value.src && !this.options.silent) console.error('Vue Lazyload warning: miss src with ' + value)
+ src = value.src
+ loading = value.loading || this.options.loading
+ error = value.error || this.options.error
+ }
return {
- src,
- loading,
- error
- }
- }
+ src,
+ loading,
+ error
+ }
}
+ }
}
diff --git a/src/listener.js b/src/listener.js
index c5b5553..f0cb0f0 100644
--- a/src/listener.js
+++ b/src/listener.js
@@ -2,204 +2,212 @@ import { loadImageAsync, ObjectKeys } from './util'
let imageCache = {}
-export default class ReactiveListener {
- constructor ({ el, src, error, loading, bindType, $parent, options, elRenderer }) {
- this.el = el
- this.src = src
- this.error = error
- this.loading = loading
- this.bindType = bindType
- this.attempt = 0
-
- this.naturalHeight = 0
- this.naturalWidth = 0
-
- this.options = options
-
- this.filter()
+// el: {
+// state,
+// src,
+// error,
+// loading
+// }
- this.initState()
+export default class ReactiveListener {
+ constructor ({ el, src, error, loading, bindType, $parent, options, elRenderer }) {
+ this.el = el
+ this.src = src
+ this.error = error
+ this.loading = loading
+ this.bindType = bindType
+ this.attempt = 0
- this.performanceData = {
- init: Date.now(),
- loadStart: null,
- loadEnd: null
- }
+ this.naturalHeight = 0
+ this.naturalWidth = 0
- this.rect = el.getBoundingClientRect()
+ this.options = options
- this.$parent = $parent
- this.elRenderer = elRenderer
- this.render('loading', false)
- }
+ this.rect = null
- /**
- * init listener state
- * @return
- */
- initState () {
- this.state = {
- error: false,
- loaded: false,
- rendered: false
- }
- }
+ this.$parent = $parent
+ this.elRenderer = elRenderer
- /**
- * record performance
- * @return
- */
- record (event) {
- this.performanceData[event] = Date.now()
+ this.performanceData = {
+ init: Date.now(),
+ loadStart: 0,
+ loadEnd: 0
}
- /**
- * update image listener data
- * @param {String} image uri
- * @param {String} loading image uri
- * @param {String} error image uri
- * @return
- */
- update ({ src, loading, error }) {
- const oldSrc = this.src
- this.src = src
- this.loading = loading
- this.error = error
- this.filter()
- if (oldSrc !== this.src) {
- this.attempt = 0
- this.initState()
- }
+ this.filter()
+ this.initState()
+ this.render('loading', false)
+ }
+
+ /*
+ * init listener state
+ * @return
+ */
+ initState () {
+ this.el.dataset.src = this.src
+ this.state = {
+ error: false,
+ loaded: false,
+ rendered: false
}
-
- /**
- * get el node rect
- * @return
- */
- getRect () {
- this.rect = this.el.getBoundingClientRect()
+ }
+
+ /*
+ * record performance
+ * @return
+ */
+ record (event) {
+ this.performanceData[event] = Date.now()
+ }
+
+ /*
+ * update image listener data
+ * @param {String} image uri
+ * @param {String} loading image uri
+ * @param {String} error image uri
+ * @return
+ */
+ update ({ src, loading, error }) {
+ const oldSrc = this.src
+ this.src = src
+ this.loading = loading
+ this.error = error
+ this.filter()
+ if (oldSrc !== this.src) {
+ this.attempt = 0
+ this.initState()
}
-
- /**
- * check el is in view
- * @return {Boolean} el is in view
- */
- checkInView () {
- this.getRect()
- return (this.rect.top < window.innerHeight * this.options.preLoad && this.rect.bottom > this.options.preLoadTop) &&
+ }
+
+ /*
+ * get el node rect
+ * @return
+ */
+ getRect () {
+ this.rect = this.el.getBoundingClientRect()
+ }
+
+ /*
+ * check el is in view
+ * @return {Boolean} el is in view
+ */
+ checkInView () {
+ this.getRect()
+ return (this.rect.top < window.innerHeight * this.options.preLoad && this.rect.bottom > this.options.preLoadTop) &&
(this.rect.left < window.innerWidth * this.options.preLoad && this.rect.right > 0)
+ }
+
+ /*
+ * listener filter
+ */
+ filter () {
+ ObjectKeys(this.options.filter).map(key => {
+ this.options.filter[key](this, this.options)
+ })
+ }
+
+ /*
+ * render loading first
+ * @params cb:Function
+ * @return
+ */
+ renderLoading (cb) {
+ loadImageAsync({
+ src: this.loading
+ }, data => {
+ this.render('loading', false)
+ cb()
+ }, () => {
+ // handler `loading image` load failed
+ cb()
+ if (!this.options.silent) console.warn(`VueLazyload log: load failed with loading image(${this.loading})`)
+ })
+ }
+
+ /*
+ * try load image and render it
+ * @return
+ */
+ load (onFinish) {
+ if ((this.attempt > this.options.attempt - 1) && this.state.error) {
+ if (!this.options.silent) console.log(`VueLazyload log: ${this.src} tried too more than ${this.options.attempt} times`)
+ onFinish()
+ return
}
- /**
- * listener filter
- */
- filter () {
- ObjectKeys(this.options.filter).map(key => {
- this.options.filter[key](this, this.options)
- })
- }
-
- /**
- * render loading first
- * @params cb:Function
- * @return
- */
- renderLoading (cb) {
- loadImageAsync({
- src: this.loading
- }, data => {
- this.render('loading', false)
- cb()
- }, err => {
- // handler `loading image` load failed
- cb()
- if (!this.options.silent) console.warn(`VueLazyload log: load failed with loading image(${this.loading})`)
- })
+ if (this.state.loaded || imageCache[this.src]) {
+ this.state.loaded = true
+ onFinish()
+ return this.render('loaded', true)
}
- /**
- * try load image and render it
- * @return
- */
- load (onFinish) {
- if ((this.attempt > this.options.attempt - 1) && this.state.error) {
- if (!this.options.silent) console.log(`VueLazyload log: ${this.src} tried too more than ${this.options.attempt} times`)
- onFinish()
- return
- }
-
- if (this.state.loaded || imageCache[this.src]) {
- this.state.loaded = true
- onFinish()
- return this.render('loaded', true)
- }
-
- this.renderLoading(() => {
- this.attempt++
-
- this.record('loadStart')
-
- loadImageAsync({
- src: this.src
- }, data => {
- this.naturalHeight = data.naturalHeight
- this.naturalWidth = data.naturalWidth
- this.state.loaded = true
- this.state.error = false
- this.record('loadEnd')
- this.render('loaded', false)
- imageCache[this.src] = 1
- onFinish()
- }, err => {
- this.state.error = true
- this.state.loaded = false
- this.render('error', false)
- })
- })
+ this.renderLoading(() => {
+ this.attempt++
+
+ this.record('loadStart')
+
+ loadImageAsync({
+ src: this.src
+ }, data => {
+ this.naturalHeight = data.naturalHeight
+ this.naturalWidth = data.naturalWidth
+ this.state.loaded = true
+ this.state.error = false
+ this.record('loadEnd')
+ this.render('loaded', false)
+ imageCache[this.src] = 1
+ onFinish()
+ }, err => {
+ console.error(err)
+ this.state.error = true
+ this.state.loaded = false
+ this.render('error', false)
+ })
+ })
+ }
+
+ /*
+ * render image
+ * @param {String} state to render // ['loading', 'src', 'error']
+ * @param {String} is form cache
+ * @return
+ */
+ render (state, cache) {
+ this.elRenderer(this, state, cache)
+ }
+
+ /*
+ * output performance data
+ * @return {Object} performance data
+ */
+ performance () {
+ let state = 'loading'
+ let time = 0
+
+ if (this.state.loaded) {
+ state = 'loaded'
+ time = (this.performanceData.loadEnd - this.performanceData.loadStart) / 1000
}
- /**
- * render image
- * @param {String} state to render // ['loading', 'src', 'error']
- * @param {String} is form cache
- * @return
- */
- render (state, cache) {
- this.elRenderer(this, state, cache)
- }
-
- /**
- * output performance data
- * @return {Object} performance data
- */
- performance () {
- let state = 'loading'
- let time = 0
-
- if (this.state.loaded) {
- state = 'loaded'
- time = (this.performanceData.loadEnd - this.performanceData.loadStart) / 1000
- }
-
- if (this.state.error) state = 'error'
-
- return {
- src: this.src,
- state,
- time
- }
- }
+ if (this.state.error) state = 'error'
- /**
- * destroy
- * @return
- */
- destroy () {
- this.el = null
- this.src = null
- this.error = null
- this.loading = null
- this.bindType = null
- this.attempt = 0
+ return {
+ src: this.src,
+ state,
+ time
}
+ }
+
+ /*
+ * destroy
+ * @return
+ */
+ destroy () {
+ this.el = null
+ this.src = null
+ this.error = null
+ this.loading = null
+ this.bindType = null
+ this.attempt = 0
+ }
}
diff --git a/src/util.js b/src/util.js
index 73a3439..088e0d3 100644
--- a/src/util.js
+++ b/src/util.js
@@ -2,238 +2,251 @@ const inBrowser = typeof window !== 'undefined'
export const hasIntersectionObserver = inBrowser && 'IntersectionObserver' in window
export const modeType = {
- event: 'event',
- observer: 'observer'
+ 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 }
+ 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)
+ if (!arr.length) return
+ const index = arr.indexOf(item)
+ if (index > -1) return arr.splice(index, 1)
}
function assign (target, source) {
- if (!target || !source) return target || {}
- if (target instanceof Object) {
- for (let key in source) {
- target[key] = source[key]
- }
+ if (!target || !source) return target || {}
+ if (target instanceof Object) {
+ for (let key in source) {
+ target[key] = source[key]
}
- return target
+ }
+ return target
}
function some (arr, fn) {
- let has = false
- for (let i = 0, len = arr.length; i < len; i++) {
- if (fn(arr[i])) {
- has = true
- break
- }
+ let has = false
+ for (let i = 0, len = arr.length; i < len; i++) {
+ if (fn(arr[i])) {
+ has = true
+ break
}
- return has
+ }
+ 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])
- })
+ if (el.tagName !== 'IMG' || !el.getAttribute('data-srcset')) return
- 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
- const resultCount = result.length
-
- for (let i = 0; i < resultCount; i++) {
- tmpOption = result[i]
- if (tmpOption[0] >= containerWidth) {
- bestSelectedSrc = tmpOption[1]
- break
- }
+ 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
+ const resultCount = result.length
+
+ for (let i = 0; i < resultCount; i++) {
+ tmpOption = result[i]
+ if (tmpOption[0] >= containerWidth) {
+ bestSelectedSrc = tmpOption[1]
+ break
}
+ }
- return bestSelectedSrc
+ 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
- }
+ let item
+ for (let i = 0, len = arr.length; i < len; i++) {
+ if (fn(arr[i])) {
+ item = arr[i]
+ break
}
- return item
+ }
+ return item
}
const getDPR = (scale = 1) => inBrowser && window.devicePixelRatio || scale
function supportWebp () {
- if (!inBrowser) return false
-
- let support = true
- const d = document
-
- try {
- let el = d.createElement('object')
- el.type = 'image/webp'
- el.style.visibility = 'hidden'
- el.innerHTML = '!'
- d.body.appendChild(el)
- support = !el.offsetWidth
- d.body.removeChild(el)
- } catch (err) {
- support = false
- }
-
- return support
+ if (!inBrowser) return false
+
+ let support = true
+ const d = document
+
+ try {
+ let el = d.createElement('object')
+ el.type = 'image/webp'
+ el.style.visibility = 'hidden'
+ el.innerHTML = '!'
+ d.body.appendChild(el)
+ support = !el.offsetWidth
+ d.body.removeChild(el)
+ } catch (err) {
+ support = false
+ }
+
+ return support
}
function throttle (action, delay) {
- let timeout = null
- let lastRun = 0
- return function () {
- 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)
- }
+ let timeout = null
+ let lastRun = 0
+ return function () {
+ 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)
}
+ }
}
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
+ 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)
+ 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()
- image.src = item.src
-
- image.onload = function () {
- resolve({
- naturalHeight: image.naturalHeight,
- naturalWidth: image.naturalWidth,
- src: image.src
- })
- }
+ let image = new Image()
+ image.src = item.src
+
+ image.onload = function () {
+ resolve({
+ naturalHeight: image.naturalHeight,
+ naturalWidth: image.naturalWidth,
+ src: image.src
+ })
+ }
- image.onerror = function (e) {
- reject(e)
- }
+ image.onerror = function (e) {
+ reject(e)
+ }
}
const style = (el, prop) => {
- return typeof getComputedStyle !== 'undefined'
+ 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')
+ 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
+ if (!inBrowser) return
+ if (!(el instanceof HTMLElement)) {
+ return window
+ }
- while (parent) {
- if (parent === document.body || parent === document.documentElement) {
- break
- }
+ let parent = el
- if (!parent.parentNode) {
- break
- }
+ while (parent) {
+ if (parent === document.body || parent === document.documentElement) {
+ break
+ }
- if (/(scroll|auto)/.test(overflow(parent))) {
- return parent
- }
+ if (!parent.parentNode) {
+ break
+ }
- parent = parent.parentNode
+ if (/(scroll|auto)/.test(overflow(parent))) {
+ return parent
}
- return window
+ parent = parent.parentNode
+ }
+
+ return window
}
function isObject (obj) {
@@ -241,33 +254,44 @@ function isObject (obj) {
}
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
+ 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
}
export {
- inBrowser,
- remove,
- some,
- find,
- assign,
- _,
- isObject,
- throttle,
- supportWebp,
- getDPR,
- scrollParent,
- loadImageAsync,
- getBestSelectionFromSrcset,
- ObjectKeys
+ inBrowser,
+ CustomEvent,
+ remove,
+ some,
+ find,
+ assign,
+ ArrayFrom,
+ _,
+ isObject,
+ throttle,
+ supportWebp,
+ getDPR,
+ scrollParent,
+ loadImageAsync,
+ getBestSelectionFromSrcset,
+ ObjectKeys
}
diff --git a/test/tests.js b/test/test-spec.js
similarity index 55%
rename from test/tests.js
rename to test/test-spec.js
index a0a05d8..8496651 100644
--- a/test/tests.js
+++ b/test/test-spec.js
@@ -1,19 +1,15 @@
-'use strict'
-const it = require('mocha').it
-const chai = require('chai')
-const expect = require('chai').expect
-const lazyload = require('../lib')
-const Vue = require('vue')
+import Vue from 'vue'
+import VueLazyload from '../src'
+import genLazyCore from '../src/lazy'
describe('VueLazyload.js Test Suite', function () {
it('install', function () {
- Vue.use(lazyload)
+ Vue.use(VueLazyload)
const vm = new Vue()
- expect(vm.$Lazyload, 'has $Lazyload')
+ assert(vm.$Lazyload, 'has $Lazyload')
})
it('_valueFormatter', function () {
- const genLazyCore = require('../lib/lazy').default
const LazyCore = genLazyCore(Vue)
const lazyload = new LazyCore({
@@ -43,19 +39,4 @@ describe('VueLazyload.js Test Suite', function () {
loading: 'loading',
}).loading).to.equal('loading')
})
-
- // it('add and remove TargetListener', function () {
- // Vue.use(lazyload)
- // const vm = new Vue()
-
- // const list = Array.from({ length: 10 }).map((v, i) => {
- // return { i, addEventListener () {}, removeEventListener () {} }
- // })
-
- // list.map(el => vm.$Lazyload._addListenerTarget(el))
-
- // list.map(el => vm.$Lazyload._removeListenerTarget(el))
-
- // expect(vm.$Lazyload.TargetQueue.length).to.equal(0)
- // })
})
diff --git a/vue-lazyload.js b/vue-lazyload.js
index 9d76411..9888769 100644
--- a/vue-lazyload.js
+++ b/vue-lazyload.js
@@ -1,6 +1,6 @@
/*!
- * Vue-Lazyload.js v1.1.4
+ * Vue-Lazyload.js v1.2.0
* (c) 2018 Awe
* Released under the MIT License.
*/
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.VueLazyload=t()}(this,function(){"use strict";var e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},t=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},n=function(){function e(e,t){for(var n=0;n-1?e.splice(n,1):void 0}}function a(e,t){if(!e||!t)return e||{};if(e instanceof Object)for(var n in t)e[n]=t[n];return e}function u(e,t){if("IMG"===e.tagName&&e.getAttribute("data-srcset")){var n=e.getAttribute("data-srcset"),i=[],r=e.parentNode.offsetWidth*t,o=void 0,s=void 0,a=void 0;(n=n.trim().split(",")).map(function(e){e=e.trim(),-1===(o=e.lastIndexOf(" "))?(s=e,a=999998):(s=e.substr(0,o),a=parseInt(e.substr(o+1,e.length-o-2),10)),i.push([a,s])}),i.sort(function(e,t){if(e[0]t[0])return 1;if(e[0]===t[0]){if(-1!==t[1].indexOf(".webp",t[1].length-5))return 1;if(-1!==e[1].indexOf(".webp",e[1].length-5))return-1}return 0});for(var u="",l=void 0,d=i.length,h=0;h=r){u=l[1];break}return u}}function l(e,t){for(var n=void 0,i=0,r=e.length;i0&&void 0!==arguments[0]?arguments[0]:1;return i&&window.devicePixelRatio||e};var h=function(){if(i){var e=!1;try{var t=Object.defineProperty({},"passive",{get:function(){e=!0}});window.addEventListener("test",null,t)}catch(e){}return e}}(),c={on:function(e,t,n){var i=arguments.length>3&&void 0!==arguments[3]&&arguments[3];h?e.addEventListener(t,n,{capture:i,passive:!0}):e.addEventListener(t,n,i)},off:function(e,t,n){var i=arguments.length>3&&void 0!==arguments[3]&&arguments[3];e.removeEventListener(t,n,i)}},f=function(e,t,n){var i=new Image;i.src=e.src,i.onload=function(){t({naturalHeight:i.naturalHeight,naturalWidth:i.naturalWidth,src:i.src})},i.onerror=function(e){n(e)}},v=function(e,t){return"undefined"!=typeof getComputedStyle?getComputedStyle(e,null).getPropertyValue(t):e.style[t]},p=function(e){if(i){if(!(e instanceof HTMLElement))return window;for(var t,n=e;n&&n!==document.body&&n!==document.documentElement&&n.parentNode;){if(/(scroll|auto)/.test(v(t=n,"overflow")+v(t,"overflow-y")+v(t,"overflow-x")))return n;n=n.parentNode}return window}};var g={},y=function(){function e(n){var i=n.el,r=n.src,o=n.error,s=n.loading,a=n.bindType,u=n.$parent,l=n.options,d=n.elRenderer;t(this,e),this.el=i,this.src=r,this.error=o,this.loading=s,this.bindType=a,this.attempt=0,this.naturalHeight=0,this.naturalWidth=0,this.options=l,this.filter(),this.initState(),this.performanceData={init:Date.now(),loadStart:null,loadEnd:null},this.rect=i.getBoundingClientRect(),this.$parent=u,this.elRenderer=d,this.render("loading",!1)}return n(e,[{key:"initState",value:function(){this.state={error:!1,loaded:!1,rendered:!1}}},{key:"record",value:function(e){this.performanceData[e]=Date.now()}},{key:"update",value:function(e){var t=e.src,n=e.loading,i=e.error,r=this.src;this.src=t,this.loading=n,this.error=i,this.filter(),r!==this.src&&(this.attempt=0,this.initState())}},{key:"getRect",value:function(){this.rect=this.el.getBoundingClientRect()}},{key:"checkInView",value:function(){return this.getRect(),this.rect.topthis.options.preLoadTop&&this.rect.left0}},{key:"filter",value:function(){var e=this;(function(e){if(!(e instanceof Object))return[];if(Object.keys)return Object.keys(e);var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(n);return t})(this.options.filter).map(function(t){e.options.filter[t](e,e.options)})}},{key:"renderLoading",value:function(e){var t=this;f({src:this.loading},function(n){t.render("loading",!1),e()},function(n){e(),t.options.silent||console.warn("VueLazyload log: load failed with loading image("+t.loading+")")})}},{key:"load",value:function(e){var t=this;return this.attempt>this.options.attempt-1&&this.state.error?(this.options.silent||console.log("VueLazyload log: "+this.src+" tried too more than "+this.options.attempt+" times"),void e()):this.state.loaded||g[this.src]?(this.state.loaded=!0,e(),this.render("loaded",!0)):void this.renderLoading(function(){t.attempt++,t.record("loadStart"),f({src:t.src},function(n){t.naturalHeight=n.naturalHeight,t.naturalWidth=n.naturalWidth,t.state.loaded=!0,t.state.error=!1,t.record("loadEnd"),t.render("loaded",!1),g[t.src]=1,e()},function(e){t.state.error=!0,t.state.loaded=!1,t.render("error",!1)})})}},{key:"render",value:function(e,t){this.elRenderer(this,e,t)}},{key:"performance",value:function(){var e="loading",t=0;return this.state.loaded&&(e="loaded",t=(this.performanceData.loadEnd-this.performanceData.loadStart)/1e3),this.state.error&&(e="error"),{src:this.src,state:e,time:t}}},{key:"destroy",value:function(){this.el=null,this.src=null,this.error=null,this.loading=null,this.bindType=null,this.attempt=0}}]),e}(),b="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",m=["scroll","wheel","mousewheel","resize","animationend","transitionend","touchmove"],L={rootMargin:"0px",threshold:0};function w(h){return function(){function f(e){var n,r,s,a,u=e.preLoad,l=e.error,h=e.throttleWait,c=e.preLoadTop,v=e.dispatchEvent,p=e.loading,g=e.attempt,y=e.silent,w=void 0===y||y,_=e.scale,k=e.listenEvents,E=(e.hasbind,e.filter),A=e.adapter,T=e.observer,$=e.observerOptions;t(this,f),this.version="1.1.4",this.mode=o.event,this.ListenerQueue=[],this.TargetIndex=0,this.TargetQueue=[],this.options={silent:w,dispatchEvent:!!v,throttleWait:h||200,preLoad:u||1.3,preLoadTop:c||0,error:l||b,loading:p||b,attempt:g||3,scale:_||d(_),ListenEvents:k||m,hasbind:!1,supportWebp:function(){if(!i)return!1;var e=!0,t=document;try{var n=t.createElement("object");n.type="image/webp",n.style.visibility="hidden",n.innerHTML="!",t.body.appendChild(n),e=!n.offsetWidth,t.body.removeChild(n)}catch(t){e=!1}return e}(),filter:E||{},adapter:A||{},observer:!!T,observerOptions:$||L},this._initEvent(),this.lazyLoadHandler=(n=this._lazyLoadHandler.bind(this),r=this.options.throttleWait,s=null,a=0,function(){if(!s){var e=this,t=arguments,i=function(){a=Date.now(),s=!1,n.apply(e,t)};Date.now()-a>=r?i():s=setTimeout(i,r)}}),this.setMode(this.options.observer?o.observer:o.event)}return n(f,[{key:"config",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};a(this.options,e)}},{key:"performance",value:function(){var e=[];return this.ListenerQueue.map(function(t){e.push(t.performance())}),e}},{key:"addLazyBox",value:function(e){this.ListenerQueue.push(e),i&&(this._addListenerTarget(window),this._observer&&this._observer.observe(e.el),e.$el&&e.$el.parentNode&&this._addListenerTarget(e.$el.parentNode))}},{key:"add",value:function(e,t,n){var r=this;if(function(e,t){for(var n=!1,i=0,r=e.length;i1&&void 0!==arguments[1]?arguments[1]:{},r=new(w(e))(n),o="2"===e.version.split(".")[0];e.prototype.$Lazyload=r,n.lazyComponent&&e.component("lazy-component",(t=r,{props:{tag:{type:String,default:"div"}},render:function(e){return!1===this.show?e(this.tag):e(this.tag,null,this.$slots.default)},data:function(){return{el:null,state:{loaded:!1},rect:{},show:!1}},mounted:function(){this.el=this.$el,t.addLazyBox(this),t.lazyLoadHandler()},beforeDestroy:function(){t.removeComponent(this)},methods:{getRect:function(){this.rect=this.$el.getBoundingClientRect()},checkInView:function(){return this.getRect(),i&&this.rect.top0&&this.rect.left0},load:function(){this.show=!0,this.state.loaded=!0,this.$emit("show",this)}}})),o?e.directive("lazy",{bind:r.add.bind(r),update:r.update.bind(r),componentUpdated:r.lazyLoadHandler.bind(r),unbind:r.remove.bind(r)}):e.directive("lazy",{bind:r.lazyLoadHandler.bind(r),update:function(e,t){a(this.vm.$refs,this.vm.$els),r.add(this.el,{modifiers:this.modifiers||{},arg:this.arg,value:e,oldValue:t},{context:this.vm})},unbind:function(){r.remove(this.el)}})}}});
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.VueLazyload=t()}(this,function(){"use strict";function e(e,t){if(e.length){var n=e.indexOf(t);return n>-1?e.splice(n,1):void 0}}function t(e,t){if(!e||!t)return e||{};if(e instanceof Object)for(var n in t)e[n]=t[n];return e}function n(e,t){for(var n=!1,i=0,r=e.length;it[0])return 1;if(e[0]===t[0]){if(-1!==t[1].indexOf(".webp",t[1].length-5))return 1;if(-1!==e[1].indexOf(".webp",e[1].length-5))return-1}return 0});for(var l="",d=void 0,h=i.length,c=0;c=o){l=d[1];break}return l}}function r(e,t){for(var n=void 0,i=0,r=e.length;i=t?a():n=setTimeout(a,t)}}}function a(e){return null!==e&&"object"===(void 0===e?"undefined":d(e))}function u(e){if(!(e instanceof Object))return[];if(Object.keys)return Object.keys(e);var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(n);return t}function l(e){for(var t=e.length,n=[],i=0;i0&&void 0!==arguments[0]?arguments[0]:1;return f&&window.devicePixelRatio||e},g=function(){if(f){var e=!1;try{var t=Object.defineProperty({},"passive",{get:function(){e=!0}});window.addEventListener("test",null,t)}catch(e){}return e}}(),m={on:function(e,t,n){var i=arguments.length>3&&void 0!==arguments[3]&&arguments[3];g?e.addEventListener(t,n,{capture:i,passive:!0}):e.addEventListener(t,n,i)},off:function(e,t,n){var i=arguments.length>3&&void 0!==arguments[3]&&arguments[3];e.removeEventListener(t,n,i)}},L=function(e,t,n){var i=new Image;i.src=e.src,i.onload=function(){t({naturalHeight:i.naturalHeight,naturalWidth:i.naturalWidth,src:i.src})},i.onerror=function(e){n(e)}},w=function(e,t){return"undefined"!=typeof getComputedStyle?getComputedStyle(e,null).getPropertyValue(t):e.style[t]},_=function(e){return w(e,"overflow")+w(e,"overflow-y")+w(e,"overflow-x")},k=function(e){if(f){if(!(e instanceof HTMLElement))return window;for(var t=e;t&&t!==document.body&&t!==document.documentElement&&t.parentNode;){if(/(scroll|auto)/.test(_(t)))return t;t=t.parentNode}return window}},E={},z=function(){function e(t){var n=t.el,i=t.src,r=t.error,o=t.loading,s=t.bindType,a=t.$parent,u=t.options,l=t.elRenderer;h(this,e),this.el=n,this.src=i,this.error=r,this.loading=o,this.bindType=s,this.attempt=0,this.naturalHeight=0,this.naturalWidth=0,this.options=u,this.rect=null,this.$parent=a,this.elRenderer=l,this.performanceData={init:Date.now(),loadStart:0,loadEnd:0},this.filter(),this.initState(),this.render("loading",!1)}return c(e,[{key:"initState",value:function(){this.el.dataset.src=this.src,this.state={error:!1,loaded:!1,rendered:!1}}},{key:"record",value:function(e){this.performanceData[e]=Date.now()}},{key:"update",value:function(e){var t=e.src,n=e.loading,i=e.error,r=this.src;this.src=t,this.loading=n,this.error=i,this.filter(),r!==this.src&&(this.attempt=0,this.initState())}},{key:"getRect",value:function(){this.rect=this.el.getBoundingClientRect()}},{key:"checkInView",value:function(){return this.getRect(),this.rect.topthis.options.preLoadTop&&this.rect.left0}},{key:"filter",value:function(){var e=this;u(this.options.filter).map(function(t){e.options.filter[t](e,e.options)})}},{key:"renderLoading",value:function(e){var t=this;L({src:this.loading},function(n){t.render("loading",!1),e()},function(){e(),t.options.silent||console.warn("VueLazyload log: load failed with loading image("+t.loading+")")})}},{key:"load",value:function(e){var t=this;return this.attempt>this.options.attempt-1&&this.state.error?(this.options.silent||console.log("VueLazyload log: "+this.src+" tried too more than "+this.options.attempt+" times"),void e()):this.state.loaded||E[this.src]?(this.state.loaded=!0,e(),this.render("loaded",!0)):void this.renderLoading(function(){t.attempt++,t.record("loadStart"),L({src:t.src},function(n){t.naturalHeight=n.naturalHeight,t.naturalWidth=n.naturalWidth,t.state.loaded=!0,t.state.error=!1,t.record("loadEnd"),t.render("loaded",!1),E[t.src]=1,e()},function(e){console.error(e),t.state.error=!0,t.state.loaded=!1,t.render("error",!1)})})}},{key:"render",value:function(e,t){this.elRenderer(this,e,t)}},{key:"performance",value:function(){var e="loading",t=0;return this.state.loaded&&(e="loaded",t=(this.performanceData.loadEnd-this.performanceData.loadStart)/1e3),this.state.error&&(e="error"),{src:this.src,state:e,time:t}}},{key:"destroy",value:function(){this.el=null,this.src=null,this.error=null,this.loading=null,this.bindType=null,this.attempt=0}}]),e}(),A="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",T=["scroll","wheel","mousewheel","resize","animationend","transitionend","touchmove"],$={rootMargin:"0px",threshold:0},H=function(u){return function(){function l(e){var t=e.preLoad,n=e.error,i=e.throttleWait,r=e.preLoadTop,a=e.dispatchEvent,u=e.loading,d=e.attempt,c=e.silent,f=void 0===c||c,v=e.scale,b=e.listenEvents,g=(e.hasbind,e.filter),m=e.adapter,L=e.observer,w=e.observerOptions;h(this,l),this.version="1.2.0",this.mode=p.event,this.ListenerQueue=[],this.TargetIndex=0,this.TargetQueue=[],this.options={silent:f,dispatchEvent:!!a,throttleWait:i||200,preLoad:t||1.3,preLoadTop:r||0,error:n||A,loading:u||A,attempt:d||3,scale:v||y(v),ListenEvents:b||T,hasbind:!1,supportWebp:o(),filter:g||{},adapter:m||{},observer:!!L,observerOptions:w||$},this._initEvent(),this.lazyLoadHandler=s(this._lazyLoadHandler.bind(this),this.options.throttleWait),this.setMode(this.options.observer?p.observer:p.event)}return c(l,[{key:"config",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};t(this.options,e)}},{key:"performance",value:function(){var e=[];return this.ListenerQueue.map(function(t){e.push(t.performance())}),e}},{key:"addLazyBox",value:function(e){this.ListenerQueue.push(e),f&&(this._addListenerTarget(window),this._observer&&this._observer.observe(e.el),e.$el&&e.$el.parentNode&&this._addListenerTarget(e.$el.parentNode))}},{key:"add",value:function(e,t,r){var o=this;if(n(this.ListenerQueue,function(t){return t.el===e}))return this.update(e,t),u.nextTick(this.lazyLoadHandler);var s=this._valueFormatter(t.value),a=s.src,l=s.loading,d=s.error;u.nextTick(function(){a=i(e,o.options.scale)||a,o._observer&&o._observer.observe(e);var n=Object.keys(t.modifiers)[0],s=void 0;n&&(s=r.context.$refs[n],s=s?s.$el||s:document.getElementById(n)),s||(s=k(e));var h=new z({bindType:t.arg,$parent:s,el:e,loading:l,error:d,src:a,elRenderer:o._elRenderer.bind(o),options:o.options});o.ListenerQueue.push(h),f&&(o._addListenerTarget(window),o._addListenerTarget(s)),o.lazyLoadHandler(),u.nextTick(function(){return o.lazyLoadHandler()})})}},{key:"update",value:function(e,t){var n=this,o=this._valueFormatter(t.value),s=o.src,a=o.loading,l=o.error;s=i(e,this.options.scale)||s;var d=r(this.ListenerQueue,function(t){return t.el===e});d&&d.update({src:s,loading:a,error:l}),this._observer&&this._observer.observe(e),this.lazyLoadHandler(),u.nextTick(function(){return n.lazyLoadHandler()})}},{key:"remove",value:function(t){if(t){this._observer&&this._observer.unobserve(t);var n=r(this.ListenerQueue,function(e){return e.el===t});n&&(this._removeListenerTarget(n.$parent),this._removeListenerTarget(window),e(this.ListenerQueue,n)&&n.destroy())}}},{key:"removeComponent",value:function(t){t&&(e(this.ListenerQueue,t),this._observer&&this._observer.unobserve(t.el),t.$parent&&t.$el.parentNode&&this._removeListenerTarget(t.$el.parentNode),this._removeListenerTarget(window))}},{key:"setMode",value:function(e){var t=this;v||e!==p.observer||(e=p.event),this.mode=e,e===p.event?(this._observer&&(this.ListenerQueue.forEach(function(e){t._observer.unobserve(e.el)}),this._observer=null),this.TargetQueue.forEach(function(e){t._initListen(e.el,!0)})):(this.TargetQueue.forEach(function(e){t._initListen(e.el,!1)}),this._initIntersectionObserver())}},{key:"_addListenerTarget",value:function(e){if(e){var t=r(this.TargetQueue,function(t){return t.el===e});return t?t.childrenCount++:(t={el:e,id:++this.TargetIndex,childrenCount:1,listened:!0},this.mode===p.event&&this._initListen(t.el,!0),this.TargetQueue.push(t)),this.TargetIndex}}},{key:"_removeListenerTarget",value:function(e){var t=this;this.TargetQueue.forEach(function(n,i){n.el===e&&(--n.childrenCount||(t._initListen(n.el,!1),t.TargetQueue.splice(i,1),n=null))})}},{key:"_initListen",value:function(e,t){var n=this;this.options.ListenEvents.forEach(function(i){return m[t?"on":"off"](e,i,n.lazyLoadHandler)})}},{key:"_initEvent",value:function(){var t=this;this.Event={listeners:{loading:[],loaded:[],error:[]}},this.$on=function(e,n){t.Event.listeners[e].push(n)},this.$once=function(e,n){function i(){r.$off(e,i),n.apply(r,arguments)}var r=t;t.$on(e,i)},this.$off=function(n,i){if(!i)return void(t.Event.listeners[n]=[]);e(t.Event.listeners[n],i)},this.$emit=function(e,n,i){t.Event.listeners[e].forEach(function(e){return e(n,i)})}}},{key:"_lazyLoadHandler",value:function(){var e=this,t=!1;this.ListenerQueue.forEach(function(n,i){n.state.loaded||(t=n.checkInView())&&n.load(function(){!n.error&&n.loaded&&e.ListenerQueue.splice(i,1)})})}},{key:"_initIntersectionObserver",value:function(){var e=this;v&&(this._observer=new IntersectionObserver(this._observerHandler.bind(this),this.options.observerOptions),this.ListenerQueue.length&&this.ListenerQueue.forEach(function(t){e._observer.observe(t.el)}))}},{key:"_observerHandler",value:function(e,t){var n=this;e.forEach(function(e){e.isIntersecting&&n.ListenerQueue.forEach(function(t){if(t.el===e.target){if(t.state.loaded)return n._observer.unobserve(t.el);t.load()}})})}},{key:"_elRenderer",value:function(e,t,n){if(e.el){var i=e.el,r=e.bindType,o=void 0;switch(t){case"loading":o=e.loading;break;case"error":o=e.error;break;default:o=e.src}if(r?i.style[r]="url("+o+")":i.getAttribute("src")!==o&&i.setAttribute("src",o),i.setAttribute("lazy",t),this.$emit(t,e,n),this.options.adapter[t]&&this.options.adapter[t](e,this.options),this.options.dispatchEvent){var s=new b(t,{detail:e});i.dispatchEvent(s)}}}},{key:"_valueFormatter",value:function(e){var t=e,n=this.options.loading,i=this.options.error;return a(e)&&(e.src||this.options.silent||console.error("Vue Lazyload warning: miss src with "+e),t=e.src,n=e.loading||this.options.loading,i=e.error||this.options.error),{src:t,loading:n,error:i}}}]),l}()},O=function(e){return{props:{tag:{type:String,default:"div"}},render:function(e){return!1===this.show?e(this.tag):e(this.tag,null,this.$slots.default)},data:function(){return{el:null,state:{loaded:!1},rect:{},show:!1}},mounted:function(){this.el=this.$el,e.addLazyBox(this),e.lazyLoadHandler()},beforeDestroy:function(){e.removeComponent(this)},methods:{getRect:function(){this.rect=this.$el.getBoundingClientRect()},checkInView:function(){return this.getRect(),f&&this.rect.top0&&this.rect.left0},load:function(){this.show=!0,this.state.loaded=!0,this.$emit("show",this)}}}},Q=function(){function t(e){var n=e.lazy;h(this,t),this.lazy=n,n.lazyContainerMananger=this,this._queue=[]}return c(t,[{key:"bind",value:function(e,t,n){var i=new x({el:e,binding:t,vnode:n,lazy:this.lazy});this._queue.push(i)}},{key:"update",value:function(e,t,n){var i=r(this._queue,function(t){return t.el===e});i&&i.update({el:e,binding:t,vnode:n})}},{key:"unbind",value:function(t,n,i){var o=r(this._queue,function(e){return e.el===t});o&&(o.clear(),e(this._queue,o))}}]),t}(),I={selector:"img"},x=function(){function e(t){var n=t.el,i=t.binding,r=t.vnode,o=t.lazy;h(this,e),this.el=null,this.vnode=r,this.binding=i,this.options={},this.lazy=o,this._queue=[],this.update({el:n,binding:i})}return c(e,[{key:"update",value:function(e){var n=this,i=e.el,r=e.binding;this.el=i,this.options=t({},I,r.value),this.getImgs().forEach(function(e){n.lazy.add(e,Object.assign({},n.binding,{value:{src:e.getAttribute("data-src")}}),n.vnode)})}},{key:"getImgs",value:function(){return l(this.el.querySelectorAll(this.options.selector))}},{key:"clear",value:function(){var e=this;this.getImgs().forEach(function(t){return e.lazy.remove(t)}),this.vnode=null,this.binding=null,this.lazy=null}}]),e}();return{install:function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=H(e),r=new i(n),o=new Q({lazy:r}),s="2"===e.version.split(".")[0];e.prototype.$Lazyload=r,n.lazyComponent&&e.component("lazy-component",O(r)),s?(e.directive("lazy",{bind:r.add.bind(r),update:r.update.bind(r),componentUpdated:r.lazyLoadHandler.bind(r),unbind:r.remove.bind(r)}),e.directive("lazy-container",{bind:o.bind.bind(o),update:o.update.bind(o),unbind:o.unbind.bind(o)})):(e.directive("lazy",{bind:r.lazyLoadHandler.bind(r),update:function(e,n){t(this.vm.$refs,this.vm.$els),r.add(this.el,{modifiers:this.modifiers||{},arg:this.arg,value:e,oldValue:n},{context:this.vm})},unbind:function(){r.remove(this.el)}}),e.directive("lazy-container",{update:function(e,t){o.update(this.el,{modifiers:this.modifiers||{},arg:this.arg,value:e,oldValue:t},{context:this.vm})},unbind:function(){o.unbind(this.el)}}))}}});