jquery.fancybox.pack.js 157 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632
  1. // ==================================================
  2. // fancyBox v3.5.7
  3. //
  4. // Licensed GPLv3 for open source use
  5. // or fancyBox Commercial License for commercial use
  6. //
  7. // http://fancyapps.com/fancybox/
  8. // Copyright 2019 fancyApps
  9. //
  10. // ==================================================
  11. (function (window, document, $, undefined) {
  12. "use strict";
  13. window.console = window.console || {
  14. info: function (stuff) {}
  15. };
  16. // If there's no jQuery, fancyBox can't work
  17. // =========================================
  18. if (!$) {
  19. return;
  20. }
  21. // Check if fancyBox is already initialized
  22. // ========================================
  23. if ($.fn.fancybox) {
  24. console.info("fancyBox already initialized");
  25. return;
  26. }
  27. // Private default settings
  28. // ========================
  29. var defaults = {
  30. // Close existing modals
  31. // Set this to false if you do not need to stack multiple instances
  32. closeExisting: false,
  33. // Enable infinite gallery navigation
  34. loop: false,
  35. // Horizontal space between slides
  36. gutter: 50,
  37. // Enable keyboard navigation
  38. keyboard: true,
  39. // Should allow caption to overlap the content
  40. preventCaptionOverlap: true,
  41. // Should display navigation arrows at the screen edges
  42. arrows: true,
  43. // Should display counter at the top left corner
  44. infobar: true,
  45. // Should display close button (using `btnTpl.smallBtn` template) over the content
  46. // Can be true, false, "auto"
  47. // If "auto" - will be automatically enabled for "html", "inline" or "ajax" items
  48. smallBtn: "auto",
  49. // Should display toolbar (buttons at the top)
  50. // Can be true, false, "auto"
  51. // If "auto" - will be automatically hidden if "smallBtn" is enabled
  52. toolbar: "auto",
  53. // What buttons should appear in the top right corner.
  54. // Buttons will be created using templates from `btnTpl` option
  55. // and they will be placed into toolbar (class="fancybox-toolbar"` element)
  56. buttons: [
  57. "zoom",
  58. //"share",
  59. "slideShow",
  60. //"fullScreen",
  61. //"download",
  62. "thumbs",
  63. "close"
  64. ],
  65. // Detect "idle" time in seconds
  66. idleTime: 3,
  67. // Disable right-click and use simple image protection for images
  68. protect: false,
  69. // Shortcut to make content "modal" - disable keyboard navigtion, hide buttons, etc
  70. modal: false,
  71. image: {
  72. // Wait for images to load before displaying
  73. // true - wait for image to load and then display;
  74. // false - display thumbnail and load the full-sized image over top,
  75. // requires predefined image dimensions (`data-width` and `data-height` attributes)
  76. preload: false
  77. },
  78. ajax: {
  79. // Object containing settings for ajax request
  80. settings: {
  81. // This helps to indicate that request comes from the modal
  82. // Feel free to change naming
  83. data: {
  84. fancybox: true
  85. }
  86. }
  87. },
  88. iframe: {
  89. // Iframe template
  90. tpl: '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" allowfullscreen="allowfullscreen" allow="autoplay; fullscreen" src=""></iframe>',
  91. // Preload iframe before displaying it
  92. // This allows to calculate iframe content width and height
  93. // (note: Due to "Same Origin Policy", you can't get cross domain data).
  94. preload: true,
  95. // Custom CSS styling for iframe wrapping element
  96. // You can use this to set custom iframe dimensions
  97. css: {},
  98. // Iframe tag attributes
  99. attr: {
  100. scrolling: "auto"
  101. }
  102. },
  103. // For HTML5 video only
  104. video: {
  105. tpl: '<video class="fancybox-video" controls controlsList="nodownload" poster="{{poster}}">' +
  106. '<source src="{{src}}" type="{{format}}" />' +
  107. 'Sorry, your browser doesn\'t support embedded videos, <a href="{{src}}">download</a> and watch with your favorite video player!' +
  108. "</video>",
  109. format: "", // custom video format
  110. autoStart: true
  111. },
  112. // Default content type if cannot be detected automatically
  113. defaultType: "image",
  114. // Open/close animation type
  115. // Possible values:
  116. // false - disable
  117. // "zoom" - zoom images from/to thumbnail
  118. // "fade"
  119. // "zoom-in-out"
  120. //
  121. animationEffect: "zoom",
  122. // Duration in ms for open/close animation
  123. animationDuration: 366,
  124. // Should image change opacity while zooming
  125. // If opacity is "auto", then opacity will be changed if image and thumbnail have different aspect ratios
  126. zoomOpacity: "auto",
  127. // Transition effect between slides
  128. //
  129. // Possible values:
  130. // false - disable
  131. // "fade'
  132. // "slide'
  133. // "circular'
  134. // "tube'
  135. // "zoom-in-out'
  136. // "rotate'
  137. //
  138. transitionEffect: "fade",
  139. // Duration in ms for transition animation
  140. transitionDuration: 366,
  141. // Custom CSS class for slide element
  142. slideClass: "",
  143. // Custom CSS class for layout
  144. baseClass: "",
  145. // Base template for layout
  146. baseTpl: '<div class="fancybox-container" role="dialog" tabindex="-1">' +
  147. '<div class="fancybox-bg"></div>' +
  148. '<div class="fancybox-inner">' +
  149. '<div class="fancybox-infobar"><span data-fancybox-index></span>&nbsp;/&nbsp;<span data-fancybox-count></span></div>' +
  150. '<div class="fancybox-toolbar">{{buttons}}</div>' +
  151. '<div class="fancybox-navigation">{{arrows}}</div>' +
  152. '<div class="fancybox-stage"></div>' +
  153. '<div class="fancybox-caption"><div class="fancybox-caption__body"></div></div>' +
  154. "</div>" +
  155. "</div>",
  156. // Loading indicator template
  157. spinnerTpl: '<div class="fancybox-loading"></div>',
  158. // Error message template
  159. errorTpl: '<div class="fancybox-error"><p>{{ERROR}}</p></div>',
  160. btnTpl: {
  161. download: '<a download data-fancybox-download class="fancybox-button fancybox-button--download" title="{{DOWNLOAD}}" href="javascript:;">' +
  162. '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.62 17.09V19H5.38v-1.91zm-2.97-6.96L17 11.45l-5 4.87-5-4.87 1.36-1.32 2.68 2.64V5h1.92v7.77z"/></svg>' +
  163. "</a>",
  164. zoom: '<button data-fancybox-zoom class="fancybox-button fancybox-button--zoom" title="{{ZOOM}}">' +
  165. '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.7 17.3l-3-3a5.9 5.9 0 0 0-.6-7.6 5.9 5.9 0 0 0-8.4 0 5.9 5.9 0 0 0 0 8.4 5.9 5.9 0 0 0 7.7.7l3 3a1 1 0 0 0 1.3 0c.4-.5.4-1 0-1.5zM8.1 13.8a4 4 0 0 1 0-5.7 4 4 0 0 1 5.7 0 4 4 0 0 1 0 5.7 4 4 0 0 1-5.7 0z"/></svg>' +
  166. "</button>",
  167. close: '<button data-fancybox-close class="fancybox-button fancybox-button--close" title="{{CLOSE}}">' +
  168. '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 10.6L6.6 5.2 5.2 6.6l5.4 5.4-5.4 5.4 1.4 1.4 5.4-5.4 5.4 5.4 1.4-1.4-5.4-5.4 5.4-5.4-1.4-1.4-5.4 5.4z"/></svg>' +
  169. "</button>",
  170. // Arrows
  171. arrowLeft: '<button data-fancybox-prev class="fancybox-button fancybox-button--arrow_left" title="{{PREV}}">' +
  172. '<div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M11.28 15.7l-1.34 1.37L5 12l4.94-5.07 1.34 1.38-2.68 2.72H19v1.94H8.6z"/></svg></div>' +
  173. "</button>",
  174. arrowRight: '<button data-fancybox-next class="fancybox-button fancybox-button--arrow_right" title="{{NEXT}}">' +
  175. '<div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.4 12.97l-2.68 2.72 1.34 1.38L19 12l-4.94-5.07-1.34 1.38 2.68 2.72H5v1.94z"/></svg></div>' +
  176. "</button>",
  177. // This small close button will be appended to your html/inline/ajax content by default,
  178. // if "smallBtn" option is not set to false
  179. smallBtn: '<button type="button" data-fancybox-close class="fancybox-button fancybox-close-small" title="{{CLOSE}}">' +
  180. '<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 24 24"><path d="M13 12l5-5-1-1-5 5-5-5-1 1 5 5-5 5 1 1 5-5 5 5 1-1z"/></svg>' +
  181. "</button>"
  182. },
  183. // Container is injected into this element
  184. parentEl: "body",
  185. // Hide browser vertical scrollbars; use at your own risk
  186. hideScrollbar: true,
  187. // Focus handling
  188. // ==============
  189. // Try to focus on the first focusable element after opening
  190. autoFocus: true,
  191. // Put focus back to active element after closing
  192. backFocus: true,
  193. // Do not let user to focus on element outside modal content
  194. trapFocus: true,
  195. // Module specific options
  196. // =======================
  197. fullScreen: {
  198. autoStart: false
  199. },
  200. // Set `touch: false` to disable panning/swiping
  201. touch: {
  202. vertical: true, // Allow to drag content vertically
  203. momentum: true // Continue movement after releasing mouse/touch when panning
  204. },
  205. // Hash value when initializing manually,
  206. // set `false` to disable hash change
  207. hash: null,
  208. // Customize or add new media types
  209. // Example:
  210. /*
  211. media : {
  212. youtube : {
  213. params : {
  214. autoplay : 0
  215. }
  216. }
  217. }
  218. */
  219. media: {},
  220. slideShow: {
  221. autoStart: false,
  222. speed: 3000
  223. },
  224. thumbs: {
  225. autoStart: false, // Display thumbnails on opening
  226. hideOnClose: true, // Hide thumbnail grid when closing animation starts
  227. parentEl: ".fancybox-container", // Container is injected into this element
  228. axis: "y" // Vertical (y) or horizontal (x) scrolling
  229. },
  230. // Use mousewheel to navigate gallery
  231. // If 'auto' - enabled for images only
  232. wheel: "auto",
  233. // Callbacks
  234. //==========
  235. // See Documentation/API/Events for more information
  236. // Example:
  237. /*
  238. afterShow: function( instance, current ) {
  239. console.info( 'Clicked element:' );
  240. console.info( current.opts.$orig );
  241. }
  242. */
  243. onInit: $.noop, // When instance has been initialized
  244. beforeLoad: $.noop, // Before the content of a slide is being loaded
  245. afterLoad: $.noop, // When the content of a slide is done loading
  246. beforeShow: $.noop, // Before open animation starts
  247. afterShow: $.noop, // When content is done loading and animating
  248. beforeClose: $.noop, // Before the instance attempts to close. Return false to cancel the close.
  249. afterClose: $.noop, // After instance has been closed
  250. onActivate: $.noop, // When instance is brought to front
  251. onDeactivate: $.noop, // When other instance has been activated
  252. // Interaction
  253. // ===========
  254. // Use options below to customize taken action when user clicks or double clicks on the fancyBox area,
  255. // each option can be string or method that returns value.
  256. //
  257. // Possible values:
  258. // "close" - close instance
  259. // "next" - move to next gallery item
  260. // "nextOrClose" - move to next gallery item or close if gallery has only one item
  261. // "toggleControls" - show/hide controls
  262. // "zoom" - zoom image (if loaded)
  263. // false - do nothing
  264. // Clicked on the content
  265. clickContent: function (current, event) {
  266. return current.type === "image" ? "zoom" : false;
  267. },
  268. // Clicked on the slide
  269. clickSlide: "close",
  270. // Clicked on the background (backdrop) element;
  271. // if you have not changed the layout, then most likely you need to use `clickSlide` option
  272. clickOutside: "close",
  273. // Same as previous two, but for double click
  274. dblclickContent: false,
  275. dblclickSlide: false,
  276. dblclickOutside: false,
  277. // Custom options when mobile device is detected
  278. // =============================================
  279. mobile: {
  280. preventCaptionOverlap: false,
  281. idleTime: false,
  282. clickContent: function (current, event) {
  283. return current.type === "image" ? "toggleControls" : false;
  284. },
  285. clickSlide: function (current, event) {
  286. return current.type === "image" ? "toggleControls" : "close";
  287. },
  288. dblclickContent: function (current, event) {
  289. return current.type === "image" ? "zoom" : false;
  290. },
  291. dblclickSlide: function (current, event) {
  292. return current.type === "image" ? "zoom" : false;
  293. }
  294. },
  295. // Internationalization
  296. // ====================
  297. lang: "en",
  298. i18n: {
  299. en: {
  300. CLOSE: "Close",
  301. NEXT: "Next",
  302. PREV: "Previous",
  303. ERROR: "The requested content cannot be loaded. <br/> Please try again later.",
  304. PLAY_START: "Start slideshow",
  305. PLAY_STOP: "Pause slideshow",
  306. FULL_SCREEN: "Full screen",
  307. THUMBS: "Thumbnails",
  308. DOWNLOAD: "Download",
  309. SHARE: "Share",
  310. ZOOM: "Zoom"
  311. },
  312. de: {
  313. CLOSE: "Schlie&szlig;en",
  314. NEXT: "Weiter",
  315. PREV: "Zur&uuml;ck",
  316. ERROR: "Die angeforderten Daten konnten nicht geladen werden. <br/> Bitte versuchen Sie es sp&auml;ter nochmal.",
  317. PLAY_START: "Diaschau starten",
  318. PLAY_STOP: "Diaschau beenden",
  319. FULL_SCREEN: "Vollbild",
  320. THUMBS: "Vorschaubilder",
  321. DOWNLOAD: "Herunterladen",
  322. SHARE: "Teilen",
  323. ZOOM: "Vergr&ouml;&szlig;ern"
  324. }
  325. }
  326. };
  327. // Few useful variables and methods
  328. // ================================
  329. var $W = $(window);
  330. var $D = $(document);
  331. var called = 0;
  332. // Check if an object is a jQuery object and not a native JavaScript object
  333. // ========================================================================
  334. var isQuery = function (obj) {
  335. return obj && obj.hasOwnProperty && obj instanceof $;
  336. };
  337. // Handle multiple browsers for "requestAnimationFrame" and "cancelAnimationFrame"
  338. // ===============================================================================
  339. var requestAFrame = (function () {
  340. return (
  341. window.requestAnimationFrame ||
  342. window.webkitRequestAnimationFrame ||
  343. window.mozRequestAnimationFrame ||
  344. window.oRequestAnimationFrame ||
  345. // if all else fails, use setTimeout
  346. function (callback) {
  347. return window.setTimeout(callback, 1000 / 60);
  348. }
  349. );
  350. })();
  351. var cancelAFrame = (function () {
  352. return (
  353. window.cancelAnimationFrame ||
  354. window.webkitCancelAnimationFrame ||
  355. window.mozCancelAnimationFrame ||
  356. window.oCancelAnimationFrame ||
  357. function (id) {
  358. window.clearTimeout(id);
  359. }
  360. );
  361. })();
  362. // Detect the supported transition-end event property name
  363. // =======================================================
  364. var transitionEnd = (function () {
  365. var el = document.createElement("fakeelement"),
  366. t;
  367. var transitions = {
  368. transition: "transitionend",
  369. OTransition: "oTransitionEnd",
  370. MozTransition: "transitionend",
  371. WebkitTransition: "webkitTransitionEnd"
  372. };
  373. for (t in transitions) {
  374. if (el.style[t] !== undefined) {
  375. return transitions[t];
  376. }
  377. }
  378. return "transitionend";
  379. })();
  380. // Force redraw on an element.
  381. // This helps in cases where the browser doesn't redraw an updated element properly
  382. // ================================================================================
  383. var forceRedraw = function ($el) {
  384. return $el && $el.length && $el[0].offsetHeight;
  385. };
  386. // Exclude array (`buttons`) options from deep merging
  387. // ===================================================
  388. var mergeOpts = function (opts1, opts2) {
  389. var rez = $.extend(true, {}, opts1, opts2);
  390. $.each(opts2, function (key, value) {
  391. if ($.isArray(value)) {
  392. rez[key] = value;
  393. }
  394. });
  395. return rez;
  396. };
  397. // How much of an element is visible in viewport
  398. // =============================================
  399. var inViewport = function (elem) {
  400. var elemCenter, rez;
  401. if (!elem || elem.ownerDocument !== document) {
  402. return false;
  403. }
  404. $(".fancybox-container").css("pointer-events", "none");
  405. elemCenter = {
  406. x: elem.getBoundingClientRect().left + elem.offsetWidth / 2,
  407. y: elem.getBoundingClientRect().top + elem.offsetHeight / 2
  408. };
  409. rez = document.elementFromPoint(elemCenter.x, elemCenter.y) === elem;
  410. $(".fancybox-container").css("pointer-events", "");
  411. return rez;
  412. };
  413. // Class definition
  414. // ================
  415. var FancyBox = function (content, opts, index) {
  416. var self = this;
  417. self.opts = mergeOpts({
  418. index: index
  419. }, $.fancybox.defaults);
  420. if ($.isPlainObject(opts)) {
  421. self.opts = mergeOpts(self.opts, opts);
  422. }
  423. if ($.fancybox.isMobile) {
  424. self.opts = mergeOpts(self.opts, self.opts.mobile);
  425. }
  426. self.id = self.opts.id || ++called;
  427. self.currIndex = parseInt(self.opts.index, 10) || 0;
  428. self.prevIndex = null;
  429. self.prevPos = null;
  430. self.currPos = 0;
  431. self.firstRun = true;
  432. // All group items
  433. self.group = [];
  434. // Existing slides (for current, next and previous gallery items)
  435. self.slides = {};
  436. // Create group elements
  437. self.addContent(content);
  438. if (!self.group.length) {
  439. return;
  440. }
  441. self.init();
  442. };
  443. $.extend(FancyBox.prototype, {
  444. // Create DOM structure
  445. // ====================
  446. init: function () {
  447. var self = this,
  448. firstItem = self.group[self.currIndex],
  449. firstItemOpts = firstItem.opts,
  450. $container,
  451. buttonStr;
  452. if (firstItemOpts.closeExisting) {
  453. $.fancybox.close(true);
  454. }
  455. // Hide scrollbars
  456. // ===============
  457. $("body").addClass("fancybox-active");
  458. if (
  459. !$.fancybox.getInstance() &&
  460. firstItemOpts.hideScrollbar !== false &&
  461. !$.fancybox.isMobile &&
  462. document.body.scrollHeight > window.innerHeight
  463. ) {
  464. $("head").append(
  465. '<style id="fancybox-style-noscroll" type="text/css">.compensate-for-scrollbar{margin-right:' +
  466. (window.innerWidth - document.documentElement.clientWidth) +
  467. "px;}</style>"
  468. );
  469. $("body").addClass("compensate-for-scrollbar");
  470. }
  471. // Build html markup and set references
  472. // ====================================
  473. // Build html code for buttons and insert into main template
  474. buttonStr = "";
  475. $.each(firstItemOpts.buttons, function (index, value) {
  476. buttonStr += firstItemOpts.btnTpl[value] || "";
  477. });
  478. // Create markup from base template, it will be initially hidden to
  479. // avoid unnecessary work like painting while initializing is not complete
  480. $container = $(
  481. self.translate(
  482. self,
  483. firstItemOpts.baseTpl
  484. .replace("{{buttons}}", buttonStr)
  485. .replace("{{arrows}}", firstItemOpts.btnTpl.arrowLeft + firstItemOpts.btnTpl.arrowRight)
  486. )
  487. )
  488. .attr("id", "fancybox-container-" + self.id)
  489. .addClass(firstItemOpts.baseClass)
  490. .data("FancyBox", self)
  491. .appendTo(firstItemOpts.parentEl);
  492. // Create object holding references to jQuery wrapped nodes
  493. self.$refs = {
  494. container: $container
  495. };
  496. ["bg", "inner", "infobar", "toolbar", "stage", "caption", "navigation"].forEach(function (item) {
  497. self.$refs[item] = $container.find(".fancybox-" + item);
  498. });
  499. self.trigger("onInit");
  500. // Enable events, deactive previous instances
  501. self.activate();
  502. // Build slides, load and reveal content
  503. self.jumpTo(self.currIndex);
  504. },
  505. // Simple i18n support - replaces object keys found in template
  506. // with corresponding values
  507. // ============================================================
  508. translate: function (obj, str) {
  509. var arr = obj.opts.i18n[obj.opts.lang] || obj.opts.i18n.en;
  510. return str.replace(/\{\{(\w+)\}\}/g, function (match, n) {
  511. return arr[n] === undefined ? match : arr[n];
  512. });
  513. },
  514. // Populate current group with fresh content
  515. // Check if each object has valid type and content
  516. // ===============================================
  517. addContent: function (content) {
  518. var self = this,
  519. items = $.makeArray(content),
  520. thumbs;
  521. $.each(items, function (i, item) {
  522. var obj = {},
  523. opts = {},
  524. $item,
  525. type,
  526. found,
  527. src,
  528. srcParts;
  529. // Step 1 - Make sure we have an object
  530. // ====================================
  531. if ($.isPlainObject(item)) {
  532. // We probably have manual usage here, something like
  533. // $.fancybox.open( [ { src : "image.jpg", type : "image" } ] )
  534. obj = item;
  535. opts = item.opts || item;
  536. } else if ($.type(item) === "object" && $(item).length) {
  537. // Here we probably have jQuery collection returned by some selector
  538. $item = $(item);
  539. // Support attributes like `data-options='{"touch" : false}'` and `data-touch='false'`
  540. opts = $item.data() || {};
  541. opts = $.extend(true, {}, opts, opts.options);
  542. // Here we store clicked element
  543. opts.$orig = $item;
  544. obj.src = self.opts.src || opts.src || $item.attr("href");
  545. // Assume that simple syntax is used, for example:
  546. // `$.fancybox.open( $("#test"), {} );`
  547. if (!obj.type && !obj.src) {
  548. obj.type = "inline";
  549. obj.src = item;
  550. }
  551. } else {
  552. // Assume we have a simple html code, for example:
  553. // $.fancybox.open( '<div><h1>Hi!</h1></div>' );
  554. obj = {
  555. type: "html",
  556. src: item + ""
  557. };
  558. }
  559. // Each gallery object has full collection of options
  560. obj.opts = $.extend(true, {}, self.opts, opts);
  561. // Do not merge buttons array
  562. if ($.isArray(opts.buttons)) {
  563. obj.opts.buttons = opts.buttons;
  564. }
  565. if ($.fancybox.isMobile && obj.opts.mobile) {
  566. obj.opts = mergeOpts(obj.opts, obj.opts.mobile);
  567. }
  568. // Step 2 - Make sure we have content type, if not - try to guess
  569. // ==============================================================
  570. type = obj.type || obj.opts.type;
  571. src = obj.src || "";
  572. if (!type && src) {
  573. if ((found = src.match(/\.(mp4|mov|ogv|webm)((\?|#).*)?$/i))) {
  574. type = "video";
  575. if (!obj.opts.video.format) {
  576. obj.opts.video.format = "video/" + (found[1] === "ogv" ? "ogg" : found[1]);
  577. }
  578. } else if (src.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i)) {
  579. type = "image";
  580. } else if (src.match(/\.(pdf)((\?|#).*)?$/i)) {
  581. type = "iframe";
  582. obj = $.extend(true, obj, {
  583. contentType: "pdf",
  584. opts: {
  585. iframe: {
  586. preload: false
  587. }
  588. }
  589. });
  590. } else if (src.charAt(0) === "#") {
  591. type = "inline";
  592. }
  593. }
  594. if (type) {
  595. obj.type = type;
  596. } else {
  597. self.trigger("objectNeedsType", obj);
  598. }
  599. if (!obj.contentType) {
  600. obj.contentType = $.inArray(obj.type, ["html", "inline", "ajax"]) > -1 ? "html" : obj.type;
  601. }
  602. // Step 3 - Some adjustments
  603. // =========================
  604. obj.index = self.group.length;
  605. if (obj.opts.smallBtn == "auto") {
  606. obj.opts.smallBtn = $.inArray(obj.type, ["html", "inline", "ajax"]) > -1;
  607. }
  608. if (obj.opts.toolbar === "auto") {
  609. obj.opts.toolbar = !obj.opts.smallBtn;
  610. }
  611. // Find thumbnail image, check if exists and if is in the viewport
  612. obj.$thumb = obj.opts.$thumb || null;
  613. if (obj.opts.$trigger && obj.index === self.opts.index) {
  614. obj.$thumb = obj.opts.$trigger.find("img:first");
  615. if (obj.$thumb.length) {
  616. obj.opts.$orig = obj.opts.$trigger;
  617. }
  618. }
  619. if (!(obj.$thumb && obj.$thumb.length) && obj.opts.$orig) {
  620. obj.$thumb = obj.opts.$orig.find("img:first");
  621. }
  622. if (obj.$thumb && !obj.$thumb.length) {
  623. obj.$thumb = null;
  624. }
  625. obj.thumb = obj.opts.thumb || (obj.$thumb ? obj.$thumb[0].src : null);
  626. // "caption" is a "special" option, it can be used to customize caption per gallery item
  627. if ($.type(obj.opts.caption) === "function") {
  628. obj.opts.caption = obj.opts.caption.apply(item, [self, obj]);
  629. }
  630. if ($.type(self.opts.caption) === "function") {
  631. obj.opts.caption = self.opts.caption.apply(item, [self, obj]);
  632. }
  633. // Make sure we have caption as a string or jQuery object
  634. if (!(obj.opts.caption instanceof $)) {
  635. obj.opts.caption = obj.opts.caption === undefined ? "" : obj.opts.caption + "";
  636. }
  637. // Check if url contains "filter" used to filter the content
  638. // Example: "ajax.html #something"
  639. if (obj.type === "ajax") {
  640. srcParts = src.split(/\s+/, 2);
  641. if (srcParts.length > 1) {
  642. obj.src = srcParts.shift();
  643. obj.opts.filter = srcParts.shift();
  644. }
  645. }
  646. // Hide all buttons and disable interactivity for modal items
  647. if (obj.opts.modal) {
  648. obj.opts = $.extend(true, obj.opts, {
  649. trapFocus: true,
  650. // Remove buttons
  651. infobar: 0,
  652. toolbar: 0,
  653. smallBtn: 0,
  654. // Disable keyboard navigation
  655. keyboard: 0,
  656. // Disable some modules
  657. slideShow: 0,
  658. fullScreen: 0,
  659. thumbs: 0,
  660. touch: 0,
  661. // Disable click event handlers
  662. clickContent: false,
  663. clickSlide: false,
  664. clickOutside: false,
  665. dblclickContent: false,
  666. dblclickSlide: false,
  667. dblclickOutside: false
  668. });
  669. }
  670. // Step 4 - Add processed object to group
  671. // ======================================
  672. self.group.push(obj);
  673. });
  674. // Update controls if gallery is already opened
  675. if (Object.keys(self.slides).length) {
  676. self.updateControls();
  677. // Update thumbnails, if needed
  678. thumbs = self.Thumbs;
  679. if (thumbs && thumbs.isActive) {
  680. thumbs.create();
  681. thumbs.focus();
  682. }
  683. }
  684. },
  685. // Attach an event handler functions for:
  686. // - navigation buttons
  687. // - browser scrolling, resizing;
  688. // - focusing
  689. // - keyboard
  690. // - detecting inactivity
  691. // ======================================
  692. addEvents: function () {
  693. var self = this;
  694. self.removeEvents();
  695. // Make navigation elements clickable
  696. // ==================================
  697. self.$refs.container
  698. .on("click.fb-close", "[data-fancybox-close]", function (e) {
  699. e.stopPropagation();
  700. e.preventDefault();
  701. self.close(e);
  702. })
  703. .on("touchstart.fb-prev click.fb-prev", "[data-fancybox-prev]", function (e) {
  704. e.stopPropagation();
  705. e.preventDefault();
  706. self.previous();
  707. })
  708. .on("touchstart.fb-next click.fb-next", "[data-fancybox-next]", function (e) {
  709. e.stopPropagation();
  710. e.preventDefault();
  711. self.next();
  712. })
  713. .on("click.fb", "[data-fancybox-zoom]", function (e) {
  714. // Click handler for zoom button
  715. self[self.isScaledDown() ? "scaleToActual" : "scaleToFit"]();
  716. });
  717. // Handle page scrolling and browser resizing
  718. // ==========================================
  719. $W.on("orientationchange.fb resize.fb", function (e) {
  720. if (e && e.originalEvent && e.originalEvent.type === "resize") {
  721. if (self.requestId) {
  722. cancelAFrame(self.requestId);
  723. }
  724. self.requestId = requestAFrame(function () {
  725. self.update(e);
  726. });
  727. } else {
  728. if (self.current && self.current.type === "iframe") {
  729. self.$refs.stage.hide();
  730. }
  731. setTimeout(
  732. function () {
  733. self.$refs.stage.show();
  734. self.update(e);
  735. },
  736. $.fancybox.isMobile ? 600 : 250
  737. );
  738. }
  739. });
  740. $D.on("keydown.fb", function (e) {
  741. var instance = $.fancybox ? $.fancybox.getInstance() : null,
  742. current = instance.current,
  743. keycode = e.keyCode || e.which;
  744. // Trap keyboard focus inside of the modal
  745. // =======================================
  746. if (keycode == 9) {
  747. if (current.opts.trapFocus) {
  748. self.focus(e);
  749. }
  750. return;
  751. }
  752. // Enable keyboard navigation
  753. // ==========================
  754. if (!current.opts.keyboard || e.ctrlKey || e.altKey || e.shiftKey || $(e.target).is("input,textarea,video,audio,select")) {
  755. return;
  756. }
  757. // Backspace and Esc keys
  758. if (keycode === 8 || keycode === 27) {
  759. e.preventDefault();
  760. self.close(e);
  761. return;
  762. }
  763. // Left arrow and Up arrow
  764. if (keycode === 37 || keycode === 38) {
  765. e.preventDefault();
  766. self.previous();
  767. return;
  768. }
  769. // Righ arrow and Down arrow
  770. if (keycode === 39 || keycode === 40) {
  771. e.preventDefault();
  772. self.next();
  773. return;
  774. }
  775. self.trigger("afterKeydown", e, keycode);
  776. });
  777. // Hide controls after some inactivity period
  778. if (self.group[self.currIndex].opts.idleTime) {
  779. self.idleSecondsCounter = 0;
  780. $D.on(
  781. "mousemove.fb-idle mouseleave.fb-idle mousedown.fb-idle touchstart.fb-idle touchmove.fb-idle scroll.fb-idle keydown.fb-idle",
  782. function (e) {
  783. self.idleSecondsCounter = 0;
  784. if (self.isIdle) {
  785. self.showControls();
  786. }
  787. self.isIdle = false;
  788. }
  789. );
  790. self.idleInterval = window.setInterval(function () {
  791. self.idleSecondsCounter++;
  792. if (self.idleSecondsCounter >= self.group[self.currIndex].opts.idleTime && !self.isDragging) {
  793. self.isIdle = true;
  794. self.idleSecondsCounter = 0;
  795. self.hideControls();
  796. }
  797. }, 1000);
  798. }
  799. },
  800. // Remove events added by the core
  801. // ===============================
  802. removeEvents: function () {
  803. var self = this;
  804. $W.off("orientationchange.fb resize.fb");
  805. $D.off("keydown.fb .fb-idle");
  806. this.$refs.container.off(".fb-close .fb-prev .fb-next");
  807. if (self.idleInterval) {
  808. window.clearInterval(self.idleInterval);
  809. self.idleInterval = null;
  810. }
  811. },
  812. // Change to previous gallery item
  813. // ===============================
  814. previous: function (duration) {
  815. return this.jumpTo(this.currPos - 1, duration);
  816. },
  817. // Change to next gallery item
  818. // ===========================
  819. next: function (duration) {
  820. return this.jumpTo(this.currPos + 1, duration);
  821. },
  822. // Switch to selected gallery item
  823. // ===============================
  824. jumpTo: function (pos, duration) {
  825. var self = this,
  826. groupLen = self.group.length,
  827. firstRun,
  828. isMoved,
  829. loop,
  830. current,
  831. previous,
  832. slidePos,
  833. stagePos,
  834. prop,
  835. diff;
  836. if (self.isDragging || self.isClosing || (self.isAnimating && self.firstRun)) {
  837. return;
  838. }
  839. // Should loop?
  840. pos = parseInt(pos, 10);
  841. loop = self.current ? self.current.opts.loop : self.opts.loop;
  842. if (!loop && (pos < 0 || pos >= groupLen)) {
  843. return false;
  844. }
  845. // Check if opening for the first time; this helps to speed things up
  846. firstRun = self.firstRun = !Object.keys(self.slides).length;
  847. // Create slides
  848. previous = self.current;
  849. self.prevIndex = self.currIndex;
  850. self.prevPos = self.currPos;
  851. current = self.createSlide(pos);
  852. if (groupLen > 1) {
  853. if (loop || current.index < groupLen - 1) {
  854. self.createSlide(pos + 1);
  855. }
  856. if (loop || current.index > 0) {
  857. self.createSlide(pos - 1);
  858. }
  859. }
  860. self.current = current;
  861. self.currIndex = current.index;
  862. self.currPos = current.pos;
  863. self.trigger("beforeShow", firstRun);
  864. self.updateControls();
  865. // Validate duration length
  866. current.forcedDuration = undefined;
  867. if ($.isNumeric(duration)) {
  868. current.forcedDuration = duration;
  869. } else {
  870. duration = current.opts[firstRun ? "animationDuration" : "transitionDuration"];
  871. }
  872. duration = parseInt(duration, 10);
  873. // Check if user has swiped the slides or if still animating
  874. isMoved = self.isMoved(current);
  875. // Make sure current slide is visible
  876. current.$slide.addClass("fancybox-slide--current");
  877. // Fresh start - reveal container, current slide and start loading content
  878. if (firstRun) {
  879. if (current.opts.animationEffect && duration) {
  880. self.$refs.container.css("transition-duration", duration + "ms");
  881. }
  882. self.$refs.container.addClass("fancybox-is-open").trigger("focus");
  883. // Attempt to load content into slide
  884. // This will later call `afterLoad` -> `revealContent`
  885. self.loadSlide(current);
  886. self.preload("image");
  887. return;
  888. }
  889. // Get actual slide/stage positions (before cleaning up)
  890. slidePos = $.fancybox.getTranslate(previous.$slide);
  891. stagePos = $.fancybox.getTranslate(self.$refs.stage);
  892. // Clean up all slides
  893. $.each(self.slides, function (index, slide) {
  894. $.fancybox.stop(slide.$slide, true);
  895. });
  896. if (previous.pos !== current.pos) {
  897. previous.isComplete = false;
  898. }
  899. previous.$slide.removeClass("fancybox-slide--complete fancybox-slide--current");
  900. // If slides are out of place, then animate them to correct position
  901. if (isMoved) {
  902. // Calculate horizontal swipe distance
  903. diff = slidePos.left - (previous.pos * slidePos.width + previous.pos * previous.opts.gutter);
  904. $.each(self.slides, function (index, slide) {
  905. slide.$slide.removeClass("fancybox-animated").removeClass(function (index, className) {
  906. return (className.match(/(^|\s)fancybox-fx-\S+/g) || []).join(" ");
  907. });
  908. // Make sure that each slide is in equal distance
  909. // This is mostly needed for freshly added slides, because they are not yet positioned
  910. var leftPos = slide.pos * slidePos.width + slide.pos * slide.opts.gutter;
  911. $.fancybox.setTranslate(slide.$slide, {
  912. top: 0,
  913. left: leftPos - stagePos.left + diff
  914. });
  915. if (slide.pos !== current.pos) {
  916. slide.$slide.addClass("fancybox-slide--" + (slide.pos > current.pos ? "next" : "previous"));
  917. }
  918. // Redraw to make sure that transition will start
  919. forceRedraw(slide.$slide);
  920. // Animate the slide
  921. $.fancybox.animate(
  922. slide.$slide, {
  923. top: 0,
  924. left: (slide.pos - current.pos) * slidePos.width + (slide.pos - current.pos) * slide.opts.gutter
  925. },
  926. duration,
  927. function () {
  928. slide.$slide
  929. .css({
  930. transform: "",
  931. opacity: ""
  932. })
  933. .removeClass("fancybox-slide--next fancybox-slide--previous");
  934. if (slide.pos === self.currPos) {
  935. self.complete();
  936. }
  937. }
  938. );
  939. });
  940. } else if (duration && current.opts.transitionEffect) {
  941. // Set transition effect for previously active slide
  942. prop = "fancybox-animated fancybox-fx-" + current.opts.transitionEffect;
  943. previous.$slide.addClass("fancybox-slide--" + (previous.pos > current.pos ? "next" : "previous"));
  944. $.fancybox.animate(
  945. previous.$slide,
  946. prop,
  947. duration,
  948. function () {
  949. previous.$slide.removeClass(prop).removeClass("fancybox-slide--next fancybox-slide--previous");
  950. },
  951. false
  952. );
  953. }
  954. if (current.isLoaded) {
  955. self.revealContent(current);
  956. } else {
  957. self.loadSlide(current);
  958. }
  959. self.preload("image");
  960. },
  961. // Create new "slide" element
  962. // These are gallery items that are actually added to DOM
  963. // =======================================================
  964. createSlide: function (pos) {
  965. var self = this,
  966. $slide,
  967. index;
  968. index = pos % self.group.length;
  969. index = index < 0 ? self.group.length + index : index;
  970. if (!self.slides[pos] && self.group[index]) {
  971. $slide = $('<div class="fancybox-slide"></div>').appendTo(self.$refs.stage);
  972. self.slides[pos] = $.extend(true, {}, self.group[index], {
  973. pos: pos,
  974. $slide: $slide,
  975. isLoaded: false
  976. });
  977. self.updateSlide(self.slides[pos]);
  978. }
  979. return self.slides[pos];
  980. },
  981. // Scale image to the actual size of the image;
  982. // x and y values should be relative to the slide
  983. // ==============================================
  984. scaleToActual: function (x, y, duration) {
  985. var self = this,
  986. current = self.current,
  987. $content = current.$content,
  988. canvasWidth = $.fancybox.getTranslate(current.$slide).width,
  989. canvasHeight = $.fancybox.getTranslate(current.$slide).height,
  990. newImgWidth = current.width,
  991. newImgHeight = current.height,
  992. imgPos,
  993. posX,
  994. posY,
  995. scaleX,
  996. scaleY;
  997. if (self.isAnimating || self.isMoved() || !$content || !(current.type == "image" && current.isLoaded && !current.hasError)) {
  998. return;
  999. }
  1000. self.isAnimating = true;
  1001. $.fancybox.stop($content);
  1002. x = x === undefined ? canvasWidth * 0.5 : x;
  1003. y = y === undefined ? canvasHeight * 0.5 : y;
  1004. imgPos = $.fancybox.getTranslate($content);
  1005. imgPos.top -= $.fancybox.getTranslate(current.$slide).top;
  1006. imgPos.left -= $.fancybox.getTranslate(current.$slide).left;
  1007. scaleX = newImgWidth / imgPos.width;
  1008. scaleY = newImgHeight / imgPos.height;
  1009. // Get center position for original image
  1010. posX = canvasWidth * 0.5 - newImgWidth * 0.5;
  1011. posY = canvasHeight * 0.5 - newImgHeight * 0.5;
  1012. // Make sure image does not move away from edges
  1013. if (newImgWidth > canvasWidth) {
  1014. posX = imgPos.left * scaleX - (x * scaleX - x);
  1015. if (posX > 0) {
  1016. posX = 0;
  1017. }
  1018. if (posX < canvasWidth - newImgWidth) {
  1019. posX = canvasWidth - newImgWidth;
  1020. }
  1021. }
  1022. if (newImgHeight > canvasHeight) {
  1023. posY = imgPos.top * scaleY - (y * scaleY - y);
  1024. if (posY > 0) {
  1025. posY = 0;
  1026. }
  1027. if (posY < canvasHeight - newImgHeight) {
  1028. posY = canvasHeight - newImgHeight;
  1029. }
  1030. }
  1031. self.updateCursor(newImgWidth, newImgHeight);
  1032. $.fancybox.animate(
  1033. $content, {
  1034. top: posY,
  1035. left: posX,
  1036. scaleX: scaleX,
  1037. scaleY: scaleY
  1038. },
  1039. duration || 366,
  1040. function () {
  1041. self.isAnimating = false;
  1042. }
  1043. );
  1044. // Stop slideshow
  1045. if (self.SlideShow && self.SlideShow.isActive) {
  1046. self.SlideShow.stop();
  1047. }
  1048. },
  1049. // Scale image to fit inside parent element
  1050. // ========================================
  1051. scaleToFit: function (duration) {
  1052. var self = this,
  1053. current = self.current,
  1054. $content = current.$content,
  1055. end;
  1056. if (self.isAnimating || self.isMoved() || !$content || !(current.type == "image" && current.isLoaded && !current.hasError)) {
  1057. return;
  1058. }
  1059. self.isAnimating = true;
  1060. $.fancybox.stop($content);
  1061. end = self.getFitPos(current);
  1062. self.updateCursor(end.width, end.height);
  1063. $.fancybox.animate(
  1064. $content, {
  1065. top: end.top,
  1066. left: end.left,
  1067. scaleX: end.width / $content.width(),
  1068. scaleY: end.height / $content.height()
  1069. },
  1070. duration || 366,
  1071. function () {
  1072. self.isAnimating = false;
  1073. }
  1074. );
  1075. },
  1076. // Calculate image size to fit inside viewport
  1077. // ===========================================
  1078. getFitPos: function (slide) {
  1079. var self = this,
  1080. $content = slide.$content,
  1081. $slide = slide.$slide,
  1082. width = slide.width || slide.opts.width,
  1083. height = slide.height || slide.opts.height,
  1084. maxWidth,
  1085. maxHeight,
  1086. minRatio,
  1087. aspectRatio,
  1088. rez = {};
  1089. if (!slide.isLoaded || !$content || !$content.length) {
  1090. return false;
  1091. }
  1092. maxWidth = $.fancybox.getTranslate(self.$refs.stage).width;
  1093. maxHeight = $.fancybox.getTranslate(self.$refs.stage).height;
  1094. maxWidth -=
  1095. parseFloat($slide.css("paddingLeft")) +
  1096. parseFloat($slide.css("paddingRight")) +
  1097. parseFloat($content.css("marginLeft")) +
  1098. parseFloat($content.css("marginRight"));
  1099. maxHeight -=
  1100. parseFloat($slide.css("paddingTop")) +
  1101. parseFloat($slide.css("paddingBottom")) +
  1102. parseFloat($content.css("marginTop")) +
  1103. parseFloat($content.css("marginBottom"));
  1104. if (!width || !height) {
  1105. width = maxWidth;
  1106. height = maxHeight;
  1107. }
  1108. minRatio = Math.min(1, maxWidth / width, maxHeight / height);
  1109. width = minRatio * width;
  1110. height = minRatio * height;
  1111. // Adjust width/height to precisely fit into container
  1112. if (width > maxWidth - 0.5) {
  1113. width = maxWidth;
  1114. }
  1115. if (height > maxHeight - 0.5) {
  1116. height = maxHeight;
  1117. }
  1118. if (slide.type === "image") {
  1119. rez.top = Math.floor((maxHeight - height) * 0.5) + parseFloat($slide.css("paddingTop"));
  1120. rez.left = Math.floor((maxWidth - width) * 0.5) + parseFloat($slide.css("paddingLeft"));
  1121. } else if (slide.contentType === "video") {
  1122. // Force aspect ratio for the video
  1123. // "I say the whole world must learn of our peaceful ways… by force!"
  1124. aspectRatio = slide.opts.width && slide.opts.height ? width / height : slide.opts.ratio || 16 / 9;
  1125. if (height > width / aspectRatio) {
  1126. height = width / aspectRatio;
  1127. } else if (width > height * aspectRatio) {
  1128. width = height * aspectRatio;
  1129. }
  1130. }
  1131. rez.width = width;
  1132. rez.height = height;
  1133. return rez;
  1134. },
  1135. // Update content size and position for all slides
  1136. // ==============================================
  1137. update: function (e) {
  1138. var self = this;
  1139. $.each(self.slides, function (key, slide) {
  1140. self.updateSlide(slide, e);
  1141. });
  1142. },
  1143. // Update slide content position and size
  1144. // ======================================
  1145. updateSlide: function (slide, e) {
  1146. var self = this,
  1147. $content = slide && slide.$content,
  1148. width = slide.width || slide.opts.width,
  1149. height = slide.height || slide.opts.height,
  1150. $slide = slide.$slide;
  1151. // First, prevent caption overlap, if needed
  1152. self.adjustCaption(slide);
  1153. // Then resize content to fit inside the slide
  1154. if ($content && (width || height || slide.contentType === "video") && !slide.hasError) {
  1155. $.fancybox.stop($content);
  1156. $.fancybox.setTranslate($content, self.getFitPos(slide));
  1157. if (slide.pos === self.currPos) {
  1158. self.isAnimating = false;
  1159. self.updateCursor();
  1160. }
  1161. }
  1162. // Then some adjustments
  1163. self.adjustLayout(slide);
  1164. if ($slide.length) {
  1165. $slide.trigger("refresh");
  1166. if (slide.pos === self.currPos) {
  1167. self.$refs.toolbar
  1168. .add(self.$refs.navigation.find(".fancybox-button--arrow_right"))
  1169. .toggleClass("compensate-for-scrollbar", $slide.get(0).scrollHeight > $slide.get(0).clientHeight);
  1170. }
  1171. }
  1172. self.trigger("onUpdate", slide, e);
  1173. },
  1174. // Horizontally center slide
  1175. // =========================
  1176. centerSlide: function (duration) {
  1177. var self = this,
  1178. current = self.current,
  1179. $slide = current.$slide;
  1180. if (self.isClosing || !current) {
  1181. return;
  1182. }
  1183. $slide.siblings().css({
  1184. transform: "",
  1185. opacity: ""
  1186. });
  1187. $slide
  1188. .parent()
  1189. .children()
  1190. .removeClass("fancybox-slide--previous fancybox-slide--next");
  1191. $.fancybox.animate(
  1192. $slide, {
  1193. top: 0,
  1194. left: 0,
  1195. opacity: 1
  1196. },
  1197. duration === undefined ? 0 : duration,
  1198. function () {
  1199. // Clean up
  1200. $slide.css({
  1201. transform: "",
  1202. opacity: ""
  1203. });
  1204. if (!current.isComplete) {
  1205. self.complete();
  1206. }
  1207. },
  1208. false
  1209. );
  1210. },
  1211. // Check if current slide is moved (swiped)
  1212. // ========================================
  1213. isMoved: function (slide) {
  1214. var current = slide || this.current,
  1215. slidePos,
  1216. stagePos;
  1217. if (!current) {
  1218. return false;
  1219. }
  1220. stagePos = $.fancybox.getTranslate(this.$refs.stage);
  1221. slidePos = $.fancybox.getTranslate(current.$slide);
  1222. return (
  1223. !current.$slide.hasClass("fancybox-animated") &&
  1224. (Math.abs(slidePos.top - stagePos.top) > 0.5 || Math.abs(slidePos.left - stagePos.left) > 0.5)
  1225. );
  1226. },
  1227. // Update cursor style depending if content can be zoomed
  1228. // ======================================================
  1229. updateCursor: function (nextWidth, nextHeight) {
  1230. var self = this,
  1231. current = self.current,
  1232. $container = self.$refs.container,
  1233. canPan,
  1234. isZoomable;
  1235. if (!current || self.isClosing || !self.Guestures) {
  1236. return;
  1237. }
  1238. $container.removeClass("fancybox-is-zoomable fancybox-can-zoomIn fancybox-can-zoomOut fancybox-can-swipe fancybox-can-pan");
  1239. canPan = self.canPan(nextWidth, nextHeight);
  1240. isZoomable = canPan ? true : self.isZoomable();
  1241. $container.toggleClass("fancybox-is-zoomable", isZoomable);
  1242. $("[data-fancybox-zoom]").prop("disabled", !isZoomable);
  1243. if (canPan) {
  1244. $container.addClass("fancybox-can-pan");
  1245. } else if (
  1246. isZoomable &&
  1247. (current.opts.clickContent === "zoom" || ($.isFunction(current.opts.clickContent) && current.opts.clickContent(current) == "zoom"))
  1248. ) {
  1249. $container.addClass("fancybox-can-zoomIn");
  1250. } else if (current.opts.touch && (current.opts.touch.vertical || self.group.length > 1) && current.contentType !== "video") {
  1251. $container.addClass("fancybox-can-swipe");
  1252. }
  1253. },
  1254. // Check if current slide is zoomable
  1255. // ==================================
  1256. isZoomable: function () {
  1257. var self = this,
  1258. current = self.current,
  1259. fitPos;
  1260. // Assume that slide is zoomable if:
  1261. // - image is still loading
  1262. // - actual size of the image is smaller than available area
  1263. if (current && !self.isClosing && current.type === "image" && !current.hasError) {
  1264. if (!current.isLoaded) {
  1265. return true;
  1266. }
  1267. fitPos = self.getFitPos(current);
  1268. if (fitPos && (current.width > fitPos.width || current.height > fitPos.height)) {
  1269. return true;
  1270. }
  1271. }
  1272. return false;
  1273. },
  1274. // Check if current image dimensions are smaller than actual
  1275. // =========================================================
  1276. isScaledDown: function (nextWidth, nextHeight) {
  1277. var self = this,
  1278. rez = false,
  1279. current = self.current,
  1280. $content = current.$content;
  1281. if (nextWidth !== undefined && nextHeight !== undefined) {
  1282. rez = nextWidth < current.width && nextHeight < current.height;
  1283. } else if ($content) {
  1284. rez = $.fancybox.getTranslate($content);
  1285. rez = rez.width < current.width && rez.height < current.height;
  1286. }
  1287. return rez;
  1288. },
  1289. // Check if image dimensions exceed parent element
  1290. // ===============================================
  1291. canPan: function (nextWidth, nextHeight) {
  1292. var self = this,
  1293. current = self.current,
  1294. pos = null,
  1295. rez = false;
  1296. if (current.type === "image" && (current.isComplete || (nextWidth && nextHeight)) && !current.hasError) {
  1297. rez = self.getFitPos(current);
  1298. if (nextWidth !== undefined && nextHeight !== undefined) {
  1299. pos = {
  1300. width: nextWidth,
  1301. height: nextHeight
  1302. };
  1303. } else if (current.isComplete) {
  1304. pos = $.fancybox.getTranslate(current.$content);
  1305. }
  1306. if (pos && rez) {
  1307. rez = Math.abs(pos.width - rez.width) > 1.5 || Math.abs(pos.height - rez.height) > 1.5;
  1308. }
  1309. }
  1310. return rez;
  1311. },
  1312. // Load content into the slide
  1313. // ===========================
  1314. loadSlide: function (slide) {
  1315. var self = this,
  1316. type,
  1317. $slide,
  1318. ajaxLoad;
  1319. if (slide.isLoading || slide.isLoaded) {
  1320. return;
  1321. }
  1322. slide.isLoading = true;
  1323. if (self.trigger("beforeLoad", slide) === false) {
  1324. slide.isLoading = false;
  1325. return false;
  1326. }
  1327. type = slide.type;
  1328. $slide = slide.$slide;
  1329. $slide
  1330. .off("refresh")
  1331. .trigger("onReset")
  1332. .addClass(slide.opts.slideClass);
  1333. // Create content depending on the type
  1334. switch (type) {
  1335. case "image":
  1336. self.setImage(slide);
  1337. break;
  1338. case "iframe":
  1339. self.setIframe(slide);
  1340. break;
  1341. case "html":
  1342. self.setContent(slide, slide.src || slide.content);
  1343. break;
  1344. case "video":
  1345. self.setContent(
  1346. slide,
  1347. slide.opts.video.tpl
  1348. .replace(/\{\{src\}\}/gi, slide.src)
  1349. .replace("{{format}}", slide.opts.videoFormat || slide.opts.video.format || "")
  1350. .replace("{{poster}}", slide.thumb || "")
  1351. );
  1352. break;
  1353. case "inline":
  1354. if ($(slide.src).length) {
  1355. self.setContent(slide, $(slide.src));
  1356. } else {
  1357. self.setError(slide);
  1358. }
  1359. break;
  1360. case "ajax":
  1361. self.showLoading(slide);
  1362. ajaxLoad = $.ajax(
  1363. $.extend({}, slide.opts.ajax.settings, {
  1364. url: slide.src,
  1365. success: function (data, textStatus) {
  1366. if (textStatus === "success") {
  1367. self.setContent(slide, data);
  1368. }
  1369. },
  1370. error: function (jqXHR, textStatus) {
  1371. if (jqXHR && textStatus !== "abort") {
  1372. self.setError(slide);
  1373. }
  1374. }
  1375. })
  1376. );
  1377. $slide.one("onReset", function () {
  1378. ajaxLoad.abort();
  1379. });
  1380. break;
  1381. default:
  1382. self.setError(slide);
  1383. break;
  1384. }
  1385. return true;
  1386. },
  1387. // Use thumbnail image, if possible
  1388. // ================================
  1389. setImage: function (slide) {
  1390. var self = this,
  1391. ghost;
  1392. // Check if need to show loading icon
  1393. setTimeout(function () {
  1394. var $img = slide.$image;
  1395. if (!self.isClosing && slide.isLoading && (!$img || !$img.length || !$img[0].complete) && !slide.hasError) {
  1396. self.showLoading(slide);
  1397. }
  1398. }, 50);
  1399. //Check if image has srcset
  1400. self.checkSrcset(slide);
  1401. // This will be wrapper containing both ghost and actual image
  1402. slide.$content = $('<div class="fancybox-content"></div>')
  1403. .addClass("fancybox-is-hidden")
  1404. .appendTo(slide.$slide.addClass("fancybox-slide--image"));
  1405. // If we have a thumbnail, we can display it while actual image is loading
  1406. // Users will not stare at black screen and actual image will appear gradually
  1407. if (slide.opts.preload !== false && slide.opts.width && slide.opts.height && slide.thumb) {
  1408. slide.width = slide.opts.width;
  1409. slide.height = slide.opts.height;
  1410. ghost = document.createElement("img");
  1411. ghost.onerror = function () {
  1412. $(this).remove();
  1413. slide.$ghost = null;
  1414. };
  1415. ghost.onload = function () {
  1416. self.afterLoad(slide);
  1417. };
  1418. slide.$ghost = $(ghost)
  1419. .addClass("fancybox-image")
  1420. .appendTo(slide.$content)
  1421. .attr("src", slide.thumb);
  1422. }
  1423. // Start loading actual image
  1424. self.setBigImage(slide);
  1425. },
  1426. // Check if image has srcset and get the source
  1427. // ============================================
  1428. checkSrcset: function (slide) {
  1429. var srcset = slide.opts.srcset || slide.opts.image.srcset,
  1430. found,
  1431. temp,
  1432. pxRatio,
  1433. windowWidth;
  1434. // If we have "srcset", then we need to find first matching "src" value.
  1435. // This is necessary, because when you set an src attribute, the browser will preload the image
  1436. // before any javascript or even CSS is applied.
  1437. if (srcset) {
  1438. pxRatio = window.devicePixelRatio || 1;
  1439. windowWidth = window.innerWidth * pxRatio;
  1440. temp = srcset.split(",").map(function (el) {
  1441. var ret = {};
  1442. el.trim()
  1443. .split(/\s+/)
  1444. .forEach(function (el, i) {
  1445. var value = parseInt(el.substring(0, el.length - 1), 10);
  1446. if (i === 0) {
  1447. return (ret.url = el);
  1448. }
  1449. if (value) {
  1450. ret.value = value;
  1451. ret.postfix = el[el.length - 1];
  1452. }
  1453. });
  1454. return ret;
  1455. });
  1456. // Sort by value
  1457. temp.sort(function (a, b) {
  1458. return a.value - b.value;
  1459. });
  1460. // Ok, now we have an array of all srcset values
  1461. for (var j = 0; j < temp.length; j++) {
  1462. var el = temp[j];
  1463. if ((el.postfix === "w" && el.value >= windowWidth) || (el.postfix === "x" && el.value >= pxRatio)) {
  1464. found = el;
  1465. break;
  1466. }
  1467. }
  1468. // If not found, take the last one
  1469. if (!found && temp.length) {
  1470. found = temp[temp.length - 1];
  1471. }
  1472. if (found) {
  1473. slide.src = found.url;
  1474. // If we have default width/height values, we can calculate height for matching source
  1475. if (slide.width && slide.height && found.postfix == "w") {
  1476. slide.height = (slide.width / slide.height) * found.value;
  1477. slide.width = found.value;
  1478. }
  1479. slide.opts.srcset = srcset;
  1480. }
  1481. }
  1482. },
  1483. // Create full-size image
  1484. // ======================
  1485. setBigImage: function (slide) {
  1486. var self = this,
  1487. img = document.createElement("img"),
  1488. $img = $(img);
  1489. slide.$image = $img
  1490. .one("error", function () {
  1491. self.setError(slide);
  1492. })
  1493. .one("load", function () {
  1494. var sizes;
  1495. if (!slide.$ghost) {
  1496. self.resolveImageSlideSize(slide, this.naturalWidth, this.naturalHeight);
  1497. self.afterLoad(slide);
  1498. }
  1499. if (self.isClosing) {
  1500. return;
  1501. }
  1502. if (slide.opts.srcset) {
  1503. sizes = slide.opts.sizes;
  1504. if (!sizes || sizes === "auto") {
  1505. sizes =
  1506. (slide.width / slide.height > 1 && $W.width() / $W.height() > 1 ? "100" : Math.round((slide.width / slide.height) * 100)) +
  1507. "vw";
  1508. }
  1509. $img.attr("sizes", sizes).attr("srcset", slide.opts.srcset);
  1510. }
  1511. // Hide temporary image after some delay
  1512. if (slide.$ghost) {
  1513. setTimeout(function () {
  1514. if (slide.$ghost && !self.isClosing) {
  1515. slide.$ghost.hide();
  1516. }
  1517. }, Math.min(300, Math.max(1000, slide.height / 1600)));
  1518. }
  1519. self.hideLoading(slide);
  1520. })
  1521. .addClass("fancybox-image")
  1522. .attr("src", slide.src)
  1523. .appendTo(slide.$content);
  1524. if ((img.complete || img.readyState == "complete") && $img.naturalWidth && $img.naturalHeight) {
  1525. $img.trigger("load");
  1526. } else if (img.error) {
  1527. $img.trigger("error");
  1528. }
  1529. },
  1530. // Computes the slide size from image size and maxWidth/maxHeight
  1531. // ==============================================================
  1532. resolveImageSlideSize: function (slide, imgWidth, imgHeight) {
  1533. var maxWidth = parseInt(slide.opts.width, 10),
  1534. maxHeight = parseInt(slide.opts.height, 10);
  1535. // Sets the default values from the image
  1536. slide.width = imgWidth;
  1537. slide.height = imgHeight;
  1538. if (maxWidth > 0) {
  1539. slide.width = maxWidth;
  1540. slide.height = Math.floor((maxWidth * imgHeight) / imgWidth);
  1541. }
  1542. if (maxHeight > 0) {
  1543. slide.width = Math.floor((maxHeight * imgWidth) / imgHeight);
  1544. slide.height = maxHeight;
  1545. }
  1546. },
  1547. // Create iframe wrapper, iframe and bindings
  1548. // ==========================================
  1549. setIframe: function (slide) {
  1550. var self = this,
  1551. opts = slide.opts.iframe,
  1552. $slide = slide.$slide,
  1553. $iframe;
  1554. slide.$content = $('<div class="fancybox-content' + (opts.preload ? " fancybox-is-hidden" : "") + '"></div>')
  1555. .css(opts.css)
  1556. .appendTo($slide);
  1557. $slide.addClass("fancybox-slide--" + slide.contentType);
  1558. slide.$iframe = $iframe = $(opts.tpl.replace(/\{rnd\}/g, new Date().getTime()))
  1559. .attr(opts.attr)
  1560. .appendTo(slide.$content);
  1561. if (opts.preload) {
  1562. self.showLoading(slide);
  1563. // Unfortunately, it is not always possible to determine if iframe is successfully loaded
  1564. // (due to browser security policy)
  1565. $iframe.on("load.fb error.fb", function (e) {
  1566. this.isReady = 1;
  1567. slide.$slide.trigger("refresh");
  1568. self.afterLoad(slide);
  1569. });
  1570. // Recalculate iframe content size
  1571. // ===============================
  1572. $slide.on("refresh.fb", function () {
  1573. var $content = slide.$content,
  1574. frameWidth = opts.css.width,
  1575. frameHeight = opts.css.height,
  1576. $contents,
  1577. $body;
  1578. if ($iframe[0].isReady !== 1) {
  1579. return;
  1580. }
  1581. try {
  1582. $contents = $iframe.contents();
  1583. $body = $contents.find("body");
  1584. } catch (ignore) {}
  1585. // Calculate content dimensions, if it is accessible
  1586. if ($body && $body.length && $body.children().length) {
  1587. // Avoid scrolling to top (if multiple instances)
  1588. $slide.css("overflow", "visible");
  1589. $content.css({
  1590. width: "100%",
  1591. "max-width": "100%",
  1592. height: "9999px"
  1593. });
  1594. if (frameWidth === undefined) {
  1595. frameWidth = Math.ceil(Math.max($body[0].clientWidth, $body.outerWidth(true)));
  1596. }
  1597. $content.css("width", frameWidth ? frameWidth : "").css("max-width", "");
  1598. if (frameHeight === undefined) {
  1599. frameHeight = Math.ceil(Math.max($body[0].clientHeight, $body.outerHeight(true)));
  1600. }
  1601. $content.css("height", frameHeight ? frameHeight : "");
  1602. $slide.css("overflow", "auto");
  1603. }
  1604. $content.removeClass("fancybox-is-hidden");
  1605. });
  1606. } else {
  1607. self.afterLoad(slide);
  1608. }
  1609. $iframe.attr("src", slide.src);
  1610. // Remove iframe if closing or changing gallery item
  1611. $slide.one("onReset", function () {
  1612. // This helps IE not to throw errors when closing
  1613. try {
  1614. $(this)
  1615. .find("iframe")
  1616. .hide()
  1617. .unbind()
  1618. .attr("src", "//about:blank");
  1619. } catch (ignore) {}
  1620. $(this)
  1621. .off("refresh.fb")
  1622. .empty();
  1623. slide.isLoaded = false;
  1624. slide.isRevealed = false;
  1625. });
  1626. },
  1627. // Wrap and append content to the slide
  1628. // ======================================
  1629. setContent: function (slide, content) {
  1630. var self = this;
  1631. if (self.isClosing) {
  1632. return;
  1633. }
  1634. self.hideLoading(slide);
  1635. if (slide.$content) {
  1636. $.fancybox.stop(slide.$content);
  1637. }
  1638. slide.$slide.empty();
  1639. // If content is a jQuery object, then it will be moved to the slide.
  1640. // The placeholder is created so we will know where to put it back.
  1641. if (isQuery(content) && content.parent().length) {
  1642. // Make sure content is not already moved to fancyBox
  1643. if (content.hasClass("fancybox-content") || content.parent().hasClass("fancybox-content")) {
  1644. content.parents(".fancybox-slide").trigger("onReset");
  1645. }
  1646. // Create temporary element marking original place of the content
  1647. slide.$placeholder = $("<div>")
  1648. .hide()
  1649. .insertAfter(content);
  1650. // Make sure content is visible
  1651. content.css("display", "inline-block");
  1652. } else if (!slide.hasError) {
  1653. // If content is just a plain text, try to convert it to html
  1654. if ($.type(content) === "string") {
  1655. content = $("<div>")
  1656. .append($.trim(content))
  1657. .contents();
  1658. }
  1659. // If "filter" option is provided, then filter content
  1660. if (slide.opts.filter) {
  1661. content = $("<div>")
  1662. .html(content)
  1663. .find(slide.opts.filter);
  1664. }
  1665. }
  1666. slide.$slide.one("onReset", function () {
  1667. // Pause all html5 video/audio
  1668. $(this)
  1669. .find("video,audio")
  1670. .trigger("pause");
  1671. // Put content back
  1672. if (slide.$placeholder) {
  1673. slide.$placeholder.after(content.removeClass("fancybox-content").hide()).remove();
  1674. slide.$placeholder = null;
  1675. }
  1676. // Remove custom close button
  1677. if (slide.$smallBtn) {
  1678. slide.$smallBtn.remove();
  1679. slide.$smallBtn = null;
  1680. }
  1681. // Remove content and mark slide as not loaded
  1682. if (!slide.hasError) {
  1683. $(this).empty();
  1684. slide.isLoaded = false;
  1685. slide.isRevealed = false;
  1686. }
  1687. });
  1688. $(content).appendTo(slide.$slide);
  1689. if ($(content).is("video,audio")) {
  1690. $(content).addClass("fancybox-video");
  1691. $(content).wrap("<div></div>");
  1692. slide.contentType = "video";
  1693. slide.opts.width = slide.opts.width || $(content).attr("width");
  1694. slide.opts.height = slide.opts.height || $(content).attr("height");
  1695. }
  1696. slide.$content = slide.$slide
  1697. .children()
  1698. .filter("div,form,main,video,audio,article,.fancybox-content")
  1699. .first();
  1700. slide.$content.siblings().hide();
  1701. // Re-check if there is a valid content
  1702. // (in some cases, ajax response can contain various elements or plain text)
  1703. if (!slide.$content.length) {
  1704. slide.$content = slide.$slide
  1705. .wrapInner("<div></div>")
  1706. .children()
  1707. .first();
  1708. }
  1709. slide.$content.addClass("fancybox-content");
  1710. slide.$slide.addClass("fancybox-slide--" + slide.contentType);
  1711. self.afterLoad(slide);
  1712. },
  1713. // Display error message
  1714. // =====================
  1715. setError: function (slide) {
  1716. slide.hasError = true;
  1717. slide.$slide
  1718. .trigger("onReset")
  1719. .removeClass("fancybox-slide--" + slide.contentType)
  1720. .addClass("fancybox-slide--error");
  1721. slide.contentType = "html";
  1722. this.setContent(slide, this.translate(slide, slide.opts.errorTpl));
  1723. if (slide.pos === this.currPos) {
  1724. this.isAnimating = false;
  1725. }
  1726. },
  1727. // Show loading icon inside the slide
  1728. // ==================================
  1729. showLoading: function (slide) {
  1730. var self = this;
  1731. slide = slide || self.current;
  1732. if (slide && !slide.$spinner) {
  1733. slide.$spinner = $(self.translate(self, self.opts.spinnerTpl))
  1734. .appendTo(slide.$slide)
  1735. .hide()
  1736. .fadeIn("fast");
  1737. }
  1738. },
  1739. // Remove loading icon from the slide
  1740. // ==================================
  1741. hideLoading: function (slide) {
  1742. var self = this;
  1743. slide = slide || self.current;
  1744. if (slide && slide.$spinner) {
  1745. slide.$spinner.stop().remove();
  1746. delete slide.$spinner;
  1747. }
  1748. },
  1749. // Adjustments after slide content has been loaded
  1750. // ===============================================
  1751. afterLoad: function (slide) {
  1752. var self = this;
  1753. if (self.isClosing) {
  1754. return;
  1755. }
  1756. slide.isLoading = false;
  1757. slide.isLoaded = true;
  1758. self.trigger("afterLoad", slide);
  1759. self.hideLoading(slide);
  1760. // Add small close button
  1761. if (slide.opts.smallBtn && (!slide.$smallBtn || !slide.$smallBtn.length)) {
  1762. slide.$smallBtn = $(self.translate(slide, slide.opts.btnTpl.smallBtn)).appendTo(slide.$content);
  1763. }
  1764. // Disable right click
  1765. if (slide.opts.protect && slide.$content && !slide.hasError) {
  1766. slide.$content.on("contextmenu.fb", function (e) {
  1767. if (e.button == 2) {
  1768. e.preventDefault();
  1769. }
  1770. return true;
  1771. });
  1772. // Add fake element on top of the image
  1773. // This makes a bit harder for user to select image
  1774. if (slide.type === "image") {
  1775. $('<div class="fancybox-spaceball"></div>').appendTo(slide.$content);
  1776. }
  1777. }
  1778. self.adjustCaption(slide);
  1779. self.adjustLayout(slide);
  1780. if (slide.pos === self.currPos) {
  1781. self.updateCursor();
  1782. }
  1783. self.revealContent(slide);
  1784. },
  1785. // Prevent caption overlap,
  1786. // fix css inconsistency across browsers
  1787. // =====================================
  1788. adjustCaption: function (slide) {
  1789. var self = this,
  1790. current = slide || self.current,
  1791. caption = current.opts.caption,
  1792. preventOverlap = current.opts.preventCaptionOverlap,
  1793. $caption = self.$refs.caption,
  1794. $clone,
  1795. captionH = false;
  1796. $caption.toggleClass("fancybox-caption--separate", preventOverlap);
  1797. if (preventOverlap && caption && caption.length) {
  1798. if (current.pos !== self.currPos) {
  1799. $clone = $caption.clone().appendTo($caption.parent());
  1800. $clone
  1801. .children()
  1802. .eq(0)
  1803. .empty()
  1804. .html(caption);
  1805. captionH = $clone.outerHeight(true);
  1806. $clone.empty().remove();
  1807. } else if (self.$caption) {
  1808. captionH = self.$caption.outerHeight(true);
  1809. }
  1810. current.$slide.css("padding-bottom", captionH || "");
  1811. }
  1812. },
  1813. // Simple hack to fix inconsistency across browsers, described here (affects Edge, too):
  1814. // https://bugzilla.mozilla.org/show_bug.cgi?id=748518
  1815. // ====================================================================================
  1816. adjustLayout: function (slide) {
  1817. var self = this,
  1818. current = slide || self.current,
  1819. scrollHeight,
  1820. marginBottom,
  1821. inlinePadding,
  1822. actualPadding;
  1823. if (current.isLoaded && current.opts.disableLayoutFix !== true) {
  1824. current.$content.css("margin-bottom", "");
  1825. // If we would always set margin-bottom for the content,
  1826. // then it would potentially break vertical align
  1827. if (current.$content.outerHeight() > current.$slide.height() + 0.5) {
  1828. inlinePadding = current.$slide[0].style["padding-bottom"];
  1829. actualPadding = current.$slide.css("padding-bottom");
  1830. if (parseFloat(actualPadding) > 0) {
  1831. scrollHeight = current.$slide[0].scrollHeight;
  1832. current.$slide.css("padding-bottom", 0);
  1833. if (Math.abs(scrollHeight - current.$slide[0].scrollHeight) < 1) {
  1834. marginBottom = actualPadding;
  1835. }
  1836. current.$slide.css("padding-bottom", inlinePadding);
  1837. }
  1838. }
  1839. current.$content.css("margin-bottom", marginBottom);
  1840. }
  1841. },
  1842. // Make content visible
  1843. // This method is called right after content has been loaded or
  1844. // user navigates gallery and transition should start
  1845. // ============================================================
  1846. revealContent: function (slide) {
  1847. var self = this,
  1848. $slide = slide.$slide,
  1849. end = false,
  1850. start = false,
  1851. isMoved = self.isMoved(slide),
  1852. isRevealed = slide.isRevealed,
  1853. effect,
  1854. effectClassName,
  1855. duration,
  1856. opacity;
  1857. slide.isRevealed = true;
  1858. effect = slide.opts[self.firstRun ? "animationEffect" : "transitionEffect"];
  1859. duration = slide.opts[self.firstRun ? "animationDuration" : "transitionDuration"];
  1860. duration = parseInt(slide.forcedDuration === undefined ? duration : slide.forcedDuration, 10);
  1861. if (isMoved || slide.pos !== self.currPos || !duration) {
  1862. effect = false;
  1863. }
  1864. // Check if can zoom
  1865. if (effect === "zoom") {
  1866. if (slide.pos === self.currPos && duration && slide.type === "image" && !slide.hasError && (start = self.getThumbPos(slide))) {
  1867. end = self.getFitPos(slide);
  1868. } else {
  1869. effect = "fade";
  1870. }
  1871. }
  1872. // Zoom animation
  1873. // ==============
  1874. if (effect === "zoom") {
  1875. self.isAnimating = true;
  1876. end.scaleX = end.width / start.width;
  1877. end.scaleY = end.height / start.height;
  1878. // Check if we need to animate opacity
  1879. opacity = slide.opts.zoomOpacity;
  1880. if (opacity == "auto") {
  1881. opacity = Math.abs(slide.width / slide.height - start.width / start.height) > 0.1;
  1882. }
  1883. if (opacity) {
  1884. start.opacity = 0.1;
  1885. end.opacity = 1;
  1886. }
  1887. // Draw image at start position
  1888. $.fancybox.setTranslate(slide.$content.removeClass("fancybox-is-hidden"), start);
  1889. forceRedraw(slide.$content);
  1890. // Start animation
  1891. $.fancybox.animate(slide.$content, end, duration, function () {
  1892. self.isAnimating = false;
  1893. self.complete();
  1894. });
  1895. return;
  1896. }
  1897. self.updateSlide(slide);
  1898. // Simply show content if no effect
  1899. // ================================
  1900. if (!effect) {
  1901. slide.$content.removeClass("fancybox-is-hidden");
  1902. if (!isRevealed && isMoved && slide.type === "image" && !slide.hasError) {
  1903. slide.$content.hide().fadeIn("fast");
  1904. }
  1905. if (slide.pos === self.currPos) {
  1906. self.complete();
  1907. }
  1908. return;
  1909. }
  1910. // Prepare for CSS transiton
  1911. // =========================
  1912. $.fancybox.stop($slide);
  1913. //effectClassName = "fancybox-animated fancybox-slide--" + (slide.pos >= self.prevPos ? "next" : "previous") + " fancybox-fx-" + effect;
  1914. effectClassName = "fancybox-slide--" + (slide.pos >= self.prevPos ? "next" : "previous") + " fancybox-animated fancybox-fx-" + effect;
  1915. $slide.addClass(effectClassName).removeClass("fancybox-slide--current"); //.addClass(effectClassName);
  1916. slide.$content.removeClass("fancybox-is-hidden");
  1917. // Force reflow
  1918. forceRedraw($slide);
  1919. if (slide.type !== "image") {
  1920. slide.$content.hide().show(0);
  1921. }
  1922. $.fancybox.animate(
  1923. $slide,
  1924. "fancybox-slide--current",
  1925. duration,
  1926. function () {
  1927. $slide.removeClass(effectClassName).css({
  1928. transform: "",
  1929. opacity: ""
  1930. });
  1931. if (slide.pos === self.currPos) {
  1932. self.complete();
  1933. }
  1934. },
  1935. true
  1936. );
  1937. },
  1938. // Check if we can and have to zoom from thumbnail
  1939. //================================================
  1940. getThumbPos: function (slide) {
  1941. var rez = false,
  1942. $thumb = slide.$thumb,
  1943. thumbPos,
  1944. btw,
  1945. brw,
  1946. bbw,
  1947. blw;
  1948. if (!$thumb || !inViewport($thumb[0])) {
  1949. return false;
  1950. }
  1951. thumbPos = $.fancybox.getTranslate($thumb);
  1952. btw = parseFloat($thumb.css("border-top-width") || 0);
  1953. brw = parseFloat($thumb.css("border-right-width") || 0);
  1954. bbw = parseFloat($thumb.css("border-bottom-width") || 0);
  1955. blw = parseFloat($thumb.css("border-left-width") || 0);
  1956. rez = {
  1957. top: thumbPos.top + btw,
  1958. left: thumbPos.left + blw,
  1959. width: thumbPos.width - brw - blw,
  1960. height: thumbPos.height - btw - bbw,
  1961. scaleX: 1,
  1962. scaleY: 1
  1963. };
  1964. return thumbPos.width > 0 && thumbPos.height > 0 ? rez : false;
  1965. },
  1966. // Final adjustments after current gallery item is moved to position
  1967. // and it`s content is loaded
  1968. // ==================================================================
  1969. complete: function () {
  1970. var self = this,
  1971. current = self.current,
  1972. slides = {},
  1973. $el;
  1974. if (self.isMoved() || !current.isLoaded) {
  1975. return;
  1976. }
  1977. if (!current.isComplete) {
  1978. current.isComplete = true;
  1979. current.$slide.siblings().trigger("onReset");
  1980. self.preload("inline");
  1981. // Trigger any CSS transiton inside the slide
  1982. forceRedraw(current.$slide);
  1983. current.$slide.addClass("fancybox-slide--complete");
  1984. // Remove unnecessary slides
  1985. $.each(self.slides, function (key, slide) {
  1986. if (slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1) {
  1987. slides[slide.pos] = slide;
  1988. } else if (slide) {
  1989. $.fancybox.stop(slide.$slide);
  1990. slide.$slide.off().remove();
  1991. }
  1992. });
  1993. self.slides = slides;
  1994. }
  1995. self.isAnimating = false;
  1996. self.updateCursor();
  1997. self.trigger("afterShow");
  1998. // Autoplay first html5 video/audio
  1999. if (!!current.opts.video.autoStart) {
  2000. current.$slide
  2001. .find("video,audio")
  2002. .filter(":visible:first")
  2003. .trigger("play")
  2004. .one("ended", function () {
  2005. if (Document.exitFullscreen) {
  2006. Document.exitFullscreen();
  2007. } else if (this.webkitExitFullscreen) {
  2008. this.webkitExitFullscreen();
  2009. }
  2010. self.next();
  2011. });
  2012. }
  2013. // Try to focus on the first focusable element
  2014. if (current.opts.autoFocus && current.contentType === "html") {
  2015. // Look for the first input with autofocus attribute
  2016. $el = current.$content.find("input[autofocus]:enabled:visible:first");
  2017. if ($el.length) {
  2018. $el.trigger("focus");
  2019. } else {
  2020. self.focus(null, true);
  2021. }
  2022. }
  2023. // Avoid jumping
  2024. current.$slide.scrollTop(0).scrollLeft(0);
  2025. },
  2026. // Preload next and previous slides
  2027. // ================================
  2028. preload: function (type) {
  2029. var self = this,
  2030. prev,
  2031. next;
  2032. if (self.group.length < 2) {
  2033. return;
  2034. }
  2035. next = self.slides[self.currPos + 1];
  2036. prev = self.slides[self.currPos - 1];
  2037. if (prev && prev.type === type) {
  2038. self.loadSlide(prev);
  2039. }
  2040. if (next && next.type === type) {
  2041. self.loadSlide(next);
  2042. }
  2043. },
  2044. // Try to find and focus on the first focusable element
  2045. // ====================================================
  2046. focus: function (e, firstRun) {
  2047. var self = this,
  2048. focusableStr = [
  2049. "a[href]",
  2050. "area[href]",
  2051. 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])',
  2052. "select:not([disabled]):not([aria-hidden])",
  2053. "textarea:not([disabled]):not([aria-hidden])",
  2054. "button:not([disabled]):not([aria-hidden])",
  2055. "iframe",
  2056. "object",
  2057. "embed",
  2058. "video",
  2059. "audio",
  2060. "[contenteditable]",
  2061. '[tabindex]:not([tabindex^="-"])'
  2062. ].join(","),
  2063. focusableItems,
  2064. focusedItemIndex;
  2065. if (self.isClosing) {
  2066. return;
  2067. }
  2068. if (e || !self.current || !self.current.isComplete) {
  2069. // Focus on any element inside fancybox
  2070. focusableItems = self.$refs.container.find("*:visible");
  2071. } else {
  2072. // Focus inside current slide
  2073. focusableItems = self.current.$slide.find("*:visible" + (firstRun ? ":not(.fancybox-close-small)" : ""));
  2074. }
  2075. focusableItems = focusableItems.filter(focusableStr).filter(function () {
  2076. return $(this).css("visibility") !== "hidden" && !$(this).hasClass("disabled");
  2077. });
  2078. if (focusableItems.length) {
  2079. focusedItemIndex = focusableItems.index(document.activeElement);
  2080. if (e && e.shiftKey) {
  2081. // Back tab
  2082. if (focusedItemIndex < 0 || focusedItemIndex == 0) {
  2083. e.preventDefault();
  2084. focusableItems.eq(focusableItems.length - 1).trigger("focus");
  2085. }
  2086. } else {
  2087. // Outside or Forward tab
  2088. if (focusedItemIndex < 0 || focusedItemIndex == focusableItems.length - 1) {
  2089. if (e) {
  2090. e.preventDefault();
  2091. }
  2092. focusableItems.eq(0).trigger("focus");
  2093. }
  2094. }
  2095. } else {
  2096. self.$refs.container.trigger("focus");
  2097. }
  2098. },
  2099. // Activates current instance - brings container to the front and enables keyboard,
  2100. // notifies other instances about deactivating
  2101. // =================================================================================
  2102. activate: function () {
  2103. var self = this;
  2104. // Deactivate all instances
  2105. $(".fancybox-container").each(function () {
  2106. var instance = $(this).data("FancyBox");
  2107. // Skip self and closing instances
  2108. if (instance && instance.id !== self.id && !instance.isClosing) {
  2109. instance.trigger("onDeactivate");
  2110. instance.removeEvents();
  2111. instance.isVisible = false;
  2112. }
  2113. });
  2114. self.isVisible = true;
  2115. if (self.current || self.isIdle) {
  2116. self.update();
  2117. self.updateControls();
  2118. }
  2119. self.trigger("onActivate");
  2120. self.addEvents();
  2121. },
  2122. // Start closing procedure
  2123. // This will start "zoom-out" animation if needed and clean everything up afterwards
  2124. // =================================================================================
  2125. close: function (e, d) {
  2126. var self = this,
  2127. current = self.current,
  2128. effect,
  2129. duration,
  2130. $content,
  2131. domRect,
  2132. opacity,
  2133. start,
  2134. end;
  2135. var done = function () {
  2136. self.cleanUp(e);
  2137. };
  2138. if (self.isClosing) {
  2139. return false;
  2140. }
  2141. self.isClosing = true;
  2142. // If beforeClose callback prevents closing, make sure content is centered
  2143. if (self.trigger("beforeClose", e) === false) {
  2144. self.isClosing = false;
  2145. requestAFrame(function () {
  2146. self.update();
  2147. });
  2148. return false;
  2149. }
  2150. // Remove all events
  2151. // If there are multiple instances, they will be set again by "activate" method
  2152. self.removeEvents();
  2153. $content = current.$content;
  2154. effect = current.opts.animationEffect;
  2155. duration = $.isNumeric(d) ? d : effect ? current.opts.animationDuration : 0;
  2156. current.$slide.removeClass("fancybox-slide--complete fancybox-slide--next fancybox-slide--previous fancybox-animated");
  2157. if (e !== true) {
  2158. $.fancybox.stop(current.$slide);
  2159. } else {
  2160. effect = false;
  2161. }
  2162. // Remove other slides
  2163. current.$slide
  2164. .siblings()
  2165. .trigger("onReset")
  2166. .remove();
  2167. // Trigger animations
  2168. if (duration) {
  2169. self.$refs.container
  2170. .removeClass("fancybox-is-open")
  2171. .addClass("fancybox-is-closing")
  2172. .css("transition-duration", duration + "ms");
  2173. }
  2174. // Clean up
  2175. self.hideLoading(current);
  2176. self.hideControls(true);
  2177. self.updateCursor();
  2178. // Check if possible to zoom-out
  2179. if (
  2180. effect === "zoom" &&
  2181. !($content && duration && current.type === "image" && !self.isMoved() && !current.hasError && (end = self.getThumbPos(current)))
  2182. ) {
  2183. effect = "fade";
  2184. }
  2185. if (effect === "zoom") {
  2186. $.fancybox.stop($content);
  2187. domRect = $.fancybox.getTranslate($content);
  2188. start = {
  2189. top: domRect.top,
  2190. left: domRect.left,
  2191. scaleX: domRect.width / end.width,
  2192. scaleY: domRect.height / end.height,
  2193. width: end.width,
  2194. height: end.height
  2195. };
  2196. // Check if we need to animate opacity
  2197. opacity = current.opts.zoomOpacity;
  2198. if (opacity == "auto") {
  2199. opacity = Math.abs(current.width / current.height - end.width / end.height) > 0.1;
  2200. }
  2201. if (opacity) {
  2202. end.opacity = 0;
  2203. }
  2204. $.fancybox.setTranslate($content, start);
  2205. forceRedraw($content);
  2206. $.fancybox.animate($content, end, duration, done);
  2207. return true;
  2208. }
  2209. if (effect && duration) {
  2210. $.fancybox.animate(
  2211. current.$slide.addClass("fancybox-slide--previous").removeClass("fancybox-slide--current"),
  2212. "fancybox-animated fancybox-fx-" + effect,
  2213. duration,
  2214. done
  2215. );
  2216. } else {
  2217. // If skip animation
  2218. if (e === true) {
  2219. setTimeout(done, duration);
  2220. } else {
  2221. done();
  2222. }
  2223. }
  2224. return true;
  2225. },
  2226. // Final adjustments after removing the instance
  2227. // =============================================
  2228. cleanUp: function (e) {
  2229. var self = this,
  2230. instance,
  2231. $focus = self.current.opts.$orig,
  2232. x,
  2233. y;
  2234. self.current.$slide.trigger("onReset");
  2235. self.$refs.container.empty().remove();
  2236. self.trigger("afterClose", e);
  2237. // Place back focus
  2238. if (!!self.current.opts.backFocus) {
  2239. if (!$focus || !$focus.length || !$focus.is(":visible")) {
  2240. $focus = self.$trigger;
  2241. }
  2242. if ($focus && $focus.length) {
  2243. x = window.scrollX;
  2244. y = window.scrollY;
  2245. $focus.trigger("focus");
  2246. $("html, body")
  2247. .scrollTop(y)
  2248. .scrollLeft(x);
  2249. }
  2250. }
  2251. self.current = null;
  2252. // Check if there are other instances
  2253. instance = $.fancybox.getInstance();
  2254. if (instance) {
  2255. instance.activate();
  2256. } else {
  2257. $("body").removeClass("fancybox-active compensate-for-scrollbar");
  2258. $("#fancybox-style-noscroll").remove();
  2259. }
  2260. },
  2261. // Call callback and trigger an event
  2262. // ==================================
  2263. trigger: function (name, slide) {
  2264. var args = Array.prototype.slice.call(arguments, 1),
  2265. self = this,
  2266. obj = slide && slide.opts ? slide : self.current,
  2267. rez;
  2268. if (obj) {
  2269. args.unshift(obj);
  2270. } else {
  2271. obj = self;
  2272. }
  2273. args.unshift(self);
  2274. if ($.isFunction(obj.opts[name])) {
  2275. rez = obj.opts[name].apply(obj, args);
  2276. }
  2277. if (rez === false) {
  2278. return rez;
  2279. }
  2280. if (name === "afterClose" || !self.$refs) {
  2281. $D.trigger(name + ".fb", args);
  2282. } else {
  2283. self.$refs.container.trigger(name + ".fb", args);
  2284. }
  2285. },
  2286. // Update infobar values, navigation button states and reveal caption
  2287. // ==================================================================
  2288. updateControls: function () {
  2289. var self = this,
  2290. current = self.current,
  2291. index = current.index,
  2292. $container = self.$refs.container,
  2293. $caption = self.$refs.caption,
  2294. caption = current.opts.caption;
  2295. // Recalculate content dimensions
  2296. current.$slide.trigger("refresh");
  2297. // Set caption
  2298. if (caption && caption.length) {
  2299. self.$caption = $caption;
  2300. $caption
  2301. .children()
  2302. .eq(0)
  2303. .html(caption);
  2304. } else {
  2305. self.$caption = null;
  2306. }
  2307. if (!self.hasHiddenControls && !self.isIdle) {
  2308. self.showControls();
  2309. }
  2310. // Update info and navigation elements
  2311. $container.find("[data-fancybox-count]").html(self.group.length);
  2312. $container.find("[data-fancybox-index]").html(index + 1);
  2313. $container.find("[data-fancybox-prev]").prop("disabled", !current.opts.loop && index <= 0);
  2314. $container.find("[data-fancybox-next]").prop("disabled", !current.opts.loop && index >= self.group.length - 1);
  2315. if (current.type === "image") {
  2316. // Re-enable buttons; update download button source
  2317. $container
  2318. .find("[data-fancybox-zoom]")
  2319. .show()
  2320. .end()
  2321. .find("[data-fancybox-download]")
  2322. .attr("href", current.opts.image.src || current.src)
  2323. .show();
  2324. } else if (current.opts.toolbar) {
  2325. $container.find("[data-fancybox-download],[data-fancybox-zoom]").hide();
  2326. }
  2327. // Make sure focus is not on disabled button/element
  2328. if ($(document.activeElement).is(":hidden,[disabled]")) {
  2329. self.$refs.container.trigger("focus");
  2330. }
  2331. },
  2332. // Hide toolbar and caption
  2333. // ========================
  2334. hideControls: function (andCaption) {
  2335. var self = this,
  2336. arr = ["infobar", "toolbar", "nav"];
  2337. if (andCaption || !self.current.opts.preventCaptionOverlap) {
  2338. arr.push("caption");
  2339. }
  2340. this.$refs.container.removeClass(
  2341. arr
  2342. .map(function (i) {
  2343. return "fancybox-show-" + i;
  2344. })
  2345. .join(" ")
  2346. );
  2347. this.hasHiddenControls = true;
  2348. },
  2349. showControls: function () {
  2350. var self = this,
  2351. opts = self.current ? self.current.opts : self.opts,
  2352. $container = self.$refs.container;
  2353. self.hasHiddenControls = false;
  2354. self.idleSecondsCounter = 0;
  2355. $container
  2356. .toggleClass("fancybox-show-toolbar", !!(opts.toolbar && opts.buttons))
  2357. .toggleClass("fancybox-show-infobar", !!(opts.infobar && self.group.length > 1))
  2358. .toggleClass("fancybox-show-caption", !!self.$caption)
  2359. .toggleClass("fancybox-show-nav", !!(opts.arrows && self.group.length > 1))
  2360. .toggleClass("fancybox-is-modal", !!opts.modal);
  2361. },
  2362. // Toggle toolbar and caption
  2363. // ==========================
  2364. toggleControls: function () {
  2365. if (this.hasHiddenControls) {
  2366. this.showControls();
  2367. } else {
  2368. this.hideControls();
  2369. }
  2370. }
  2371. });
  2372. $.fancybox = {
  2373. version: "3.5.7",
  2374. defaults: defaults,
  2375. // Get current instance and execute a command.
  2376. //
  2377. // Examples of usage:
  2378. //
  2379. // $instance = $.fancybox.getInstance();
  2380. // $.fancybox.getInstance().jumpTo( 1 );
  2381. // $.fancybox.getInstance( 'jumpTo', 1 );
  2382. // $.fancybox.getInstance( function() {
  2383. // console.info( this.currIndex );
  2384. // });
  2385. // ======================================================
  2386. getInstance: function (command) {
  2387. var instance = $('.fancybox-container:not(".fancybox-is-closing"):last').data("FancyBox"),
  2388. args = Array.prototype.slice.call(arguments, 1);
  2389. if (instance instanceof FancyBox) {
  2390. if ($.type(command) === "string") {
  2391. instance[command].apply(instance, args);
  2392. } else if ($.type(command) === "function") {
  2393. command.apply(instance, args);
  2394. }
  2395. return instance;
  2396. }
  2397. return false;
  2398. },
  2399. // Create new instance
  2400. // ===================
  2401. open: function (items, opts, index) {
  2402. return new FancyBox(items, opts, index);
  2403. },
  2404. // Close current or all instances
  2405. // ==============================
  2406. close: function (all) {
  2407. var instance = this.getInstance();
  2408. if (instance) {
  2409. instance.close();
  2410. // Try to find and close next instance
  2411. if (all === true) {
  2412. this.close(all);
  2413. }
  2414. }
  2415. },
  2416. // Close all instances and unbind all events
  2417. // =========================================
  2418. destroy: function () {
  2419. this.close(true);
  2420. $D.add("body").off("click.fb-start", "**");
  2421. },
  2422. // Try to detect mobile devices
  2423. // ============================
  2424. isMobile: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),
  2425. // Detect if 'translate3d' support is available
  2426. // ============================================
  2427. use3d: (function () {
  2428. var div = document.createElement("div");
  2429. return (
  2430. window.getComputedStyle &&
  2431. window.getComputedStyle(div) &&
  2432. window.getComputedStyle(div).getPropertyValue("transform") &&
  2433. !(document.documentMode && document.documentMode < 11)
  2434. );
  2435. })(),
  2436. // Helper function to get current visual state of an element
  2437. // returns array[ top, left, horizontal-scale, vertical-scale, opacity ]
  2438. // =====================================================================
  2439. getTranslate: function ($el) {
  2440. var domRect;
  2441. if (!$el || !$el.length) {
  2442. return false;
  2443. }
  2444. domRect = $el[0].getBoundingClientRect();
  2445. return {
  2446. top: domRect.top || 0,
  2447. left: domRect.left || 0,
  2448. width: domRect.width,
  2449. height: domRect.height,
  2450. opacity: parseFloat($el.css("opacity"))
  2451. };
  2452. },
  2453. // Shortcut for setting "translate3d" properties for element
  2454. // Can set be used to set opacity, too
  2455. // ========================================================
  2456. setTranslate: function ($el, props) {
  2457. var str = "",
  2458. css = {};
  2459. if (!$el || !props) {
  2460. return;
  2461. }
  2462. if (props.left !== undefined || props.top !== undefined) {
  2463. str =
  2464. (props.left === undefined ? $el.position().left : props.left) +
  2465. "px, " +
  2466. (props.top === undefined ? $el.position().top : props.top) +
  2467. "px";
  2468. if (this.use3d) {
  2469. str = "translate3d(" + str + ", 0px)";
  2470. } else {
  2471. str = "translate(" + str + ")";
  2472. }
  2473. }
  2474. if (props.scaleX !== undefined && props.scaleY !== undefined) {
  2475. str += " scale(" + props.scaleX + ", " + props.scaleY + ")";
  2476. } else if (props.scaleX !== undefined) {
  2477. str += " scaleX(" + props.scaleX + ")";
  2478. }
  2479. if (str.length) {
  2480. css.transform = str;
  2481. }
  2482. if (props.opacity !== undefined) {
  2483. css.opacity = props.opacity;
  2484. }
  2485. if (props.width !== undefined) {
  2486. css.width = props.width;
  2487. }
  2488. if (props.height !== undefined) {
  2489. css.height = props.height;
  2490. }
  2491. return $el.css(css);
  2492. },
  2493. // Simple CSS transition handler
  2494. // =============================
  2495. animate: function ($el, to, duration, callback, leaveAnimationName) {
  2496. var self = this,
  2497. from;
  2498. if ($.isFunction(duration)) {
  2499. callback = duration;
  2500. duration = null;
  2501. }
  2502. self.stop($el);
  2503. from = self.getTranslate($el);
  2504. $el.on(transitionEnd, function (e) {
  2505. // Skip events from child elements and z-index change
  2506. if (e && e.originalEvent && (!$el.is(e.originalEvent.target) || e.originalEvent.propertyName == "z-index")) {
  2507. return;
  2508. }
  2509. self.stop($el);
  2510. if ($.isNumeric(duration)) {
  2511. $el.css("transition-duration", "");
  2512. }
  2513. if ($.isPlainObject(to)) {
  2514. if (to.scaleX !== undefined && to.scaleY !== undefined) {
  2515. self.setTranslate($el, {
  2516. top: to.top,
  2517. left: to.left,
  2518. width: from.width * to.scaleX,
  2519. height: from.height * to.scaleY,
  2520. scaleX: 1,
  2521. scaleY: 1
  2522. });
  2523. }
  2524. } else if (leaveAnimationName !== true) {
  2525. $el.removeClass(to);
  2526. }
  2527. if ($.isFunction(callback)) {
  2528. callback(e);
  2529. }
  2530. });
  2531. if ($.isNumeric(duration)) {
  2532. $el.css("transition-duration", duration + "ms");
  2533. }
  2534. // Start animation by changing CSS properties or class name
  2535. if ($.isPlainObject(to)) {
  2536. if (to.scaleX !== undefined && to.scaleY !== undefined) {
  2537. delete to.width;
  2538. delete to.height;
  2539. if ($el.parent().hasClass("fancybox-slide--image")) {
  2540. $el.parent().addClass("fancybox-is-scaling");
  2541. }
  2542. }
  2543. $.fancybox.setTranslate($el, to);
  2544. } else {
  2545. $el.addClass(to);
  2546. }
  2547. // Make sure that `transitionend` callback gets fired
  2548. $el.data(
  2549. "timer",
  2550. setTimeout(function () {
  2551. $el.trigger(transitionEnd);
  2552. }, duration + 33)
  2553. );
  2554. },
  2555. stop: function ($el, callCallback) {
  2556. if ($el && $el.length) {
  2557. clearTimeout($el.data("timer"));
  2558. if (callCallback) {
  2559. $el.trigger(transitionEnd);
  2560. }
  2561. $el.off(transitionEnd).css("transition-duration", "");
  2562. $el.parent().removeClass("fancybox-is-scaling");
  2563. }
  2564. }
  2565. };
  2566. // Default click handler for "fancyboxed" links
  2567. // ============================================
  2568. function _run(e, opts) {
  2569. var items = [],
  2570. index = 0,
  2571. $target,
  2572. value,
  2573. instance;
  2574. // Avoid opening multiple times
  2575. if (e && e.isDefaultPrevented()) {
  2576. return;
  2577. }
  2578. e.preventDefault();
  2579. opts = opts || {};
  2580. if (e && e.data) {
  2581. opts = mergeOpts(e.data.options, opts);
  2582. }
  2583. $target = opts.$target || $(e.currentTarget).trigger("blur");
  2584. instance = $.fancybox.getInstance();
  2585. if (instance && instance.$trigger && instance.$trigger.is($target)) {
  2586. return;
  2587. }
  2588. if (opts.selector) {
  2589. items = $(opts.selector);
  2590. } else {
  2591. // Get all related items and find index for clicked one
  2592. value = $target.attr("data-fancybox") || "";
  2593. if (value) {
  2594. items = e.data ? e.data.items : [];
  2595. items = items.length ? items.filter('[data-fancybox="' + value + '"]') : $('[data-fancybox="' + value + '"]');
  2596. } else {
  2597. items = [$target];
  2598. }
  2599. }
  2600. index = $(items).index($target);
  2601. // Sometimes current item can not be found
  2602. if (index < 0) {
  2603. index = 0;
  2604. }
  2605. instance = $.fancybox.open(items, opts, index);
  2606. // Save last active element
  2607. instance.$trigger = $target;
  2608. }
  2609. // Create a jQuery plugin
  2610. // ======================
  2611. $.fn.fancybox = function (options) {
  2612. var selector;
  2613. options = options || {};
  2614. selector = options.selector || false;
  2615. if (selector) {
  2616. // Use body element instead of document so it executes first
  2617. $("body")
  2618. .off("click.fb-start", selector)
  2619. .on("click.fb-start", selector, {
  2620. options: options
  2621. }, _run);
  2622. } else {
  2623. this.off("click.fb-start").on(
  2624. "click.fb-start", {
  2625. items: this,
  2626. options: options
  2627. },
  2628. _run
  2629. );
  2630. }
  2631. return this;
  2632. };
  2633. // Self initializing plugin for all elements having `data-fancybox` attribute
  2634. // ==========================================================================
  2635. $D.on("click.fb-start", "[data-fancybox]", _run);
  2636. // Enable "trigger elements"
  2637. // =========================
  2638. $D.on("click.fb-start", "[data-fancybox-trigger]", function (e) {
  2639. $('[data-fancybox="' + $(this).attr("data-fancybox-trigger") + '"]')
  2640. .eq($(this).attr("data-fancybox-index") || 0)
  2641. .trigger("click.fb-start", {
  2642. $trigger: $(this)
  2643. });
  2644. });
  2645. // Track focus event for better accessibility styling
  2646. // ==================================================
  2647. (function () {
  2648. var buttonStr = ".fancybox-button",
  2649. focusStr = "fancybox-focus",
  2650. $pressed = null;
  2651. $D.on("mousedown mouseup focus blur", buttonStr, function (e) {
  2652. switch (e.type) {
  2653. case "mousedown":
  2654. $pressed = $(this);
  2655. break;
  2656. case "mouseup":
  2657. $pressed = null;
  2658. break;
  2659. case "focusin":
  2660. $(buttonStr).removeClass(focusStr);
  2661. if (!$(this).is($pressed) && !$(this).is("[disabled]")) {
  2662. $(this).addClass(focusStr);
  2663. }
  2664. break;
  2665. case "focusout":
  2666. $(buttonStr).removeClass(focusStr);
  2667. break;
  2668. }
  2669. });
  2670. })();
  2671. })(window, document, jQuery);
  2672. // ==========================================================================
  2673. //
  2674. // Media
  2675. // Adds additional media type support
  2676. //
  2677. // ==========================================================================
  2678. (function ($) {
  2679. "use strict";
  2680. // Object containing properties for each media type
  2681. var defaults = {
  2682. youtube: {
  2683. matcher: /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i,
  2684. params: {
  2685. autoplay: 1,
  2686. autohide: 1,
  2687. fs: 1,
  2688. rel: 0,
  2689. hd: 1,
  2690. wmode: "transparent",
  2691. enablejsapi: 1,
  2692. html5: 1
  2693. },
  2694. paramPlace: 8,
  2695. type: "iframe",
  2696. url: "https://www.youtube-nocookie.com/embed/$4",
  2697. thumb: "https://img.youtube.com/vi/$4/hqdefault.jpg"
  2698. },
  2699. vimeo: {
  2700. matcher: /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/,
  2701. params: {
  2702. autoplay: 1,
  2703. hd: 1,
  2704. show_title: 1,
  2705. show_byline: 1,
  2706. show_portrait: 0,
  2707. fullscreen: 1
  2708. },
  2709. paramPlace: 3,
  2710. type: "iframe",
  2711. url: "//player.vimeo.com/video/$2"
  2712. },
  2713. instagram: {
  2714. matcher: /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i,
  2715. type: "image",
  2716. url: "//$1/p/$2/media/?size=l"
  2717. },
  2718. // Examples:
  2719. // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16
  2720. // https://www.google.com/maps/@37.7852006,-122.4146355,14.65z
  2721. // https://www.google.com/maps/@52.2111123,2.9237542,6.61z?hl=en
  2722. // https://www.google.com/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572
  2723. gmap_place: {
  2724. matcher: /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i,
  2725. type: "iframe",
  2726. url: function (rez) {
  2727. return (
  2728. "//maps.google." +
  2729. rez[2] +
  2730. "/?ll=" +
  2731. (rez[9] ? rez[9] + "&z=" + Math.floor(rez[10]) + (rez[12] ? rez[12].replace(/^\//, "&") : "") : rez[12] + "").replace(/\?/, "&") +
  2732. "&output=" +
  2733. (rez[12] && rez[12].indexOf("layer=c") > 0 ? "svembed" : "embed")
  2734. );
  2735. }
  2736. },
  2737. // Examples:
  2738. // https://www.google.com/maps/search/Empire+State+Building/
  2739. // https://www.google.com/maps/search/?api=1&query=centurylink+field
  2740. // https://www.google.com/maps/search/?api=1&query=47.5951518,-122.3316393
  2741. gmap_search: {
  2742. matcher: /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(maps\/search\/)(.*)/i,
  2743. type: "iframe",
  2744. url: function (rez) {
  2745. return "//maps.google." + rez[2] + "/maps?q=" + rez[5].replace("query=", "q=").replace("api=1", "") + "&output=embed";
  2746. }
  2747. }
  2748. };
  2749. // Formats matching url to final form
  2750. var format = function (url, rez, params) {
  2751. if (!url) {
  2752. return;
  2753. }
  2754. params = params || "";
  2755. if ($.type(params) === "object") {
  2756. params = $.param(params, true);
  2757. }
  2758. $.each(rez, function (key, value) {
  2759. url = url.replace("$" + key, value || "");
  2760. });
  2761. if (params.length) {
  2762. url += (url.indexOf("?") > 0 ? "&" : "?") + params;
  2763. }
  2764. return url;
  2765. };
  2766. $(document).on("objectNeedsType.fb", function (e, instance, item) {
  2767. var url = item.src || "",
  2768. type = false,
  2769. media,
  2770. thumb,
  2771. rez,
  2772. params,
  2773. urlParams,
  2774. paramObj,
  2775. provider;
  2776. media = $.extend(true, {}, defaults, item.opts.media);
  2777. // Look for any matching media type
  2778. $.each(media, function (providerName, providerOpts) {
  2779. rez = url.match(providerOpts.matcher);
  2780. if (!rez) {
  2781. return;
  2782. }
  2783. type = providerOpts.type;
  2784. provider = providerName;
  2785. paramObj = {};
  2786. if (providerOpts.paramPlace && rez[providerOpts.paramPlace]) {
  2787. urlParams = rez[providerOpts.paramPlace];
  2788. if (urlParams[0] == "?") {
  2789. urlParams = urlParams.substring(1);
  2790. }
  2791. urlParams = urlParams.split("&");
  2792. for (var m = 0; m < urlParams.length; ++m) {
  2793. var p = urlParams[m].split("=", 2);
  2794. if (p.length == 2) {
  2795. paramObj[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
  2796. }
  2797. }
  2798. }
  2799. params = $.extend(true, {}, providerOpts.params, item.opts[providerName], paramObj);
  2800. url =
  2801. $.type(providerOpts.url) === "function" ? providerOpts.url.call(this, rez, params, item) : format(providerOpts.url, rez, params);
  2802. thumb =
  2803. $.type(providerOpts.thumb) === "function" ? providerOpts.thumb.call(this, rez, params, item) : format(providerOpts.thumb, rez);
  2804. if (providerName === "youtube") {
  2805. url = url.replace(/&t=((\d+)m)?(\d+)s/, function (match, p1, m, s) {
  2806. return "&start=" + ((m ? parseInt(m, 10) * 60 : 0) + parseInt(s, 10));
  2807. });
  2808. } else if (providerName === "vimeo") {
  2809. url = url.replace("&%23", "#");
  2810. }
  2811. return false;
  2812. });
  2813. // If it is found, then change content type and update the url
  2814. if (type) {
  2815. if (!item.opts.thumb && !(item.opts.$thumb && item.opts.$thumb.length)) {
  2816. item.opts.thumb = thumb;
  2817. }
  2818. if (type === "iframe") {
  2819. item.opts = $.extend(true, item.opts, {
  2820. iframe: {
  2821. preload: false,
  2822. attr: {
  2823. scrolling: "no"
  2824. }
  2825. }
  2826. });
  2827. }
  2828. $.extend(item, {
  2829. type: type,
  2830. src: url,
  2831. origSrc: item.src,
  2832. contentSource: provider,
  2833. contentType: type === "image" ? "image" : provider == "gmap_place" || provider == "gmap_search" ? "map" : "video"
  2834. });
  2835. } else if (url) {
  2836. item.type = item.opts.defaultType;
  2837. }
  2838. });
  2839. // Load YouTube/Video API on request to detect when video finished playing
  2840. var VideoAPILoader = {
  2841. youtube: {
  2842. src: "https://www.youtube.com/iframe_api",
  2843. class: "YT",
  2844. loading: false,
  2845. loaded: false
  2846. },
  2847. vimeo: {
  2848. src: "https://player.vimeo.com/api/player.js",
  2849. class: "Vimeo",
  2850. loading: false,
  2851. loaded: false
  2852. },
  2853. load: function (vendor) {
  2854. var _this = this,
  2855. script;
  2856. if (this[vendor].loaded) {
  2857. setTimeout(function () {
  2858. _this.done(vendor);
  2859. });
  2860. return;
  2861. }
  2862. if (this[vendor].loading) {
  2863. return;
  2864. }
  2865. this[vendor].loading = true;
  2866. script = document.createElement("script");
  2867. script.type = "text/javascript";
  2868. script.src = this[vendor].src;
  2869. if (vendor === "youtube") {
  2870. window.onYouTubeIframeAPIReady = function () {
  2871. _this[vendor].loaded = true;
  2872. _this.done(vendor);
  2873. };
  2874. } else {
  2875. script.onload = function () {
  2876. _this[vendor].loaded = true;
  2877. _this.done(vendor);
  2878. };
  2879. }
  2880. document.body.appendChild(script);
  2881. },
  2882. done: function (vendor) {
  2883. var instance, $el, player;
  2884. if (vendor === "youtube") {
  2885. delete window.onYouTubeIframeAPIReady;
  2886. }
  2887. instance = $.fancybox.getInstance();
  2888. if (instance) {
  2889. $el = instance.current.$content.find("iframe");
  2890. if (vendor === "youtube" && YT !== undefined && YT) {
  2891. player = new YT.Player($el.attr("id"), {
  2892. events: {
  2893. onStateChange: function (e) {
  2894. if (e.data == 0) {
  2895. instance.next();
  2896. }
  2897. }
  2898. }
  2899. });
  2900. } else if (vendor === "vimeo" && Vimeo !== undefined && Vimeo) {
  2901. player = new Vimeo.Player($el);
  2902. player.on("ended", function () {
  2903. instance.next();
  2904. });
  2905. }
  2906. }
  2907. }
  2908. };
  2909. $(document).on({
  2910. "afterShow.fb": function (e, instance, current) {
  2911. if (instance.group.length > 1 && (current.contentSource === "youtube" || current.contentSource === "vimeo")) {
  2912. VideoAPILoader.load(current.contentSource);
  2913. }
  2914. }
  2915. });
  2916. })(jQuery);
  2917. // ==========================================================================
  2918. //
  2919. // Guestures
  2920. // Adds touch guestures, handles click and tap events
  2921. //
  2922. // ==========================================================================
  2923. (function (window, document, $) {
  2924. "use strict";
  2925. var requestAFrame = (function () {
  2926. return (
  2927. window.requestAnimationFrame ||
  2928. window.webkitRequestAnimationFrame ||
  2929. window.mozRequestAnimationFrame ||
  2930. window.oRequestAnimationFrame ||
  2931. // if all else fails, use setTimeout
  2932. function (callback) {
  2933. return window.setTimeout(callback, 1000 / 60);
  2934. }
  2935. );
  2936. })();
  2937. var cancelAFrame = (function () {
  2938. return (
  2939. window.cancelAnimationFrame ||
  2940. window.webkitCancelAnimationFrame ||
  2941. window.mozCancelAnimationFrame ||
  2942. window.oCancelAnimationFrame ||
  2943. function (id) {
  2944. window.clearTimeout(id);
  2945. }
  2946. );
  2947. })();
  2948. var getPointerXY = function (e) {
  2949. var result = [];
  2950. e = e.originalEvent || e || window.e;
  2951. e = e.touches && e.touches.length ? e.touches : e.changedTouches && e.changedTouches.length ? e.changedTouches : [e];
  2952. for (var key in e) {
  2953. if (e[key].pageX) {
  2954. result.push({
  2955. x: e[key].pageX,
  2956. y: e[key].pageY
  2957. });
  2958. } else if (e[key].clientX) {
  2959. result.push({
  2960. x: e[key].clientX,
  2961. y: e[key].clientY
  2962. });
  2963. }
  2964. }
  2965. return result;
  2966. };
  2967. var distance = function (point2, point1, what) {
  2968. if (!point1 || !point2) {
  2969. return 0;
  2970. }
  2971. if (what === "x") {
  2972. return point2.x - point1.x;
  2973. } else if (what === "y") {
  2974. return point2.y - point1.y;
  2975. }
  2976. return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
  2977. };
  2978. var isClickable = function ($el) {
  2979. if (
  2980. $el.is('a,area,button,[role="button"],input,label,select,summary,textarea,video,audio,iframe') ||
  2981. $.isFunction($el.get(0).onclick) ||
  2982. $el.data("selectable")
  2983. ) {
  2984. return true;
  2985. }
  2986. // Check for attributes like data-fancybox-next or data-fancybox-close
  2987. for (var i = 0, atts = $el[0].attributes, n = atts.length; i < n; i++) {
  2988. if (atts[i].nodeName.substr(0, 14) === "data-fancybox-") {
  2989. return true;
  2990. }
  2991. }
  2992. return false;
  2993. };
  2994. var hasScrollbars = function (el) {
  2995. var overflowY = window.getComputedStyle(el)["overflow-y"],
  2996. overflowX = window.getComputedStyle(el)["overflow-x"],
  2997. vertical = (overflowY === "scroll" || overflowY === "auto") && el.scrollHeight > el.clientHeight,
  2998. horizontal = (overflowX === "scroll" || overflowX === "auto") && el.scrollWidth > el.clientWidth;
  2999. return vertical || horizontal;
  3000. };
  3001. var isScrollable = function ($el) {
  3002. var rez = false;
  3003. while (true) {
  3004. rez = hasScrollbars($el.get(0));
  3005. if (rez) {
  3006. break;
  3007. }
  3008. $el = $el.parent();
  3009. if (!$el.length || $el.hasClass("fancybox-stage") || $el.is("body")) {
  3010. break;
  3011. }
  3012. }
  3013. return rez;
  3014. };
  3015. var Guestures = function (instance) {
  3016. var self = this;
  3017. self.instance = instance;
  3018. self.$bg = instance.$refs.bg;
  3019. self.$stage = instance.$refs.stage;
  3020. self.$container = instance.$refs.container;
  3021. self.destroy();
  3022. self.$container.on("touchstart.fb.touch mousedown.fb.touch", $.proxy(self, "ontouchstart"));
  3023. };
  3024. Guestures.prototype.destroy = function () {
  3025. var self = this;
  3026. self.$container.off(".fb.touch");
  3027. $(document).off(".fb.touch");
  3028. if (self.requestId) {
  3029. cancelAFrame(self.requestId);
  3030. self.requestId = null;
  3031. }
  3032. if (self.tapped) {
  3033. clearTimeout(self.tapped);
  3034. self.tapped = null;
  3035. }
  3036. };
  3037. Guestures.prototype.ontouchstart = function (e) {
  3038. var self = this,
  3039. $target = $(e.target),
  3040. instance = self.instance,
  3041. current = instance.current,
  3042. $slide = current.$slide,
  3043. $content = current.$content,
  3044. isTouchDevice = e.type == "touchstart";
  3045. // Do not respond to both (touch and mouse) events
  3046. if (isTouchDevice) {
  3047. self.$container.off("mousedown.fb.touch");
  3048. }
  3049. // Ignore right click
  3050. if (e.originalEvent && e.originalEvent.button == 2) {
  3051. return;
  3052. }
  3053. // Ignore taping on links, buttons, input elements
  3054. if (!$slide.length || !$target.length || isClickable($target) || isClickable($target.parent())) {
  3055. return;
  3056. }
  3057. // Ignore clicks on the scrollbar
  3058. if (!$target.is("img") && e.originalEvent.clientX > $target[0].clientWidth + $target.offset().left) {
  3059. return;
  3060. }
  3061. // Ignore clicks while zooming or closing
  3062. if (!current || instance.isAnimating || current.$slide.hasClass("fancybox-animated")) {
  3063. e.stopPropagation();
  3064. e.preventDefault();
  3065. return;
  3066. }
  3067. self.realPoints = self.startPoints = getPointerXY(e);
  3068. if (!self.startPoints.length) {
  3069. return;
  3070. }
  3071. // Allow other scripts to catch touch event if "touch" is set to false
  3072. if (current.touch) {
  3073. e.stopPropagation();
  3074. }
  3075. self.startEvent = e;
  3076. self.canTap = true;
  3077. self.$target = $target;
  3078. self.$content = $content;
  3079. self.opts = current.opts.touch;
  3080. self.isPanning = false;
  3081. self.isSwiping = false;
  3082. self.isZooming = false;
  3083. self.isScrolling = false;
  3084. self.canPan = instance.canPan();
  3085. self.startTime = new Date().getTime();
  3086. self.distanceX = self.distanceY = self.distance = 0;
  3087. self.canvasWidth = Math.round($slide[0].clientWidth);
  3088. self.canvasHeight = Math.round($slide[0].clientHeight);
  3089. self.contentLastPos = null;
  3090. self.contentStartPos = $.fancybox.getTranslate(self.$content) || {
  3091. top: 0,
  3092. left: 0
  3093. };
  3094. self.sliderStartPos = $.fancybox.getTranslate($slide);
  3095. // Since position will be absolute, but we need to make it relative to the stage
  3096. self.stagePos = $.fancybox.getTranslate(instance.$refs.stage);
  3097. self.sliderStartPos.top -= self.stagePos.top;
  3098. self.sliderStartPos.left -= self.stagePos.left;
  3099. self.contentStartPos.top -= self.stagePos.top;
  3100. self.contentStartPos.left -= self.stagePos.left;
  3101. $(document)
  3102. .off(".fb.touch")
  3103. .on(isTouchDevice ? "touchend.fb.touch touchcancel.fb.touch" : "mouseup.fb.touch mouseleave.fb.touch", $.proxy(self, "ontouchend"))
  3104. .on(isTouchDevice ? "touchmove.fb.touch" : "mousemove.fb.touch", $.proxy(self, "ontouchmove"));
  3105. if ($.fancybox.isMobile) {
  3106. document.addEventListener("scroll", self.onscroll, true);
  3107. }
  3108. // Skip if clicked outside the sliding area
  3109. if (!(self.opts || self.canPan) || !($target.is(self.$stage) || self.$stage.find($target).length)) {
  3110. if ($target.is(".fancybox-image")) {
  3111. e.preventDefault();
  3112. }
  3113. if (!($.fancybox.isMobile && $target.parents(".fancybox-caption").length)) {
  3114. return;
  3115. }
  3116. }
  3117. self.isScrollable = isScrollable($target) || isScrollable($target.parent());
  3118. // Check if element is scrollable and try to prevent default behavior (scrolling)
  3119. if (!($.fancybox.isMobile && self.isScrollable)) {
  3120. e.preventDefault();
  3121. }
  3122. // One finger or mouse click - swipe or pan an image
  3123. if (self.startPoints.length === 1 || current.hasError) {
  3124. if (self.canPan) {
  3125. $.fancybox.stop(self.$content);
  3126. self.isPanning = true;
  3127. } else {
  3128. self.isSwiping = true;
  3129. }
  3130. self.$container.addClass("fancybox-is-grabbing");
  3131. }
  3132. // Two fingers - zoom image
  3133. if (self.startPoints.length === 2 && current.type === "image" && (current.isLoaded || current.$ghost)) {
  3134. self.canTap = false;
  3135. self.isSwiping = false;
  3136. self.isPanning = false;
  3137. self.isZooming = true;
  3138. $.fancybox.stop(self.$content);
  3139. self.centerPointStartX = (self.startPoints[0].x + self.startPoints[1].x) * 0.5 - $(window).scrollLeft();
  3140. self.centerPointStartY = (self.startPoints[0].y + self.startPoints[1].y) * 0.5 - $(window).scrollTop();
  3141. self.percentageOfImageAtPinchPointX = (self.centerPointStartX - self.contentStartPos.left) / self.contentStartPos.width;
  3142. self.percentageOfImageAtPinchPointY = (self.centerPointStartY - self.contentStartPos.top) / self.contentStartPos.height;
  3143. self.startDistanceBetweenFingers = distance(self.startPoints[0], self.startPoints[1]);
  3144. }
  3145. };
  3146. Guestures.prototype.onscroll = function (e) {
  3147. var self = this;
  3148. self.isScrolling = true;
  3149. document.removeEventListener("scroll", self.onscroll, true);
  3150. };
  3151. Guestures.prototype.ontouchmove = function (e) {
  3152. var self = this;
  3153. // Make sure user has not released over iframe or disabled element
  3154. if (e.originalEvent.buttons !== undefined && e.originalEvent.buttons === 0) {
  3155. self.ontouchend(e);
  3156. return;
  3157. }
  3158. if (self.isScrolling) {
  3159. self.canTap = false;
  3160. return;
  3161. }
  3162. self.newPoints = getPointerXY(e);
  3163. if (!(self.opts || self.canPan) || !self.newPoints.length || !self.newPoints.length) {
  3164. return;
  3165. }
  3166. if (!(self.isSwiping && self.isSwiping === true)) {
  3167. e.preventDefault();
  3168. }
  3169. self.distanceX = distance(self.newPoints[0], self.startPoints[0], "x");
  3170. self.distanceY = distance(self.newPoints[0], self.startPoints[0], "y");
  3171. self.distance = distance(self.newPoints[0], self.startPoints[0]);
  3172. // Skip false ontouchmove events (Chrome)
  3173. if (self.distance > 0) {
  3174. if (self.isSwiping) {
  3175. self.onSwipe(e);
  3176. } else if (self.isPanning) {
  3177. self.onPan();
  3178. } else if (self.isZooming) {
  3179. self.onZoom();
  3180. }
  3181. }
  3182. };
  3183. Guestures.prototype.onSwipe = function (e) {
  3184. var self = this,
  3185. instance = self.instance,
  3186. swiping = self.isSwiping,
  3187. left = self.sliderStartPos.left || 0,
  3188. angle;
  3189. // If direction is not yet determined
  3190. if (swiping === true) {
  3191. // We need at least 10px distance to correctly calculate an angle
  3192. if (Math.abs(self.distance) > 10) {
  3193. self.canTap = false;
  3194. if (instance.group.length < 2 && self.opts.vertical) {
  3195. self.isSwiping = "y";
  3196. } else if (instance.isDragging || self.opts.vertical === false || (self.opts.vertical === "auto" && $(window).width() > 800)) {
  3197. self.isSwiping = "x";
  3198. } else {
  3199. angle = Math.abs((Math.atan2(self.distanceY, self.distanceX) * 180) / Math.PI);
  3200. self.isSwiping = angle > 45 && angle < 135 ? "y" : "x";
  3201. }
  3202. if (self.isSwiping === "y" && $.fancybox.isMobile && self.isScrollable) {
  3203. self.isScrolling = true;
  3204. return;
  3205. }
  3206. instance.isDragging = self.isSwiping;
  3207. // Reset points to avoid jumping, because we dropped first swipes to calculate the angle
  3208. self.startPoints = self.newPoints;
  3209. $.each(instance.slides, function (index, slide) {
  3210. var slidePos, stagePos;
  3211. $.fancybox.stop(slide.$slide);
  3212. slidePos = $.fancybox.getTranslate(slide.$slide);
  3213. stagePos = $.fancybox.getTranslate(instance.$refs.stage);
  3214. slide.$slide
  3215. .css({
  3216. transform: "",
  3217. opacity: "",
  3218. "transition-duration": ""
  3219. })
  3220. .removeClass("fancybox-animated")
  3221. .removeClass(function (index, className) {
  3222. return (className.match(/(^|\s)fancybox-fx-\S+/g) || []).join(" ");
  3223. });
  3224. if (slide.pos === instance.current.pos) {
  3225. self.sliderStartPos.top = slidePos.top - stagePos.top;
  3226. self.sliderStartPos.left = slidePos.left - stagePos.left;
  3227. }
  3228. $.fancybox.setTranslate(slide.$slide, {
  3229. top: slidePos.top - stagePos.top,
  3230. left: slidePos.left - stagePos.left
  3231. });
  3232. });
  3233. // Stop slideshow
  3234. if (instance.SlideShow && instance.SlideShow.isActive) {
  3235. instance.SlideShow.stop();
  3236. }
  3237. }
  3238. return;
  3239. }
  3240. // Sticky edges
  3241. if (swiping == "x") {
  3242. if (
  3243. self.distanceX > 0 &&
  3244. (self.instance.group.length < 2 || (self.instance.current.index === 0 && !self.instance.current.opts.loop))
  3245. ) {
  3246. left = left + Math.pow(self.distanceX, 0.8);
  3247. } else if (
  3248. self.distanceX < 0 &&
  3249. (self.instance.group.length < 2 ||
  3250. (self.instance.current.index === self.instance.group.length - 1 && !self.instance.current.opts.loop))
  3251. ) {
  3252. left = left - Math.pow(-self.distanceX, 0.8);
  3253. } else {
  3254. left = left + self.distanceX;
  3255. }
  3256. }
  3257. self.sliderLastPos = {
  3258. top: swiping == "x" ? 0 : self.sliderStartPos.top + self.distanceY,
  3259. left: left
  3260. };
  3261. if (self.requestId) {
  3262. cancelAFrame(self.requestId);
  3263. self.requestId = null;
  3264. }
  3265. self.requestId = requestAFrame(function () {
  3266. if (self.sliderLastPos) {
  3267. $.each(self.instance.slides, function (index, slide) {
  3268. var pos = slide.pos - self.instance.currPos;
  3269. $.fancybox.setTranslate(slide.$slide, {
  3270. top: self.sliderLastPos.top,
  3271. left: self.sliderLastPos.left + pos * self.canvasWidth + pos * slide.opts.gutter
  3272. });
  3273. });
  3274. self.$container.addClass("fancybox-is-sliding");
  3275. }
  3276. });
  3277. };
  3278. Guestures.prototype.onPan = function () {
  3279. var self = this;
  3280. // Prevent accidental movement (sometimes, when tapping casually, finger can move a bit)
  3281. if (distance(self.newPoints[0], self.realPoints[0]) < ($.fancybox.isMobile ? 10 : 5)) {
  3282. self.startPoints = self.newPoints;
  3283. return;
  3284. }
  3285. self.canTap = false;
  3286. self.contentLastPos = self.limitMovement();
  3287. if (self.requestId) {
  3288. cancelAFrame(self.requestId);
  3289. }
  3290. self.requestId = requestAFrame(function () {
  3291. $.fancybox.setTranslate(self.$content, self.contentLastPos);
  3292. });
  3293. };
  3294. // Make panning sticky to the edges
  3295. Guestures.prototype.limitMovement = function () {
  3296. var self = this;
  3297. var canvasWidth = self.canvasWidth;
  3298. var canvasHeight = self.canvasHeight;
  3299. var distanceX = self.distanceX;
  3300. var distanceY = self.distanceY;
  3301. var contentStartPos = self.contentStartPos;
  3302. var currentOffsetX = contentStartPos.left;
  3303. var currentOffsetY = contentStartPos.top;
  3304. var currentWidth = contentStartPos.width;
  3305. var currentHeight = contentStartPos.height;
  3306. var minTranslateX, minTranslateY, maxTranslateX, maxTranslateY, newOffsetX, newOffsetY;
  3307. if (currentWidth > canvasWidth) {
  3308. newOffsetX = currentOffsetX + distanceX;
  3309. } else {
  3310. newOffsetX = currentOffsetX;
  3311. }
  3312. newOffsetY = currentOffsetY + distanceY;
  3313. // Slow down proportionally to traveled distance
  3314. minTranslateX = Math.max(0, canvasWidth * 0.5 - currentWidth * 0.5);
  3315. minTranslateY = Math.max(0, canvasHeight * 0.5 - currentHeight * 0.5);
  3316. maxTranslateX = Math.min(canvasWidth - currentWidth, canvasWidth * 0.5 - currentWidth * 0.5);
  3317. maxTranslateY = Math.min(canvasHeight - currentHeight, canvasHeight * 0.5 - currentHeight * 0.5);
  3318. // ->
  3319. if (distanceX > 0 && newOffsetX > minTranslateX) {
  3320. newOffsetX = minTranslateX - 1 + Math.pow(-minTranslateX + currentOffsetX + distanceX, 0.8) || 0;
  3321. }
  3322. // <-
  3323. if (distanceX < 0 && newOffsetX < maxTranslateX) {
  3324. newOffsetX = maxTranslateX + 1 - Math.pow(maxTranslateX - currentOffsetX - distanceX, 0.8) || 0;
  3325. }
  3326. // \/
  3327. if (distanceY > 0 && newOffsetY > minTranslateY) {
  3328. newOffsetY = minTranslateY - 1 + Math.pow(-minTranslateY + currentOffsetY + distanceY, 0.8) || 0;
  3329. }
  3330. // /\
  3331. if (distanceY < 0 && newOffsetY < maxTranslateY) {
  3332. newOffsetY = maxTranslateY + 1 - Math.pow(maxTranslateY - currentOffsetY - distanceY, 0.8) || 0;
  3333. }
  3334. return {
  3335. top: newOffsetY,
  3336. left: newOffsetX
  3337. };
  3338. };
  3339. Guestures.prototype.limitPosition = function (newOffsetX, newOffsetY, newWidth, newHeight) {
  3340. var self = this;
  3341. var canvasWidth = self.canvasWidth;
  3342. var canvasHeight = self.canvasHeight;
  3343. if (newWidth > canvasWidth) {
  3344. newOffsetX = newOffsetX > 0 ? 0 : newOffsetX;
  3345. newOffsetX = newOffsetX < canvasWidth - newWidth ? canvasWidth - newWidth : newOffsetX;
  3346. } else {
  3347. // Center horizontally
  3348. newOffsetX = Math.max(0, canvasWidth / 2 - newWidth / 2);
  3349. }
  3350. if (newHeight > canvasHeight) {
  3351. newOffsetY = newOffsetY > 0 ? 0 : newOffsetY;
  3352. newOffsetY = newOffsetY < canvasHeight - newHeight ? canvasHeight - newHeight : newOffsetY;
  3353. } else {
  3354. // Center vertically
  3355. newOffsetY = Math.max(0, canvasHeight / 2 - newHeight / 2);
  3356. }
  3357. return {
  3358. top: newOffsetY,
  3359. left: newOffsetX
  3360. };
  3361. };
  3362. Guestures.prototype.onZoom = function () {
  3363. var self = this;
  3364. // Calculate current distance between points to get pinch ratio and new width and height
  3365. var contentStartPos = self.contentStartPos;
  3366. var currentWidth = contentStartPos.width;
  3367. var currentHeight = contentStartPos.height;
  3368. var currentOffsetX = contentStartPos.left;
  3369. var currentOffsetY = contentStartPos.top;
  3370. var endDistanceBetweenFingers = distance(self.newPoints[0], self.newPoints[1]);
  3371. var pinchRatio = endDistanceBetweenFingers / self.startDistanceBetweenFingers;
  3372. var newWidth = Math.floor(currentWidth * pinchRatio);
  3373. var newHeight = Math.floor(currentHeight * pinchRatio);
  3374. // This is the translation due to pinch-zooming
  3375. var translateFromZoomingX = (currentWidth - newWidth) * self.percentageOfImageAtPinchPointX;
  3376. var translateFromZoomingY = (currentHeight - newHeight) * self.percentageOfImageAtPinchPointY;
  3377. // Point between the two touches
  3378. var centerPointEndX = (self.newPoints[0].x + self.newPoints[1].x) / 2 - $(window).scrollLeft();
  3379. var centerPointEndY = (self.newPoints[0].y + self.newPoints[1].y) / 2 - $(window).scrollTop();
  3380. // And this is the translation due to translation of the centerpoint
  3381. // between the two fingers
  3382. var translateFromTranslatingX = centerPointEndX - self.centerPointStartX;
  3383. var translateFromTranslatingY = centerPointEndY - self.centerPointStartY;
  3384. // The new offset is the old/current one plus the total translation
  3385. var newOffsetX = currentOffsetX + (translateFromZoomingX + translateFromTranslatingX);
  3386. var newOffsetY = currentOffsetY + (translateFromZoomingY + translateFromTranslatingY);
  3387. var newPos = {
  3388. top: newOffsetY,
  3389. left: newOffsetX,
  3390. scaleX: pinchRatio,
  3391. scaleY: pinchRatio
  3392. };
  3393. self.canTap = false;
  3394. self.newWidth = newWidth;
  3395. self.newHeight = newHeight;
  3396. self.contentLastPos = newPos;
  3397. if (self.requestId) {
  3398. cancelAFrame(self.requestId);
  3399. }
  3400. self.requestId = requestAFrame(function () {
  3401. $.fancybox.setTranslate(self.$content, self.contentLastPos);
  3402. });
  3403. };
  3404. Guestures.prototype.ontouchend = function (e) {
  3405. var self = this;
  3406. var swiping = self.isSwiping;
  3407. var panning = self.isPanning;
  3408. var zooming = self.isZooming;
  3409. var scrolling = self.isScrolling;
  3410. self.endPoints = getPointerXY(e);
  3411. self.dMs = Math.max(new Date().getTime() - self.startTime, 1);
  3412. self.$container.removeClass("fancybox-is-grabbing");
  3413. $(document).off(".fb.touch");
  3414. document.removeEventListener("scroll", self.onscroll, true);
  3415. if (self.requestId) {
  3416. cancelAFrame(self.requestId);
  3417. self.requestId = null;
  3418. }
  3419. self.isSwiping = false;
  3420. self.isPanning = false;
  3421. self.isZooming = false;
  3422. self.isScrolling = false;
  3423. self.instance.isDragging = false;
  3424. if (self.canTap) {
  3425. return self.onTap(e);
  3426. }
  3427. self.speed = 100;
  3428. // Speed in px/ms
  3429. self.velocityX = (self.distanceX / self.dMs) * 0.5;
  3430. self.velocityY = (self.distanceY / self.dMs) * 0.5;
  3431. if (panning) {
  3432. self.endPanning();
  3433. } else if (zooming) {
  3434. self.endZooming();
  3435. } else {
  3436. self.endSwiping(swiping, scrolling);
  3437. }
  3438. return;
  3439. };
  3440. Guestures.prototype.endSwiping = function (swiping, scrolling) {
  3441. var self = this,
  3442. ret = false,
  3443. len = self.instance.group.length,
  3444. distanceX = Math.abs(self.distanceX),
  3445. canAdvance = swiping == "x" && len > 1 && ((self.dMs > 130 && distanceX > 10) || distanceX > 50),
  3446. speedX = 300;
  3447. self.sliderLastPos = null;
  3448. // Close if swiped vertically / navigate if horizontally
  3449. if (swiping == "y" && !scrolling && Math.abs(self.distanceY) > 50) {
  3450. // Continue vertical movement
  3451. $.fancybox.animate(
  3452. self.instance.current.$slide, {
  3453. top: self.sliderStartPos.top + self.distanceY + self.velocityY * 150,
  3454. opacity: 0
  3455. },
  3456. 200
  3457. );
  3458. ret = self.instance.close(true, 250);
  3459. } else if (canAdvance && self.distanceX > 0) {
  3460. ret = self.instance.previous(speedX);
  3461. } else if (canAdvance && self.distanceX < 0) {
  3462. ret = self.instance.next(speedX);
  3463. }
  3464. if (ret === false && (swiping == "x" || swiping == "y")) {
  3465. self.instance.centerSlide(200);
  3466. }
  3467. self.$container.removeClass("fancybox-is-sliding");
  3468. };
  3469. // Limit panning from edges
  3470. // ========================
  3471. Guestures.prototype.endPanning = function () {
  3472. var self = this,
  3473. newOffsetX,
  3474. newOffsetY,
  3475. newPos;
  3476. if (!self.contentLastPos) {
  3477. return;
  3478. }
  3479. if (self.opts.momentum === false || self.dMs > 350) {
  3480. newOffsetX = self.contentLastPos.left;
  3481. newOffsetY = self.contentLastPos.top;
  3482. } else {
  3483. // Continue movement
  3484. newOffsetX = self.contentLastPos.left + self.velocityX * 500;
  3485. newOffsetY = self.contentLastPos.top + self.velocityY * 500;
  3486. }
  3487. newPos = self.limitPosition(newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height);
  3488. newPos.width = self.contentStartPos.width;
  3489. newPos.height = self.contentStartPos.height;
  3490. $.fancybox.animate(self.$content, newPos, 366);
  3491. };
  3492. Guestures.prototype.endZooming = function () {
  3493. var self = this;
  3494. var current = self.instance.current;
  3495. var newOffsetX, newOffsetY, newPos, reset;
  3496. var newWidth = self.newWidth;
  3497. var newHeight = self.newHeight;
  3498. if (!self.contentLastPos) {
  3499. return;
  3500. }
  3501. newOffsetX = self.contentLastPos.left;
  3502. newOffsetY = self.contentLastPos.top;
  3503. reset = {
  3504. top: newOffsetY,
  3505. left: newOffsetX,
  3506. width: newWidth,
  3507. height: newHeight,
  3508. scaleX: 1,
  3509. scaleY: 1
  3510. };
  3511. // Reset scalex/scaleY values; this helps for perfomance and does not break animation
  3512. $.fancybox.setTranslate(self.$content, reset);
  3513. if (newWidth < self.canvasWidth && newHeight < self.canvasHeight) {
  3514. self.instance.scaleToFit(150);
  3515. } else if (newWidth > current.width || newHeight > current.height) {
  3516. self.instance.scaleToActual(self.centerPointStartX, self.centerPointStartY, 150);
  3517. } else {
  3518. newPos = self.limitPosition(newOffsetX, newOffsetY, newWidth, newHeight);
  3519. $.fancybox.animate(self.$content, newPos, 150);
  3520. }
  3521. };
  3522. Guestures.prototype.onTap = function (e) {
  3523. var self = this;
  3524. var $target = $(e.target);
  3525. var instance = self.instance;
  3526. var current = instance.current;
  3527. var endPoints = (e && getPointerXY(e)) || self.startPoints;
  3528. var tapX = endPoints[0] ? endPoints[0].x - $(window).scrollLeft() - self.stagePos.left : 0;
  3529. var tapY = endPoints[0] ? endPoints[0].y - $(window).scrollTop() - self.stagePos.top : 0;
  3530. var where;
  3531. var process = function (prefix) {
  3532. var action = current.opts[prefix];
  3533. if ($.isFunction(action)) {
  3534. action = action.apply(instance, [current, e]);
  3535. }
  3536. if (!action) {
  3537. return;
  3538. }
  3539. switch (action) {
  3540. case "close":
  3541. instance.close(self.startEvent);
  3542. break;
  3543. case "toggleControls":
  3544. instance.toggleControls();
  3545. break;
  3546. case "next":
  3547. instance.next();
  3548. break;
  3549. case "nextOrClose":
  3550. if (instance.group.length > 1) {
  3551. instance.next();
  3552. } else {
  3553. instance.close(self.startEvent);
  3554. }
  3555. break;
  3556. case "zoom":
  3557. if (current.type == "image" && (current.isLoaded || current.$ghost)) {
  3558. if (instance.canPan()) {
  3559. instance.scaleToFit();
  3560. } else if (instance.isScaledDown()) {
  3561. instance.scaleToActual(tapX, tapY);
  3562. } else if (instance.group.length < 2) {
  3563. instance.close(self.startEvent);
  3564. }
  3565. }
  3566. break;
  3567. }
  3568. };
  3569. // Ignore right click
  3570. if (e.originalEvent && e.originalEvent.button == 2) {
  3571. return;
  3572. }
  3573. // Skip if clicked on the scrollbar
  3574. if (!$target.is("img") && tapX > $target[0].clientWidth + $target.offset().left) {
  3575. return;
  3576. }
  3577. // Check where is clicked
  3578. if ($target.is(".fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-container")) {
  3579. where = "Outside";
  3580. } else if ($target.is(".fancybox-slide")) {
  3581. where = "Slide";
  3582. } else if (
  3583. instance.current.$content &&
  3584. instance.current.$content
  3585. .find($target)
  3586. .addBack()
  3587. .filter($target).length
  3588. ) {
  3589. where = "Content";
  3590. } else {
  3591. return;
  3592. }
  3593. // Check if this is a double tap
  3594. if (self.tapped) {
  3595. // Stop previously created single tap
  3596. clearTimeout(self.tapped);
  3597. self.tapped = null;
  3598. // Skip if distance between taps is too big
  3599. if (Math.abs(tapX - self.tapX) > 50 || Math.abs(tapY - self.tapY) > 50) {
  3600. return this;
  3601. }
  3602. // OK, now we assume that this is a double-tap
  3603. process("dblclick" + where);
  3604. } else {
  3605. // Single tap will be processed if user has not clicked second time within 300ms
  3606. // or there is no need to wait for double-tap
  3607. self.tapX = tapX;
  3608. self.tapY = tapY;
  3609. if (current.opts["dblclick" + where] && current.opts["dblclick" + where] !== current.opts["click" + where]) {
  3610. self.tapped = setTimeout(function () {
  3611. self.tapped = null;
  3612. if (!instance.isAnimating) {
  3613. process("click" + where);
  3614. }
  3615. }, 500);
  3616. } else {
  3617. process("click" + where);
  3618. }
  3619. }
  3620. return this;
  3621. };
  3622. $(document)
  3623. .on("onActivate.fb", function (e, instance) {
  3624. if (instance && !instance.Guestures) {
  3625. instance.Guestures = new Guestures(instance);
  3626. }
  3627. })
  3628. .on("beforeClose.fb", function (e, instance) {
  3629. if (instance && instance.Guestures) {
  3630. instance.Guestures.destroy();
  3631. }
  3632. });
  3633. })(window, document, jQuery);
  3634. // ==========================================================================
  3635. //
  3636. // SlideShow
  3637. // Enables slideshow functionality
  3638. //
  3639. // Example of usage:
  3640. // $.fancybox.getInstance().SlideShow.start()
  3641. //
  3642. // ==========================================================================
  3643. (function (document, $) {
  3644. "use strict";
  3645. $.extend(true, $.fancybox.defaults, {
  3646. btnTpl: {
  3647. slideShow: '<button data-fancybox-play class="fancybox-button fancybox-button--play" title="{{PLAY_START}}">' +
  3648. '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.5 5.4v13.2l11-6.6z"/></svg>' +
  3649. '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M8.33 5.75h2.2v12.5h-2.2V5.75zm5.15 0h2.2v12.5h-2.2V5.75z"/></svg>' +
  3650. "</button>"
  3651. },
  3652. slideShow: {
  3653. autoStart: false,
  3654. speed: 3000,
  3655. progress: true
  3656. }
  3657. });
  3658. var SlideShow = function (instance) {
  3659. this.instance = instance;
  3660. this.init();
  3661. };
  3662. $.extend(SlideShow.prototype, {
  3663. timer: null,
  3664. isActive: false,
  3665. $button: null,
  3666. init: function () {
  3667. var self = this,
  3668. instance = self.instance,
  3669. opts = instance.group[instance.currIndex].opts.slideShow;
  3670. self.$button = instance.$refs.toolbar.find("[data-fancybox-play]").on("click", function () {
  3671. self.toggle();
  3672. });
  3673. if (instance.group.length < 2 || !opts) {
  3674. self.$button.hide();
  3675. } else if (opts.progress) {
  3676. self.$progress = $('<div class="fancybox-progress"></div>').appendTo(instance.$refs.inner);
  3677. }
  3678. },
  3679. set: function (force) {
  3680. var self = this,
  3681. instance = self.instance,
  3682. current = instance.current;
  3683. // Check if reached last element
  3684. if (current && (force === true || current.opts.loop || instance.currIndex < instance.group.length - 1)) {
  3685. if (self.isActive && current.contentType !== "video") {
  3686. if (self.$progress) {
  3687. $.fancybox.animate(self.$progress.show(), {
  3688. scaleX: 1
  3689. }, current.opts.slideShow.speed);
  3690. }
  3691. self.timer = setTimeout(function () {
  3692. if (!instance.current.opts.loop && instance.current.index == instance.group.length - 1) {
  3693. instance.jumpTo(0);
  3694. } else {
  3695. instance.next();
  3696. }
  3697. }, current.opts.slideShow.speed);
  3698. }
  3699. } else {
  3700. self.stop();
  3701. instance.idleSecondsCounter = 0;
  3702. instance.showControls();
  3703. }
  3704. },
  3705. clear: function () {
  3706. var self = this;
  3707. clearTimeout(self.timer);
  3708. self.timer = null;
  3709. if (self.$progress) {
  3710. self.$progress.removeAttr("style").hide();
  3711. }
  3712. },
  3713. start: function () {
  3714. var self = this,
  3715. current = self.instance.current;
  3716. if (current) {
  3717. self.$button
  3718. .attr("title", (current.opts.i18n[current.opts.lang] || current.opts.i18n.en).PLAY_STOP)
  3719. .removeClass("fancybox-button--play")
  3720. .addClass("fancybox-button--pause");
  3721. self.isActive = true;
  3722. if (current.isComplete) {
  3723. self.set(true);
  3724. }
  3725. self.instance.trigger("onSlideShowChange", true);
  3726. }
  3727. },
  3728. stop: function () {
  3729. var self = this,
  3730. current = self.instance.current;
  3731. self.clear();
  3732. self.$button
  3733. .attr("title", (current.opts.i18n[current.opts.lang] || current.opts.i18n.en).PLAY_START)
  3734. .removeClass("fancybox-button--pause")
  3735. .addClass("fancybox-button--play");
  3736. self.isActive = false;
  3737. self.instance.trigger("onSlideShowChange", false);
  3738. if (self.$progress) {
  3739. self.$progress.removeAttr("style").hide();
  3740. }
  3741. },
  3742. toggle: function () {
  3743. var self = this;
  3744. if (self.isActive) {
  3745. self.stop();
  3746. } else {
  3747. self.start();
  3748. }
  3749. }
  3750. });
  3751. $(document).on({
  3752. "onInit.fb": function (e, instance) {
  3753. if (instance && !instance.SlideShow) {
  3754. instance.SlideShow = new SlideShow(instance);
  3755. }
  3756. },
  3757. "beforeShow.fb": function (e, instance, current, firstRun) {
  3758. var SlideShow = instance && instance.SlideShow;
  3759. if (firstRun) {
  3760. if (SlideShow && current.opts.slideShow.autoStart) {
  3761. SlideShow.start();
  3762. }
  3763. } else if (SlideShow && SlideShow.isActive) {
  3764. SlideShow.clear();
  3765. }
  3766. },
  3767. "afterShow.fb": function (e, instance, current) {
  3768. var SlideShow = instance && instance.SlideShow;
  3769. if (SlideShow && SlideShow.isActive) {
  3770. SlideShow.set();
  3771. }
  3772. },
  3773. "afterKeydown.fb": function (e, instance, current, keypress, keycode) {
  3774. var SlideShow = instance && instance.SlideShow;
  3775. // "P" or Spacebar
  3776. if (SlideShow && current.opts.slideShow && (keycode === 80 || keycode === 32) && !$(document.activeElement).is("button,a,input")) {
  3777. keypress.preventDefault();
  3778. SlideShow.toggle();
  3779. }
  3780. },
  3781. "beforeClose.fb onDeactivate.fb": function (e, instance) {
  3782. var SlideShow = instance && instance.SlideShow;
  3783. if (SlideShow) {
  3784. SlideShow.stop();
  3785. }
  3786. }
  3787. });
  3788. // Page Visibility API to pause slideshow when window is not active
  3789. $(document).on("visibilitychange", function () {
  3790. var instance = $.fancybox.getInstance(),
  3791. SlideShow = instance && instance.SlideShow;
  3792. if (SlideShow && SlideShow.isActive) {
  3793. if (document.hidden) {
  3794. SlideShow.clear();
  3795. } else {
  3796. SlideShow.set();
  3797. }
  3798. }
  3799. });
  3800. })(document, jQuery);
  3801. // ==========================================================================
  3802. //
  3803. // FullScreen
  3804. // Adds fullscreen functionality
  3805. //
  3806. // ==========================================================================
  3807. (function (document, $) {
  3808. "use strict";
  3809. // Collection of methods supported by user browser
  3810. var fn = (function () {
  3811. var fnMap = [
  3812. ["requestFullscreen", "exitFullscreen", "fullscreenElement", "fullscreenEnabled", "fullscreenchange", "fullscreenerror"],
  3813. // new WebKit
  3814. [
  3815. "webkitRequestFullscreen",
  3816. "webkitExitFullscreen",
  3817. "webkitFullscreenElement",
  3818. "webkitFullscreenEnabled",
  3819. "webkitfullscreenchange",
  3820. "webkitfullscreenerror"
  3821. ],
  3822. // old WebKit (Safari 5.1)
  3823. [
  3824. "webkitRequestFullScreen",
  3825. "webkitCancelFullScreen",
  3826. "webkitCurrentFullScreenElement",
  3827. "webkitCancelFullScreen",
  3828. "webkitfullscreenchange",
  3829. "webkitfullscreenerror"
  3830. ],
  3831. [
  3832. "mozRequestFullScreen",
  3833. "mozCancelFullScreen",
  3834. "mozFullScreenElement",
  3835. "mozFullScreenEnabled",
  3836. "mozfullscreenchange",
  3837. "mozfullscreenerror"
  3838. ],
  3839. ["msRequestFullscreen", "msExitFullscreen", "msFullscreenElement", "msFullscreenEnabled", "MSFullscreenChange", "MSFullscreenError"]
  3840. ];
  3841. var ret = {};
  3842. for (var i = 0; i < fnMap.length; i++) {
  3843. var val = fnMap[i];
  3844. if (val && val[1] in document) {
  3845. for (var j = 0; j < val.length; j++) {
  3846. ret[fnMap[0][j]] = val[j];
  3847. }
  3848. return ret;
  3849. }
  3850. }
  3851. return false;
  3852. })();
  3853. if (fn) {
  3854. var FullScreen = {
  3855. request: function (elem) {
  3856. elem = elem || document.documentElement;
  3857. elem[fn.requestFullscreen](elem.ALLOW_KEYBOARD_INPUT);
  3858. },
  3859. exit: function () {
  3860. document[fn.exitFullscreen]();
  3861. },
  3862. toggle: function (elem) {
  3863. elem = elem || document.documentElement;
  3864. if (this.isFullscreen()) {
  3865. this.exit();
  3866. } else {
  3867. this.request(elem);
  3868. }
  3869. },
  3870. isFullscreen: function () {
  3871. return Boolean(document[fn.fullscreenElement]);
  3872. },
  3873. enabled: function () {
  3874. return Boolean(document[fn.fullscreenEnabled]);
  3875. }
  3876. };
  3877. $.extend(true, $.fancybox.defaults, {
  3878. btnTpl: {
  3879. fullScreen: '<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fsenter" title="{{FULL_SCREEN}}">' +
  3880. '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>' +
  3881. '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5 16h3v3h2v-5H5zm3-8H5v2h5V5H8zm6 11h2v-3h3v-2h-5zm2-11V5h-2v5h5V8z"/></svg>' +
  3882. "</button>"
  3883. },
  3884. fullScreen: {
  3885. autoStart: false
  3886. }
  3887. });
  3888. $(document).on(fn.fullscreenchange, function () {
  3889. var isFullscreen = FullScreen.isFullscreen(),
  3890. instance = $.fancybox.getInstance();
  3891. if (instance) {
  3892. // If image is zooming, then force to stop and reposition properly
  3893. if (instance.current && instance.current.type === "image" && instance.isAnimating) {
  3894. instance.isAnimating = false;
  3895. instance.update(true, true, 0);
  3896. if (!instance.isComplete) {
  3897. instance.complete();
  3898. }
  3899. }
  3900. instance.trigger("onFullscreenChange", isFullscreen);
  3901. instance.$refs.container.toggleClass("fancybox-is-fullscreen", isFullscreen);
  3902. instance.$refs.toolbar
  3903. .find("[data-fancybox-fullscreen]")
  3904. .toggleClass("fancybox-button--fsenter", !isFullscreen)
  3905. .toggleClass("fancybox-button--fsexit", isFullscreen);
  3906. }
  3907. });
  3908. }
  3909. $(document).on({
  3910. "onInit.fb": function (e, instance) {
  3911. var $container;
  3912. if (!fn) {
  3913. instance.$refs.toolbar.find("[data-fancybox-fullscreen]").remove();
  3914. return;
  3915. }
  3916. if (instance && instance.group[instance.currIndex].opts.fullScreen) {
  3917. $container = instance.$refs.container;
  3918. $container.on("click.fb-fullscreen", "[data-fancybox-fullscreen]", function (e) {
  3919. e.stopPropagation();
  3920. e.preventDefault();
  3921. FullScreen.toggle();
  3922. });
  3923. if (instance.opts.fullScreen && instance.opts.fullScreen.autoStart === true) {
  3924. FullScreen.request();
  3925. }
  3926. // Expose API
  3927. instance.FullScreen = FullScreen;
  3928. } else if (instance) {
  3929. instance.$refs.toolbar.find("[data-fancybox-fullscreen]").hide();
  3930. }
  3931. },
  3932. "afterKeydown.fb": function (e, instance, current, keypress, keycode) {
  3933. // "F"
  3934. if (instance && instance.FullScreen && keycode === 70) {
  3935. keypress.preventDefault();
  3936. instance.FullScreen.toggle();
  3937. }
  3938. },
  3939. "beforeClose.fb": function (e, instance) {
  3940. if (instance && instance.FullScreen && instance.$refs.container.hasClass("fancybox-is-fullscreen")) {
  3941. FullScreen.exit();
  3942. }
  3943. }
  3944. });
  3945. })(document, jQuery);
  3946. // ==========================================================================
  3947. //
  3948. // Thumbs
  3949. // Displays thumbnails in a grid
  3950. //
  3951. // ==========================================================================
  3952. (function (document, $) {
  3953. "use strict";
  3954. var CLASS = "fancybox-thumbs",
  3955. CLASS_ACTIVE = CLASS + "-active";
  3956. // Make sure there are default values
  3957. $.fancybox.defaults = $.extend(
  3958. true, {
  3959. btnTpl: {
  3960. thumbs: '<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="{{THUMBS}}">' +
  3961. '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14.59 14.59h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76H5.65v-3.76zm8.94-4.47h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76H5.65v-3.76zm8.94-4.47h3.76v3.76h-3.76V5.65zm-4.47 0h3.76v3.76h-3.76V5.65zm-4.47 0h3.76v3.76H5.65V5.65z"/></svg>' +
  3962. "</button>"
  3963. },
  3964. thumbs: {
  3965. autoStart: false, // Display thumbnails on opening
  3966. hideOnClose: true, // Hide thumbnail grid when closing animation starts
  3967. parentEl: ".fancybox-container", // Container is injected into this element
  3968. axis: "y" // Vertical (y) or horizontal (x) scrolling
  3969. }
  3970. },
  3971. $.fancybox.defaults
  3972. );
  3973. var FancyThumbs = function (instance) {
  3974. this.init(instance);
  3975. };
  3976. $.extend(FancyThumbs.prototype, {
  3977. $button: null,
  3978. $grid: null,
  3979. $list: null,
  3980. isVisible: false,
  3981. isActive: false,
  3982. init: function (instance) {
  3983. var self = this,
  3984. group = instance.group,
  3985. enabled = 0;
  3986. self.instance = instance;
  3987. self.opts = group[instance.currIndex].opts.thumbs;
  3988. instance.Thumbs = self;
  3989. self.$button = instance.$refs.toolbar.find("[data-fancybox-thumbs]");
  3990. // Enable thumbs if at least two group items have thumbnails
  3991. for (var i = 0, len = group.length; i < len; i++) {
  3992. if (group[i].thumb) {
  3993. enabled++;
  3994. }
  3995. if (enabled > 1) {
  3996. break;
  3997. }
  3998. }
  3999. if (enabled > 1 && !!self.opts) {
  4000. self.$button.removeAttr("style").on("click", function () {
  4001. self.toggle();
  4002. });
  4003. self.isActive = true;
  4004. } else {
  4005. self.$button.hide();
  4006. }
  4007. },
  4008. create: function () {
  4009. var self = this,
  4010. instance = self.instance,
  4011. parentEl = self.opts.parentEl,
  4012. list = [],
  4013. src;
  4014. if (!self.$grid) {
  4015. // Create main element
  4016. self.$grid = $('<div class="' + CLASS + " " + CLASS + "-" + self.opts.axis + '"></div>').appendTo(
  4017. instance.$refs.container
  4018. .find(parentEl)
  4019. .addBack()
  4020. .filter(parentEl)
  4021. );
  4022. // Add "click" event that performs gallery navigation
  4023. self.$grid.on("click", "a", function () {
  4024. instance.jumpTo($(this).attr("data-index"));
  4025. });
  4026. }
  4027. // Build the list
  4028. if (!self.$list) {
  4029. self.$list = $('<div class="' + CLASS + '__list">').appendTo(self.$grid);
  4030. }
  4031. $.each(instance.group, function (i, item) {
  4032. src = item.thumb;
  4033. if (!src && item.type === "image") {
  4034. src = item.src;
  4035. }
  4036. list.push(
  4037. '<a href="javascript:;" tabindex="0" data-index="' +
  4038. i +
  4039. '"' +
  4040. (src && src.length ? ' style="background-image:url(' + src + ')"' : 'class="fancybox-thumbs-missing"') +
  4041. "></a>"
  4042. );
  4043. });
  4044. self.$list[0].innerHTML = list.join("");
  4045. if (self.opts.axis === "x") {
  4046. // Set fixed width for list element to enable horizontal scrolling
  4047. self.$list.width(
  4048. parseInt(self.$grid.css("padding-right"), 10) +
  4049. instance.group.length *
  4050. self.$list
  4051. .children()
  4052. .eq(0)
  4053. .outerWidth(true)
  4054. );
  4055. }
  4056. },
  4057. focus: function (duration) {
  4058. var self = this,
  4059. $list = self.$list,
  4060. $grid = self.$grid,
  4061. thumb,
  4062. thumbPos;
  4063. if (!self.instance.current) {
  4064. return;
  4065. }
  4066. thumb = $list
  4067. .children()
  4068. .removeClass(CLASS_ACTIVE)
  4069. .filter('[data-index="' + self.instance.current.index + '"]')
  4070. .addClass(CLASS_ACTIVE);
  4071. thumbPos = thumb.position();
  4072. // Check if need to scroll to make current thumb visible
  4073. if (self.opts.axis === "y" && (thumbPos.top < 0 || thumbPos.top > $list.height() - thumb.outerHeight())) {
  4074. $list.stop().animate({
  4075. scrollTop: $list.scrollTop() + thumbPos.top
  4076. },
  4077. duration
  4078. );
  4079. } else if (
  4080. self.opts.axis === "x" &&
  4081. (thumbPos.left < $grid.scrollLeft() || thumbPos.left > $grid.scrollLeft() + ($grid.width() - thumb.outerWidth()))
  4082. ) {
  4083. $list
  4084. .parent()
  4085. .stop()
  4086. .animate({
  4087. scrollLeft: thumbPos.left
  4088. },
  4089. duration
  4090. );
  4091. }
  4092. },
  4093. update: function () {
  4094. var that = this;
  4095. that.instance.$refs.container.toggleClass("fancybox-show-thumbs", this.isVisible);
  4096. if (that.isVisible) {
  4097. if (!that.$grid) {
  4098. that.create();
  4099. }
  4100. that.instance.trigger("onThumbsShow");
  4101. that.focus(0);
  4102. } else if (that.$grid) {
  4103. that.instance.trigger("onThumbsHide");
  4104. }
  4105. // Update content position
  4106. that.instance.update();
  4107. },
  4108. hide: function () {
  4109. this.isVisible = false;
  4110. this.update();
  4111. },
  4112. show: function () {
  4113. this.isVisible = true;
  4114. this.update();
  4115. },
  4116. toggle: function () {
  4117. this.isVisible = !this.isVisible;
  4118. this.update();
  4119. }
  4120. });
  4121. $(document).on({
  4122. "onInit.fb": function (e, instance) {
  4123. var Thumbs;
  4124. if (instance && !instance.Thumbs) {
  4125. Thumbs = new FancyThumbs(instance);
  4126. if (Thumbs.isActive && Thumbs.opts.autoStart === true) {
  4127. Thumbs.show();
  4128. }
  4129. }
  4130. },
  4131. "beforeShow.fb": function (e, instance, item, firstRun) {
  4132. var Thumbs = instance && instance.Thumbs;
  4133. if (Thumbs && Thumbs.isVisible) {
  4134. Thumbs.focus(firstRun ? 0 : 250);
  4135. }
  4136. },
  4137. "afterKeydown.fb": function (e, instance, current, keypress, keycode) {
  4138. var Thumbs = instance && instance.Thumbs;
  4139. // "G"
  4140. if (Thumbs && Thumbs.isActive && keycode === 71) {
  4141. keypress.preventDefault();
  4142. Thumbs.toggle();
  4143. }
  4144. },
  4145. "beforeClose.fb": function (e, instance) {
  4146. var Thumbs = instance && instance.Thumbs;
  4147. if (Thumbs && Thumbs.isVisible && Thumbs.opts.hideOnClose !== false) {
  4148. Thumbs.$grid.hide();
  4149. }
  4150. }
  4151. });
  4152. })(document, jQuery);
  4153. //// ==========================================================================
  4154. //
  4155. // Share
  4156. // Displays simple form for sharing current url
  4157. //
  4158. // ==========================================================================
  4159. (function (document, $) {
  4160. "use strict";
  4161. $.extend(true, $.fancybox.defaults, {
  4162. btnTpl: {
  4163. share: '<button data-fancybox-share class="fancybox-button fancybox-button--share" title="{{SHARE}}">' +
  4164. '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M2.55 19c1.4-8.4 9.1-9.8 11.9-9.8V5l7 7-7 6.3v-3.5c-2.8 0-10.5 2.1-11.9 4.2z"/></svg>' +
  4165. "</button>"
  4166. },
  4167. share: {
  4168. url: function (instance, item) {
  4169. return (
  4170. (!instance.currentHash && !(item.type === "inline" || item.type === "html") ? item.origSrc || item.src : false) || window.location
  4171. );
  4172. },
  4173. tpl: '<div class="fancybox-share">' +
  4174. "<h1>{{SHARE}}</h1>" +
  4175. "<p>" +
  4176. '<a class="fancybox-share__button fancybox-share__button--fb" href="https://www.facebook.com/sharer/sharer.php?u={{url}}">' +
  4177. '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m287 456v-299c0-21 6-35 35-35h38v-63c-7-1-29-3-55-3-54 0-91 33-91 94v306m143-254h-205v72h196" /></svg>' +
  4178. "<span>Facebook</span>" +
  4179. "</a>" +
  4180. '<a class="fancybox-share__button fancybox-share__button--tw" href="https://twitter.com/intent/tweet?url={{url}}&text={{descr}}">' +
  4181. '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m456 133c-14 7-31 11-47 13 17-10 30-27 37-46-15 10-34 16-52 20-61-62-157-7-141 75-68-3-129-35-169-85-22 37-11 86 26 109-13 0-26-4-37-9 0 39 28 72 65 80-12 3-25 4-37 2 10 33 41 57 77 57-42 30-77 38-122 34 170 111 378-32 359-208 16-11 30-25 41-42z" /></svg>' +
  4182. "<span>Twitter</span>" +
  4183. "</a>" +
  4184. '<a class="fancybox-share__button fancybox-share__button--pt" href="https://www.pinterest.com/pin/create/button/?url={{url}}&description={{descr}}&media={{media}}">' +
  4185. '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m265 56c-109 0-164 78-164 144 0 39 15 74 47 87 5 2 10 0 12-5l4-19c2-6 1-8-3-13-9-11-15-25-15-45 0-58 43-110 113-110 62 0 96 38 96 88 0 67-30 122-73 122-24 0-42-19-36-44 6-29 20-60 20-81 0-19-10-35-31-35-25 0-44 26-44 60 0 21 7 36 7 36l-30 125c-8 37-1 83 0 87 0 3 4 4 5 2 2-3 32-39 42-75l16-64c8 16 31 29 56 29 74 0 124-67 124-157 0-69-58-132-146-132z" fill="#fff"/></svg>' +
  4186. "<span>Pinterest</span>" +
  4187. "</a>" +
  4188. "</p>" +
  4189. '<p><input class="fancybox-share__input" type="text" value="{{url_raw}}" onclick="select()" /></p>' +
  4190. "</div>"
  4191. }
  4192. });
  4193. function escapeHtml(string) {
  4194. var entityMap = {
  4195. "&": "&amp;",
  4196. "<": "&lt;",
  4197. ">": "&gt;",
  4198. '"': "&quot;",
  4199. "'": "&#39;",
  4200. "/": "&#x2F;",
  4201. "`": "&#x60;",
  4202. "=": "&#x3D;"
  4203. };
  4204. return String(string).replace(/[&<>"'`=\/]/g, function (s) {
  4205. return entityMap[s];
  4206. });
  4207. }
  4208. $(document).on("click", "[data-fancybox-share]", function () {
  4209. var instance = $.fancybox.getInstance(),
  4210. current = instance.current || null,
  4211. url,
  4212. tpl;
  4213. if (!current) {
  4214. return;
  4215. }
  4216. if ($.type(current.opts.share.url) === "function") {
  4217. url = current.opts.share.url.apply(current, [instance, current]);
  4218. }
  4219. tpl = current.opts.share.tpl
  4220. .replace(/\{\{media\}\}/g, current.type === "image" ? encodeURIComponent(current.src) : "")
  4221. .replace(/\{\{url\}\}/g, encodeURIComponent(url))
  4222. .replace(/\{\{url_raw\}\}/g, escapeHtml(url))
  4223. .replace(/\{\{descr\}\}/g, instance.$caption ? encodeURIComponent(instance.$caption.text()) : "");
  4224. $.fancybox.open({
  4225. src: instance.translate(instance, tpl),
  4226. type: "html",
  4227. opts: {
  4228. touch: false,
  4229. animationEffect: false,
  4230. afterLoad: function (shareInstance, shareCurrent) {
  4231. // Close self if parent instance is closing
  4232. instance.$refs.container.one("beforeClose.fb", function () {
  4233. shareInstance.close(null, 0);
  4234. });
  4235. // Opening links in a popup window
  4236. shareCurrent.$content.find(".fancybox-share__button").click(function () {
  4237. window.open(this.href, "Share", "width=550, height=450");
  4238. return false;
  4239. });
  4240. },
  4241. mobile: {
  4242. autoFocus: false
  4243. }
  4244. }
  4245. });
  4246. });
  4247. })(document, jQuery);
  4248. // ==========================================================================
  4249. //
  4250. // Hash
  4251. // Enables linking to each modal
  4252. //
  4253. // ==========================================================================
  4254. (function (window, document, $) {
  4255. "use strict";
  4256. // Simple $.escapeSelector polyfill (for jQuery prior v3)
  4257. if (!$.escapeSelector) {
  4258. $.escapeSelector = function (sel) {
  4259. var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;
  4260. var fcssescape = function (ch, asCodePoint) {
  4261. if (asCodePoint) {
  4262. // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
  4263. if (ch === "\0") {
  4264. return "\uFFFD";
  4265. }
  4266. // Control characters and (dependent upon position) numbers get escaped as code points
  4267. return ch.slice(0, -1) + "\\" + ch.charCodeAt(ch.length - 1).toString(16) + " ";
  4268. }
  4269. // Other potentially-special ASCII characters get backslash-escaped
  4270. return "\\" + ch;
  4271. };
  4272. return (sel + "").replace(rcssescape, fcssescape);
  4273. };
  4274. }
  4275. // Get info about gallery name and current index from url
  4276. function parseUrl() {
  4277. var hash = window.location.hash.substr(1),
  4278. rez = hash.split("-"),
  4279. index = rez.length > 1 && /^\+?\d+$/.test(rez[rez.length - 1]) ? parseInt(rez.pop(-1), 10) || 1 : 1,
  4280. gallery = rez.join("-");
  4281. return {
  4282. hash: hash,
  4283. /* Index is starting from 1 */
  4284. index: index < 1 ? 1 : index,
  4285. gallery: gallery
  4286. };
  4287. }
  4288. // Trigger click evnt on links to open new fancyBox instance
  4289. function triggerFromUrl(url) {
  4290. if (url.gallery !== "") {
  4291. // If we can find element matching 'data-fancybox' atribute,
  4292. // then triggering click event should start fancyBox
  4293. $("[data-fancybox='" + $.escapeSelector(url.gallery) + "']")
  4294. .eq(url.index - 1)
  4295. .focus()
  4296. .trigger("click.fb-start");
  4297. }
  4298. }
  4299. // Get gallery name from current instance
  4300. function getGalleryID(instance) {
  4301. var opts, ret;
  4302. if (!instance) {
  4303. return false;
  4304. }
  4305. opts = instance.current ? instance.current.opts : instance.opts;
  4306. ret = opts.hash || (opts.$orig ? opts.$orig.data("fancybox") || opts.$orig.data("fancybox-trigger") : "");
  4307. return ret === "" ? false : ret;
  4308. }
  4309. // Start when DOM becomes ready
  4310. $(function () {
  4311. // Check if user has disabled this module
  4312. if ($.fancybox.defaults.hash === false) {
  4313. return;
  4314. }
  4315. // Update hash when opening/closing fancyBox
  4316. $(document).on({
  4317. "onInit.fb": function (e, instance) {
  4318. var url, gallery;
  4319. if (instance.group[instance.currIndex].opts.hash === false) {
  4320. return;
  4321. }
  4322. url = parseUrl();
  4323. gallery = getGalleryID(instance);
  4324. // Make sure gallery start index matches index from hash
  4325. if (gallery && url.gallery && gallery == url.gallery) {
  4326. instance.currIndex = url.index - 1;
  4327. }
  4328. },
  4329. "beforeShow.fb": function (e, instance, current, firstRun) {
  4330. var gallery;
  4331. if (!current || current.opts.hash === false) {
  4332. return;
  4333. }
  4334. // Check if need to update window hash
  4335. gallery = getGalleryID(instance);
  4336. if (!gallery) {
  4337. return;
  4338. }
  4339. // Variable containing last hash value set by fancyBox
  4340. // It will be used to determine if fancyBox needs to close after hash change is detected
  4341. instance.currentHash = gallery + (instance.group.length > 1 ? "-" + (current.index + 1) : "");
  4342. // If current hash is the same (this instance most likely is opened by hashchange), then do nothing
  4343. if (window.location.hash === "#" + instance.currentHash) {
  4344. return;
  4345. }
  4346. if (firstRun && !instance.origHash) {
  4347. instance.origHash = window.location.hash;
  4348. }
  4349. if (instance.hashTimer) {
  4350. clearTimeout(instance.hashTimer);
  4351. }
  4352. // Update hash
  4353. instance.hashTimer = setTimeout(function () {
  4354. if ("replaceState" in window.history) {
  4355. window.history[firstRun ? "pushState" : "replaceState"]({},
  4356. document.title,
  4357. window.location.pathname + window.location.search + "#" + instance.currentHash
  4358. );
  4359. if (firstRun) {
  4360. instance.hasCreatedHistory = true;
  4361. }
  4362. } else {
  4363. window.location.hash = instance.currentHash;
  4364. }
  4365. instance.hashTimer = null;
  4366. }, 300);
  4367. },
  4368. "beforeClose.fb": function (e, instance, current) {
  4369. if (!current || current.opts.hash === false) {
  4370. return;
  4371. }
  4372. clearTimeout(instance.hashTimer);
  4373. // Goto previous history entry
  4374. if (instance.currentHash && instance.hasCreatedHistory) {
  4375. window.history.back();
  4376. } else if (instance.currentHash) {
  4377. if ("replaceState" in window.history) {
  4378. window.history.replaceState({}, document.title, window.location.pathname + window.location.search + (instance.origHash || ""));
  4379. } else {
  4380. window.location.hash = instance.origHash;
  4381. }
  4382. }
  4383. instance.currentHash = null;
  4384. }
  4385. });
  4386. // Check if need to start/close after url has changed
  4387. $(window).on("hashchange.fb", function () {
  4388. var url = parseUrl(),
  4389. fb = null;
  4390. // Find last fancyBox instance that has "hash"
  4391. $.each(
  4392. $(".fancybox-container")
  4393. .get()
  4394. .reverse(),
  4395. function (index, value) {
  4396. var tmp = $(value).data("FancyBox");
  4397. if (tmp && tmp.currentHash) {
  4398. fb = tmp;
  4399. return false;
  4400. }
  4401. }
  4402. );
  4403. if (fb) {
  4404. // Now, compare hash values
  4405. if (fb.currentHash !== url.gallery + "-" + url.index && !(url.index === 1 && fb.currentHash == url.gallery)) {
  4406. fb.currentHash = null;
  4407. fb.close();
  4408. }
  4409. } else if (url.gallery !== "") {
  4410. triggerFromUrl(url);
  4411. }
  4412. });
  4413. // Check current hash and trigger click event on matching element to start fancyBox, if needed
  4414. setTimeout(function () {
  4415. if (!$.fancybox.getInstance()) {
  4416. triggerFromUrl(parseUrl());
  4417. }
  4418. }, 50);
  4419. });
  4420. })(window, document, jQuery);
  4421. // ==========================================================================
  4422. //
  4423. // Wheel
  4424. // Basic mouse weheel support for gallery navigation
  4425. //
  4426. // ==========================================================================
  4427. (function (document, $) {
  4428. "use strict";
  4429. var prevTime = new Date().getTime();
  4430. $(document).on({
  4431. "onInit.fb": function (e, instance, current) {
  4432. instance.$refs.stage.on("mousewheel DOMMouseScroll wheel MozMousePixelScroll", function (e) {
  4433. var current = instance.current,
  4434. currTime = new Date().getTime();
  4435. if (instance.group.length < 2 || current.opts.wheel === false || (current.opts.wheel === "auto" && current.type !== "image")) {
  4436. return;
  4437. }
  4438. e.preventDefault();
  4439. e.stopPropagation();
  4440. if (current.$slide.hasClass("fancybox-animated")) {
  4441. return;
  4442. }
  4443. e = e.originalEvent || e;
  4444. if (currTime - prevTime < 250) {
  4445. return;
  4446. }
  4447. prevTime = currTime;
  4448. instance[(-e.deltaY || -e.deltaX || e.wheelDelta || -e.detail) < 0 ? "next" : "previous"]();
  4449. });
  4450. }
  4451. });
  4452. })(document, jQuery);