import PhotoSwipe from 'photoswipe' ; import defaultUI from 'photoswipe/dist/photoswipe-ui-default' ; import { PswpItem , Size , FindIndex , Closest , Single , PswpDirectiveOptions , CreatePhotoSwipe , BindEvent , HandleWithoutSize , Pswp , GetContainSize , RotateDirection , CurrentPswpItem , StyleKey , ManualCreateArgs , ManualOpenItem , } from '@/type' ; import { customEvents , GlobalOption } from '@/config' ; import Vue from 'vue' ; import PswpUI from '@/components/pswpUI.vue' ; export const isMobile = ( ) : boolean => /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i . test ( navigator . userAgent ) ; export const isNum = ( value ) : value is number => typeof value === 'number' ; export const isStr = ( value ) : value is string => typeof value === 'string' ; export const isObj = ( value ) : value is object => Object . prototype . toString . call ( value ) === '[object Object]' ; export const isFunction = ( value ) : value is Function => Object . prototype . toString . call ( value ) === '[object Function]' ; const isDef = ( value ) : boolean => value !== undefined && value !== null ; export const isImg = ( value ) : value is HTMLImageElement => value && value . tagName === 'IMG' ; const isEle = ( node ) : node is HTMLElement => node . nodeType === 1 ; export const isBgImg = ( el ) : boolean => ! isImg ( el ) && ! ! el . dataset . pswpSrc ; param hint export const errorHandler = ( hint ) : never => { throw new Error ( `[vue-pswipe] ${ hint }` ) ; } ; /**
 * get current active PhotoSwipe, else null
 */ export namespace CurrentPswp { let currentPswp = null ; const setupClean = ( pswp ) => { pswp . listen ( 'destroy' , ( ) => { currentPswp = null ; } ) ; } ; export const get = ( ) => currentPswp ; export const set = ( pswp ) => { currentPswp = pswp ; if ( pswp ) setupClean ( pswp ) ; } ; } param path return export const getImageSize = ( path ) => new Promise < Size > ( ( resolve ) => { const img = new Image ( ) ; let timer ; img . src = path ; img . addEventListener ( 'error' , ( ) => { clearTimeout ( timer ) ; } ) ; const check = ( ) => { if ( img . width > 0 || img . height > 0 ) { return resolve ( { w : img . width , h : img . height , } ) ; } timer = window . setTimeout ( check , 40 ) ; } ; check ( ) ; } ) ; param array param predicate return export const findIndex = ( array , predicate ) => { let index = - 1 ; array . some ( ( item , idx ) => { const result = predicate ( item , idx ) ; if ( result ) index = idx ; return result ; } ) ; return index ; } ; return export const parseHash = ( ) => { const hash = window . location . hash . substring ( 1 ) ; const params = { } ; if ( hash . length < 5 ) return params ; hash . split ( '&' ) . reduce ( ( acc , cur ) => { if ( ! cur ) return acc ; const pair = cur . split ( '=' ) ; if ( pair . length < 2 ) return acc ; const [ key , value ] = pair ; acc [ key ] = value ; return acc ; } , params ) ; return params ; } ; param selector param context return export const querySelectorList = < T extends HTMLElement > ( selector , context = document ) => [ ... context . querySelectorAll ( selector ) ] as T [ ] ; param el param predicate returns export const closest = ( el , predicate ) => ! ! el && isEle ( el ) && ( predicate ( el ) ? el : closest ( el . parentNode , predicate ) ) ; param fn return export const single = ( fn ) => { let result ; return function ( this , ... args ) { return result || ( result = fn . apply ( this , args ) ) ; } ; } ; param el return const append = ( el ) => document . body . appendChild ( el ) ; /**
 * append element to body only once
 */ export const appendOnce = single < HTMLElement > ( append ) ; param el param size export const setSize = ( el , { w , h } ) => { if ( el && el . dataset ) { el . dataset . pswpSize = `${ w }x${ h }` ; } } ; param target param auto export const getSrc = ( target , auto ) : string => auto && isImg ( target ) ? target . src : target . dataset . pswpSrc || '' ; /**
 * determine whether el is a valid element based on auto and filter
 */ export const relevant = ( el , auto , filter ) : boolean => ( auto ? isImg ( el ) && filter ( el ) : ! ! el . dataset . pswpSrc ) ; /**
 * Convert the first letter to uppercase
 */ const upperFirst = ( str ) => str . replace ( /^\S/ , ( match ) => match . toUpperCase ( ) ) ; /**
 * convert property to pswp property, eg: src => pswpSrc
 */ const getPswpDataKey = ( property ) => `pswp${ upperFirst ( property ) }` ; /**
 * Set pswp data to the data attribute of the specified element
 */ export const setPswpData = ( options , el ) => { Object . keys ( options ) . forEach ( ( key ) => { el . dataset [ getPswpDataKey ( key ) ] = `${ options [ key as keyof PswpDirectiveOptions ] }` ; } ) ; } ; /**
 * Set the pswp data according to the type of the parameter
 */ export const setPswpDataByCond = ( el , value ) => { if ( isStr ( value ) ) setPswpData ( { src : value } , el ) ; if ( isObj ( value ) ) setPswpData ( value as PswpDirectiveOptions , el ) ; } ; /**
 * preset loaded msrc size to PswpItem
 */ export const presetSize = ( item ) : void => { const { src , msrc , el } = item ; if ( item . w || item . h || ! msrc ) return ; let img = new Image ( ) ; img . src = msrc ; const { width : w , height : h } = img ; if ( w && h ) { item . w = w ; item . h = h ; src === msrc && setSize ( el , { w , h } ) ; } img = null ; } ; param pswp param context const bindEvent = ( pswp , context ) => { if ( ! context ) return ; Object . keys ( context . $listeners ) . filter ( ( event ) => ! customEvents . includes ( event ) ) . forEach ( ( event ) => { const fn = context . $listeners [ event ] ; if ( isFunction ( fn ) ) { pswp . listen ( event , ( ... args ) => { context . $emit ( event , ... args ) ; } ) ; } } ) ; } ; param item param pswp const hackItemImg = ( item , pswp ) => { let img = null ; Object . defineProperty ( item , 'img' , { get ( ) { return img ; } , set ( value ) { if ( isImg ( value ) ) { value . addEventListener ( 'load' , ( ) => { const { naturalWidth : w , naturalHeight : h } = value ; item . w = w ; item . h = h ; setSize ( item . el , { w , h } ) ; if ( CurrentPswp . get ( ) === pswp ) { pswp . updateSize ( true ) ; } } ) ; } img = value ; } , } ) ; } ; param pswp const handleWithoutSize = ( pswp ) => { pswp . listen ( 'gettingData' , ( index , item ) => { presetSize ( item ) ; if ( ( item . el && item . el . dataset . pswpSize ) || Object . getOwnPropertyDescriptor ( item , 'img' ) ) return ; if ( pswp . currItem === item ) { pswp . options . showAnimationDuration = 0 ; } hackItemImg ( item , pswp ) ; } ) ; } ; const revertRotate = ( pswp ) => { pswp . listen ( 'gettingData' , ( index , item ) => { if ( ! item . verticalRotated ) return ; const { w } = item ; item . w = item . h ; item . h = w ; item . verticalRotated = false ; } ) ; } ; /**
 * manipulate Photoswipe default UI element
 */ export namespace UI { export let el ; export const mount = ( ) => { if ( ! el ) { const PswpUIComponent = new Vue ( PswpUI ) . $mount ( ) ; el = < HTMLElement > PswpUIComponent . $el ; } } ; export const append = ( ) => { mount ( ) ; appendOnce ( el ) ; } ; } param items const defineSize = ( items ) => items . map ( ( item ) => { if ( ! isDef ( item . w ) ) item . w = 0 ; if ( ! isDef ( item . h ) ) item . h = 0 ; return item ; } ) ; return export const createPhotoSwipe = ( { items , options , context } ) => { const pswp = new PhotoSwipe ( UI . el , defaultUI , items , options ) ; bindEvent ( pswp , context ) ; handleWithoutSize ( pswp ) ; revertRotate ( pswp ) ; CurrentPswp . set ( pswp ) ; pswp . init ( ) ; return pswp ; } ; /**
 * used for this.$Pswp.open()
 */ export const manualCreate = ( { items , options } ) => createPhotoSwipe ( { items : defineSize ( items ) , options : { ... GlobalOption . get ( ) , hideAnimationDuration : 0 , showAnimationDuration : 0 , ... options , history : false , } , } ) ; param areaWidth param areaHeight param width param height return export const getContainSize = ( areaWidth , areaHeight , width , height ) => { if ( width <= areaWidth && height <= areaHeight ) return { w : width , h : height } ; const ratio = width / height ; const areaRatio = areaWidth / areaHeight ; return areaRatio < ratio ? { w : areaWidth , h : areaWidth / ratio } : { w : areaHeight * ratio , h : areaHeight } ; } ; /**
 * custom event
 */ export namespace Event { const event = { } ; export const off = ( name , fn ? ) => { if ( ! fn ) return ( event [ name ] . length = 0 ) ; const pools = event [ name ] ; const index = pools . indexOf ( fn ) ; if ( index !== - 1 ) pools . splice ( index , 1 ) ; } ; export const on = ( name , fn ) => { if ( ! event [ name ] ) event [ name ] = [ ] ; event [ name ] . push ( fn ) ; return ( ) => off ( name , fn ) ; } ; export const once = ( name , fn ) => { const teardown = on ( name , ( ... args ) => { teardown ( ) ; fn ( ... args ) ; } ) ; } ; export const emit = ( name , ... args ) => { const pools = event [ name ] ; if ( ! Array . isArray ( pools ) ) return ; pools . forEach ( ( fn ) => fn ( ... args ) ) ; } ; } param img param direction return export const getTransformDeg = ( img , direction ) => { const deg = Number ( img . dataset . rotateDeg ) || 0 ; const offsets = direction === 'left' ? - 90 : 90 ; const transformDeg = deg + offsets ; img . dataset . rotateDeg = `${ transformDeg }` ; return [ deg , transformDeg ] ; } ; param container param currentItem return export const getContainerSize = ( container , currentItem ) => { const containerWidth = container . clientWidth ; const containerHeight = currentItem . vGap ? container . clientHeight - currentItem . vGap . top - currentItem . vGap . bottom : container . clientHeight ; return { w : containerWidth , h : containerHeight , } ; } ; param w param h export const getScale = ( w , h = w ) => `scale(${ w }, ${ h })` ; param containerSize param img param isVertical export const getCalculatedScale = ( containerSize , img , isVertical ) : string [ ] => { const { naturalWidth , naturalHeight } = img ; const { w : horizontalWidth , h : horizontalHeight } = getContainSize ( containerSize . w , containerSize . h , naturalWidth , naturalHeight ) ; const { w : verticalWidth , h : verticalHeight } = getContainSize ( containerSize . w , containerSize . h , naturalHeight , naturalWidth ) ; const animatedScale = isVertical ? getScale ( verticalHeight / horizontalWidth ) : getScale ( horizontalWidth / verticalWidth , horizontalHeight / verticalHeight ) ; const verticalSilencedScale = getScale ( verticalHeight / verticalWidth , verticalWidth / verticalHeight ) ; return [ animatedScale , verticalSilencedScale ] ; } ; /**
 * add vendor prefix to css property
 */ export const modernize = ( ( ) => { const cache = { } ; const detectElement = document . createElement ( 'div' ) ; const { style } = detectElement ; return < T extends StyleKey > ( styleKey ) : T => { const cached = cache [ styleKey ] ; if ( cached ) return cached as T ; let key = styleKey ; if ( ! isDef ( style [ styleKey ] ) ) { ; [ 'Moz' , 'ms' , 'O' , 'Webkit' ] . some ( ( prefix ) => { const prefixedStyleKey = < T > ( prefix + upperFirst ( styleKey ) ) ; if ( isDef ( style [ prefixedStyleKey ] ) ) { return ( key = prefixedStyleKey ) ; } } ) ; } cache [ styleKey ] = key ; return key ; } ; } ) ( ) ; /**
 * get prefixed transition end event name
 */ export const transitionEndEventName = ( ( ) => { const transitions = { transition : 'transitionend' , OTransition : 'oTransitionEnd' , MozTransition : 'transitionend' , WebkitTransition : 'webkitTransitionEnd' , } ; const detected = modernize ( 'transition' ) ; return transitions [ detected ] ; } ) ( ) ; /**
 * register v-pswp directive if needed
 */ export const registerDirective = ( ) => { const pswpDirective = Vue . directive ( 'pswp' ) ; if ( ! pswpDirective ) { Vue . directive ( 'pswp' , { bind ( el , { value } ) { setPswpDataByCond ( el , value ) ; } , update ( el , { value , oldValue } ) { if ( value === oldValue ) return ; setPswpDataByCond ( el , value ) ; } , } ) ; } } ;