import $ from 'jquery';
import rafSchd from 'raf-schd';
import { throttle } from 'throttle-debounce';

const { VPData } = window;
const { __ } = VPData;
const $wnd = $( window );

/**
 * Emit Resize Event.
 */
function windowResizeEmit() {
	if ( typeof window.Event === 'function' ) {
		// modern browsers
		window.dispatchEvent( new window.Event( 'resize' ) );
	} else {
		// for IE and other old browsers
		// causes deprecation warning on modern browsers
		const evt = window.document.createEvent( 'UIEvents' );
		evt.initUIEvent( 'resize', true, false, window, 0 );
		window.dispatchEvent( evt );
	}
}

const visibilityData = {};
let shouldCheckVisibility = false;
let checkVisibilityTimeout = false;
let isFocusVisible = false;

// fix portfolio inside Tabs and Accordions
// check visibility by timer https://stackoverflow.com/questions/19669786/check-if-element-is-visible-in-dom/33456469
//
// https://github.com/nk-crew/visual-portfolio/issues/11
// https://github.com/nk-crew/visual-portfolio/issues/113
function checkVisibility() {
	clearTimeout( checkVisibilityTimeout );

	if ( ! shouldCheckVisibility ) {
		return;
	}

	const $items = $( '.vp-portfolio__ready' );

	if ( $items.length ) {
		let isVisibilityChanged = false;

		$items.each( function () {
			const { vpf } = this;

			if ( ! vpf ) {
				return;
			}

			const currentState = visibilityData[ vpf.uid ] || 'none';

			visibilityData[ vpf.uid ] =
				this.offsetParent === null ? 'hidden' : 'visible';

			// changed from hidden to visible.
			if (
				currentState === 'hidden' &&
				visibilityData[ vpf.uid ] === 'visible'
			) {
				isVisibilityChanged = true;
			}
		} );

		// resize, if visibility changed.
		if ( isVisibilityChanged ) {
			windowResizeEmit();
		}
	} else {
		shouldCheckVisibility = false;
	}

	// run again.
	checkVisibilityTimeout = setTimeout( checkVisibility, 500 );
}

// run check function only after portfolio inited.
$( document ).on( 'inited.vpf', ( event ) => {
	if ( event.namespace !== 'vpf' ) {
		return;
	}

	shouldCheckVisibility = true;

	checkVisibility();
} );

/**
 * If the most recent user interaction was via the keyboard;
 * and the key press did not include a meta, alt/option, or control key;
 * then the modality is keyboard. Otherwise, the modality is not keyboard.
 */
document.addEventListener(
	'keydown',
	function ( e ) {
		if ( e.metaKey || e.altKey || e.ctrlKey ) {
			return;
		}

		isFocusVisible = true;
	},
	true
);

/**
 * If at any point a user clicks with a pointing device, ensure that we change
 * the modality away from keyboard.
 * This avoids the situation where a user presses a key on an already focused
 * element, and then clicks on a different element, focusing it with a
 * pointing device, while we still think we're in keyboard modality.
 */
document.addEventListener(
	'mousedown',
	() => {
		isFocusVisible = false;
	},
	true
);
document.addEventListener(
	'pointerdown',
	() => {
		isFocusVisible = false;
	},
	true
);
document.addEventListener(
	'touchstart',
	() => {
		isFocusVisible = false;
	},
	true
);

/**
 * Main VP class
 */
class VP {
	constructor( $item, userOptions ) {
		const self = this;

		self.$item = $item;

		// get id from class
		const classes = $item[ 0 ].className.split( /\s+/ );
		for ( let k = 0; k < classes.length; k += 1 ) {
			if ( classes[ k ] && /^vp-uid-/.test( classes[ k ] ) ) {
				self.uid = classes[ k ].replace( /^vp-uid-/, '' );
			}
			if ( classes[ k ] && /^vp-id-/.test( classes[ k ] ) ) {
				self.id = classes[ k ].replace( /^vp-id-/, '' );
			}
		}
		if ( ! self.uid ) {
			// eslint-disable-next-line no-console
			console.error( __.couldnt_retrieve_vp );
			return;
		}

		self.href = window.location.href;

		self.$items_wrap = $item.find( '.vp-portfolio__items' );
		self.$slider_thumbnails_wrap = $item.find(
			'.vp-portfolio__thumbnails'
		);
		self.$pagination = $item.find( '.vp-portfolio__pagination-wrap' );
		self.$filter = $item.find( '.vp-portfolio__filter-wrap' );
		self.$sort = $item.find( '.vp-portfolio__sort-wrap' );

		// find single filter block.
		if ( self.id ) {
			self.$filter = self.$filter.add(
				`.vp-single-filter.vp-id-${ self.id } .vp-portfolio__filter-wrap`
			);
		}

		// find single sort block.
		if ( self.id ) {
			self.$sort = self.$sort.add(
				`.vp-single-sort.vp-id-${ self.id } .vp-portfolio__sort-wrap`
			);
		}

		// user options
		self.userOptions = userOptions;

		self.firstRun = true;

		self.init();
	}

	// emit event
	// Example:
	// $(document).on('init.vpf', function (event, infiniteObject) {
	//     console.log(infiniteObject);
	// });
	emitEvent( event, data ) {
		data = data ? [ this ].concat( data ) : [ this ];
		this.$item.trigger( `${ event }.vpf`, data );
		this.$item.trigger( `${ event }.vpf-uid-${ this.uid }`, data );
	}

	/**
	 * Init
	 */
	init() {
		const self = this;

		// destroy if already inited
		if ( ! self.firstRun ) {
			self.destroy();
		}

		self.destroyed = false;

		self.$item.addClass( 'vp-portfolio__ready' );

		// init options
		self.initOptions();

		// init events
		self.initEvents();

		// init layout
		self.initLayout();

		// init custom colors
		self.initCustomColors();

		self.emitEvent( 'init' );

		if ( self.id ) {
			$( `.vp-single-filter.vp-id-${ self.id }` )
				.addClass( 'vp-single-filter__ready' )
				.parent( '.vp-portfolio__layout-elements' )
				.addClass( 'vp-portfolio__layout-elements__ready' );
			$( `.vp-single-sort.vp-id-${ self.id }` )
				.addClass( 'vp-single-sort__ready' )
				.parent( '.vp-portfolio__layout-elements' )
				.addClass( 'vp-portfolio__layout-elements__ready' );
		}

		// resized
		self.resized();

		// images loaded
		self.imagesLoaded();

		self.emitEvent( 'inited' );

		self.firstRun = false;
	}

	/**
	 * Check if script loaded in preview.
	 *
	 * @return {boolean} is in preview.
	 */
	isPreview() {
		const self = this;

		return !! self.$item.closest( '#vp_preview' ).length;
	}

	/**
	 * Called after resized container.
	 */
	resized() {
		windowResizeEmit();

		this.emitEvent( 'resized' );
	}

	/**
	 * Images loaded.
	 */
	imagesLoaded() {
		const self = this;

		if ( ! self.$items_wrap.imagesLoaded ) {
			return;
		}

		self.$items_wrap.imagesLoaded().progress( () => {
			this.emitEvent( 'imagesLoaded' );
		} );
	}

	/**
	 * Destroy
	 */
	destroy() {
		const self = this;

		// remove loaded class
		self.$item.removeClass( 'vp-portfolio__ready' );

		if ( self.id ) {
			$( `.vp-single-filter.vp-id-${ self.id }` )
				.removeClass( 'vp-single-filter__ready' )
				.parent( '.vp-portfolio__layout-elements' )
				.removeClass( 'vp-portfolio__layout-elements__ready' );
			$( `.vp-single-sort.vp-id-${ self.id }` )
				.removeClass( 'vp-single-sort__ready' )
				.parent( '.vp-portfolio__layout-elements' )
				.removeClass( 'vp-portfolio__layout-elements__ready' );
		}

		// destroy events
		self.destroyEvents();

		// remove all generated styles
		self.removeStyle();
		self.renderStyle();

		self.emitEvent( 'destroy' );

		self.destroyed = true;
	}

	/**
	 * Add style to the current portfolio list
	 *
	 * @param {string} selector css selector
	 * @param {string} styles   object with styles
	 * @param {string} media    string with media query
	 */
	addStyle( selector, styles, media ) {
		media = media || '';

		const self = this;
		const { uid } = self;

		if ( ! self.stylesList ) {
			self.stylesList = {};
		}

		if ( typeof self.stylesList[ uid ] === 'undefined' ) {
			self.stylesList[ uid ] = {};
		}
		if ( typeof self.stylesList[ uid ][ media ] === 'undefined' ) {
			self.stylesList[ uid ][ media ] = {};
		}
		if (
			typeof self.stylesList[ uid ][ media ][ selector ] === 'undefined'
		) {
			self.stylesList[ uid ][ media ][ selector ] = {};
		}

		self.stylesList[ uid ][ media ][ selector ] = $.extend(
			self.stylesList[ uid ][ media ][ selector ],
			styles
		);

		self.emitEvent( 'addStyle', [
			selector,
			styles,
			media,
			self.stylesList,
		] );
	}

	/**
	 * Remove style from the current portfolio list
	 *
	 * @param {string} selector css selector (if not set - removed all styles)
	 * @param {string} styles   object with styles
	 * @param {string} media    string with media query
	 */
	removeStyle( selector, styles, media ) {
		media = media || '';

		const self = this;
		const { uid } = self;

		if ( ! self.stylesList ) {
			self.stylesList = {};
		}

		if ( typeof self.stylesList[ uid ] !== 'undefined' && ! selector ) {
			self.stylesList[ uid ] = {};
		}

		if (
			typeof self.stylesList[ uid ] !== 'undefined' &&
			typeof self.stylesList[ uid ][ media ] !== 'undefined' &&
			typeof self.stylesList[ uid ][ media ][ selector ] !==
				'undefined' &&
			selector
		) {
			delete self.stylesList[ uid ][ media ][ selector ];
		}

		self.emitEvent( 'removeStyle', [ selector, styles, self.stylesList ] );
	}

	/**
	 * Render style for the current portfolio list
	 */
	renderStyle() {
		const self = this;

		// timeout for the case, when styles added one by one
		const { uid } = self;
		let stylesString = '';

		if ( ! self.stylesList ) {
			self.stylesList = {};
		}

		// create string with styles
		if ( typeof self.stylesList[ uid ] !== 'undefined' ) {
			Object.keys( self.stylesList[ uid ] ).forEach( ( m ) => {
				// media
				if ( m ) {
					stylesString += `@media ${ m } {`;
				}
				Object.keys( self.stylesList[ uid ][ m ] ).forEach( ( s ) => {
					// selector
					const selectorParent = `.vp-uid-${ uid }`;
					let selector = `${ selectorParent } ${ s }`;

					// add parent selector after `,`.
					selector = selector.replace(
						/, |,/g,
						`, ${ selectorParent } `
					);

					stylesString += `${ selector } {`;
					Object.keys( self.stylesList[ uid ][ m ][ s ] ).forEach(
						( p ) => {
							// property and value
							stylesString += `${ p }:${ self.stylesList[ uid ][ m ][ s ][ p ] };`;
						}
					);
					stylesString += '}';
				} );
				// media
				if ( m ) {
					stylesString += '}';
				}
			} );
		}

		// add in style tag
		let $style = $( `#vp-style-${ uid }` );
		if ( ! $style.length ) {
			$style = $( '<style>' )
				.attr( 'id', `vp-style-${ uid }` )
				.appendTo( 'head' );
		}
		$style.html( stylesString );

		self.emitEvent( 'renderStyle', [
			stylesString,
			self.stylesList,
			$style,
		] );
	}

	/**
	 * First char to lower case
	 *
	 * @param {string} str string to transform
	 * @return {string} result string
	 */
	firstToLowerCase( str ) {
		return str.substr( 0, 1 ).toLowerCase() + str.substr( 1 );
	}

	/**
	 * Init options
	 *
	 * @param {Object} userOptions user options
	 */
	initOptions( userOptions ) {
		const self = this;

		// default options
		self.defaults = {
			layout: 'tile',
			itemsGap: 0,
			pagination: 'load-more',
		};

		// new user options
		if ( userOptions ) {
			self.userOptions = userOptions;
		}

		// prepare data options
		const dataOptions = self.$item[ 0 ].dataset;

		const pureDataOptions = {};
		Object.keys( dataOptions ).forEach( ( k ) => {
			if ( k && k.substring( 0, 2 ) === 'vp' ) {
				pureDataOptions[ self.firstToLowerCase( k.substring( 2 ) ) ] =
					dataOptions[ k ];
			}
		} );

		self.options = $.extend(
			{},
			self.defaults,
			pureDataOptions,
			self.userOptions
		);

		self.emitEvent( 'initOptions' );
	}

	/**
	 * Init events
	 */
	initEvents() {
		const self = this;
		const evp = `.vpf-uid-${ self.uid }`;

		// Stretch
		function stretch() {
			const rect = self.$item[ 0 ].getBoundingClientRect();
			const { left } = rect;
			const right = window.innerWidth - rect.right;

			const ml = parseFloat( self.$item.css( 'margin-left' ) || 0 );
			const mr = parseFloat( self.$item.css( 'margin-right' ) || 0 );
			self.$item.css( {
				marginLeft: ml - left,
				marginRight: mr - right,
				maxWidth: 'none',
				width: 'auto',
			} );
		}
		if (
			self.$item.hasClass( 'vp-portfolio__stretch' ) &&
			! self.isPreview()
		) {
			$wnd.on(
				`load${ evp } resize${ evp } orientationchange${ evp }`,
				() => {
					stretch();
				}
			);
			stretch();
		}

		// add helper focus class
		// TODO: change to CSS :has() when will be widely available
		// @link https://caniuse.com/?search=%3Ahas
		self.$item.on( `focus${ evp }`, '.vp-portfolio__item a', function () {
			const $item = $( this ).closest( '.vp-portfolio__item' );

			$item.addClass( 'vp-portfolio__item-focus' );

			if ( isFocusVisible ) {
				$item.addClass( 'vp-portfolio__item-focus-visible' );
			}
		} );
		self.$item.on( `blur${ evp }`, '.vp-portfolio__item a', function () {
			$( this )
				.closest( '.vp-portfolio__item' )
				.removeClass(
					'vp-portfolio__item-focus vp-portfolio__item-focus-visible'
				);
		} );

		self.$filter.on(
			`click${ evp }`,
			'.vp-filter .vp-filter__item a',
			function ( e ) {
				e.preventDefault();
				const $this = $( this );
				if ( ! self.loading ) {
					$this
						.closest( '.vp-filter__item' )
						.addClass( 'vp-filter__item-active' )
						.siblings()
						.removeClass( 'vp-filter__item-active' );
				}
				self.loadNewItems( $this.attr( 'href' ), true );
			}
		);

		// on sort click
		self.$sort.on(
			`click${ evp }`,
			'.vp-sort .vp-sort__item a',
			function ( e ) {
				e.preventDefault();
				const $this = $( this );
				if ( ! self.loading ) {
					$this
						.closest( '.vp-sort__item' )
						.addClass( 'vp-sort__item-active' )
						.siblings()
						.removeClass( 'vp-sort__item-active' );
				}
				self.loadNewItems( $this.attr( 'href' ), true );
			}
		);

		// on filter/sort select change
		self.$filter
			.add( self.$sort )
			.on(
				`change${ evp }`,
				'.vp-filter select, .vp-sort select',
				function () {
					const $this = $( this );
					const value = $this.val();
					const $option = $this.find(
						`[value="${ value }"][data-vp-url]`
					);

					if ( $option.length ) {
						self.loadNewItems(
							$option.attr( 'data-vp-url' ),
							true
						);
					}
				}
			);

		// Function to handle scrolling to top
		function scrollToTop( $pagination ) {
			if (
				self.options.pagination === 'paged' &&
				$pagination.hasClass( 'vp-pagination__scroll-top' )
			) {
				const $adminBar = $( '#wpadminbar' );
				const currentTop =
					window.pageYOffset || document.documentElement.scrollTop;
				let { top } = self.$item.offset();

				// Custom user offset
				if ( $pagination.attr( 'data-vp-pagination-scroll-top' ) ) {
					top -=
						parseInt(
							$pagination.attr( 'data-vp-pagination-scroll-top' ),
							10
						) || 0;
				}

				// Admin bar offset
				if (
					$adminBar.length &&
					$adminBar.css( 'position' ) === 'fixed'
				) {
					top -= $adminBar.outerHeight();
				}

				// Limit max offset
				top = Math.max( 0, top );

				if ( currentTop > top ) {
					window.scrollTo( {
						top,
						behavior: 'smooth',
					} );
				}
			}
		}

		// Handle pagination click
		self.$item.on(
			`click${ evp }`,
			'.vp-pagination .vp-pagination__item a',
			function ( e ) {
				e.preventDefault();

				const $this = $( this );
				const $pagination = $this.closest( '.vp-pagination' );

				if (
					$pagination.hasClass( 'vp-pagination__no-more' ) &&
					self.options.pagination !== 'paged'
				) {
					return;
				}

				self.loadNewItems(
					$this.attr( 'href' ),
					self.options.pagination === 'paged'
				);

				scrollToTop( $pagination );
			}
		);

		// on categories of item click
		self.$item.on(
			`click${ evp }`,
			'.vp-portfolio__items .vp-portfolio__item-meta-category a',
			function ( e ) {
				e.preventDefault();
				e.stopPropagation();
				self.loadNewItems( $( this ).attr( 'href' ), true );
			}
		);

		// resized container
		self.$item.on(
			`transitionend${ evp }`,
			'.vp-portfolio__items',
			( e ) => {
				if ( e.currentTarget === e.target ) {
					self.resized();
				}
			}
		);

		self.emitEvent( 'initEvents' );
	}

	/**
	 * Destroy events
	 */
	destroyEvents() {
		const self = this;
		const evp = `.vpf-uid-${ self.uid }`;

		// destroy click events
		self.$item.off( evp );
		self.$filter.off( evp );
		self.$sort.off( evp );

		// destroy window events
		$wnd.off( evp );

		self.emitEvent( 'destroyEvents' );
	}

	/**
	 * Init layout
	 */
	initLayout() {
		const self = this;

		self.emitEvent( 'initLayout' );

		self.renderStyle();
	}

	/**
	 * Init custom color by data attributes:
	 * data-vp-bg-color
	 * data-vp-text-color
	 */
	initCustomColors() {
		const self = this;

		self.$item.find( '[data-vp-bg-color]' ).each( function () {
			const val = $( this ).attr( 'data-vp-bg-color' );
			self.addStyle( `[data-vp-bg-color="${ val }"]`, {
				'background-color': `${ val } !important`,
			} );
		} );

		self.$item.find( '[data-vp-text-color]' ).each( function () {
			const val = $( this ).attr( 'data-vp-text-color' );
			self.addStyle( `[data-vp-text-color="${ val }"]`, {
				color: `${ val } !important`,
			} );
		} );

		self.renderStyle();

		self.emitEvent( 'initCustomColors' );
	}

	/**
	 * Add New Items
	 *
	 * @param {object|dom|jQuery} $items         - elements.
	 * @param {bool}              removeExisting - remove existing elements.
	 * @param {Object}            $newVP         - new visual portfolio jQuery.
	 */
	addItems( $items, removeExisting, $newVP ) {
		const self = this;

		self.emitEvent( 'addItems', [ $items, removeExisting, $newVP ] );
	}

	/**
	 * Remove Items
	 *
	 * @param {object|dom|jQuery} $items - elements.
	 */
	removeItems( $items ) {
		const self = this;

		self.emitEvent( 'removeItems', [ $items ] );
	}

	/**
	 * AJAX Load New Items
	 *
	 * @param {string}   url            - url to request.
	 * @param {bool}     removeExisting - remove existing elements.
	 * @param {Function} cb             - callback.
	 */
	loadNewItems( url, removeExisting, cb ) {
		const self = this;
		const { randomSeed } = self.options;

		if (
			( self.loading &&
				typeof self.loading.readyState === 'undefined' ) ||
			! url ||
			self.href === url
		) {
			return;
		}

		// Abort previous AJAX loader to prevent conflict.
		// We need it mostly for Search feature, because users can type also when already in loading state.
		if ( self.loading && self.loading.readyState && self.loading.abort ) {
			self.loading.abort();
		}

		const ajaxData = {
			method: 'POST',
			url,
			data: {
				vpf_ajax_call: true,
				vpf_random_seed:
					typeof randomSeed !== 'undefined' ? randomSeed : false,
			},
			complete( { responseText } ) {
				self.href = url;
				self.replaceItems( responseText, removeExisting, cb );
			},
		};

		self.loading = true;

		self.$item.addClass( 'vp-portfolio__loading' );

		self.emitEvent( 'startLoadingNewItems', [ url, ajaxData ] );

		self.loading = $.ajax( ajaxData );
	}

	/**
	 * Replace items to the new loaded using AJAX
	 *
	 * @param {string}   content        - new page content.
	 * @param {bool}     removeExisting - remove existing elements.
	 * @param {Function} cb             - callback.
	 */
	replaceItems( content, removeExisting, cb ) {
		const self = this;

		if ( ! content ) {
			return;
		}

		// load to invisible container, then append to posts container
		content = content
			.replace( '<body', '<body><div id="vp-ajax-load-body"' )
			.replace( '</body>', '</div></body>' );
		const $body = $( content ).filter( '#vp-ajax-load-body' );

		// find current block on new page
		const $newVP = $body.find( `.vp-portfolio.vp-uid-${ self.uid }` );

		// insert new items
		if ( $newVP.length ) {
			const newItems = $newVP.find( '.vp-portfolio__items' ).html();
			const nothingFound = $newVP.hasClass( 'vp-portfolio-not-found' );

			// We should clean up notices here, as they may be cloned over and over.
			self.$item.find( '.vp-notice' ).remove();

			if ( nothingFound ) {
				self.$item
					.find( '.vp-portfolio__items-wrap' )
					.before( $newVP.find( '.vp-notice' ).clone() );
				self.$item.addClass( 'vp-portfolio-not-found' );
			} else {
				self.$item.removeClass( 'vp-portfolio-not-found' );
			}

			// update filter
			if ( self.$filter.length ) {
				self.$filter.each( function () {
					const $filter = $( this );
					let newFilterContent = '';

					if ( $filter.parent().hasClass( 'vp-single-filter' ) ) {
						newFilterContent = $body
							.find(
								`[class="${ $filter
									.parent()
									.attr( 'class' )
									.replace(
										' vp-single-filter__ready',
										''
									) }"] .vp-portfolio__filter-wrap`
							)
							.html();
					} else {
						newFilterContent = $newVP
							.find( '.vp-portfolio__filter-wrap' )
							.html();
					}

					$filter.html( newFilterContent );
				} );
			}

			// update sort
			if ( self.$sort.length ) {
				self.$sort.each( function () {
					const $sort = $( this );
					let newFilterContent = '';

					if ( $sort.parent().hasClass( 'vp-single-sort' ) ) {
						newFilterContent = $body
							.find(
								`[class="${ $sort
									.parent()
									.attr( 'class' )
									.replace(
										' vp-single-sort__ready',
										''
									) }"] .vp-portfolio__sort-wrap`
							)
							.html();
					} else {
						newFilterContent = $newVP
							.find( '.vp-portfolio__sort-wrap' )
							.html();
					}

					$sort.html( newFilterContent );
				} );
			}

			// update pagination
			if ( self.$pagination.length ) {
				self.$pagination.html(
					$newVP.find( '.vp-portfolio__pagination-wrap' ).html()
				);
			}

			self.addItems( $( newItems ), removeExisting, $newVP );

			self.emitEvent( 'loadedNewItems', [
				$newVP,
				removeExisting,
				content,
			] );

			if ( cb ) {
				cb();
			}
		}

		// update next page data
		const nextPageUrl = $newVP.attr( 'data-vp-next-page-url' );
		self.options.nextPageUrl = nextPageUrl;
		self.$item.attr( 'data-vp-next-page-url', nextPageUrl );

		self.$item.removeClass( 'vp-portfolio__loading' );

		self.loading = false;

		self.emitEvent( 'endLoadingNewItems' );

		// images loaded
		self.imagesLoaded();

		// init custom colors
		self.initCustomColors();
	}
}

// extend VP object.
$( document ).trigger( 'extendClass.vpf', [ VP ] );

// global definition
const plugin = function ( options, ...args ) {
	let ret;

	this.each( function () {
		if ( typeof ret !== 'undefined' ) {
			return;
		}

		if ( typeof options === 'object' || typeof options === 'undefined' ) {
			if ( ! this.vpf ) {
				this.vpf = new VP( $( this ), options );
			}
		} else if ( this.vpf ) {
			ret = this.vpf[ options ]( ...args );
		}
	} );

	return typeof ret !== 'undefined' ? ret : this;
};
plugin.constructor = VP;

// no conflict
const oldPlugin = $.fn.vpf;
$.fn.vpf = plugin;
$.fn.vpf.noConflict = function () {
	$.fn.vpf = oldPlugin;
	return this;
};

// initialization
$( () => {
	$( '.vp-portfolio' ).vpf();
} );

const throttledInit = throttle(
	200,
	rafSchd( () => {
		$( '.vp-portfolio:not(.vp-portfolio__ready)' ).vpf();
	} )
);
if ( window.MutationObserver ) {
	new window.MutationObserver( throttledInit ).observe(
		document.documentElement,
		{
			childList: true,
			subtree: true,
		}
	);
} else {
	$( document ).on( 'DOMContentLoaded DOMNodeInserted load', () => {
		throttledInit();
	} );
}
