· 5 years ago · Sep 22, 2020, 01:42 AM
1// ==UserScript==
2// @name QC Suite
3// @namespace http://tampermonkey.net/
4// @version 3.5
5// @description Quickly upload QC to imgur, view qc in toaboa.
6// @author AColdFloor
7// @include /^https?:\/\/((?:item|2)\.taobao|detail\.tmall)\.com\/(item|meal_detail)\.htm\?/
8// @include /^https?:\/\/((?:item|2)\.taobao|detail\.tmall)\.com\/(item|meal_detail)\.html\?/
9// @include /^https?:\/\/world.(?:taobao|tmall).com/item/\d+.htm/
10// @include /^https?:\/\/world.(?:taobao|tmall).com/item/\d+.html/
11// @include https://www.superbuy.com/order*
12// @include https://www.wegobuy.com/order*
13// @require http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js
14// @require http://cdnjs.cloudflare.com/ajax/libs/sugar/1.3/sugar.min.js
15// @grant GM_setClipboard
16// @grant GM_openInTab
17// @grant GM_addStyle
18// @run-at document-end
19// @icon https://www.superbuy.com/favicon.ico
20// ==/UserScript==
21
22
23/*
24 * jQuery MD5 Plugin 1.2.1
25 * https://github.com/blueimp/jQuery-MD5
26 *
27 * Copyright 2010, Sebastian Tschan
28 * https://blueimp.net
29 *
30 * Licensed under the MIT license:
31 * http://creativecommons.org/licenses/MIT/
32 *
33 * Based on
34 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
35 * Digest Algorithm, as defined in RFC 1321.
36 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
37 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
38 * Distributed under the BSD License
39 * See http://pajhome.org.uk/crypt/md5 for more info.
40 */
41
42/*jslint bitwise: true */
43/*global unescape, jQuery */
44
45/*!
46* tingle.js
47* @author robin_parisi
48* @version 0.13.2
49* @url
50*/
51/*!
52* tingle.js
53* @author robin_parisi
54* @version 0.13.2
55* @url
56*/
57var _version = "3.5";
58
59var ClientIds = ["97a5f748b20b0ad","1fb6eded0178bb6","4d01890bba4949c","fe9f4d770f42e53","0999af252aaaba1","6870f32f716ff3b","bf02cd90c8a4f1a","5ed540f56122e1c"];
60var ActiveClientId = ClientIds[Math.floor(Math.random()*ClientIds.length)];
61
62/*! iFrame Resizer (iframeSizer.min.js ) - v4.2.10 - 2020-02-04
63 * Desc: Force cross domain iframes to size to content.
64 * Requires: iframeResizer.contentWindow.min.js to be loaded into the target frame.
65 * Copyright: (c) 2020 David J. Bradshaw - dave@bradshaw.net
66 * License: MIT
67 */
68
69!function(l){if("undefined"!=typeof window){var e,m=0,g=!1,o=!1,v="message".length,I="[iFrameSizer]",x=I.length,F=null,r=window.requestAnimationFrame,h={max:1,scroll:1,bodyScroll:1,documentElementScroll:1},M={},i=null,w={autoResize:!0,bodyBackground:null,bodyMargin:null,bodyMarginV1:8,bodyPadding:null,checkOrigin:!0,inPageLinks:!1,enablePublicMethods:!0,heightCalculationMethod:"bodyOffset",id:"iFrameResizer",interval:32,log:!1,maxHeight:1/0,maxWidth:1/0,minHeight:0,minWidth:0,resizeFrom:"parent",scrolling:!1,sizeHeight:!0,sizeWidth:!1,warningTimeout:5e3,tolerance:0,widthCalculationMethod:"scroll",onClose:function(){return!0},onClosed:function(){},onInit:function(){},onMessage:function(){E("onMessage function not defined")},onResized:function(){},onScroll:function(){return!0}},k={};window.jQuery&&((e=window.jQuery).fn?e.fn.iFrameResize||(e.fn.iFrameResize=function(i){return this.filter("iframe").each(function(e,n){d(n,i)}).end()}):T("","Unable to bind to jQuery, it is not fully loaded.")),"function"==typeof define&&define.amd?define([],q):"object"==typeof module&&"object"==typeof module.exports&&(module.exports=q()),window.iFrameResize=window.iFrameResize||q()}function p(){return window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver}function z(e,n,i){e.addEventListener(n,i,!1)}function O(e,n,i){e.removeEventListener(n,i,!1)}function a(e){return I+"["+function(e){var n="Host page: "+e;return window.top!==window.self&&(n=window.parentIFrame&&window.parentIFrame.getId?window.parentIFrame.getId()+": "+e:"Nested host page: "+e),n}(e)+"]"}function t(e){return M[e]?M[e].log:g}function R(e,n){s("log",e,n,t(e))}function T(e,n){s("info",e,n,t(e))}function E(e,n){s("warn",e,n,!0)}function s(e,n,i,t){!0===t&&"object"==typeof window.console&&console[e](a(n),i)}function n(n){function e(){i("Height"),i("Width"),A(function(){P(b),S(y),d("onResized",b)},b,"init")}function i(e){var n=Number(M[y]["max"+e]),i=Number(M[y]["min"+e]),t=e.toLowerCase(),o=Number(b[t]);R(y,"Checking "+t+" is in range "+i+"-"+n),o<i&&(o=i,R(y,"Set "+t+" to min value")),n<o&&(o=n,R(y,"Set "+t+" to max value")),b[t]=""+o}function t(e){return p.substr(p.indexOf(":")+v+e)}function a(e,n){!function(e,n,i){k[i]||(k[i]=setTimeout(function(){k[i]=null,e()},n))}(function(){B("Send Page Info","pageInfo:"+function(){var e=document.body.getBoundingClientRect(),n=b.iframe.getBoundingClientRect();return JSON.stringify({iframeHeight:n.height,iframeWidth:n.width,clientHeight:Math.max(document.documentElement.clientHeight,window.innerHeight||0),clientWidth:Math.max(document.documentElement.clientWidth,window.innerWidth||0),offsetTop:parseInt(n.top-e.top,10),offsetLeft:parseInt(n.left-e.left,10),scrollTop:window.pageYOffset,scrollLeft:window.pageXOffset,documentHeight:document.documentElement.clientHeight,documentWidth:document.documentElement.clientWidth,windowHeight:window.innerHeight,windowWidth:window.innerWidth})}(),e,n)},32,n)}function r(e){var n=e.getBoundingClientRect();return N(y),{x:Math.floor(Number(n.left)+Number(F.x)),y:Math.floor(Number(n.top)+Number(F.y))}}function o(e){var n=e?r(b.iframe):{x:0,y:0},i={x:Number(b.width)+n.x,y:Number(b.height)+n.y};R(y,"Reposition requested from iFrame (offset x:"+n.x+" y:"+n.y+")"),window.top!==window.self?window.parentIFrame?window.parentIFrame["scrollTo"+(e?"Offset":"")](i.x,i.y):E(y,"Unable to scroll to requested position, window.parentIFrame not found"):(F=i,s(),R(y,"--"))}function s(){!1!==d("onScroll",F)?S(y):H()}function d(e,n){return W(y,e,n)}var c,u,f,l,m,g,h,w,p=n.data,b={},y=null;"[iFrameResizerChild]Ready"===p?function(){for(var e in M)B("iFrame requested init",L(e),M[e].iframe,e)}():I===(""+p).substr(0,x)&&p.substr(x).split(":")[0]in M?(m=p.substr(x).split(":"),g=m[1]?parseInt(m[1],10):0,h=M[m[0]]&&M[m[0]].iframe,w=getComputedStyle(h),b={iframe:h,id:m[0],height:g+function(e){if("border-box"!==e.boxSizing)return 0;var n=e.paddingTop?parseInt(e.paddingTop,10):0,i=e.paddingBottom?parseInt(e.paddingBottom,10):0;return n+i}(w)+function(e){if("border-box"!==e.boxSizing)return 0;var n=e.borderTopWidth?parseInt(e.borderTopWidth,10):0,i=e.borderBottomWidth?parseInt(e.borderBottomWidth,10):0;return n+i}(w),width:m[2],type:m[3]},y=b.id,M[y]&&(M[y].loaded=!0),(l=b.type in{true:1,false:1,undefined:1})&&R(y,"Ignoring init message from meta parent page"),!l&&(f=!0,M[u=y]||(f=!1,E(b.type+" No settings for "+u+". Message was: "+p)),f)&&(R(y,"Received: "+p),c=!0,null===b.iframe&&(E(y,"IFrame ("+b.id+") not found"),c=!1),c&&function(){var e,i=n.origin,t=M[y]&&M[y].checkOrigin;if(t&&""+i!="null"&&!(t.constructor===Array?function(){var e=0,n=!1;for(R(y,"Checking connection is from allowed list of origins: "+t);e<t.length;e++)if(t[e]===i){n=!0;break}return n}():(e=M[y]&&M[y].remoteHost,R(y,"Checking connection is from: "+e),i===e)))throw new Error("Unexpected message received from: "+i+" for "+b.iframe.id+". Message was: "+n.data+". This error can be disabled by setting the checkOrigin: false option or by providing of array of trusted domains.");return!0}()&&function(){switch(M[y]&&M[y].firstRun&&M[y]&&(M[y].firstRun=!1),b.type){case"close":C(b.iframe);break;case"message":!function(e){R(y,"onMessage passed: {iframe: "+b.iframe.id+", message: "+e+"}"),d("onMessage",{iframe:b.iframe,message:JSON.parse(e)}),R(y,"--")}(t(6));break;case"autoResize":M[y].autoResize=JSON.parse(t(9));break;case"scrollTo":o(!1);break;case"scrollToOffset":o(!0);break;case"pageInfo":a(M[y]&&M[y].iframe,y),function(){function e(n,i){function t(){M[r]?a(M[r].iframe,r):o()}["scroll","resize"].forEach(function(e){R(r,n+e+" listener for sendPageInfo"),i(window,e,t)})}function o(){e("Remove ",O)}var r=y;e("Add ",z),M[r]&&(M[r].stopPageInfo=o)}();break;case"pageInfoStop":M[y]&&M[y].stopPageInfo&&(M[y].stopPageInfo(),delete M[y].stopPageInfo);break;case"inPageLink":!function(e){var n,i=e.split("#")[1]||"",t=decodeURIComponent(i),o=document.getElementById(t)||document.getElementsByName(t)[0];o?(n=r(o),R(y,"Moving to in page link (#"+i+") at x: "+n.x+" y: "+n.y),F={x:n.x,y:n.y},s(),R(y,"--")):window.top!==window.self?window.parentIFrame?window.parentIFrame.moveToAnchor(i):R(y,"In page link #"+i+" not found and window.parentIFrame not found"):R(y,"In page link #"+i+" not found")}(t(9));break;case"reset":j(b);break;case"init":e(),d("onInit",b.iframe);break;default:e()}}())):T(y,"Ignored: "+p)}function W(e,n,i){var t=null,o=null;if(M[e]){if("function"!=typeof(t=M[e][n]))throw new TypeError(n+" on iFrame["+e+"] is not a function");o=t(i)}return o}function b(e){var n=e.id;delete M[n]}function C(e){var n=e.id;if(!1!==W(n,"onClose",n)){R(n,"Removing iFrame: "+n);try{e.parentNode&&e.parentNode.removeChild(e)}catch(e){E(e)}W(n,"onClosed",n),R(n,"--"),b(e)}else R(n,"Close iframe cancelled by onClose event")}function N(e){null===F&&R(e,"Get page position: "+(F={x:window.pageXOffset!==l?window.pageXOffset:document.documentElement.scrollLeft,y:window.pageYOffset!==l?window.pageYOffset:document.documentElement.scrollTop}).x+","+F.y)}function S(e){null!==F&&(window.scrollTo(F.x,F.y),R(e,"Set page position: "+F.x+","+F.y),H())}function H(){F=null}function j(e){R(e.id,"Size reset requested by "+("init"===e.type?"host page":"iFrame")),N(e.id),A(function(){P(e),B("reset","reset",e.iframe,e.id)},e,"reset")}function P(n){function i(e){o||"0"!==n[e]||(o=!0,R(t,"Hidden iFrame detected, creating visibility listener"),function(){function n(){Object.keys(M).forEach(function(e){!function(n){function e(e){return"0px"===(M[n]&&M[n].iframe.style[e])}M[n]&&null!==M[n].iframe.offsetParent&&(e("height")||e("width"))&&B("Visibility change","resize",M[n].iframe,n)}(e)})}function i(e){R("window","Mutation observed: "+e[0].target+" "+e[0].type),c(n,16)}var t=p();t&&function(){var e=document.querySelector("body");new t(i).observe(e,{attributes:!0,attributeOldValue:!1,characterData:!0,characterDataOldValue:!1,childList:!0,subtree:!0})}()}())}function e(e){!function(e){n.id?(n.iframe.style[e]=n[e]+"px",R(n.id,"IFrame ("+t+") "+e+" set to "+n[e]+"px")):R("undefined","messageData id not set")}(e),i(e)}var t=n.iframe.id;M[t]&&(M[t].sizeHeight&&e("height"),M[t].sizeWidth&&e("width"))}function A(e,n,i){i!==n.type&&r&&!window.jasmine?(R(n.id,"Requesting animation frame"),r(e)):e()}function B(e,n,i,t,o){var r,a=!1;t=t||i.id,M[t]&&(i&&"contentWindow"in i&&null!==i.contentWindow?(r=M[t]&&M[t].targetOrigin,R(t,"["+e+"] Sending msg to iframe["+t+"] ("+n+") targetOrigin: "+r),i.contentWindow.postMessage(I+n,r)):E(t,"["+e+"] IFrame("+t+") not found"),o&&M[t]&&M[t].warningTimeout&&(M[t].msgTimeout=setTimeout(function(){!M[t]||M[t].loaded||a||(a=!0,E(t,"IFrame has not responded within "+M[t].warningTimeout/1e3+" seconds. Check iFrameResizer.contentWindow.js has been loaded in iFrame. This message can be ignored if everything is working, or you can set the warningTimeout option to a higher value or zero to suppress this warning."))},M[t].warningTimeout)))}function L(e){return e+":"+M[e].bodyMarginV1+":"+M[e].sizeWidth+":"+M[e].log+":"+M[e].interval+":"+M[e].enablePublicMethods+":"+M[e].autoResize+":"+M[e].bodyMargin+":"+M[e].heightCalculationMethod+":"+M[e].bodyBackground+":"+M[e].bodyPadding+":"+M[e].tolerance+":"+M[e].inPageLinks+":"+M[e].resizeFrom+":"+M[e].widthCalculationMethod}function d(i,e){function n(e){var n=e.split("Callback");if(2===n.length){var i="on"+n[0].charAt(0).toUpperCase()+n[0].slice(1);this[i]=this[e],delete this[e],E(c,"Deprecated: '"+e+"' has been renamed '"+i+"'. The old method will be removed in the next major version.")}}var t,o,r,a,s,d,c=(""===(o=i.id)&&(i.id=(t=e&&e.id||w.id+m++,null!==document.getElementById(t)&&(t+=m++),o=t),g=(e||{}).log,R(o,"Added missing iframe ID: "+o+" ("+i.src+")")),o);function u(e){1/0!==M[c][e]&&0!==M[c][e]&&(i.style[e]=M[c][e]+"px",R(c,"Set "+e+" = "+M[c][e]+"px"))}function f(e){if(M[c]["min"+e]>M[c]["max"+e])throw new Error("Value for min"+e+" can not be greater than max"+e)}c in M&&"iFrameResizer"in i?E(c,"Ignored iFrame, already setup."):(d=(d=e)||{},M[c]={firstRun:!0,iframe:i,remoteHost:i.src&&i.src.split("/").slice(0,3).join("/")},function(e){if("object"!=typeof e)throw new TypeError("Options is not an object")}(d),Object.keys(d).forEach(n,d),function(e){for(var n in w)Object.prototype.hasOwnProperty.call(w,n)&&(M[c][n]=Object.prototype.hasOwnProperty.call(e,n)?e[n]:w[n])}(d),M[c]&&(M[c].targetOrigin=!0===M[c].checkOrigin?function(e){return""===e||null!==e.match(/^(about:blank|javascript:|file:\/\/)/)?"*":e}(M[c].remoteHost):"*"),function(){switch(R(c,"IFrame scrolling "+(M[c]&&M[c].scrolling?"enabled":"disabled")+" for "+c),i.style.overflow=!1===(M[c]&&M[c].scrolling)?"hidden":"auto",M[c]&&M[c].scrolling){case"omit":break;case!0:i.scrolling="yes";break;case!1:i.scrolling="no";break;default:i.scrolling=M[c]?M[c].scrolling:"no"}}(),f("Height"),f("Width"),u("maxHeight"),u("minHeight"),u("maxWidth"),u("minWidth"),"number"!=typeof(M[c]&&M[c].bodyMargin)&&"0"!==(M[c]&&M[c].bodyMargin)||(M[c].bodyMarginV1=M[c].bodyMargin,M[c].bodyMargin=M[c].bodyMargin+"px"),r=L(c),(s=p())&&(a=s,i.parentNode&&new a(function(e){e.forEach(function(e){Array.prototype.slice.call(e.removedNodes).forEach(function(e){e===i&&C(i)})})}).observe(i.parentNode,{childList:!0})),z(i,"load",function(){B("iFrame.onload",r,i,l,!0),function(){var e=M[c]&&M[c].firstRun,n=M[c]&&M[c].heightCalculationMethod in h;!e&&n&&j({iframe:i,height:0,width:0,type:"init"})}()}),B("init",r,i,l,!0),M[c]&&(M[c].iframe.iFrameResizer={close:C.bind(null,M[c].iframe),removeListeners:b.bind(null,M[c].iframe),resize:B.bind(null,"Window resize","resize",M[c].iframe),moveToAnchor:function(e){B("Move to anchor","moveToAnchor:"+e,M[c].iframe,c)},sendMessage:function(e){B("Send Message","message:"+(e=JSON.stringify(e)),M[c].iframe,c)}}))}function c(e,n){null===i&&(i=setTimeout(function(){i=null,e()},n))}function u(){"hidden"!==document.visibilityState&&(R("document","Trigger event: Visiblity change"),c(function(){f("Tab Visable","resize")},16))}function f(n,i){Object.keys(M).forEach(function(e){!function(e){return M[e]&&"parent"===M[e].resizeFrom&&M[e].autoResize&&!M[e].firstRun}(e)||B(n,i,M[e].iframe,e)})}function y(){z(window,"message",n),z(window,"resize",function(){!function(e){R("window","Trigger event: "+e),c(function(){f("Window "+e,"resize")},16)}("resize")}),z(document,"visibilitychange",u),z(document,"-webkit-visibilitychange",u)}function q(){function i(e,n){n&&(function(){if(!n.tagName)throw new TypeError("Object is not a valid DOM element");if("IFRAME"!==n.tagName.toUpperCase())throw new TypeError("Expected <IFRAME> tag, found <"+n.tagName+">")}(),d(n,e),t.push(n))}var t;return function(){var e,n=["moz","webkit","o","ms"];for(e=0;e<n.length&&!r;e+=1)r=window[n[e]+"RequestAnimationFrame"];r?r=r.bind(window):R("setup","RequestAnimationFrame not supported")}(),y(),function(e,n){switch(t=[],function(e){e&&e.enablePublicMethods&&E("enablePublicMethods option has been removed, public methods are now always available in the iFrame")}(e),typeof n){case"undefined":case"string":Array.prototype.forEach.call(document.querySelectorAll(n||"iframe"),i.bind(l,e));break;case"object":i(e,n);break;default:throw new TypeError("Unexpected data type ("+typeof n+")")}return t}}}();
70//# sourceMappingURL=iframeResizer.map
71
72// ==================================================
73// fancyBox v3.3.5
74//
75// Licensed GPLv3 for open source use
76// or fancyBox Commercial License for commercial use
77//
78// http://fancyapps.com/fancybox/
79// Copyright 2018 fancyApps
80//
81// ==================================================
82// ==================================================
83// fancyBox v3.5.2
84//
85// Licensed GPLv3 for open source use
86// or fancyBox Commercial License for commercial use
87//
88// http://fancyapps.com/fancybox/
89// Copyright 2018 fancyApps
90//
91// ==================================================
92(function(window, document, $, undefined) {
93 "use strict";
94
95 window.console = window.console || {
96 info: function(stuff) {}
97 };
98
99 // If there's no jQuery, fancyBox can't work
100 // =========================================
101
102 if (!$) {
103 return;
104 }
105
106 // Check if fancyBox is already initialized
107 // ========================================
108
109 if ($.fn.fancybox) {
110 console.info("fancyBox already initialized");
111
112 return;
113 }
114
115 // Private default settings
116 // ========================
117
118 var defaults = {
119 // Close existing modals
120 // Set this to false if you do not need to stack multiple instances
121 closeExisting: false,
122
123 // Enable infinite gallery navigation
124 loop: false,
125
126 // Horizontal space between slides
127 gutter: 50,
128
129 // Enable keyboard navigation
130 keyboard: true,
131
132 // Should allow caption to overlap the content
133 preventCaptionOverlap: true,
134
135 // Should display navigation arrows at the screen edges
136 arrows: true,
137
138 // Should display counter at the top left corner
139 infobar: true,
140
141 // Should display close button (using `btnTpl.smallBtn` template) over the content
142 // Can be true, false, "auto"
143 // If "auto" - will be automatically enabled for "html", "inline" or "ajax" items
144 smallBtn: "auto",
145
146 // Should display toolbar (buttons at the top)
147 // Can be true, false, "auto"
148 // If "auto" - will be automatically hidden if "smallBtn" is enabled
149 toolbar: "auto",
150
151 // What buttons should appear in the top right corner.
152 // Buttons will be created using templates from `btnTpl` option
153 // and they will be placed into toolbar (class="fancybox-toolbar"` element)
154 buttons: [
155 "zoom",
156 //"share",
157 "slideShow",
158 //"fullScreen",
159 //"download",
160 "thumbs",
161 "close"
162 ],
163
164 // Detect "idle" time in seconds
165 idleTime: 3,
166
167 // Disable right-click and use simple image protection for images
168 protect: false,
169
170 // Shortcut to make content "modal" - disable keyboard navigtion, hide buttons, etc
171 modal: false,
172
173 image: {
174 // Wait for images to load before displaying
175 // true - wait for image to load and then display;
176 // false - display thumbnail and load the full-sized image over top,
177 // requires predefined image dimensions (`data-width` and `data-height` attributes)
178 preload: false
179 },
180
181 ajax: {
182 // Object containing settings for ajax request
183 settings: {
184 // This helps to indicate that request comes from the modal
185 // Feel free to change naming
186 data: {
187 fancybox: true
188 }
189 }
190 },
191
192 iframe: {
193 // Iframe template
194 tpl:
195 '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" allowfullscreen allow="autoplay; fullscreen" src=""></iframe>',
196
197 // Preload iframe before displaying it
198 // This allows to calculate iframe content width and height
199 // (note: Due to "Same Origin Policy", you can't get cross domain data).
200 preload: true,
201
202 // Custom CSS styling for iframe wrapping element
203 // You can use this to set custom iframe dimensions
204 css: {},
205
206 // Iframe tag attributes
207 attr: {
208 scrolling: "auto"
209 }
210 },
211
212 // For HTML5 video only
213 video: {
214 tpl:
215 '<video class="fancybox-video" controls controlsList="nodownload" poster="{{poster}}">' +
216 '<source src="{{src}}" type="{{format}}" />' +
217 'Sorry, your browser doesn\'t support embedded videos, <a href="{{src}}">download</a> and watch with your favorite video player!' +
218 "</video>",
219 format: "", // custom video format
220 autoStart: true
221 },
222
223 // Default content type if cannot be detected automatically
224 defaultType: "image",
225
226 // Open/close animation type
227 // Possible values:
228 // false - disable
229 // "zoom" - zoom images from/to thumbnail
230 // "fade"
231 // "zoom-in-out"
232 //
233 animationEffect: "zoom",
234
235 // Duration in ms for open/close animation
236 animationDuration: 366,
237
238 // Should image change opacity while zooming
239 // If opacity is "auto", then opacity will be changed if image and thumbnail have different aspect ratios
240 zoomOpacity: "auto",
241
242 // Transition effect between slides
243 //
244 // Possible values:
245 // false - disable
246 // "fade'
247 // "slide'
248 // "circular'
249 // "tube'
250 // "zoom-in-out'
251 // "rotate'
252 //
253 transitionEffect: "fade",
254
255 // Duration in ms for transition animation
256 transitionDuration: 366,
257
258 // Custom CSS class for slide element
259 slideClass: "",
260
261 // Custom CSS class for layout
262 baseClass: "",
263
264 // Base template for layout
265 baseTpl:
266 '<div class="fancybox-container" role="dialog" tabindex="-1">' +
267 '<div class="fancybox-bg"></div>' +
268 '<div class="fancybox-inner">' +
269 '<div class="fancybox-infobar"><span data-fancybox-index></span> / <span data-fancybox-count></span></div>' +
270 '<div class="fancybox-toolbar">{{buttons}}</div>' +
271 '<div class="fancybox-navigation">{{arrows}}</div>' +
272 '<div class="fancybox-stage"></div>' +
273 '<div class="fancybox-caption"></div>' +
274 "</div>" +
275 "</div>",
276
277 // Loading indicator template
278 spinnerTpl: '<div class="fancybox-loading"></div>',
279
280 // Error message template
281 errorTpl: '<div class="fancybox-error"><p>{{ERROR}}</p></div>',
282
283 btnTpl: {
284 download:
285 '<a download data-fancybox-download class="fancybox-button fancybox-button--download" title="{{DOWNLOAD}}" href="javascript:;">' +
286 '<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>' +
287 "</a>",
288
289 zoom:
290 '<button data-fancybox-zoom class="fancybox-button fancybox-button--zoom" title="{{ZOOM}}">' +
291 '<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>' +
292 "</button>",
293
294 close:
295 '<button data-fancybox-close class="fancybox-button fancybox-button--close" title="{{CLOSE}}">' +
296 '<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>' +
297 "</button>",
298
299 // Arrows
300 arrowLeft:
301 '<button data-fancybox-prev class="fancybox-button fancybox-button--arrow_left" title="{{PREV}}">' +
302 '<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>' +
303 "</button>",
304
305 arrowRight:
306 '<button data-fancybox-next class="fancybox-button fancybox-button--arrow_right" title="{{NEXT}}">' +
307 '<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>' +
308 "</button>",
309
310 // This small close button will be appended to your html/inline/ajax content by default,
311 // if "smallBtn" option is not set to false
312 smallBtn:
313 '<button type="button" data-fancybox-close class="fancybox-button fancybox-close-small" title="{{CLOSE}}">' +
314 '<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>' +
315 "</button>"
316 },
317
318 // Container is injected into this element
319 parentEl: "body",
320
321 // Hide browser vertical scrollbars; use at your own risk
322 hideScrollbar: true,
323
324 // Focus handling
325 // ==============
326
327 // Try to focus on the first focusable element after opening
328 autoFocus: true,
329
330 // Put focus back to active element after closing
331 backFocus: true,
332
333 // Do not let user to focus on element outside modal content
334 trapFocus: true,
335
336 // Module specific options
337 // =======================
338
339 fullScreen: {
340 autoStart: false
341 },
342
343 // Set `touch: false` to disable panning/swiping
344 touch: {
345 vertical: true, // Allow to drag content vertically
346 momentum: true // Continue movement after releasing mouse/touch when panning
347 },
348
349 // Hash value when initializing manually,
350 // set `false` to disable hash change
351 hash: null,
352
353 // Customize or add new media types
354 // Example:
355 /*
356 media : {
357 youtube : {
358 params : {
359 autoplay : 0
360 }
361 }
362 }
363 */
364 media: {},
365
366 slideShow: {
367 autoStart: false,
368 speed: 3000
369 },
370
371 thumbs: {
372 autoStart: false, // Display thumbnails on opening
373 hideOnClose: true, // Hide thumbnail grid when closing animation starts
374 parentEl: ".fancybox-container", // Container is injected into this element
375 axis: "y" // Vertical (y) or horizontal (x) scrolling
376 },
377
378 // Use mousewheel to navigate gallery
379 // If 'auto' - enabled for images only
380 wheel: "auto",
381
382 // Callbacks
383 //==========
384
385 // See Documentation/API/Events for more information
386 // Example:
387 /*
388 afterShow: function( instance, current ) {
389 console.info( 'Clicked element:' );
390 console.info( current.opts.$orig );
391 }
392 */
393
394 onInit: $.noop, // When instance has been initialized
395
396 beforeLoad: $.noop, // Before the content of a slide is being loaded
397 afterLoad: $.noop, // When the content of a slide is done loading
398
399 beforeShow: $.noop, // Before open animation starts
400 afterShow: $.noop, // When content is done loading and animating
401
402 beforeClose: $.noop, // Before the instance attempts to close. Return false to cancel the close.
403 afterClose: $.noop, // After instance has been closed
404
405 onActivate: $.noop, // When instance is brought to front
406 onDeactivate: $.noop, // When other instance has been activated
407
408 // Interaction
409 // ===========
410
411 // Use options below to customize taken action when user clicks or double clicks on the fancyBox area,
412 // each option can be string or method that returns value.
413 //
414 // Possible values:
415 // "close" - close instance
416 // "next" - move to next gallery item
417 // "nextOrClose" - move to next gallery item or close if gallery has only one item
418 // "toggleControls" - show/hide controls
419 // "zoom" - zoom image (if loaded)
420 // false - do nothing
421
422 // Clicked on the content
423 clickContent: function(current, event) {
424 return current.type === "image" ? "zoom" : false;
425 },
426
427 // Clicked on the slide
428 clickSlide: "close",
429
430 // Clicked on the background (backdrop) element;
431 // if you have not changed the layout, then most likely you need to use `clickSlide` option
432 clickOutside: "close",
433
434 // Same as previous two, but for double click
435 dblclickContent: false,
436 dblclickSlide: false,
437 dblclickOutside: false,
438
439 // Custom options when mobile device is detected
440 // =============================================
441
442 mobile: {
443 preventCaptionOverlap: false,
444 idleTime: false,
445 clickContent: function(current, event) {
446 return current.type === "image" ? "toggleControls" : false;
447 },
448 clickSlide: function(current, event) {
449 return current.type === "image" ? "toggleControls" : "close";
450 },
451 dblclickContent: function(current, event) {
452 return current.type === "image" ? "zoom" : false;
453 },
454 dblclickSlide: function(current, event) {
455 return current.type === "image" ? "zoom" : false;
456 }
457 },
458
459 // Internationalization
460 // ====================
461
462 lang: "en",
463 i18n: {
464 en: {
465 CLOSE: "Close",
466 NEXT: "Next",
467 PREV: "Previous",
468 ERROR: "The requested content cannot be loaded. <br/> Please try again later.",
469 PLAY_START: "Start slideshow",
470 PLAY_STOP: "Pause slideshow",
471 FULL_SCREEN: "Full screen",
472 THUMBS: "Thumbnails",
473 DOWNLOAD: "Download",
474 SHARE: "Share",
475 ZOOM: "Zoom"
476 },
477 de: {
478 CLOSE: "Schliessen",
479 NEXT: "Weiter",
480 PREV: "Zurück",
481 ERROR: "Die angeforderten Daten konnten nicht geladen werden. <br/> Bitte versuchen Sie es später nochmal.",
482 PLAY_START: "Diaschau starten",
483 PLAY_STOP: "Diaschau beenden",
484 FULL_SCREEN: "Vollbild",
485 THUMBS: "Vorschaubilder",
486 DOWNLOAD: "Herunterladen",
487 SHARE: "Teilen",
488 ZOOM: "Maßstab"
489 }
490 }
491 };
492
493 // Few useful variables and methods
494 // ================================
495
496 var $W = $(window);
497 var $D = $(document);
498
499 var called = 0;
500
501 // Check if an object is a jQuery object and not a native JavaScript object
502 // ========================================================================
503 var isQuery = function(obj) {
504 return obj && obj.hasOwnProperty && obj instanceof $;
505 };
506
507 // Handle multiple browsers for "requestAnimationFrame" and "cancelAnimationFrame"
508 // ===============================================================================
509 var requestAFrame = (function() {
510 return (
511 window.requestAnimationFrame ||
512 window.webkitRequestAnimationFrame ||
513 window.mozRequestAnimationFrame ||
514 window.oRequestAnimationFrame ||
515 // if all else fails, use setTimeout
516 function(callback) {
517 return window.setTimeout(callback, 1000 / 60);
518 }
519 );
520 })();
521
522 var cancelAFrame = (function() {
523 return (
524 window.cancelAnimationFrame ||
525 window.webkitCancelAnimationFrame ||
526 window.mozCancelAnimationFrame ||
527 window.oCancelAnimationFrame ||
528 function(id) {
529 window.clearTimeout(id);
530 }
531 );
532 })();
533
534 // Detect the supported transition-end event property name
535 // =======================================================
536 var transitionEnd = (function() {
537 var el = document.createElement("fakeelement"),
538 t;
539
540 var transitions = {
541 transition: "transitionend",
542 OTransition: "oTransitionEnd",
543 MozTransition: "transitionend",
544 WebkitTransition: "webkitTransitionEnd"
545 };
546
547 for (t in transitions) {
548 if (el.style[t] !== undefined) {
549 return transitions[t];
550 }
551 }
552
553 return "transitionend";
554 })();
555
556 // Force redraw on an element.
557 // This helps in cases where the browser doesn't redraw an updated element properly
558 // ================================================================================
559 var forceRedraw = function($el) {
560 return $el && $el.length && $el[0].offsetHeight;
561 };
562
563 // Exclude array (`buttons`) options from deep merging
564 // ===================================================
565 var mergeOpts = function(opts1, opts2) {
566 var rez = $.extend(true, {}, opts1, opts2);
567
568 $.each(opts2, function(key, value) {
569 if ($.isArray(value)) {
570 rez[key] = value;
571 }
572 });
573
574 return rez;
575 };
576
577 // How much of an element is visible in viewport
578 // =============================================
579
580 var inViewport = function(elem) {
581 var elemCenter, rez;
582
583 if (!elem || elem.ownerDocument !== document) {
584 return false;
585 }
586
587 $(".fancybox-container").css("pointer-events", "none");
588
589 elemCenter = {
590 x: elem.getBoundingClientRect().left + elem.offsetWidth / 2,
591 y: elem.getBoundingClientRect().top + elem.offsetHeight / 2
592 };
593
594 rez = document.elementFromPoint(elemCenter.x, elemCenter.y) === elem;
595
596 $(".fancybox-container").css("pointer-events", "");
597
598 return rez;
599 };
600
601 // Class definition
602 // ================
603
604 var FancyBox = function(content, opts, index) {
605 var self = this;
606
607 self.opts = mergeOpts({index: index}, $.fancybox.defaults);
608
609 if ($.isPlainObject(opts)) {
610 self.opts = mergeOpts(self.opts, opts);
611 }
612
613 if ($.fancybox.isMobile) {
614 self.opts = mergeOpts(self.opts, self.opts.mobile);
615 }
616
617 self.id = self.opts.id || ++called;
618
619 self.currIndex = parseInt(self.opts.index, 10) || 0;
620 self.prevIndex = null;
621
622 self.prevPos = null;
623 self.currPos = 0;
624
625 self.firstRun = true;
626
627 // All group items
628 self.group = [];
629
630 // Existing slides (for current, next and previous gallery items)
631 self.slides = {};
632
633 // Create group elements
634 self.addContent(content);
635
636 if (!self.group.length) {
637 return;
638 }
639
640 self.init();
641 };
642
643 $.extend(FancyBox.prototype, {
644 // Create DOM structure
645 // ====================
646
647 init: function() {
648 var self = this,
649 firstItem = self.group[self.currIndex],
650 firstItemOpts = firstItem.opts,
651 $container,
652 buttonStr;
653
654 if (firstItemOpts.closeExisting) {
655 $.fancybox.close(true);
656 }
657
658 // Hide scrollbars
659 // ===============
660
661 $("body").addClass("fancybox-active");
662
663 if (
664 !$.fancybox.getInstance() &&
665 firstItemOpts.hideScrollbar !== false &&
666 !$.fancybox.isMobile &&
667 document.body.scrollHeight > window.innerHeight
668 ) {
669 $("head").append(
670 '<style id="fancybox-style-noscroll" type="text/css">.compensate-for-scrollbar{margin-right:' +
671 (window.innerWidth - document.documentElement.clientWidth) +
672 "px;}</style>"
673 );
674
675 $("body").addClass("compensate-for-scrollbar");
676 }
677
678 // Build html markup and set references
679 // ====================================
680
681 // Build html code for buttons and insert into main template
682 buttonStr = "";
683
684 $.each(firstItemOpts.buttons, function(index, value) {
685 buttonStr += firstItemOpts.btnTpl[value] || "";
686 });
687
688 // Create markup from base template, it will be initially hidden to
689 // avoid unnecessary work like painting while initializing is not complete
690 $container = $(
691 self.translate(
692 self,
693 firstItemOpts.baseTpl
694 .replace("{{buttons}}", buttonStr)
695 .replace("{{arrows}}", firstItemOpts.btnTpl.arrowLeft + firstItemOpts.btnTpl.arrowRight)
696 )
697 )
698 .attr("id", "fancybox-container-" + self.id)
699 .addClass(firstItemOpts.baseClass)
700 .data("FancyBox", self)
701 .appendTo(firstItemOpts.parentEl);
702
703 // Create object holding references to jQuery wrapped nodes
704 self.$refs = {
705 container: $container
706 };
707
708 ["bg", "inner", "infobar", "toolbar", "stage", "caption", "navigation"].forEach(function(item) {
709 self.$refs[item] = $container.find(".fancybox-" + item);
710 });
711
712 self.trigger("onInit");
713
714 // Enable events, deactive previous instances
715 self.activate();
716
717 // Build slides, load and reveal content
718 self.jumpTo(self.currIndex);
719 },
720
721 // Simple i18n support - replaces object keys found in template
722 // with corresponding values
723 // ============================================================
724
725 translate: function(obj, str) {
726 var arr = obj.opts.i18n[obj.opts.lang] || obj.opts.i18n.en;
727
728 return str.replace(/\{\{(\w+)\}\}/g, function(match, n) {
729 var value = arr[n];
730
731 if (value === undefined) {
732 return match;
733 }
734
735 return value;
736 });
737 },
738
739 // Populate current group with fresh content
740 // Check if each object has valid type and content
741 // ===============================================
742
743 addContent: function(content) {
744 var self = this,
745 items = $.makeArray(content),
746 thumbs;
747
748 $.each(items, function(i, item) {
749 var obj = {},
750 opts = {},
751 $item,
752 type,
753 found,
754 src,
755 srcParts;
756
757 // Step 1 - Make sure we have an object
758 // ====================================
759
760 if ($.isPlainObject(item)) {
761 // We probably have manual usage here, something like
762 // $.fancybox.open( [ { src : "image.jpg", type : "image" } ] )
763
764 obj = item;
765 opts = item.opts || item;
766 } else if ($.type(item) === "object" && $(item).length) {
767 // Here we probably have jQuery collection returned by some selector
768 $item = $(item);
769
770 // Support attributes like `data-options='{"touch" : false}'` and `data-touch='false'`
771 opts = $item.data() || {};
772 opts = $.extend(true, {}, opts, opts.options);
773
774 // Here we store clicked element
775 opts.$orig = $item;
776
777 obj.src = self.opts.src || opts.src || $item.attr("href");
778
779 // Assume that simple syntax is used, for example:
780 // `$.fancybox.open( $("#test"), {} );`
781 if (!obj.type && !obj.src) {
782 obj.type = "inline";
783 obj.src = item;
784 }
785 } else {
786 // Assume we have a simple html code, for example:
787 // $.fancybox.open( '<div><h1>Hi!</h1></div>' );
788 obj = {
789 type: "html",
790 src: item + ""
791 };
792 }
793
794 // Each gallery object has full collection of options
795 obj.opts = $.extend(true, {}, self.opts, opts);
796
797 // Do not merge buttons array
798 if ($.isArray(opts.buttons)) {
799 obj.opts.buttons = opts.buttons;
800 }
801
802 if ($.fancybox.isMobile && obj.opts.mobile) {
803 obj.opts = mergeOpts(obj.opts, obj.opts.mobile);
804 }
805
806 // Step 2 - Make sure we have content type, if not - try to guess
807 // ==============================================================
808
809 type = obj.type || obj.opts.type;
810 src = obj.src || "";
811
812 if (!type && src) {
813 if ((found = src.match(/\.(mp4|mov|ogv|webm)((\?|#).*)?$/i))) {
814 type = "video";
815
816 if (!obj.opts.video.format) {
817 obj.opts.video.format = "video/" + (found[1] === "ogv" ? "ogg" : found[1]);
818 }
819 } else if (src.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i)) {
820 type = "image";
821 } else if (src.match(/\.(pdf)((\?|#).*)?$/i)) {
822 type = "iframe";
823 obj = $.extend(true, obj, {contentType: "pdf", opts: {iframe: {preload: false}}});
824 } else if (src.charAt(0) === "#") {
825 type = "inline";
826 }
827 }
828
829 if (type) {
830 obj.type = type;
831 } else {
832 self.trigger("objectNeedsType", obj);
833 }
834
835 if (!obj.contentType) {
836 obj.contentType = $.inArray(obj.type, ["html", "inline", "ajax"]) > -1 ? "html" : obj.type;
837 }
838
839 // Step 3 - Some adjustments
840 // =========================
841
842 obj.index = self.group.length;
843
844 if (obj.opts.smallBtn == "auto") {
845 obj.opts.smallBtn = $.inArray(obj.type, ["html", "inline", "ajax"]) > -1;
846 }
847
848 if (obj.opts.toolbar === "auto") {
849 obj.opts.toolbar = !obj.opts.smallBtn;
850 }
851
852 // Find thumbnail image, check if exists and if is in the viewport
853 obj.$thumb = obj.opts.$thumb || null;
854
855 if (obj.opts.$trigger && obj.index === self.opts.index) {
856 obj.$thumb = obj.opts.$trigger.find("img:first");
857
858 if (obj.$thumb.length) {
859 obj.opts.$orig = obj.opts.$trigger;
860 }
861 }
862
863 if (!(obj.$thumb && obj.$thumb.length) && obj.opts.$orig) {
864 obj.$thumb = obj.opts.$orig.find("img:first");
865 }
866
867 if (obj.$thumb && !obj.$thumb.length) {
868 obj.$thumb = null;
869 }
870
871 obj.thumb = obj.opts.thumb || (obj.$thumb ? obj.$thumb[0].src : null);
872
873 // "caption" is a "special" option, it can be used to customize caption per gallery item
874 if ($.type(obj.opts.caption) === "function") {
875 obj.opts.caption = obj.opts.caption.apply(item, [self, obj]);
876 }
877
878 if ($.type(self.opts.caption) === "function") {
879 obj.opts.caption = self.opts.caption.apply(item, [self, obj]);
880 }
881
882 // Make sure we have caption as a string or jQuery object
883 if (!(obj.opts.caption instanceof $)) {
884 obj.opts.caption = obj.opts.caption === undefined ? "" : obj.opts.caption + "";
885 }
886
887 // Check if url contains "filter" used to filter the content
888 // Example: "ajax.html #something"
889 if (obj.type === "ajax") {
890 srcParts = src.split(/\s+/, 2);
891
892 if (srcParts.length > 1) {
893 obj.src = srcParts.shift();
894
895 obj.opts.filter = srcParts.shift();
896 }
897 }
898
899 // Hide all buttons and disable interactivity for modal items
900 if (obj.opts.modal) {
901 obj.opts = $.extend(true, obj.opts, {
902 trapFocus: true,
903 // Remove buttons
904 infobar: 0,
905 toolbar: 0,
906
907 smallBtn: 0,
908
909 // Disable keyboard navigation
910 keyboard: 0,
911
912 // Disable some modules
913 slideShow: 0,
914 fullScreen: 0,
915 thumbs: 0,
916 touch: 0,
917
918 // Disable click event handlers
919 clickContent: false,
920 clickSlide: false,
921 clickOutside: false,
922 dblclickContent: false,
923 dblclickSlide: false,
924 dblclickOutside: false
925 });
926 }
927
928 // Step 4 - Add processed object to group
929 // ======================================
930
931 self.group.push(obj);
932 });
933
934 // Update controls if gallery is already opened
935 if (Object.keys(self.slides).length) {
936 self.updateControls();
937
938 // Update thumbnails, if needed
939 thumbs = self.Thumbs;
940
941 if (thumbs && thumbs.isActive) {
942 thumbs.create();
943
944 thumbs.focus();
945 }
946 }
947 },
948
949 // Attach an event handler functions for:
950 // - navigation buttons
951 // - browser scrolling, resizing;
952 // - focusing
953 // - keyboard
954 // - detecting inactivity
955 // ======================================
956
957 addEvents: function() {
958 var self = this;
959
960 self.removeEvents();
961
962 // Make navigation elements clickable
963 // ==================================
964
965 self.$refs.container
966 .on("click.fb-close", "[data-fancybox-close]", function(e) {
967 e.stopPropagation();
968 e.preventDefault();
969
970 self.close(e);
971 })
972 .on("touchstart.fb-prev click.fb-prev", "[data-fancybox-prev]", function(e) {
973 e.stopPropagation();
974 e.preventDefault();
975
976 self.previous();
977 })
978 .on("touchstart.fb-next click.fb-next", "[data-fancybox-next]", function(e) {
979 e.stopPropagation();
980 e.preventDefault();
981
982 self.next();
983 })
984 .on("click.fb", "[data-fancybox-zoom]", function(e) {
985 // Click handler for zoom button
986 self[self.isScaledDown() ? "scaleToActual" : "scaleToFit"]();
987 });
988
989 // Handle page scrolling and browser resizing
990 // ==========================================
991
992 $W.on("orientationchange.fb resize.fb", function(e) {
993 if (e && e.originalEvent && e.originalEvent.type === "resize") {
994 if (self.requestId) {
995 cancelAFrame(self.requestId);
996 }
997
998 self.requestId = requestAFrame(function() {
999 self.update(e);
1000 });
1001 } else {
1002 if (self.current && self.current.type === "iframe") {
1003 self.$refs.stage.hide();
1004 }
1005
1006 setTimeout(function() {
1007 self.$refs.stage.show();
1008
1009 self.update(e);
1010 }, $.fancybox.isMobile ? 600 : 250);
1011 }
1012 });
1013
1014 $D.on("keydown.fb", function(e) {
1015 var instance = $.fancybox ? $.fancybox.getInstance() : null,
1016 current = instance.current,
1017 keycode = e.keyCode || e.which;
1018
1019 // Trap keyboard focus inside of the modal
1020 // =======================================
1021
1022 if (keycode == 9) {
1023 if (current.opts.trapFocus) {
1024 self.focus(e);
1025 }
1026
1027 return;
1028 }
1029
1030 // Enable keyboard navigation
1031 // ==========================
1032
1033 if (!current.opts.keyboard || e.ctrlKey || e.altKey || e.shiftKey || $(e.target).is("input") || $(e.target).is("textarea")) {
1034 return;
1035 }
1036
1037 // Backspace and Esc keys
1038 if (keycode === 8 || keycode === 27) {
1039 e.preventDefault();
1040
1041 self.close(e);
1042
1043 return;
1044 }
1045
1046 // Left arrow and Up arrow
1047 if (keycode === 37 || keycode === 38) {
1048 e.preventDefault();
1049
1050 self.previous();
1051
1052 return;
1053 }
1054
1055 // Righ arrow and Down arrow
1056 if (keycode === 39 || keycode === 40) {
1057 e.preventDefault();
1058
1059 self.next();
1060
1061 return;
1062 }
1063
1064 self.trigger("afterKeydown", e, keycode);
1065 });
1066
1067 // Hide controls after some inactivity period
1068 if (self.group[self.currIndex].opts.idleTime) {
1069 self.idleSecondsCounter = 0;
1070
1071 $D.on(
1072 "mousemove.fb-idle mouseleave.fb-idle mousedown.fb-idle touchstart.fb-idle touchmove.fb-idle scroll.fb-idle keydown.fb-idle",
1073 function(e) {
1074 self.idleSecondsCounter = 0;
1075
1076 if (self.isIdle) {
1077 self.showControls();
1078 }
1079
1080 self.isIdle = false;
1081 }
1082 );
1083
1084 self.idleInterval = window.setInterval(function() {
1085 self.idleSecondsCounter++;
1086
1087 if (self.idleSecondsCounter >= self.group[self.currIndex].opts.idleTime && !self.isDragging) {
1088 self.isIdle = true;
1089 self.idleSecondsCounter = 0;
1090
1091 self.hideControls();
1092 }
1093 }, 1000);
1094 }
1095 },
1096
1097 // Remove events added by the core
1098 // ===============================
1099
1100 removeEvents: function() {
1101 var self = this;
1102
1103 $W.off("orientationchange.fb resize.fb");
1104 $D.off("keydown.fb .fb-idle");
1105
1106 this.$refs.container.off(".fb-close .fb-prev .fb-next");
1107
1108 if (self.idleInterval) {
1109 window.clearInterval(self.idleInterval);
1110
1111 self.idleInterval = null;
1112 }
1113 },
1114
1115 // Change to previous gallery item
1116 // ===============================
1117
1118 previous: function(duration) {
1119 return this.jumpTo(this.currPos - 1, duration);
1120 },
1121
1122 // Change to next gallery item
1123 // ===========================
1124
1125 next: function(duration) {
1126 return this.jumpTo(this.currPos + 1, duration);
1127 },
1128
1129 // Switch to selected gallery item
1130 // ===============================
1131
1132 jumpTo: function(pos, duration) {
1133 var self = this,
1134 groupLen = self.group.length,
1135 firstRun,
1136 isMoved,
1137 loop,
1138 current,
1139 previous,
1140 slidePos,
1141 stagePos,
1142 prop,
1143 diff;
1144
1145 if (self.isDragging || self.isClosing || (self.isAnimating && self.firstRun)) {
1146 return;
1147 }
1148
1149 // Should loop?
1150 pos = parseInt(pos, 10);
1151 loop = self.current ? self.current.opts.loop : self.opts.loop;
1152
1153 if (!loop && (pos < 0 || pos >= groupLen)) {
1154 return false;
1155 }
1156
1157 // Check if opening for the first time; this helps to speed things up
1158 firstRun = self.firstRun = !Object.keys(self.slides).length;
1159
1160 // Create slides
1161 previous = self.current;
1162
1163 self.prevIndex = self.currIndex;
1164 self.prevPos = self.currPos;
1165
1166 current = self.createSlide(pos);
1167
1168 if (groupLen > 1) {
1169 if (loop || current.index < groupLen - 1) {
1170 self.createSlide(pos + 1);
1171 }
1172
1173 if (loop || current.index > 0) {
1174 self.createSlide(pos - 1);
1175 }
1176 }
1177
1178 self.current = current;
1179 self.currIndex = current.index;
1180 self.currPos = current.pos;
1181
1182 self.trigger("beforeShow", firstRun);
1183
1184 self.updateControls();
1185
1186 // Validate duration length
1187 current.forcedDuration = undefined;
1188
1189 if ($.isNumeric(duration)) {
1190 current.forcedDuration = duration;
1191 } else {
1192 duration = current.opts[firstRun ? "animationDuration" : "transitionDuration"];
1193 }
1194
1195 duration = parseInt(duration, 10);
1196
1197 // Check if user has swiped the slides or if still animating
1198 isMoved = self.isMoved(current);
1199
1200 // Make sure current slide is visible
1201 current.$slide.addClass("fancybox-slide--current");
1202
1203 // Fresh start - reveal container, current slide and start loading content
1204 if (firstRun) {
1205 if (current.opts.animationEffect && duration) {
1206 self.$refs.container.css("transition-duration", duration + "ms");
1207 }
1208
1209 self.$refs.container.addClass("fancybox-is-open").trigger("focus");
1210
1211 // Attempt to load content into slide
1212 // This will later call `afterLoad` -> `revealContent`
1213 self.loadSlide(current);
1214
1215 self.preload("image");
1216
1217 return;
1218 }
1219
1220 // Get actual slide/stage positions (before cleaning up)
1221 slidePos = $.fancybox.getTranslate(previous.$slide);
1222 stagePos = $.fancybox.getTranslate(self.$refs.stage);
1223
1224 // Clean up all slides
1225 $.each(self.slides, function(index, slide) {
1226 $.fancybox.stop(slide.$slide, true);
1227 });
1228
1229 if (previous.pos !== current.pos) {
1230 previous.isComplete = false;
1231 }
1232
1233 previous.$slide.removeClass("fancybox-slide--complete fancybox-slide--current");
1234
1235 // If slides are out of place, then animate them to correct position
1236 if (isMoved) {
1237 // Calculate horizontal swipe distance
1238 diff = slidePos.left - (previous.pos * slidePos.width + previous.pos * previous.opts.gutter);
1239
1240 $.each(self.slides, function(index, slide) {
1241 slide.$slide.removeClass("fancybox-animated").removeClass(function(index, className) {
1242 return (className.match(/(^|\s)fancybox-fx-\S+/g) || []).join(" ");
1243 });
1244
1245 // Make sure that each slide is in equal distance
1246 // This is mostly needed for freshly added slides, because they are not yet positioned
1247 var leftPos = slide.pos * slidePos.width + slide.pos * slide.opts.gutter;
1248
1249 $.fancybox.setTranslate(slide.$slide, {top: 0, left: leftPos - stagePos.left + diff});
1250
1251 if (slide.pos !== current.pos) {
1252 slide.$slide.addClass("fancybox-slide--" + (slide.pos > current.pos ? "next" : "previous"));
1253 }
1254
1255 // Redraw to make sure that transition will start
1256 forceRedraw(slide.$slide);
1257
1258 // Animate the slide
1259 $.fancybox.animate(
1260 slide.$slide,
1261 {
1262 top: 0,
1263 left: (slide.pos - current.pos) * slidePos.width + (slide.pos - current.pos) * slide.opts.gutter
1264 },
1265 duration,
1266 function() {
1267 slide.$slide
1268 .css({
1269 transform: "",
1270 opacity: ""
1271 })
1272 .removeClass("fancybox-slide--next fancybox-slide--previous");
1273
1274 if (slide.pos === self.currPos) {
1275 self.complete();
1276 }
1277 }
1278 );
1279 });
1280 } else if (duration && current.opts.transitionEffect) {
1281 // Set transition effect for previously active slide
1282 prop = "fancybox-animated fancybox-fx-" + current.opts.transitionEffect;
1283
1284 previous.$slide.addClass("fancybox-slide--" + (previous.pos > current.pos ? "next" : "previous"));
1285
1286 $.fancybox.animate(
1287 previous.$slide,
1288 prop,
1289 duration,
1290 function() {
1291 previous.$slide.removeClass(prop).removeClass("fancybox-slide--next fancybox-slide--previous");
1292 },
1293 false
1294 );
1295 }
1296
1297 if (current.isLoaded) {
1298 self.revealContent(current);
1299 } else {
1300 self.loadSlide(current);
1301 }
1302
1303 self.preload("image");
1304 },
1305
1306 // Create new "slide" element
1307 // These are gallery items that are actually added to DOM
1308 // =======================================================
1309
1310 createSlide: function(pos) {
1311 var self = this,
1312 $slide,
1313 index;
1314
1315 index = pos % self.group.length;
1316 index = index < 0 ? self.group.length + index : index;
1317
1318 if (!self.slides[pos] && self.group[index]) {
1319 $slide = $('<div class="fancybox-slide"></div>').appendTo(self.$refs.stage);
1320
1321 self.slides[pos] = $.extend(true, {}, self.group[index], {
1322 pos: pos,
1323 $slide: $slide,
1324 isLoaded: false
1325 });
1326
1327 self.updateSlide(self.slides[pos]);
1328 }
1329
1330 return self.slides[pos];
1331 },
1332
1333 // Scale image to the actual size of the image;
1334 // x and y values should be relative to the slide
1335 // ==============================================
1336
1337 scaleToActual: function(x, y, duration) {
1338 var self = this,
1339 current = self.current,
1340 $content = current.$content,
1341 canvasWidth = $.fancybox.getTranslate(current.$slide).width,
1342 canvasHeight = $.fancybox.getTranslate(current.$slide).height,
1343 newImgWidth = current.width,
1344 newImgHeight = current.height,
1345 imgPos,
1346 posX,
1347 posY,
1348 scaleX,
1349 scaleY;
1350
1351 if (self.isAnimating || self.isMoved() || !$content || !(current.type == "image" && current.isLoaded && !current.hasError)) {
1352 return;
1353 }
1354
1355 self.isAnimating = true;
1356
1357 $.fancybox.stop($content);
1358
1359 x = x === undefined ? canvasWidth * 0.5 : x;
1360 y = y === undefined ? canvasHeight * 0.5 : y;
1361
1362 imgPos = $.fancybox.getTranslate($content);
1363
1364 imgPos.top -= $.fancybox.getTranslate(current.$slide).top;
1365 imgPos.left -= $.fancybox.getTranslate(current.$slide).left;
1366
1367 scaleX = newImgWidth / imgPos.width;
1368 scaleY = newImgHeight / imgPos.height;
1369
1370 // Get center position for original image
1371 posX = canvasWidth * 0.5 - newImgWidth * 0.5;
1372 posY = canvasHeight * 0.5 - newImgHeight * 0.5;
1373
1374 // Make sure image does not move away from edges
1375 if (newImgWidth > canvasWidth) {
1376 posX = imgPos.left * scaleX - (x * scaleX - x);
1377
1378 if (posX > 0) {
1379 posX = 0;
1380 }
1381
1382 if (posX < canvasWidth - newImgWidth) {
1383 posX = canvasWidth - newImgWidth;
1384 }
1385 }
1386
1387 if (newImgHeight > canvasHeight) {
1388 posY = imgPos.top * scaleY - (y * scaleY - y);
1389
1390 if (posY > 0) {
1391 posY = 0;
1392 }
1393
1394 if (posY < canvasHeight - newImgHeight) {
1395 posY = canvasHeight - newImgHeight;
1396 }
1397 }
1398
1399 self.updateCursor(newImgWidth, newImgHeight);
1400
1401 $.fancybox.animate(
1402 $content,
1403 {
1404 top: posY,
1405 left: posX,
1406 scaleX: scaleX,
1407 scaleY: scaleY
1408 },
1409 duration || 330,
1410 function() {
1411 self.isAnimating = false;
1412 }
1413 );
1414
1415 // Stop slideshow
1416 if (self.SlideShow && self.SlideShow.isActive) {
1417 self.SlideShow.stop();
1418 }
1419 },
1420
1421 // Scale image to fit inside parent element
1422 // ========================================
1423
1424 scaleToFit: function(duration) {
1425 var self = this,
1426 current = self.current,
1427 $content = current.$content,
1428 end;
1429
1430 if (self.isAnimating || self.isMoved() || !$content || !(current.type == "image" && current.isLoaded && !current.hasError)) {
1431 return;
1432 }
1433
1434 self.isAnimating = true;
1435
1436 $.fancybox.stop($content);
1437
1438 end = self.getFitPos(current);
1439
1440 self.updateCursor(end.width, end.height);
1441
1442 $.fancybox.animate(
1443 $content,
1444 {
1445 top: end.top,
1446 left: end.left,
1447 scaleX: end.width / $content.width(),
1448 scaleY: end.height / $content.height()
1449 },
1450 duration || 330,
1451 function() {
1452 self.isAnimating = false;
1453 }
1454 );
1455 },
1456
1457 // Calculate image size to fit inside viewport
1458 // ===========================================
1459
1460 getFitPos: function(slide) {
1461 var self = this,
1462 $content = slide.$content,
1463 $slide = slide.$slide,
1464 width = slide.width || slide.opts.width,
1465 height = slide.height || slide.opts.height,
1466 maxWidth,
1467 maxHeight,
1468 minRatio,
1469 aspectRatio,
1470 rez = {};
1471
1472 if (!slide.isLoaded || !$content || !$content.length) {
1473 return false;
1474 }
1475
1476 maxWidth = $.fancybox.getTranslate(self.$refs.stage).width;
1477 maxHeight = $.fancybox.getTranslate(self.$refs.stage).height;
1478
1479 maxWidth -=
1480 parseFloat($slide.css("paddingLeft")) +
1481 parseFloat($slide.css("paddingRight")) +
1482 parseFloat($content.css("marginLeft")) +
1483 parseFloat($content.css("marginRight"));
1484
1485 maxHeight -=
1486 parseFloat($slide.css("paddingTop")) +
1487 parseFloat($slide.css("paddingBottom")) +
1488 parseFloat($content.css("marginTop")) +
1489 parseFloat($content.css("marginBottom"));
1490
1491 if (!width || !height) {
1492 width = maxWidth;
1493 height = maxHeight;
1494 }
1495
1496 minRatio = Math.min(1, maxWidth / width, maxHeight / height);
1497
1498 width = minRatio * width;
1499 height = minRatio * height;
1500
1501 // Adjust width/height to precisely fit into container
1502 if (width > maxWidth - 0.5) {
1503 width = maxWidth;
1504 }
1505
1506 if (height > maxHeight - 0.5) {
1507 height = maxHeight;
1508 }
1509
1510 if (slide.type === "image") {
1511 rez.top = Math.floor((maxHeight - height) * 0.5) + parseFloat($slide.css("paddingTop"));
1512 rez.left = Math.floor((maxWidth - width) * 0.5) + parseFloat($slide.css("paddingLeft"));
1513 } else if (slide.contentType === "video") {
1514 // Force aspect ratio for the video
1515 // "I say the whole world must learn of our peaceful ways… by force!"
1516 aspectRatio = slide.opts.width && slide.opts.height ? width / height : slide.opts.ratio || 16 / 9;
1517
1518 if (height > width / aspectRatio) {
1519 height = width / aspectRatio;
1520 } else if (width > height * aspectRatio) {
1521 width = height * aspectRatio;
1522 }
1523 }
1524
1525 rez.width = width;
1526 rez.height = height;
1527
1528 return rez;
1529 },
1530
1531 // Update content size and position for all slides
1532 // ==============================================
1533
1534 update: function(e) {
1535 var self = this;
1536
1537 $.each(self.slides, function(key, slide) {
1538 self.updateSlide(slide, e);
1539 });
1540 },
1541
1542 // Update slide content position and size
1543 // ======================================
1544
1545 updateSlide: function(slide, e) {
1546 var self = this,
1547 $content = slide && slide.$content,
1548 width = slide.width || slide.opts.width,
1549 height = slide.height || slide.opts.height,
1550 $slide = slide.$slide;
1551
1552 // First, prevent caption overlap, if needed
1553 self.adjustCaption(slide);
1554
1555 // Then resize content to fit inside the slide
1556 if ($content && (width || height || slide.contentType === "video") && !slide.hasError) {
1557 $.fancybox.stop($content);
1558
1559 $.fancybox.setTranslate($content, self.getFitPos(slide));
1560
1561 if (slide.pos === self.currPos) {
1562 self.isAnimating = false;
1563
1564 self.updateCursor();
1565 }
1566 }
1567
1568 // Then some adjustments
1569 self.adjustLayout(slide);
1570
1571 if ($slide.length) {
1572 $slide.trigger("refresh");
1573
1574 if (slide.pos === self.currPos) {
1575 self.$refs.toolbar
1576 .add(self.$refs.navigation.find(".fancybox-button--arrow_right"))
1577 .toggleClass("compensate-for-scrollbar", $slide.get(0).scrollHeight > $slide.get(0).clientHeight);
1578 }
1579 }
1580
1581 self.trigger("onUpdate", slide, e);
1582 },
1583
1584 // Horizontally center slide
1585 // =========================
1586
1587 centerSlide: function(duration) {
1588 var self = this,
1589 current = self.current,
1590 $slide = current.$slide;
1591
1592 if (self.isClosing || !current) {
1593 return;
1594 }
1595
1596 $slide.siblings().css({
1597 transform: "",
1598 opacity: ""
1599 });
1600
1601 $slide
1602 .parent()
1603 .children()
1604 .removeClass("fancybox-slide--previous fancybox-slide--next");
1605
1606 $.fancybox.animate(
1607 $slide,
1608 {
1609 top: 0,
1610 left: 0,
1611 opacity: 1
1612 },
1613 duration === undefined ? 0 : duration,
1614 function() {
1615 // Clean up
1616 $slide.css({
1617 transform: "",
1618 opacity: ""
1619 });
1620
1621 if (!current.isComplete) {
1622 self.complete();
1623 }
1624 },
1625 false
1626 );
1627 },
1628
1629 // Check if current slide is moved (swiped)
1630 // ========================================
1631
1632 isMoved: function(slide) {
1633 var current = slide || this.current,
1634 slidePos,
1635 stagePos;
1636
1637 if (!current) {
1638 return false;
1639 }
1640
1641 stagePos = $.fancybox.getTranslate(this.$refs.stage);
1642 slidePos = $.fancybox.getTranslate(current.$slide);
1643
1644 return (
1645 !current.$slide.hasClass("fancybox-animated") &&
1646 (Math.abs(slidePos.top - stagePos.top) > 0.5 || Math.abs(slidePos.left - stagePos.left) > 0.5)
1647 );
1648 },
1649
1650 // Update cursor style depending if content can be zoomed
1651 // ======================================================
1652
1653 updateCursor: function(nextWidth, nextHeight) {
1654 var self = this,
1655 current = self.current,
1656 $container = self.$refs.container,
1657 canPan,
1658 isZoomable;
1659
1660 if (!current || self.isClosing || !self.Guestures) {
1661 return;
1662 }
1663
1664 $container.removeClass("fancybox-is-zoomable fancybox-can-zoomIn fancybox-can-zoomOut fancybox-can-swipe fancybox-can-pan");
1665
1666 canPan = self.canPan(nextWidth, nextHeight);
1667
1668 isZoomable = canPan ? true : self.isZoomable();
1669
1670 $container.toggleClass("fancybox-is-zoomable", isZoomable);
1671
1672 $("[data-fancybox-zoom]").prop("disabled", !isZoomable);
1673
1674 if (canPan) {
1675 $container.addClass("fancybox-can-pan");
1676 } else if (
1677 isZoomable &&
1678 (current.opts.clickContent === "zoom" || ($.isFunction(current.opts.clickContent) && current.opts.clickContent(current) == "zoom"))
1679 ) {
1680 $container.addClass("fancybox-can-zoomIn");
1681 } else if (current.opts.touch && (current.opts.touch.vertical || self.group.length > 1) && current.contentType !== "video") {
1682 $container.addClass("fancybox-can-swipe");
1683 }
1684 },
1685
1686 // Check if current slide is zoomable
1687 // ==================================
1688
1689 isZoomable: function() {
1690 var self = this,
1691 current = self.current,
1692 fitPos;
1693
1694 // Assume that slide is zoomable if:
1695 // - image is still loading
1696 // - actual size of the image is smaller than available area
1697 if (current && !self.isClosing && current.type === "image" && !current.hasError) {
1698 if (!current.isLoaded) {
1699 return true;
1700 }
1701
1702 fitPos = self.getFitPos(current);
1703
1704 if (fitPos && (current.width > fitPos.width || current.height > fitPos.height)) {
1705 return true;
1706 }
1707 }
1708
1709 return false;
1710 },
1711
1712 // Check if current image dimensions are smaller than actual
1713 // =========================================================
1714
1715 isScaledDown: function(nextWidth, nextHeight) {
1716 var self = this,
1717 rez = false,
1718 current = self.current,
1719 $content = current.$content;
1720
1721 if (nextWidth !== undefined && nextHeight !== undefined) {
1722 rez = nextWidth < current.width && nextHeight < current.height;
1723 } else if ($content) {
1724 rez = $.fancybox.getTranslate($content);
1725 rez = rez.width < current.width && rez.height < current.height;
1726 }
1727
1728 return rez;
1729 },
1730
1731 // Check if image dimensions exceed parent element
1732 // ===============================================
1733
1734 canPan: function(nextWidth, nextHeight) {
1735 var self = this,
1736 current = self.current,
1737 pos = null,
1738 rez = false;
1739
1740 if (current.type === "image" && (current.isComplete || (nextWidth && nextHeight)) && !current.hasError) {
1741 rez = self.getFitPos(current);
1742
1743 if (nextWidth !== undefined && nextHeight !== undefined) {
1744 pos = {width: nextWidth, height: nextHeight};
1745 } else if (current.isComplete) {
1746 pos = $.fancybox.getTranslate(current.$content);
1747 }
1748
1749 if (pos && rez) {
1750 rez = Math.abs(pos.width - rez.width) > 1.5 || Math.abs(pos.height - rez.height) > 1.5;
1751 }
1752 }
1753
1754 return rez;
1755 },
1756
1757 // Load content into the slide
1758 // ===========================
1759
1760 loadSlide: function(slide) {
1761 var self = this,
1762 type,
1763 $slide,
1764 ajaxLoad;
1765
1766 if (slide.isLoading || slide.isLoaded) {
1767 return;
1768 }
1769
1770 slide.isLoading = true;
1771
1772 if (self.trigger("beforeLoad", slide) === false) {
1773 slide.isLoading = false;
1774
1775 return false;
1776 }
1777
1778 type = slide.type;
1779 $slide = slide.$slide;
1780
1781 $slide
1782 .off("refresh")
1783 .trigger("onReset")
1784 .addClass(slide.opts.slideClass);
1785
1786 // Create content depending on the type
1787 switch (type) {
1788 case "image":
1789 self.setImage(slide);
1790
1791 break;
1792
1793 case "iframe":
1794 self.setIframe(slide);
1795
1796 break;
1797
1798 case "html":
1799 self.setContent(slide, slide.src || slide.content);
1800
1801 break;
1802
1803 case "video":
1804 self.setContent(
1805 slide,
1806 slide.opts.video.tpl
1807 .replace(/\{\{src\}\}/gi, slide.src)
1808 .replace("{{format}}", slide.opts.videoFormat || slide.opts.video.format || "")
1809 .replace("{{poster}}", slide.thumb || "")
1810 );
1811
1812 break;
1813
1814 case "inline":
1815 if ($(slide.src).length) {
1816 self.setContent(slide, $(slide.src));
1817 } else {
1818 self.setError(slide);
1819 }
1820
1821 break;
1822
1823 case "ajax":
1824 self.showLoading(slide);
1825
1826 ajaxLoad = $.ajax(
1827 $.extend({}, slide.opts.ajax.settings, {
1828 url: slide.src,
1829 success: function(data, textStatus) {
1830 if (textStatus === "success") {
1831 self.setContent(slide, data);
1832 }
1833 },
1834 error: function(jqXHR, textStatus) {
1835 if (jqXHR && textStatus !== "abort") {
1836 self.setError(slide);
1837 }
1838 }
1839 })
1840 );
1841
1842 $slide.one("onReset", function() {
1843 ajaxLoad.abort();
1844 });
1845
1846 break;
1847
1848 default:
1849 self.setError(slide);
1850
1851 break;
1852 }
1853
1854 return true;
1855 },
1856
1857 // Use thumbnail image, if possible
1858 // ================================
1859
1860 setImage: function(slide) {
1861 var self = this,
1862 ghost;
1863
1864 // Check if need to show loading icon
1865 setTimeout(function() {
1866 var $img = slide.$image;
1867
1868 if (!self.isClosing && slide.isLoading && (!$img || !$img.length || !$img[0].complete) && !slide.hasError) {
1869 self.showLoading(slide);
1870 }
1871 }, 50);
1872
1873 //Check if image has srcset
1874 self.checkSrcset(slide);
1875
1876 // This will be wrapper containing both ghost and actual image
1877 slide.$content = $('<div class="fancybox-content"></div>')
1878 .addClass("fancybox-is-hidden")
1879 .appendTo(slide.$slide.addClass("fancybox-slide--image"));
1880
1881 // If we have a thumbnail, we can display it while actual image is loading
1882 // Users will not stare at black screen and actual image will appear gradually
1883 if (slide.opts.preload !== false && slide.opts.width && slide.opts.height && slide.thumb) {
1884 slide.width = slide.opts.width;
1885 slide.height = slide.opts.height;
1886
1887 ghost = document.createElement("img");
1888
1889 ghost.onerror = function() {
1890 $(this).remove();
1891
1892 slide.$ghost = null;
1893 };
1894
1895 ghost.onload = function() {
1896 self.afterLoad(slide);
1897 };
1898
1899 slide.$ghost = $(ghost)
1900 .addClass("fancybox-image")
1901 .appendTo(slide.$content)
1902 .attr("src", slide.thumb);
1903 }
1904
1905 // Start loading actual image
1906 self.setBigImage(slide);
1907 },
1908
1909 // Check if image has srcset and get the source
1910 // ============================================
1911 checkSrcset: function(slide) {
1912 var srcset = slide.opts.srcset || slide.opts.image.srcset,
1913 found,
1914 temp,
1915 pxRatio,
1916 windowWidth;
1917
1918 // If we have "srcset", then we need to find first matching "src" value.
1919 // This is necessary, because when you set an src attribute, the browser will preload the image
1920 // before any javascript or even CSS is applied.
1921 if (srcset) {
1922 pxRatio = window.devicePixelRatio || 1;
1923 windowWidth = window.innerWidth * pxRatio;
1924
1925 temp = srcset.split(",").map(function(el) {
1926 var ret = {};
1927
1928 el.trim()
1929 .split(/\s+/)
1930 .forEach(function(el, i) {
1931 var value = parseInt(el.substring(0, el.length - 1), 10);
1932
1933 if (i === 0) {
1934 return (ret.url = el);
1935 }
1936
1937 if (value) {
1938 ret.value = value;
1939 ret.postfix = el[el.length - 1];
1940 }
1941 });
1942
1943 return ret;
1944 });
1945
1946 // Sort by value
1947 temp.sort(function(a, b) {
1948 return a.value - b.value;
1949 });
1950
1951 // Ok, now we have an array of all srcset values
1952 for (var j = 0; j < temp.length; j++) {
1953 var el = temp[j];
1954
1955 if ((el.postfix === "w" && el.value >= windowWidth) || (el.postfix === "x" && el.value >= pxRatio)) {
1956 found = el;
1957 break;
1958 }
1959 }
1960
1961 // If not found, take the last one
1962 if (!found && temp.length) {
1963 found = temp[temp.length - 1];
1964 }
1965
1966 if (found) {
1967 slide.src = found.url;
1968
1969 // If we have default width/height values, we can calculate height for matching source
1970 if (slide.width && slide.height && found.postfix == "w") {
1971 slide.height = (slide.width / slide.height) * found.value;
1972 slide.width = found.value;
1973 }
1974
1975 slide.opts.srcset = srcset;
1976 }
1977 }
1978 },
1979
1980 // Create full-size image
1981 // ======================
1982
1983 setBigImage: function(slide) {
1984 var self = this,
1985 img = document.createElement("img"),
1986 $img = $(img);
1987
1988 slide.$image = $img
1989 .one("error", function() {
1990 self.setError(slide);
1991 })
1992 .one("load", function() {
1993 var sizes;
1994
1995 if (!slide.$ghost) {
1996 self.resolveImageSlideSize(slide, this.naturalWidth, this.naturalHeight);
1997
1998 self.afterLoad(slide);
1999 }
2000
2001 if (self.isClosing) {
2002 return;
2003 }
2004
2005 if (slide.opts.srcset) {
2006 sizes = slide.opts.sizes;
2007
2008 if (!sizes || sizes === "auto") {
2009 sizes =
2010 (slide.width / slide.height > 1 && $W.width() / $W.height() > 1 ? "100" : Math.round((slide.width / slide.height) * 100)) +
2011 "vw";
2012 }
2013
2014 $img.attr("sizes", sizes).attr("srcset", slide.opts.srcset);
2015 }
2016
2017 // Hide temporary image after some delay
2018 if (slide.$ghost) {
2019 setTimeout(function() {
2020 if (slide.$ghost && !self.isClosing) {
2021 slide.$ghost.hide();
2022 }
2023 }, Math.min(300, Math.max(1000, slide.height / 1600)));
2024 }
2025
2026 self.hideLoading(slide);
2027 })
2028 .addClass("fancybox-image")
2029 .attr("src", slide.src)
2030 .appendTo(slide.$content);
2031
2032 if ((img.complete || img.readyState == "complete") && $img.naturalWidth && $img.naturalHeight) {
2033 $img.trigger("load");
2034 } else if (img.error) {
2035 $img.trigger("error");
2036 }
2037 },
2038
2039 // Computes the slide size from image size and maxWidth/maxHeight
2040 // ==============================================================
2041
2042 resolveImageSlideSize: function(slide, imgWidth, imgHeight) {
2043 var maxWidth = parseInt(slide.opts.width, 10),
2044 maxHeight = parseInt(slide.opts.height, 10);
2045
2046 // Sets the default values from the image
2047 slide.width = imgWidth;
2048 slide.height = imgHeight;
2049
2050 if (maxWidth > 0) {
2051 slide.width = maxWidth;
2052 slide.height = Math.floor((maxWidth * imgHeight) / imgWidth);
2053 }
2054
2055 if (maxHeight > 0) {
2056 slide.width = Math.floor((maxHeight * imgWidth) / imgHeight);
2057 slide.height = maxHeight;
2058 }
2059 },
2060
2061 // Create iframe wrapper, iframe and bindings
2062 // ==========================================
2063
2064 setIframe: function(slide) {
2065 var self = this,
2066 opts = slide.opts.iframe,
2067 $slide = slide.$slide,
2068 $iframe;
2069
2070 // Fix responsive iframes on iOS (along with `position:absolute;` for iframe element)
2071 if ($.fancybox.isMobile) {
2072 opts.css.overflow = "scroll";
2073 }
2074
2075 slide.$content = $('<div class="fancybox-content' + (opts.preload ? " fancybox-is-hidden" : "") + '"></div>')
2076 .css(opts.css)
2077 .appendTo($slide);
2078
2079 $slide.addClass("fancybox-slide--" + slide.contentType);
2080
2081 slide.$iframe = $iframe = $(opts.tpl.replace(/\{rnd\}/g, new Date().getTime()))
2082 .attr(opts.attr)
2083 .appendTo(slide.$content);
2084
2085 if (opts.preload) {
2086 self.showLoading(slide);
2087
2088 // Unfortunately, it is not always possible to determine if iframe is successfully loaded
2089 // (due to browser security policy)
2090
2091 $iframe.on("load.fb error.fb", function(e) {
2092 this.isReady = 1;
2093
2094 slide.$slide.trigger("refresh");
2095
2096 self.afterLoad(slide);
2097 });
2098
2099 // Recalculate iframe content size
2100 // ===============================
2101
2102 $slide.on("refresh.fb", function() {
2103 var $content = slide.$content,
2104 frameWidth = opts.css.width,
2105 frameHeight = opts.css.height,
2106 $contents,
2107 $body;
2108
2109 if ($iframe[0].isReady !== 1) {
2110 return;
2111 }
2112
2113 try {
2114 $contents = $iframe.contents();
2115 $body = $contents.find("body");
2116 } catch (ignore) {}
2117
2118 // Calculate contnet dimensions if it is accessible
2119 if ($body && $body.length && $body.children().length) {
2120 // Avoid scrolling to top (if multiple instances)
2121 $slide.css("overflow", "visible");
2122
2123 $content.css({
2124 width: "100%",
2125 "max-width": "100%",
2126 height: "9999px"
2127 });
2128
2129 if (frameWidth === undefined) {
2130 frameWidth = Math.ceil(Math.max($body[0].clientWidth, $body.outerWidth(true)));
2131 }
2132
2133 $content.css("width", frameWidth ? frameWidth : "").css("max-width", "");
2134
2135 if (frameHeight === undefined) {
2136 frameHeight = Math.ceil(Math.max($body[0].clientHeight, $body.outerHeight(true)));
2137 }
2138
2139 $content.css("height", frameHeight ? frameHeight : "");
2140
2141 $slide.css("overflow", "auto");
2142 }
2143
2144 $content.removeClass("fancybox-is-hidden");
2145 });
2146 } else {
2147 self.afterLoad(slide);
2148 }
2149
2150 $iframe.attr("src", slide.src);
2151
2152 // Remove iframe if closing or changing gallery item
2153 $slide.one("onReset", function() {
2154 // This helps IE not to throw errors when closing
2155 try {
2156 $(this)
2157 .find("iframe")
2158 .hide()
2159 .unbind()
2160 .attr("src", "//about:blank");
2161 } catch (ignore) {}
2162
2163 $(this)
2164 .off("refresh.fb")
2165 .empty();
2166
2167 slide.isLoaded = false;
2168 slide.isRevealed = false;
2169 });
2170 },
2171
2172 // Wrap and append content to the slide
2173 // ======================================
2174
2175 setContent: function(slide, content) {
2176 var self = this;
2177
2178 if (self.isClosing) {
2179 return;
2180 }
2181
2182 self.hideLoading(slide);
2183
2184 if (slide.$content) {
2185 $.fancybox.stop(slide.$content);
2186 }
2187
2188 slide.$slide.empty();
2189
2190 // If content is a jQuery object, then it will be moved to the slide.
2191 // The placeholder is created so we will know where to put it back.
2192 if (isQuery(content) && content.parent().length) {
2193 // Make sure content is not already moved to fancyBox
2194 if (content.hasClass("fancybox-content") || content.parent().hasClass("fancybox-content")) {
2195 content.parents(".fancybox-slide").trigger("onReset");
2196 }
2197
2198 // Create temporary element marking original place of the content
2199 slide.$placeholder = $("<div>")
2200 .hide()
2201 .insertAfter(content);
2202
2203 // Make sure content is visible
2204 content.css("display", "inline-block");
2205 } else if (!slide.hasError) {
2206 // If content is just a plain text, try to convert it to html
2207 if ($.type(content) === "string") {
2208 content = $("<div>")
2209 .append($.trim(content))
2210 .contents();
2211 }
2212
2213 // If "filter" option is provided, then filter content
2214 if (slide.opts.filter) {
2215 content = $("<div>")
2216 .html(content)
2217 .find(slide.opts.filter);
2218 }
2219 }
2220
2221 slide.$slide.one("onReset", function() {
2222 // Pause all html5 video/audio
2223 $(this)
2224 .find("video,audio")
2225 .trigger("pause");
2226
2227 // Put content back
2228 if (slide.$placeholder) {
2229 slide.$placeholder.after(content.removeClass("fancybox-content").hide()).remove();
2230
2231 slide.$placeholder = null;
2232 }
2233
2234 // Remove custom close button
2235 if (slide.$smallBtn) {
2236 slide.$smallBtn.remove();
2237
2238 slide.$smallBtn = null;
2239 }
2240
2241 // Remove content and mark slide as not loaded
2242 if (!slide.hasError) {
2243 $(this).empty();
2244
2245 slide.isLoaded = false;
2246 slide.isRevealed = false;
2247 }
2248 });
2249
2250 $(content).appendTo(slide.$slide);
2251
2252 if ($(content).is("video,audio")) {
2253 $(content).addClass("fancybox-video");
2254
2255 $(content).wrap("<div></div>");
2256
2257 slide.contentType = "video";
2258
2259 slide.opts.width = slide.opts.width || $(content).attr("width");
2260 slide.opts.height = slide.opts.height || $(content).attr("height");
2261 }
2262
2263 slide.$content = slide.$slide
2264 .children()
2265 .filter("div,form,main,video,audio,article,.fancybox-content")
2266 .first();
2267
2268 slide.$content.siblings().hide();
2269
2270 // Re-check if there is a valid content
2271 // (in some cases, ajax response can contain various elements or plain text)
2272 if (!slide.$content.length) {
2273 slide.$content = slide.$slide
2274 .wrapInner("<div></div>")
2275 .children()
2276 .first();
2277 }
2278
2279 slide.$content.addClass("fancybox-content");
2280
2281 slide.$slide.addClass("fancybox-slide--" + slide.contentType);
2282
2283 self.afterLoad(slide);
2284 },
2285
2286 // Display error message
2287 // =====================
2288
2289 setError: function(slide) {
2290 slide.hasError = true;
2291
2292 slide.$slide
2293 .trigger("onReset")
2294 .removeClass("fancybox-slide--" + slide.contentType)
2295 .addClass("fancybox-slide--error");
2296
2297 slide.contentType = "html";
2298
2299 this.setContent(slide, this.translate(slide, slide.opts.errorTpl));
2300
2301 if (slide.pos === this.currPos) {
2302 this.isAnimating = false;
2303 }
2304 },
2305
2306 // Show loading icon inside the slide
2307 // ==================================
2308
2309 showLoading: function(slide) {
2310 var self = this;
2311
2312 slide = slide || self.current;
2313
2314 if (slide && !slide.$spinner) {
2315 slide.$spinner = $(self.translate(self, self.opts.spinnerTpl))
2316 .appendTo(slide.$slide)
2317 .hide()
2318 .fadeIn("fast");
2319 }
2320 },
2321
2322 // Remove loading icon from the slide
2323 // ==================================
2324
2325 hideLoading: function(slide) {
2326 var self = this;
2327
2328 slide = slide || self.current;
2329
2330 if (slide && slide.$spinner) {
2331 slide.$spinner.stop().remove();
2332
2333 delete slide.$spinner;
2334 }
2335 },
2336
2337 // Adjustments after slide content has been loaded
2338 // ===============================================
2339
2340 afterLoad: function(slide) {
2341 var self = this;
2342
2343 if (self.isClosing) {
2344 return;
2345 }
2346
2347 slide.isLoading = false;
2348 slide.isLoaded = true;
2349
2350 self.trigger("afterLoad", slide);
2351
2352 self.hideLoading(slide);
2353
2354 // Add small close button
2355 if (slide.opts.smallBtn && (!slide.$smallBtn || !slide.$smallBtn.length)) {
2356 slide.$smallBtn = $(self.translate(slide, slide.opts.btnTpl.smallBtn)).appendTo(slide.$content);
2357 }
2358
2359 // Disable right click
2360 if (slide.opts.protect && slide.$content && !slide.hasError) {
2361 slide.$content.on("contextmenu.fb", function(e) {
2362 if (e.button == 2) {
2363 e.preventDefault();
2364 }
2365
2366 return true;
2367 });
2368
2369 // Add fake element on top of the image
2370 // This makes a bit harder for user to select image
2371 if (slide.type === "image") {
2372 $('<div class="fancybox-spaceball"></div>').appendTo(slide.$content);
2373 }
2374 }
2375
2376 self.adjustCaption(slide);
2377
2378 self.adjustLayout(slide);
2379
2380 if (slide.pos === self.currPos) {
2381 self.updateCursor();
2382 }
2383
2384 self.revealContent(slide);
2385 },
2386
2387 // Prevent caption overlap,
2388 // fix css inconsistency across browsers
2389 // =====================================
2390
2391 adjustCaption: function(slide) {
2392 var self = this,
2393 current = slide || self.current,
2394 caption = current.opts.caption,
2395 $caption = self.$refs.caption,
2396 captionH = false;
2397
2398 if (current.opts.preventCaptionOverlap && caption && caption.length) {
2399 if (current.pos !== self.currPos) {
2400 $caption = $caption
2401 .clone()
2402 .empty()
2403 .appendTo($caption.parent());
2404
2405 $caption.html(caption);
2406
2407 captionH = $caption.outerHeight(true);
2408
2409 $caption.empty().remove();
2410 } else if (self.$caption) {
2411 captionH = self.$caption.outerHeight(true);
2412 }
2413
2414 current.$slide.css("padding-bottom", captionH || "");
2415 }
2416 },
2417
2418 // Simple hack to fix inconsistency across browsers, described here (affects Edge, too):
2419 // https://bugzilla.mozilla.org/show_bug.cgi?id=748518
2420 // ====================================================================================
2421
2422 adjustLayout: function(slide) {
2423 var self = this,
2424 current = slide || self.current,
2425 scrollHeight,
2426 marginBottom,
2427 inlinePadding,
2428 actualPadding;
2429
2430 if (current.isLoaded && current.opts.disableLayoutFix !== true) {
2431 current.$content.css("margin-bottom", "");
2432
2433 // If we would always set margin-bottom for the content,
2434 // then it would potentially break vertical align
2435 if (current.$content.outerHeight() > current.$slide.height() + 0.5) {
2436 inlinePadding = current.$slide[0].style["padding-bottom"];
2437 actualPadding = current.$slide.css("padding-bottom");
2438
2439 if (parseFloat(actualPadding) > 0) {
2440 scrollHeight = current.$slide[0].scrollHeight;
2441
2442 current.$slide.css("padding-bottom", 0);
2443
2444 if (Math.abs(scrollHeight - current.$slide[0].scrollHeight) < 1) {
2445 marginBottom = actualPadding;
2446 }
2447
2448 current.$slide.css("padding-bottom", inlinePadding);
2449 }
2450 }
2451
2452 current.$content.css("margin-bottom", marginBottom);
2453 }
2454 },
2455
2456 // Make content visible
2457 // This method is called right after content has been loaded or
2458 // user navigates gallery and transition should start
2459 // ============================================================
2460
2461 revealContent: function(slide) {
2462 var self = this,
2463 $slide = slide.$slide,
2464 end = false,
2465 start = false,
2466 isMoved = self.isMoved(slide),
2467 isRevealed = slide.isRevealed,
2468 effect,
2469 effectClassName,
2470 duration,
2471 opacity;
2472
2473 slide.isRevealed = true;
2474
2475 effect = slide.opts[self.firstRun ? "animationEffect" : "transitionEffect"];
2476 duration = slide.opts[self.firstRun ? "animationDuration" : "transitionDuration"];
2477
2478 duration = parseInt(slide.forcedDuration === undefined ? duration : slide.forcedDuration, 10);
2479
2480 if (isMoved || slide.pos !== self.currPos || !duration) {
2481 effect = false;
2482 }
2483
2484 // Check if can zoom
2485 if (effect === "zoom") {
2486 if (slide.pos === self.currPos && duration && slide.type === "image" && !slide.hasError && (start = self.getThumbPos(slide))) {
2487 end = self.getFitPos(slide);
2488 } else {
2489 effect = "fade";
2490 }
2491 }
2492
2493 // Zoom animation
2494 // ==============
2495 if (effect === "zoom") {
2496 self.isAnimating = true;
2497
2498 end.scaleX = end.width / start.width;
2499 end.scaleY = end.height / start.height;
2500
2501 // Check if we need to animate opacity
2502 opacity = slide.opts.zoomOpacity;
2503
2504 if (opacity == "auto") {
2505 opacity = Math.abs(slide.width / slide.height - start.width / start.height) > 0.1;
2506 }
2507
2508 if (opacity) {
2509 start.opacity = 0.1;
2510 end.opacity = 1;
2511 }
2512
2513 // Draw image at start position
2514 $.fancybox.setTranslate(slide.$content.removeClass("fancybox-is-hidden"), start);
2515
2516 forceRedraw(slide.$content);
2517
2518 // Start animation
2519 $.fancybox.animate(slide.$content, end, duration, function() {
2520 self.isAnimating = false;
2521
2522 self.complete();
2523 });
2524
2525 return;
2526 }
2527
2528 self.updateSlide(slide);
2529
2530 // Simply show content if no effect
2531 // ================================
2532 if (!effect) {
2533 slide.$content.removeClass("fancybox-is-hidden");
2534
2535 if (!isRevealed && isMoved && slide.type === "image" && !slide.hasError) {
2536 slide.$content.hide().fadeIn("fast");
2537 }
2538
2539 if (slide.pos === self.currPos) {
2540 self.complete();
2541 }
2542
2543 return;
2544 }
2545
2546 // Prepare for CSS transiton
2547 // =========================
2548 $.fancybox.stop($slide);
2549
2550 //effectClassName = "fancybox-animated fancybox-slide--" + (slide.pos >= self.prevPos ? "next" : "previous") + " fancybox-fx-" + effect;
2551 effectClassName = "fancybox-slide--" + (slide.pos >= self.prevPos ? "next" : "previous") + " fancybox-animated fancybox-fx-" + effect;
2552
2553 $slide.addClass(effectClassName).removeClass("fancybox-slide--current"); //.addClass(effectClassName);
2554
2555 slide.$content.removeClass("fancybox-is-hidden");
2556
2557 // Force reflow
2558 forceRedraw($slide);
2559
2560 if (slide.type !== "image") {
2561 slide.$content.hide().show(0);
2562 }
2563
2564 $.fancybox.animate(
2565 $slide,
2566 "fancybox-slide--current",
2567 duration,
2568 function() {
2569 $slide.removeClass(effectClassName).css({
2570 transform: "",
2571 opacity: ""
2572 });
2573
2574 if (slide.pos === self.currPos) {
2575 self.complete();
2576 }
2577 },
2578 true
2579 );
2580 },
2581
2582 // Check if we can and have to zoom from thumbnail
2583 //================================================
2584
2585 getThumbPos: function(slide) {
2586 var rez = false,
2587 $thumb = slide.$thumb,
2588 thumbPos,
2589 btw,
2590 brw,
2591 bbw,
2592 blw;
2593
2594 if (!$thumb || !inViewport($thumb[0])) {
2595 return false;
2596 }
2597
2598 thumbPos = $.fancybox.getTranslate($thumb);
2599
2600 btw = parseFloat($thumb.css("border-top-width") || 0);
2601 brw = parseFloat($thumb.css("border-right-width") || 0);
2602 bbw = parseFloat($thumb.css("border-bottom-width") || 0);
2603 blw = parseFloat($thumb.css("border-left-width") || 0);
2604
2605 rez = {
2606 top: thumbPos.top + btw,
2607 left: thumbPos.left + blw,
2608 width: thumbPos.width - brw - blw,
2609 height: thumbPos.height - btw - bbw,
2610 scaleX: 1,
2611 scaleY: 1
2612 };
2613
2614 return thumbPos.width > 0 && thumbPos.height > 0 ? rez : false;
2615 },
2616
2617 // Final adjustments after current gallery item is moved to position
2618 // and it`s content is loaded
2619 // ==================================================================
2620
2621 complete: function() {
2622 var self = this,
2623 current = self.current,
2624 slides = {},
2625 $el;
2626
2627 if (self.isMoved() || !current.isLoaded) {
2628 return;
2629 }
2630
2631 if (!current.isComplete) {
2632 current.isComplete = true;
2633
2634 current.$slide.siblings().trigger("onReset");
2635
2636 self.preload("inline");
2637
2638 // Trigger any CSS transiton inside the slide
2639 forceRedraw(current.$slide);
2640
2641 current.$slide.addClass("fancybox-slide--complete");
2642
2643 // Remove unnecessary slides
2644 $.each(self.slides, function(key, slide) {
2645 if (slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1) {
2646 slides[slide.pos] = slide;
2647 } else if (slide) {
2648 $.fancybox.stop(slide.$slide);
2649
2650 slide.$slide.off().remove();
2651 }
2652 });
2653
2654 self.slides = slides;
2655 }
2656
2657 self.isAnimating = false;
2658
2659 self.updateCursor();
2660
2661 self.trigger("afterShow");
2662
2663 // Autoplay first html5 video/audio
2664 if (!!current.opts.video.autoStart) {
2665 current.$slide
2666 .find("video,audio")
2667 .filter(":visible:first")
2668 .trigger("play")
2669 .one("ended", function() {
2670 if (this.webkitExitFullscreen) {
2671 this.webkitExitFullscreen();
2672 }
2673
2674 self.next();
2675 });
2676 }
2677
2678 // Try to focus on the first focusable element
2679 if (current.opts.autoFocus && current.contentType === "html") {
2680 // Look for the first input with autofocus attribute
2681 $el = current.$content.find("input[autofocus]:enabled:visible:first");
2682
2683 if ($el.length) {
2684 $el.trigger("focus");
2685 } else {
2686 self.focus(null, true);
2687 }
2688 }
2689
2690 // Avoid jumping
2691 current.$slide.scrollTop(0).scrollLeft(0);
2692 },
2693
2694 // Preload next and previous slides
2695 // ================================
2696
2697 preload: function(type) {
2698 var self = this,
2699 prev,
2700 next;
2701
2702 if (self.group.length < 2) {
2703 return;
2704 }
2705
2706 next = self.slides[self.currPos + 1];
2707 prev = self.slides[self.currPos - 1];
2708
2709 if (prev && prev.type === type) {
2710 self.loadSlide(prev);
2711 }
2712
2713 if (next && next.type === type) {
2714 self.loadSlide(next);
2715 }
2716 },
2717
2718 // Try to find and focus on the first focusable element
2719 // ====================================================
2720
2721 focus: function(e, firstRun) {
2722 var self = this,
2723 focusableStr = [
2724 "a[href]",
2725 "area[href]",
2726 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])',
2727 "select:not([disabled]):not([aria-hidden])",
2728 "textarea:not([disabled]):not([aria-hidden])",
2729 "button:not([disabled]):not([aria-hidden])",
2730 "iframe",
2731 "object",
2732 "embed",
2733 "[contenteditable]",
2734 '[tabindex]:not([tabindex^="-"])'
2735 ].join(","),
2736 focusableItems,
2737 focusedItemIndex;
2738
2739 if (self.isClosing) {
2740 return;
2741 }
2742
2743 if (e || !self.current || !self.current.isComplete) {
2744 // Focus on any element inside fancybox
2745 focusableItems = self.$refs.container.find("*:visible");
2746 } else {
2747 // Focus inside current slide
2748 focusableItems = self.current.$slide.find("*:visible" + (firstRun ? ":not(.fancybox-close-small)" : ""));
2749 }
2750
2751 focusableItems = focusableItems.filter(focusableStr).filter(function() {
2752 return $(this).css("visibility") !== "hidden" && !$(this).hasClass("disabled");
2753 });
2754
2755 if (focusableItems.length) {
2756 focusedItemIndex = focusableItems.index(document.activeElement);
2757
2758 if (e && e.shiftKey) {
2759 // Back tab
2760 if (focusedItemIndex < 0 || focusedItemIndex == 0) {
2761 e.preventDefault();
2762
2763 focusableItems.eq(focusableItems.length - 1).trigger("focus");
2764 }
2765 } else {
2766 // Outside or Forward tab
2767 if (focusedItemIndex < 0 || focusedItemIndex == focusableItems.length - 1) {
2768 if (e) {
2769 e.preventDefault();
2770 }
2771
2772 focusableItems.eq(0).trigger("focus");
2773 }
2774 }
2775 } else {
2776 self.$refs.container.trigger("focus");
2777 }
2778 },
2779
2780 // Activates current instance - brings container to the front and enables keyboard,
2781 // notifies other instances about deactivating
2782 // =================================================================================
2783
2784 activate: function() {
2785 var self = this;
2786
2787 // Deactivate all instances
2788 $(".fancybox-container").each(function() {
2789 var instance = $(this).data("FancyBox");
2790
2791 // Skip self and closing instances
2792 if (instance && instance.id !== self.id && !instance.isClosing) {
2793 instance.trigger("onDeactivate");
2794
2795 instance.removeEvents();
2796
2797 instance.isVisible = false;
2798 }
2799 });
2800
2801 self.isVisible = true;
2802
2803 if (self.current || self.isIdle) {
2804 self.update();
2805
2806 self.updateControls();
2807 }
2808
2809 self.trigger("onActivate");
2810
2811 self.addEvents();
2812 },
2813
2814 // Start closing procedure
2815 // This will start "zoom-out" animation if needed and clean everything up afterwards
2816 // =================================================================================
2817
2818 close: function(e, d) {
2819 var self = this,
2820 current = self.current,
2821 effect,
2822 duration,
2823 $content,
2824 domRect,
2825 opacity,
2826 start,
2827 end;
2828
2829 var done = function() {
2830 self.cleanUp(e);
2831 };
2832
2833 if (self.isClosing) {
2834 return false;
2835 }
2836
2837 self.isClosing = true;
2838
2839 // If beforeClose callback prevents closing, make sure content is centered
2840 if (self.trigger("beforeClose", e) === false) {
2841 self.isClosing = false;
2842
2843 requestAFrame(function() {
2844 self.update();
2845 });
2846
2847 return false;
2848 }
2849
2850 // Remove all events
2851 // If there are multiple instances, they will be set again by "activate" method
2852 self.removeEvents();
2853
2854 $content = current.$content;
2855 effect = current.opts.animationEffect;
2856 duration = $.isNumeric(d) ? d : effect ? current.opts.animationDuration : 0;
2857
2858 current.$slide.removeClass("fancybox-slide--complete fancybox-slide--next fancybox-slide--previous fancybox-animated");
2859
2860 if (e !== true) {
2861 $.fancybox.stop(current.$slide);
2862 } else {
2863 effect = false;
2864 }
2865
2866 // Remove other slides
2867 current.$slide
2868 .siblings()
2869 .trigger("onReset")
2870 .remove();
2871
2872 // Trigger animations
2873 if (duration) {
2874 self.$refs.container
2875 .removeClass("fancybox-is-open")
2876 .addClass("fancybox-is-closing")
2877 .css("transition-duration", duration + "ms");
2878 }
2879
2880 // Clean up
2881 self.hideLoading(current);
2882
2883 self.hideControls(true);
2884
2885 self.updateCursor();
2886
2887 // Check if possible to zoom-out
2888 if (
2889 effect === "zoom" &&
2890 !($content && duration && current.type === "image" && !self.isMoved() && !current.hasError && (end = self.getThumbPos(current)))
2891 ) {
2892 effect = "fade";
2893 }
2894
2895 if (effect === "zoom") {
2896 $.fancybox.stop($content);
2897
2898 domRect = $.fancybox.getTranslate($content);
2899
2900 start = {
2901 top: domRect.top,
2902 left: domRect.left,
2903 scaleX: domRect.width / end.width,
2904 scaleY: domRect.height / end.height,
2905 width: end.width,
2906 height: end.height
2907 };
2908
2909 // Check if we need to animate opacity
2910 opacity = current.opts.zoomOpacity;
2911
2912 if (opacity == "auto") {
2913 opacity = Math.abs(current.width / current.height - end.width / end.height) > 0.1;
2914 }
2915
2916 if (opacity) {
2917 end.opacity = 0;
2918 }
2919
2920 $.fancybox.setTranslate($content, start);
2921
2922 forceRedraw($content);
2923
2924 $.fancybox.animate($content, end, duration, done);
2925
2926 return true;
2927 }
2928
2929 if (effect && duration) {
2930 $.fancybox.animate(
2931 current.$slide.addClass("fancybox-slide--previous").removeClass("fancybox-slide--current"),
2932 "fancybox-animated fancybox-fx-" + effect,
2933 duration,
2934 done
2935 );
2936 } else {
2937 // If skip animation
2938 if (e === true) {
2939 setTimeout(done, duration);
2940 } else {
2941 done();
2942 }
2943 }
2944
2945 return true;
2946 },
2947
2948 // Final adjustments after removing the instance
2949 // =============================================
2950
2951 cleanUp: function(e) {
2952 var self = this,
2953 instance,
2954 $focus = self.current.opts.$orig,
2955 x,
2956 y;
2957
2958 self.current.$slide.trigger("onReset");
2959
2960 self.$refs.container.empty().remove();
2961
2962 self.trigger("afterClose", e);
2963
2964 // Place back focus
2965 if (!!self.current.opts.backFocus) {
2966 if (!$focus || !$focus.length || !$focus.is(":visible")) {
2967 $focus = self.$trigger;
2968 }
2969
2970 if ($focus && $focus.length) {
2971 x = window.scrollX;
2972 y = window.scrollY;
2973
2974 $focus.trigger("focus");
2975
2976 $("html, body")
2977 .scrollTop(y)
2978 .scrollLeft(x);
2979 }
2980 }
2981
2982 self.current = null;
2983
2984 // Check if there are other instances
2985 instance = $.fancybox.getInstance();
2986
2987 if (instance) {
2988 instance.activate();
2989 } else {
2990 $("body").removeClass("fancybox-active compensate-for-scrollbar");
2991
2992 $("#fancybox-style-noscroll").remove();
2993 }
2994 },
2995
2996 // Call callback and trigger an event
2997 // ==================================
2998
2999 trigger: function(name, slide) {
3000 var args = Array.prototype.slice.call(arguments, 1),
3001 self = this,
3002 obj = slide && slide.opts ? slide : self.current,
3003 rez;
3004
3005 if (obj) {
3006 args.unshift(obj);
3007 } else {
3008 obj = self;
3009 }
3010
3011 args.unshift(self);
3012
3013 if ($.isFunction(obj.opts[name])) {
3014 rez = obj.opts[name].apply(obj, args);
3015 }
3016
3017 if (rez === false) {
3018 return rez;
3019 }
3020
3021 if (name === "afterClose" || !self.$refs) {
3022 $D.trigger(name + ".fb", args);
3023 } else {
3024 self.$refs.container.trigger(name + ".fb", args);
3025 }
3026 },
3027
3028 // Update infobar values, navigation button states and reveal caption
3029 // ==================================================================
3030
3031 updateControls: function() {
3032 var self = this,
3033 current = self.current,
3034 index = current.index,
3035 $container = self.$refs.container,
3036 $caption = self.$refs.caption,
3037 caption = current.opts.caption;
3038
3039 // Recalculate content dimensions
3040 current.$slide.trigger("refresh");
3041
3042 self.$caption = caption && caption.length ? $caption.html(caption) : null;
3043
3044 if (!self.hasHiddenControls && !self.isIdle) {
3045 self.showControls();
3046 }
3047
3048 // Update info and navigation elements
3049 $container.find("[data-fancybox-count]").html(self.group.length);
3050 $container.find("[data-fancybox-index]").html(index + 1);
3051
3052 $container.find("[data-fancybox-prev]").prop("disabled", !current.opts.loop && index <= 0);
3053 $container.find("[data-fancybox-next]").prop("disabled", !current.opts.loop && index >= self.group.length - 1);
3054
3055 if (current.type === "image") {
3056 // Re-enable buttons; update download button source
3057 $container
3058 .find("[data-fancybox-zoom]")
3059 .show()
3060 .end()
3061 .find("[data-fancybox-download]")
3062 .attr("href", current.opts.image.src || current.src)
3063 .show();
3064 } else if (current.opts.toolbar) {
3065 $container.find("[data-fancybox-download],[data-fancybox-zoom]").hide();
3066 }
3067
3068 // Make sure focus is not on disabled button/element
3069 if ($(document.activeElement).is(":hidden,[disabled]")) {
3070 self.$refs.container.trigger("focus");
3071 }
3072 },
3073
3074 // Hide toolbar and caption
3075 // ========================
3076
3077 hideControls: function(andCaption) {
3078 var self = this,
3079 arr = ["infobar", "toolbar", "nav"];
3080
3081 if (andCaption || !self.current.opts.preventCaptionOverlap) {
3082 arr.push("caption");
3083 }
3084
3085 this.$refs.container.removeClass(
3086 arr
3087 .map(function(i) {
3088 return "fancybox-show-" + i;
3089 })
3090 .join(" ")
3091 );
3092
3093 this.hasHiddenControls = true;
3094 },
3095
3096 showControls: function() {
3097 var self = this,
3098 opts = self.current ? self.current.opts : self.opts,
3099 $container = self.$refs.container;
3100
3101 self.hasHiddenControls = false;
3102 self.idleSecondsCounter = 0;
3103
3104 $container
3105 .toggleClass("fancybox-show-toolbar", !!(opts.toolbar && opts.buttons))
3106 .toggleClass("fancybox-show-infobar", !!(opts.infobar && self.group.length > 1))
3107 .toggleClass("fancybox-show-caption", !!self.$caption)
3108 .toggleClass("fancybox-show-nav", !!(opts.arrows && self.group.length > 1))
3109 .toggleClass("fancybox-is-modal", !!opts.modal);
3110 },
3111
3112 // Toggle toolbar and caption
3113 // ==========================
3114
3115 toggleControls: function() {
3116 if (this.hasHiddenControls) {
3117 this.showControls();
3118 } else {
3119 this.hideControls();
3120 }
3121 }
3122 });
3123
3124 $.fancybox = {
3125 version: "3.5.2",
3126 defaults: defaults,
3127
3128 // Get current instance and execute a command.
3129 //
3130 // Examples of usage:
3131 //
3132 // $instance = $.fancybox.getInstance();
3133 // $.fancybox.getInstance().jumpTo( 1 );
3134 // $.fancybox.getInstance( 'jumpTo', 1 );
3135 // $.fancybox.getInstance( function() {
3136 // console.info( this.currIndex );
3137 // });
3138 // ======================================================
3139
3140 getInstance: function(command) {
3141 var instance = $('.fancybox-container:not(".fancybox-is-closing"):last').data("FancyBox"),
3142 args = Array.prototype.slice.call(arguments, 1);
3143
3144 if (instance instanceof FancyBox) {
3145 if ($.type(command) === "string") {
3146 instance[command].apply(instance, args);
3147 } else if ($.type(command) === "function") {
3148 command.apply(instance, args);
3149 }
3150
3151 return instance;
3152 }
3153
3154 return false;
3155 },
3156
3157 // Create new instance
3158 // ===================
3159
3160 open: function(items, opts, index) {
3161 return new FancyBox(items, opts, index);
3162 },
3163
3164 // Close current or all instances
3165 // ==============================
3166
3167 close: function(all) {
3168 var instance = this.getInstance();
3169
3170 if (instance) {
3171 instance.close();
3172
3173 // Try to find and close next instance
3174 if (all === true) {
3175 this.close(all);
3176 }
3177 }
3178 },
3179
3180 // Close all instances and unbind all events
3181 // =========================================
3182
3183 destroy: function() {
3184 this.close(true);
3185
3186 $D.add("body").off("click.fb-start", "**");
3187 },
3188
3189 // Try to detect mobile devices
3190 // ============================
3191
3192 isMobile: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),
3193
3194 // Detect if 'translate3d' support is available
3195 // ============================================
3196
3197 use3d: (function() {
3198 var div = document.createElement("div");
3199
3200 return (
3201 window.getComputedStyle &&
3202 window.getComputedStyle(div) &&
3203 window.getComputedStyle(div).getPropertyValue("transform") &&
3204 !(document.documentMode && document.documentMode < 11)
3205 );
3206 })(),
3207
3208 // Helper function to get current visual state of an element
3209 // returns array[ top, left, horizontal-scale, vertical-scale, opacity ]
3210 // =====================================================================
3211
3212 getTranslate: function($el) {
3213 var domRect;
3214
3215 if (!$el || !$el.length) {
3216 return false;
3217 }
3218
3219 domRect = $el[0].getBoundingClientRect();
3220
3221 return {
3222 top: domRect.top || 0,
3223 left: domRect.left || 0,
3224 width: domRect.width,
3225 height: domRect.height,
3226 opacity: parseFloat($el.css("opacity"))
3227 };
3228 },
3229
3230 // Shortcut for setting "translate3d" properties for element
3231 // Can set be used to set opacity, too
3232 // ========================================================
3233
3234 setTranslate: function($el, props) {
3235 var str = "",
3236 css = {};
3237
3238 if (!$el || !props) {
3239 return;
3240 }
3241
3242 if (props.left !== undefined || props.top !== undefined) {
3243 str =
3244 (props.left === undefined ? $el.position().left : props.left) +
3245 "px, " +
3246 (props.top === undefined ? $el.position().top : props.top) +
3247 "px";
3248
3249 if (this.use3d) {
3250 str = "translate3d(" + str + ", 0px)";
3251 } else {
3252 str = "translate(" + str + ")";
3253 }
3254 }
3255
3256 if (props.scaleX !== undefined && props.scaleY !== undefined) {
3257 str += " scale(" + props.scaleX + ", " + props.scaleY + ")";
3258 } else if (props.scaleX !== undefined) {
3259 str += " scaleX(" + props.scaleX + ")";
3260 }
3261
3262 if (str.length) {
3263 css.transform = str;
3264 }
3265
3266 if (props.opacity !== undefined) {
3267 css.opacity = props.opacity;
3268 }
3269
3270 if (props.width !== undefined) {
3271 css.width = props.width;
3272 }
3273
3274 if (props.height !== undefined) {
3275 css.height = props.height;
3276 }
3277
3278 return $el.css(css);
3279 },
3280
3281 // Simple CSS transition handler
3282 // =============================
3283
3284 animate: function($el, to, duration, callback, leaveAnimationName) {
3285 var self = this,
3286 from;
3287
3288 if ($.isFunction(duration)) {
3289 callback = duration;
3290 duration = null;
3291 }
3292
3293 self.stop($el);
3294
3295 from = self.getTranslate($el);
3296
3297 $el.on(transitionEnd, function(e) {
3298 // Skip events from child elements and z-index change
3299 if (e && e.originalEvent && (!$el.is(e.originalEvent.target) || e.originalEvent.propertyName == "z-index")) {
3300 return;
3301 }
3302
3303 self.stop($el);
3304
3305 if ($.isNumeric(duration)) {
3306 $el.css("transition-duration", "");
3307 }
3308
3309 if ($.isPlainObject(to)) {
3310 if (to.scaleX !== undefined && to.scaleY !== undefined) {
3311 self.setTranslate($el, {
3312 top: to.top,
3313 left: to.left,
3314 width: from.width * to.scaleX,
3315 height: from.height * to.scaleY,
3316 scaleX: 1,
3317 scaleY: 1
3318 });
3319 }
3320 } else if (leaveAnimationName !== true) {
3321 $el.removeClass(to);
3322 }
3323
3324 if ($.isFunction(callback)) {
3325 callback(e);
3326 }
3327 });
3328
3329 if ($.isNumeric(duration)) {
3330 $el.css("transition-duration", duration + "ms");
3331 }
3332
3333 // Start animation by changing CSS properties or class name
3334 if ($.isPlainObject(to)) {
3335 if (to.scaleX !== undefined && to.scaleY !== undefined) {
3336 delete to.width;
3337 delete to.height;
3338
3339 if ($el.parent().hasClass("fancybox-slide--image")) {
3340 $el.parent().addClass("fancybox-is-scaling");
3341 }
3342 }
3343
3344 $.fancybox.setTranslate($el, to);
3345 } else {
3346 $el.addClass(to);
3347 }
3348
3349 // Make sure that `transitionend` callback gets fired
3350 $el.data(
3351 "timer",
3352 setTimeout(function() {
3353 $el.trigger(transitionEnd);
3354 }, duration + 33)
3355 );
3356 },
3357
3358 stop: function($el, callCallback) {
3359 if ($el && $el.length) {
3360 clearTimeout($el.data("timer"));
3361
3362 if (callCallback) {
3363 $el.trigger(transitionEnd);
3364 }
3365
3366 $el.off(transitionEnd).css("transition-duration", "");
3367
3368 $el.parent().removeClass("fancybox-is-scaling");
3369 }
3370 }
3371 };
3372
3373 // Default click handler for "fancyboxed" links
3374 // ============================================
3375
3376 function _run(e, opts) {
3377 var items = [],
3378 index = 0,
3379 $target,
3380 value,
3381 instance;
3382
3383 // Avoid opening multiple times
3384 if (e && e.isDefaultPrevented()) {
3385 return;
3386 }
3387
3388 e.preventDefault();
3389
3390 opts = opts || {};
3391
3392 if (e && e.data) {
3393 opts = mergeOpts(e.data.options, opts);
3394 }
3395
3396 $target = opts.$target || $(e.currentTarget).trigger("blur");
3397 instance = $.fancybox.getInstance();
3398
3399 if (instance && instance.$trigger && instance.$trigger.is($target)) {
3400 return;
3401 }
3402
3403 if (opts.selector) {
3404 items = $(opts.selector);
3405 } else {
3406 // Get all related items and find index for clicked one
3407 value = $target.attr("data-fancybox") || "";
3408
3409 if (value) {
3410 items = e.data ? e.data.items : [];
3411 items = items.length ? items.filter('[data-fancybox="' + value + '"]') : $('[data-fancybox="' + value + '"]');
3412 } else {
3413 items = [$target];
3414 }
3415 }
3416
3417 index = $(items).index($target);
3418
3419 // Sometimes current item can not be found
3420 if (index < 0) {
3421 index = 0;
3422 }
3423
3424 instance = $.fancybox.open(items, opts, index);
3425
3426 // Save last active element
3427 instance.$trigger = $target;
3428 }
3429
3430 // Create a jQuery plugin
3431 // ======================
3432
3433 $.fn.fancybox = function(options) {
3434 var selector;
3435
3436 options = options || {};
3437 selector = options.selector || false;
3438
3439 if (selector) {
3440 // Use body element instead of document so it executes first
3441 $("body")
3442 .off("click.fb-start", selector)
3443 .on("click.fb-start", selector, {options: options}, _run);
3444 } else {
3445 this.off("click.fb-start").on(
3446 "click.fb-start",
3447 {
3448 items: this,
3449 options: options
3450 },
3451 _run
3452 );
3453 }
3454
3455 return this;
3456 };
3457
3458 // Self initializing plugin for all elements having `data-fancybox` attribute
3459 // ==========================================================================
3460
3461 $D.on("click.fb-start", "[data-fancybox]", _run);
3462
3463 // Enable "trigger elements"
3464 // =========================
3465
3466 $D.on("click.fb-start", "[data-fancybox-trigger]", function(e) {
3467 $('[data-fancybox="' + $(this).attr("data-fancybox-trigger") + '"]')
3468 .eq($(this).attr("data-fancybox-index") || 0)
3469 .trigger("click.fb-start", {
3470 $trigger: $(this)
3471 });
3472 });
3473
3474 // Track focus event for better accessibility styling
3475 // ==================================================
3476 (function() {
3477 var buttonStr = ".fancybox-button",
3478 focusStr = "fancybox-focus",
3479 $pressed = null;
3480
3481 $D.on("mousedown mouseup focus blur", buttonStr, function(e) {
3482 switch (e.type) {
3483 case "mousedown":
3484 $pressed = $(this);
3485 break;
3486 case "mouseup":
3487 $pressed = null;
3488 break;
3489 case "focusin":
3490 $(buttonStr).removeClass(focusStr);
3491
3492 if (!$(this).is($pressed) && !$(this).is("[disabled]")) {
3493 $(this).addClass(focusStr);
3494 }
3495 break;
3496 case "focusout":
3497 $(buttonStr).removeClass(focusStr);
3498 break;
3499 }
3500 });
3501 })();
3502})(window, document, jQuery);
3503
3504// ==========================================================================
3505//
3506// Media
3507// Adds additional media type support
3508//
3509// ==========================================================================
3510(function($) {
3511 "use strict";
3512
3513 // Object containing properties for each media type
3514 var defaults = {
3515 youtube: {
3516 matcher: /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i,
3517 params: {
3518 autoplay: 1,
3519 autohide: 1,
3520 fs: 1,
3521 rel: 0,
3522 hd: 1,
3523 wmode: "transparent",
3524 enablejsapi: 1,
3525 html5: 1
3526 },
3527 paramPlace: 8,
3528 type: "iframe",
3529 url: "//www.youtube-nocookie.com/embed/$4",
3530 thumb: "//img.youtube.com/vi/$4/hqdefault.jpg"
3531 },
3532
3533 vimeo: {
3534 matcher: /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/,
3535 params: {
3536 autoplay: 1,
3537 hd: 1,
3538 show_title: 1,
3539 show_byline: 1,
3540 show_portrait: 0,
3541 fullscreen: 1
3542 },
3543 paramPlace: 3,
3544 type: "iframe",
3545 url: "//player.vimeo.com/video/$2"
3546 },
3547
3548 instagram: {
3549 matcher: /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i,
3550 type: "image",
3551 url: "//$1/p/$2/media/?size=l"
3552 },
3553
3554 // Examples:
3555 // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16
3556 // https://www.google.com/maps/@37.7852006,-122.4146355,14.65z
3557 // https://www.google.com/maps/@52.2111123,2.9237542,6.61z?hl=en
3558 // https://www.google.com/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572
3559 gmap_place: {
3560 matcher: /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i,
3561 type: "iframe",
3562 url: function(rez) {
3563 return (
3564 "//maps.google." +
3565 rez[2] +
3566 "/?ll=" +
3567 (rez[9] ? rez[9] + "&z=" + Math.floor(rez[10]) + (rez[12] ? rez[12].replace(/^\//, "&") : "") : rez[12] + "").replace(/\?/, "&") +
3568 "&output=" +
3569 (rez[12] && rez[12].indexOf("layer=c") > 0 ? "svembed" : "embed")
3570 );
3571 }
3572 },
3573
3574 // Examples:
3575 // https://www.google.com/maps/search/Empire+State+Building/
3576 // https://www.google.com/maps/search/?api=1&query=centurylink+field
3577 // https://www.google.com/maps/search/?api=1&query=47.5951518,-122.3316393
3578 gmap_search: {
3579 matcher: /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(maps\/search\/)(.*)/i,
3580 type: "iframe",
3581 url: function(rez) {
3582 return "//maps.google." + rez[2] + "/maps?q=" + rez[5].replace("query=", "q=").replace("api=1", "") + "&output=embed";
3583 }
3584 }
3585 };
3586
3587 // Formats matching url to final form
3588 var format = function(url, rez, params) {
3589 if (!url) {
3590 return;
3591 }
3592
3593 params = params || "";
3594
3595 if ($.type(params) === "object") {
3596 params = $.param(params, true);
3597 }
3598
3599 $.each(rez, function(key, value) {
3600 url = url.replace("$" + key, value || "");
3601 });
3602
3603 if (params.length) {
3604 url += (url.indexOf("?") > 0 ? "&" : "?") + params;
3605 }
3606
3607 return url;
3608 };
3609
3610 $(document).on("objectNeedsType.fb", function(e, instance, item) {
3611 var url = item.src || "",
3612 type = false,
3613 media,
3614 thumb,
3615 rez,
3616 params,
3617 urlParams,
3618 paramObj,
3619 provider;
3620
3621 media = $.extend(true, {}, defaults, item.opts.media);
3622
3623 // Look for any matching media type
3624 $.each(media, function(providerName, providerOpts) {
3625 rez = url.match(providerOpts.matcher);
3626
3627 if (!rez) {
3628 return;
3629 }
3630
3631 type = providerOpts.type;
3632 provider = providerName;
3633 paramObj = {};
3634
3635 if (providerOpts.paramPlace && rez[providerOpts.paramPlace]) {
3636 urlParams = rez[providerOpts.paramPlace];
3637
3638 if (urlParams[0] == "?") {
3639 urlParams = urlParams.substring(1);
3640 }
3641
3642 urlParams = urlParams.split("&");
3643
3644 for (var m = 0; m < urlParams.length; ++m) {
3645 var p = urlParams[m].split("=", 2);
3646
3647 if (p.length == 2) {
3648 paramObj[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
3649 }
3650 }
3651 }
3652
3653 params = $.extend(true, {}, providerOpts.params, item.opts[providerName], paramObj);
3654
3655 url =
3656 $.type(providerOpts.url) === "function" ? providerOpts.url.call(this, rez, params, item) : format(providerOpts.url, rez, params);
3657
3658 thumb =
3659 $.type(providerOpts.thumb) === "function" ? providerOpts.thumb.call(this, rez, params, item) : format(providerOpts.thumb, rez);
3660
3661 if (providerName === "youtube") {
3662 url = url.replace(/&t=((\d+)m)?(\d+)s/, function(match, p1, m, s) {
3663 return "&start=" + ((m ? parseInt(m, 10) * 60 : 0) + parseInt(s, 10));
3664 });
3665 } else if (providerName === "vimeo") {
3666 url = url.replace("&%23", "#");
3667 }
3668
3669 return false;
3670 });
3671
3672 // If it is found, then change content type and update the url
3673
3674 if (type) {
3675 if (!item.opts.thumb && !(item.opts.$thumb && item.opts.$thumb.length)) {
3676 item.opts.thumb = thumb;
3677 }
3678
3679 if (type === "iframe") {
3680 item.opts = $.extend(true, item.opts, {
3681 iframe: {
3682 preload: false,
3683 attr: {
3684 scrolling: "no"
3685 }
3686 }
3687 });
3688 }
3689
3690 $.extend(item, {
3691 type: type,
3692 src: url,
3693 origSrc: item.src,
3694 contentSource: provider,
3695 contentType: type === "image" ? "image" : provider == "gmap_place" || provider == "gmap_search" ? "map" : "video"
3696 });
3697 } else if (url) {
3698 item.type = item.opts.defaultType;
3699 }
3700 });
3701
3702 // Load YouTube/Video API on request to detect when video finished playing
3703 var VideoAPILoader = {
3704 youtube: {
3705 src: "https://www.youtube.com/iframe_api",
3706 class: "YT",
3707 loading: false,
3708 loaded: false
3709 },
3710
3711 vimeo: {
3712 src: "https://player.vimeo.com/api/player.js",
3713 class: "Vimeo",
3714 loading: false,
3715 loaded: false
3716 },
3717
3718 load: function(vendor) {
3719 var _this = this,
3720 script;
3721
3722 if (this[vendor].loaded) {
3723 setTimeout(function() {
3724 _this.done(vendor);
3725 });
3726 return;
3727 }
3728
3729 if (this[vendor].loading) {
3730 return;
3731 }
3732
3733 this[vendor].loading = true;
3734
3735 script = document.createElement("script");
3736 script.type = "text/javascript";
3737 script.src = this[vendor].src;
3738
3739 if (vendor === "youtube") {
3740 window.onYouTubeIframeAPIReady = function() {
3741 _this[vendor].loaded = true;
3742 _this.done(vendor);
3743 };
3744 } else {
3745 script.onload = function() {
3746 _this[vendor].loaded = true;
3747 _this.done(vendor);
3748 };
3749 }
3750
3751 document.body.appendChild(script);
3752 },
3753 done: function(vendor) {
3754 var instance, $el, player;
3755
3756 if (vendor === "youtube") {
3757 delete window.onYouTubeIframeAPIReady;
3758 }
3759
3760 instance = $.fancybox.getInstance();
3761
3762 if (instance) {
3763 $el = instance.current.$content.find("iframe");
3764
3765 if (vendor === "youtube" && YT !== undefined && YT) {
3766 player = new YT.Player($el.attr("id"), {
3767 events: {
3768 onStateChange: function(e) {
3769 if (e.data == 0) {
3770 instance.next();
3771 }
3772 }
3773 }
3774 });
3775 } else if (vendor === "vimeo" && Vimeo !== undefined && Vimeo) {
3776 player = new Vimeo.Player($el);
3777
3778 player.on("ended", function() {
3779 instance.next();
3780 });
3781 }
3782 }
3783 }
3784 };
3785
3786 $(document).on({
3787 "afterShow.fb": function(e, instance, current) {
3788 if (instance.group.length > 1 && (current.contentSource === "youtube" || current.contentSource === "vimeo")) {
3789 VideoAPILoader.load(current.contentSource);
3790 }
3791 }
3792 });
3793})(jQuery);
3794
3795// ==========================================================================
3796//
3797// Guestures
3798// Adds touch guestures, handles click and tap events
3799//
3800// ==========================================================================
3801(function(window, document, $) {
3802 "use strict";
3803
3804 var requestAFrame = (function() {
3805 return (
3806 window.requestAnimationFrame ||
3807 window.webkitRequestAnimationFrame ||
3808 window.mozRequestAnimationFrame ||
3809 window.oRequestAnimationFrame ||
3810 // if all else fails, use setTimeout
3811 function(callback) {
3812 return window.setTimeout(callback, 1000 / 60);
3813 }
3814 );
3815 })();
3816
3817 var cancelAFrame = (function() {
3818 return (
3819 window.cancelAnimationFrame ||
3820 window.webkitCancelAnimationFrame ||
3821 window.mozCancelAnimationFrame ||
3822 window.oCancelAnimationFrame ||
3823 function(id) {
3824 window.clearTimeout(id);
3825 }
3826 );
3827 })();
3828
3829 var getPointerXY = function(e) {
3830 var result = [];
3831
3832 e = e.originalEvent || e || window.e;
3833 e = e.touches && e.touches.length ? e.touches : e.changedTouches && e.changedTouches.length ? e.changedTouches : [e];
3834
3835 for (var key in e) {
3836 if (e[key].pageX) {
3837 result.push({
3838 x: e[key].pageX,
3839 y: e[key].pageY
3840 });
3841 } else if (e[key].clientX) {
3842 result.push({
3843 x: e[key].clientX,
3844 y: e[key].clientY
3845 });
3846 }
3847 }
3848
3849 return result;
3850 };
3851
3852 var distance = function(point2, point1, what) {
3853 if (!point1 || !point2) {
3854 return 0;
3855 }
3856
3857 if (what === "x") {
3858 return point2.x - point1.x;
3859 } else if (what === "y") {
3860 return point2.y - point1.y;
3861 }
3862
3863 return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
3864 };
3865
3866 var isClickable = function($el) {
3867 if (
3868 $el.is('a,area,button,[role="button"],input,label,select,summary,textarea,video,audio,iframe') ||
3869 $.isFunction($el.get(0).onclick) ||
3870 $el.data("selectable")
3871 ) {
3872 return true;
3873 }
3874
3875 // Check for attributes like data-fancybox-next or data-fancybox-close
3876 for (var i = 0, atts = $el[0].attributes, n = atts.length; i < n; i++) {
3877 if (atts[i].nodeName.substr(0, 14) === "data-fancybox-") {
3878 return true;
3879 }
3880 }
3881
3882 return false;
3883 };
3884
3885 var hasScrollbars = function(el) {
3886 var overflowY = window.getComputedStyle(el)["overflow-y"],
3887 overflowX = window.getComputedStyle(el)["overflow-x"],
3888 vertical = (overflowY === "scroll" || overflowY === "auto") && el.scrollHeight > el.clientHeight,
3889 horizontal = (overflowX === "scroll" || overflowX === "auto") && el.scrollWidth > el.clientWidth;
3890
3891 return vertical || horizontal;
3892 };
3893
3894 var isScrollable = function($el) {
3895 var rez = false;
3896
3897 while (true) {
3898 rez = hasScrollbars($el.get(0));
3899
3900 if (rez) {
3901 break;
3902 }
3903
3904 $el = $el.parent();
3905
3906 if (!$el.length || $el.hasClass("fancybox-stage") || $el.is("body")) {
3907 break;
3908 }
3909 }
3910
3911 return rez;
3912 };
3913
3914 var Guestures = function(instance) {
3915 var self = this;
3916
3917 self.instance = instance;
3918
3919 self.$bg = instance.$refs.bg;
3920 self.$stage = instance.$refs.stage;
3921 self.$container = instance.$refs.container;
3922
3923 self.destroy();
3924
3925 self.$container.on("touchstart.fb.touch mousedown.fb.touch", $.proxy(self, "ontouchstart"));
3926 };
3927
3928 Guestures.prototype.destroy = function() {
3929 var self = this;
3930
3931 self.$container.off(".fb.touch");
3932
3933 $(document).off(".fb.touch");
3934
3935 if (self.requestId) {
3936 cancelAFrame(self.requestId);
3937 self.requestId = null;
3938 }
3939
3940 if (self.tapped) {
3941 clearTimeout(self.tapped);
3942 self.tapped = null;
3943 }
3944 };
3945
3946 Guestures.prototype.ontouchstart = function(e) {
3947 var self = this,
3948 $target = $(e.target),
3949 instance = self.instance,
3950 current = instance.current,
3951 $slide = current.$slide,
3952 $content = current.$content,
3953 isTouchDevice = e.type == "touchstart";
3954
3955 // Do not respond to both (touch and mouse) events
3956 if (isTouchDevice) {
3957 self.$container.off("mousedown.fb.touch");
3958 }
3959
3960 // Ignore right click
3961 if (e.originalEvent && e.originalEvent.button == 2) {
3962 return;
3963 }
3964
3965 // Ignore taping on links, buttons, input elements
3966 if (!$slide.length || !$target.length || isClickable($target) || isClickable($target.parent())) {
3967 return;
3968 }
3969 // Ignore clicks on the scrollbar
3970 if (!$target.is("img") && e.originalEvent.clientX > $target[0].clientWidth + $target.offset().left) {
3971 return;
3972 }
3973
3974 // Ignore clicks while zooming or closing
3975 if (!current || instance.isAnimating || current.$slide.hasClass("fancybox-animated")) {
3976 e.stopPropagation();
3977 e.preventDefault();
3978
3979 return;
3980 }
3981
3982 self.realPoints = self.startPoints = getPointerXY(e);
3983
3984 if (!self.startPoints.length) {
3985 return;
3986 }
3987
3988 // Allow other scripts to catch touch event if "touch" is set to false
3989 if (current.touch) {
3990 e.stopPropagation();
3991 }
3992
3993 self.startEvent = e;
3994
3995 self.canTap = true;
3996 self.$target = $target;
3997 self.$content = $content;
3998 self.opts = current.opts.touch;
3999
4000 self.isPanning = false;
4001 self.isSwiping = false;
4002 self.isZooming = false;
4003 self.isScrolling = false;
4004 self.canPan = instance.canPan();
4005
4006 self.startTime = new Date().getTime();
4007 self.distanceX = self.distanceY = self.distance = 0;
4008
4009 self.canvasWidth = Math.round($slide[0].clientWidth);
4010 self.canvasHeight = Math.round($slide[0].clientHeight);
4011
4012 self.contentLastPos = null;
4013 self.contentStartPos = $.fancybox.getTranslate(self.$content) || {top: 0, left: 0};
4014 self.sliderStartPos = $.fancybox.getTranslate($slide);
4015
4016 // Since position will be absolute, but we need to make it relative to the stage
4017 self.stagePos = $.fancybox.getTranslate(instance.$refs.stage);
4018
4019 self.sliderStartPos.top -= self.stagePos.top;
4020 self.sliderStartPos.left -= self.stagePos.left;
4021
4022 self.contentStartPos.top -= self.stagePos.top;
4023 self.contentStartPos.left -= self.stagePos.left;
4024
4025 $(document)
4026 .off(".fb.touch")
4027 .on(isTouchDevice ? "touchend.fb.touch touchcancel.fb.touch" : "mouseup.fb.touch mouseleave.fb.touch", $.proxy(self, "ontouchend"))
4028 .on(isTouchDevice ? "touchmove.fb.touch" : "mousemove.fb.touch", $.proxy(self, "ontouchmove"));
4029
4030 if ($.fancybox.isMobile) {
4031 document.addEventListener("scroll", self.onscroll, true);
4032 }
4033
4034 // Skip if clicked outside the sliding area
4035 if (!(self.opts || self.canPan) || !($target.is(self.$stage) || self.$stage.find($target).length)) {
4036 if ($target.is(".fancybox-image")) {
4037 e.preventDefault();
4038 }
4039
4040 if (!($.fancybox.isMobile && $target.hasClass("fancybox-caption"))) {
4041 return;
4042 }
4043 }
4044
4045 self.isScrollable = isScrollable($target) || isScrollable($target.parent());
4046
4047 // Check if element is scrollable and try to prevent default behavior (scrolling)
4048 if (!($.fancybox.isMobile && self.isScrollable)) {
4049 e.preventDefault();
4050 }
4051
4052 // One finger or mouse click - swipe or pan an image
4053 if (self.startPoints.length === 1 || current.hasError) {
4054 if (self.canPan) {
4055 $.fancybox.stop(self.$content);
4056
4057 self.isPanning = true;
4058 } else {
4059 self.isSwiping = true;
4060 }
4061
4062 self.$container.addClass("fancybox-is-grabbing");
4063 }
4064
4065 // Two fingers - zoom image
4066 if (self.startPoints.length === 2 && current.type === "image" && (current.isLoaded || current.$ghost)) {
4067 self.canTap = false;
4068 self.isSwiping = false;
4069 self.isPanning = false;
4070
4071 self.isZooming = true;
4072
4073 $.fancybox.stop(self.$content);
4074
4075 self.centerPointStartX = (self.startPoints[0].x + self.startPoints[1].x) * 0.5 - $(window).scrollLeft();
4076 self.centerPointStartY = (self.startPoints[0].y + self.startPoints[1].y) * 0.5 - $(window).scrollTop();
4077
4078 self.percentageOfImageAtPinchPointX = (self.centerPointStartX - self.contentStartPos.left) / self.contentStartPos.width;
4079 self.percentageOfImageAtPinchPointY = (self.centerPointStartY - self.contentStartPos.top) / self.contentStartPos.height;
4080
4081 self.startDistanceBetweenFingers = distance(self.startPoints[0], self.startPoints[1]);
4082 }
4083 };
4084
4085 Guestures.prototype.onscroll = function(e) {
4086 var self = this;
4087
4088 self.isScrolling = true;
4089
4090 document.removeEventListener("scroll", self.onscroll, true);
4091 };
4092
4093 Guestures.prototype.ontouchmove = function(e) {
4094 var self = this;
4095
4096 // Make sure user has not released over iframe or disabled element
4097 if (e.originalEvent.buttons !== undefined && e.originalEvent.buttons === 0) {
4098 self.ontouchend(e);
4099 return;
4100 }
4101
4102 if (self.isScrolling) {
4103 self.canTap = false;
4104 return;
4105 }
4106
4107 self.newPoints = getPointerXY(e);
4108
4109 if (!(self.opts || self.canPan) || !self.newPoints.length || !self.newPoints.length) {
4110 return;
4111 }
4112
4113 if (!(self.isSwiping && self.isSwiping === true)) {
4114 e.preventDefault();
4115 }
4116
4117 self.distanceX = distance(self.newPoints[0], self.startPoints[0], "x");
4118 self.distanceY = distance(self.newPoints[0], self.startPoints[0], "y");
4119
4120 self.distance = distance(self.newPoints[0], self.startPoints[0]);
4121
4122 // Skip false ontouchmove events (Chrome)
4123 if (self.distance > 0) {
4124 if (self.isSwiping) {
4125 self.onSwipe(e);
4126 } else if (self.isPanning) {
4127 self.onPan();
4128 } else if (self.isZooming) {
4129 self.onZoom();
4130 }
4131 }
4132 };
4133
4134 Guestures.prototype.onSwipe = function(e) {
4135 var self = this,
4136 instance = self.instance,
4137 swiping = self.isSwiping,
4138 left = self.sliderStartPos.left || 0,
4139 angle;
4140
4141 // If direction is not yet determined
4142 if (swiping === true) {
4143 // We need at least 10px distance to correctly calculate an angle
4144 if (Math.abs(self.distance) > 10) {
4145 self.canTap = false;
4146
4147 if (instance.group.length < 2 && self.opts.vertical) {
4148 self.isSwiping = "y";
4149 } else if (instance.isDragging || self.opts.vertical === false || (self.opts.vertical === "auto" && $(window).width() > 800)) {
4150 self.isSwiping = "x";
4151 } else {
4152 angle = Math.abs((Math.atan2(self.distanceY, self.distanceX) * 180) / Math.PI);
4153
4154 self.isSwiping = angle > 45 && angle < 135 ? "y" : "x";
4155 }
4156
4157 if (self.isSwiping === "y" && $.fancybox.isMobile && self.isScrollable) {
4158 self.isScrolling = true;
4159
4160 return;
4161 }
4162
4163 instance.isDragging = self.isSwiping;
4164
4165 // Reset points to avoid jumping, because we dropped first swipes to calculate the angle
4166 self.startPoints = self.newPoints;
4167
4168 $.each(instance.slides, function(index, slide) {
4169 var slidePos, stagePos;
4170
4171 $.fancybox.stop(slide.$slide);
4172
4173 slidePos = $.fancybox.getTranslate(slide.$slide);
4174 stagePos = $.fancybox.getTranslate(instance.$refs.stage);
4175
4176 slide.$slide
4177 .css({
4178 transform: "",
4179 opacity: "",
4180 "transition-duration": ""
4181 })
4182 .removeClass("fancybox-animated")
4183 .removeClass(function(index, className) {
4184 return (className.match(/(^|\s)fancybox-fx-\S+/g) || []).join(" ");
4185 });
4186
4187 if (slide.pos === instance.current.pos) {
4188 self.sliderStartPos.top = slidePos.top - stagePos.top;
4189 self.sliderStartPos.left = slidePos.left - stagePos.left;
4190 }
4191
4192 $.fancybox.setTranslate(slide.$slide, {
4193 top: slidePos.top - stagePos.top,
4194 left: slidePos.left - stagePos.left
4195 });
4196 });
4197
4198 // Stop slideshow
4199 if (instance.SlideShow && instance.SlideShow.isActive) {
4200 instance.SlideShow.stop();
4201 }
4202 }
4203
4204 return;
4205 }
4206
4207 // Sticky edges
4208 if (swiping == "x") {
4209 if (
4210 self.distanceX > 0 &&
4211 (self.instance.group.length < 2 || (self.instance.current.index === 0 && !self.instance.current.opts.loop))
4212 ) {
4213 left = left + Math.pow(self.distanceX, 0.8);
4214 } else if (
4215 self.distanceX < 0 &&
4216 (self.instance.group.length < 2 ||
4217 (self.instance.current.index === self.instance.group.length - 1 && !self.instance.current.opts.loop))
4218 ) {
4219 left = left - Math.pow(-self.distanceX, 0.8);
4220 } else {
4221 left = left + self.distanceX;
4222 }
4223 }
4224
4225 self.sliderLastPos = {
4226 top: swiping == "x" ? 0 : self.sliderStartPos.top + self.distanceY,
4227 left: left
4228 };
4229
4230 if (self.requestId) {
4231 cancelAFrame(self.requestId);
4232
4233 self.requestId = null;
4234 }
4235
4236 self.requestId = requestAFrame(function() {
4237 if (self.sliderLastPos) {
4238 $.each(self.instance.slides, function(index, slide) {
4239 var pos = slide.pos - self.instance.currPos;
4240
4241 $.fancybox.setTranslate(slide.$slide, {
4242 top: self.sliderLastPos.top,
4243 left: self.sliderLastPos.left + pos * self.canvasWidth + pos * slide.opts.gutter
4244 });
4245 });
4246
4247 self.$container.addClass("fancybox-is-sliding");
4248 }
4249 });
4250 };
4251
4252 Guestures.prototype.onPan = function() {
4253 var self = this;
4254
4255 // Prevent accidental movement (sometimes, when tapping casually, finger can move a bit)
4256 if (distance(self.newPoints[0], self.realPoints[0]) < ($.fancybox.isMobile ? 10 : 5)) {
4257 self.startPoints = self.newPoints;
4258 return;
4259 }
4260
4261 self.canTap = false;
4262
4263 self.contentLastPos = self.limitMovement();
4264
4265 if (self.requestId) {
4266 cancelAFrame(self.requestId);
4267 }
4268
4269 self.requestId = requestAFrame(function() {
4270 $.fancybox.setTranslate(self.$content, self.contentLastPos);
4271 });
4272 };
4273
4274 // Make panning sticky to the edges
4275 Guestures.prototype.limitMovement = function() {
4276 var self = this;
4277
4278 var canvasWidth = self.canvasWidth;
4279 var canvasHeight = self.canvasHeight;
4280
4281 var distanceX = self.distanceX;
4282 var distanceY = self.distanceY;
4283
4284 var contentStartPos = self.contentStartPos;
4285
4286 var currentOffsetX = contentStartPos.left;
4287 var currentOffsetY = contentStartPos.top;
4288
4289 var currentWidth = contentStartPos.width;
4290 var currentHeight = contentStartPos.height;
4291
4292 var minTranslateX, minTranslateY, maxTranslateX, maxTranslateY, newOffsetX, newOffsetY;
4293
4294 if (currentWidth > canvasWidth) {
4295 newOffsetX = currentOffsetX + distanceX;
4296 } else {
4297 newOffsetX = currentOffsetX;
4298 }
4299
4300 newOffsetY = currentOffsetY + distanceY;
4301
4302 // Slow down proportionally to traveled distance
4303 minTranslateX = Math.max(0, canvasWidth * 0.5 - currentWidth * 0.5);
4304 minTranslateY = Math.max(0, canvasHeight * 0.5 - currentHeight * 0.5);
4305
4306 maxTranslateX = Math.min(canvasWidth - currentWidth, canvasWidth * 0.5 - currentWidth * 0.5);
4307 maxTranslateY = Math.min(canvasHeight - currentHeight, canvasHeight * 0.5 - currentHeight * 0.5);
4308
4309 // ->
4310 if (distanceX > 0 && newOffsetX > minTranslateX) {
4311 newOffsetX = minTranslateX - 1 + Math.pow(-minTranslateX + currentOffsetX + distanceX, 0.8) || 0;
4312 }
4313
4314 // <-
4315 if (distanceX < 0 && newOffsetX < maxTranslateX) {
4316 newOffsetX = maxTranslateX + 1 - Math.pow(maxTranslateX - currentOffsetX - distanceX, 0.8) || 0;
4317 }
4318
4319 // \/
4320 if (distanceY > 0 && newOffsetY > minTranslateY) {
4321 newOffsetY = minTranslateY - 1 + Math.pow(-minTranslateY + currentOffsetY + distanceY, 0.8) || 0;
4322 }
4323
4324 // /\
4325 if (distanceY < 0 && newOffsetY < maxTranslateY) {
4326 newOffsetY = maxTranslateY + 1 - Math.pow(maxTranslateY - currentOffsetY - distanceY, 0.8) || 0;
4327 }
4328
4329 return {
4330 top: newOffsetY,
4331 left: newOffsetX
4332 };
4333 };
4334
4335 Guestures.prototype.limitPosition = function(newOffsetX, newOffsetY, newWidth, newHeight) {
4336 var self = this;
4337
4338 var canvasWidth = self.canvasWidth;
4339 var canvasHeight = self.canvasHeight;
4340
4341 if (newWidth > canvasWidth) {
4342 newOffsetX = newOffsetX > 0 ? 0 : newOffsetX;
4343 newOffsetX = newOffsetX < canvasWidth - newWidth ? canvasWidth - newWidth : newOffsetX;
4344 } else {
4345 // Center horizontally
4346 newOffsetX = Math.max(0, canvasWidth / 2 - newWidth / 2);
4347 }
4348
4349 if (newHeight > canvasHeight) {
4350 newOffsetY = newOffsetY > 0 ? 0 : newOffsetY;
4351 newOffsetY = newOffsetY < canvasHeight - newHeight ? canvasHeight - newHeight : newOffsetY;
4352 } else {
4353 // Center vertically
4354 newOffsetY = Math.max(0, canvasHeight / 2 - newHeight / 2);
4355 }
4356
4357 return {
4358 top: newOffsetY,
4359 left: newOffsetX
4360 };
4361 };
4362
4363 Guestures.prototype.onZoom = function() {
4364 var self = this;
4365
4366 // Calculate current distance between points to get pinch ratio and new width and height
4367 var contentStartPos = self.contentStartPos;
4368
4369 var currentWidth = contentStartPos.width;
4370 var currentHeight = contentStartPos.height;
4371
4372 var currentOffsetX = contentStartPos.left;
4373 var currentOffsetY = contentStartPos.top;
4374
4375 var endDistanceBetweenFingers = distance(self.newPoints[0], self.newPoints[1]);
4376
4377 var pinchRatio = endDistanceBetweenFingers / self.startDistanceBetweenFingers;
4378
4379 var newWidth = Math.floor(currentWidth * pinchRatio);
4380 var newHeight = Math.floor(currentHeight * pinchRatio);
4381
4382 // This is the translation due to pinch-zooming
4383 var translateFromZoomingX = (currentWidth - newWidth) * self.percentageOfImageAtPinchPointX;
4384 var translateFromZoomingY = (currentHeight - newHeight) * self.percentageOfImageAtPinchPointY;
4385
4386 // Point between the two touches
4387 var centerPointEndX = (self.newPoints[0].x + self.newPoints[1].x) / 2 - $(window).scrollLeft();
4388 var centerPointEndY = (self.newPoints[0].y + self.newPoints[1].y) / 2 - $(window).scrollTop();
4389
4390 // And this is the translation due to translation of the centerpoint
4391 // between the two fingers
4392 var translateFromTranslatingX = centerPointEndX - self.centerPointStartX;
4393 var translateFromTranslatingY = centerPointEndY - self.centerPointStartY;
4394
4395 // The new offset is the old/current one plus the total translation
4396 var newOffsetX = currentOffsetX + (translateFromZoomingX + translateFromTranslatingX);
4397 var newOffsetY = currentOffsetY + (translateFromZoomingY + translateFromTranslatingY);
4398
4399 var newPos = {
4400 top: newOffsetY,
4401 left: newOffsetX,
4402 scaleX: pinchRatio,
4403 scaleY: pinchRatio
4404 };
4405
4406 self.canTap = false;
4407
4408 self.newWidth = newWidth;
4409 self.newHeight = newHeight;
4410
4411 self.contentLastPos = newPos;
4412
4413 if (self.requestId) {
4414 cancelAFrame(self.requestId);
4415 }
4416
4417 self.requestId = requestAFrame(function() {
4418 $.fancybox.setTranslate(self.$content, self.contentLastPos);
4419 });
4420 };
4421
4422 Guestures.prototype.ontouchend = function(e) {
4423 var self = this;
4424
4425 var swiping = self.isSwiping;
4426 var panning = self.isPanning;
4427 var zooming = self.isZooming;
4428 var scrolling = self.isScrolling;
4429
4430 self.endPoints = getPointerXY(e);
4431 self.dMs = Math.max(new Date().getTime() - self.startTime, 1);
4432
4433 self.$container.removeClass("fancybox-is-grabbing");
4434
4435 $(document).off(".fb.touch");
4436
4437 document.removeEventListener("scroll", self.onscroll, true);
4438
4439 if (self.requestId) {
4440 cancelAFrame(self.requestId);
4441
4442 self.requestId = null;
4443 }
4444
4445 self.isSwiping = false;
4446 self.isPanning = false;
4447 self.isZooming = false;
4448 self.isScrolling = false;
4449
4450 self.instance.isDragging = false;
4451
4452 if (self.canTap) {
4453 return self.onTap(e);
4454 }
4455
4456 self.speed = 100;
4457
4458 // Speed in px/ms
4459 self.velocityX = (self.distanceX / self.dMs) * 0.5;
4460 self.velocityY = (self.distanceY / self.dMs) * 0.5;
4461
4462 if (panning) {
4463 self.endPanning();
4464 } else if (zooming) {
4465 self.endZooming();
4466 } else {
4467 self.endSwiping(swiping, scrolling);
4468 }
4469
4470 return;
4471 };
4472
4473 Guestures.prototype.endSwiping = function(swiping, scrolling) {
4474 var self = this,
4475 ret = false,
4476 len = self.instance.group.length,
4477 distanceX = Math.abs(self.distanceX),
4478 canAdvance = swiping == "x" && len > 1 && ((self.dMs > 130 && distanceX > 10) || distanceX > 50),
4479 speedX = 300;
4480
4481 self.sliderLastPos = null;
4482
4483 // Close if swiped vertically / navigate if horizontally
4484 if (swiping == "y" && !scrolling && Math.abs(self.distanceY) > 50) {
4485 // Continue vertical movement
4486 $.fancybox.animate(
4487 self.instance.current.$slide,
4488 {
4489 top: self.sliderStartPos.top + self.distanceY + self.velocityY * 150,
4490 opacity: 0
4491 },
4492 200
4493 );
4494 ret = self.instance.close(true, 250);
4495 } else if (canAdvance && self.distanceX > 0) {
4496 ret = self.instance.previous(speedX);
4497 } else if (canAdvance && self.distanceX < 0) {
4498 ret = self.instance.next(speedX);
4499 }
4500
4501 if (ret === false && (swiping == "x" || swiping == "y")) {
4502 self.instance.centerSlide(200);
4503 }
4504
4505 self.$container.removeClass("fancybox-is-sliding");
4506 };
4507
4508 // Limit panning from edges
4509 // ========================
4510 Guestures.prototype.endPanning = function() {
4511 var self = this,
4512 newOffsetX,
4513 newOffsetY,
4514 newPos;
4515
4516 if (!self.contentLastPos) {
4517 return;
4518 }
4519
4520 if (self.opts.momentum === false || self.dMs > 350) {
4521 newOffsetX = self.contentLastPos.left;
4522 newOffsetY = self.contentLastPos.top;
4523 } else {
4524 // Continue movement
4525 newOffsetX = self.contentLastPos.left + self.velocityX * 500;
4526 newOffsetY = self.contentLastPos.top + self.velocityY * 500;
4527 }
4528
4529 newPos = self.limitPosition(newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height);
4530
4531 newPos.width = self.contentStartPos.width;
4532 newPos.height = self.contentStartPos.height;
4533
4534 $.fancybox.animate(self.$content, newPos, 330);
4535 };
4536
4537 Guestures.prototype.endZooming = function() {
4538 var self = this;
4539
4540 var current = self.instance.current;
4541
4542 var newOffsetX, newOffsetY, newPos, reset;
4543
4544 var newWidth = self.newWidth;
4545 var newHeight = self.newHeight;
4546
4547 if (!self.contentLastPos) {
4548 return;
4549 }
4550
4551 newOffsetX = self.contentLastPos.left;
4552 newOffsetY = self.contentLastPos.top;
4553
4554 reset = {
4555 top: newOffsetY,
4556 left: newOffsetX,
4557 width: newWidth,
4558 height: newHeight,
4559 scaleX: 1,
4560 scaleY: 1
4561 };
4562
4563 // Reset scalex/scaleY values; this helps for perfomance and does not break animation
4564 $.fancybox.setTranslate(self.$content, reset);
4565
4566 if (newWidth < self.canvasWidth && newHeight < self.canvasHeight) {
4567 self.instance.scaleToFit(150);
4568 } else if (newWidth > current.width || newHeight > current.height) {
4569 self.instance.scaleToActual(self.centerPointStartX, self.centerPointStartY, 150);
4570 } else {
4571 newPos = self.limitPosition(newOffsetX, newOffsetY, newWidth, newHeight);
4572
4573 $.fancybox.animate(self.$content, newPos, 150);
4574 }
4575 };
4576
4577 Guestures.prototype.onTap = function(e) {
4578 var self = this;
4579 var $target = $(e.target);
4580
4581 var instance = self.instance;
4582 var current = instance.current;
4583
4584 var endPoints = (e && getPointerXY(e)) || self.startPoints;
4585
4586 var tapX = endPoints[0] ? endPoints[0].x - $(window).scrollLeft() - self.stagePos.left : 0;
4587 var tapY = endPoints[0] ? endPoints[0].y - $(window).scrollTop() - self.stagePos.top : 0;
4588
4589 var where;
4590
4591 var process = function(prefix) {
4592 var action = current.opts[prefix];
4593
4594 if ($.isFunction(action)) {
4595 action = action.apply(instance, [current, e]);
4596 }
4597
4598 if (!action) {
4599 return;
4600 }
4601
4602 switch (action) {
4603 case "close":
4604 instance.close(self.startEvent);
4605
4606 break;
4607
4608 case "toggleControls":
4609 instance.toggleControls();
4610
4611 break;
4612
4613 case "next":
4614 instance.next();
4615
4616 break;
4617
4618 case "nextOrClose":
4619 if (instance.group.length > 1) {
4620 instance.next();
4621 } else {
4622 instance.close(self.startEvent);
4623 }
4624
4625 break;
4626
4627 case "zoom":
4628 if (current.type == "image" && (current.isLoaded || current.$ghost)) {
4629 if (instance.canPan()) {
4630 instance.scaleToFit();
4631 } else if (instance.isScaledDown()) {
4632 instance.scaleToActual(tapX, tapY);
4633 } else if (instance.group.length < 2) {
4634 instance.close(self.startEvent);
4635 }
4636 }
4637
4638 break;
4639 }
4640 };
4641
4642 // Ignore right click
4643 if (e.originalEvent && e.originalEvent.button == 2) {
4644 return;
4645 }
4646
4647 // Skip if clicked on the scrollbar
4648 if (!$target.is("img") && tapX > $target[0].clientWidth + $target.offset().left) {
4649 return;
4650 }
4651
4652 // Check where is clicked
4653 if ($target.is(".fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-container")) {
4654 where = "Outside";
4655 } else if ($target.is(".fancybox-slide")) {
4656 where = "Slide";
4657 } else if (
4658 instance.current.$content &&
4659 instance.current.$content
4660 .find($target)
4661 .addBack()
4662 .filter($target).length
4663 ) {
4664 where = "Content";
4665 } else {
4666 return;
4667 }
4668
4669 // Check if this is a double tap
4670 if (self.tapped) {
4671 // Stop previously created single tap
4672 clearTimeout(self.tapped);
4673 self.tapped = null;
4674
4675 // Skip if distance between taps is too big
4676 if (Math.abs(tapX - self.tapX) > 50 || Math.abs(tapY - self.tapY) > 50) {
4677 return this;
4678 }
4679
4680 // OK, now we assume that this is a double-tap
4681 process("dblclick" + where);
4682 } else {
4683 // Single tap will be processed if user has not clicked second time within 300ms
4684 // or there is no need to wait for double-tap
4685 self.tapX = tapX;
4686 self.tapY = tapY;
4687
4688 if (current.opts["dblclick" + where] && current.opts["dblclick" + where] !== current.opts["click" + where]) {
4689 self.tapped = setTimeout(function() {
4690 self.tapped = null;
4691
4692 if (!instance.isAnimating) {
4693 process("click" + where);
4694 }
4695 }, 500);
4696 } else {
4697 process("click" + where);
4698 }
4699 }
4700
4701 return this;
4702 };
4703
4704 $(document)
4705 .on("onActivate.fb", function(e, instance) {
4706 if (instance && !instance.Guestures) {
4707 instance.Guestures = new Guestures(instance);
4708 }
4709 })
4710 .on("beforeClose.fb", function(e, instance) {
4711 if (instance && instance.Guestures) {
4712 instance.Guestures.destroy();
4713 }
4714 });
4715})(window, document, jQuery);
4716
4717// ==========================================================================
4718//
4719// SlideShow
4720// Enables slideshow functionality
4721//
4722// Example of usage:
4723// $.fancybox.getInstance().SlideShow.start()
4724//
4725// ==========================================================================
4726(function(document, $) {
4727 "use strict";
4728
4729 $.extend(true, $.fancybox.defaults, {
4730 btnTpl: {
4731 slideShow:
4732 '<button data-fancybox-play class="fancybox-button fancybox-button--play" title="{{PLAY_START}}">' +
4733 '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.5 5.4v13.2l11-6.6z"/></svg>' +
4734 '<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>' +
4735 "</button>"
4736 },
4737 slideShow: {
4738 autoStart: false,
4739 speed: 3000,
4740 progress: true
4741 }
4742 });
4743
4744 var SlideShow = function(instance) {
4745 this.instance = instance;
4746 this.init();
4747 };
4748
4749 $.extend(SlideShow.prototype, {
4750 timer: null,
4751 isActive: false,
4752 $button: null,
4753
4754 init: function() {
4755 var self = this,
4756 instance = self.instance,
4757 opts = instance.group[instance.currIndex].opts.slideShow;
4758
4759 self.$button = instance.$refs.toolbar.find("[data-fancybox-play]").on("click", function() {
4760 self.toggle();
4761 });
4762
4763 if (instance.group.length < 2 || !opts) {
4764 self.$button.hide();
4765 } else if (opts.progress) {
4766 self.$progress = $('<div class="fancybox-progress"></div>').appendTo(instance.$refs.inner);
4767 }
4768 },
4769
4770 set: function(force) {
4771 var self = this,
4772 instance = self.instance,
4773 current = instance.current;
4774
4775 // Check if reached last element
4776 if (current && (force === true || current.opts.loop || instance.currIndex < instance.group.length - 1)) {
4777 if (self.isActive && current.contentType !== "video") {
4778 if (self.$progress) {
4779 $.fancybox.animate(self.$progress.show(), {scaleX: 1}, current.opts.slideShow.speed);
4780 }
4781
4782 self.timer = setTimeout(function() {
4783 if (!instance.current.opts.loop && instance.current.index == instance.group.length - 1) {
4784 instance.jumpTo(0);
4785 } else {
4786 instance.next();
4787 }
4788 }, current.opts.slideShow.speed);
4789 }
4790 } else {
4791 self.stop();
4792 instance.idleSecondsCounter = 0;
4793 instance.showControls();
4794 }
4795 },
4796
4797 clear: function() {
4798 var self = this;
4799
4800 clearTimeout(self.timer);
4801
4802 self.timer = null;
4803
4804 if (self.$progress) {
4805 self.$progress.removeAttr("style").hide();
4806 }
4807 },
4808
4809 start: function() {
4810 var self = this,
4811 current = self.instance.current;
4812
4813 if (current) {
4814 self.$button
4815 .attr("title", (current.opts.i18n[current.opts.lang] || current.opts.i18n.en).PLAY_STOP)
4816 .removeClass("fancybox-button--play")
4817 .addClass("fancybox-button--pause");
4818
4819 self.isActive = true;
4820
4821 if (current.isComplete) {
4822 self.set(true);
4823 }
4824
4825 self.instance.trigger("onSlideShowChange", true);
4826 }
4827 },
4828
4829 stop: function() {
4830 var self = this,
4831 current = self.instance.current;
4832
4833 self.clear();
4834
4835 self.$button
4836 .attr("title", (current.opts.i18n[current.opts.lang] || current.opts.i18n.en).PLAY_START)
4837 .removeClass("fancybox-button--pause")
4838 .addClass("fancybox-button--play");
4839
4840 self.isActive = false;
4841
4842 self.instance.trigger("onSlideShowChange", false);
4843
4844 if (self.$progress) {
4845 self.$progress.removeAttr("style").hide();
4846 }
4847 },
4848
4849 toggle: function() {
4850 var self = this;
4851
4852 if (self.isActive) {
4853 self.stop();
4854 } else {
4855 self.start();
4856 }
4857 }
4858 });
4859
4860 $(document).on({
4861 "onInit.fb": function(e, instance) {
4862 if (instance && !instance.SlideShow) {
4863 instance.SlideShow = new SlideShow(instance);
4864 }
4865 },
4866
4867 "beforeShow.fb": function(e, instance, current, firstRun) {
4868 var SlideShow = instance && instance.SlideShow;
4869
4870 if (firstRun) {
4871 if (SlideShow && current.opts.slideShow.autoStart) {
4872 SlideShow.start();
4873 }
4874 } else if (SlideShow && SlideShow.isActive) {
4875 SlideShow.clear();
4876 }
4877 },
4878
4879 "afterShow.fb": function(e, instance, current) {
4880 var SlideShow = instance && instance.SlideShow;
4881
4882 if (SlideShow && SlideShow.isActive) {
4883 SlideShow.set();
4884 }
4885 },
4886
4887 "afterKeydown.fb": function(e, instance, current, keypress, keycode) {
4888 var SlideShow = instance && instance.SlideShow;
4889
4890 // "P" or Spacebar
4891 if (SlideShow && current.opts.slideShow && (keycode === 80 || keycode === 32) && !$(document.activeElement).is("button,a,input")) {
4892 keypress.preventDefault();
4893
4894 SlideShow.toggle();
4895 }
4896 },
4897
4898 "beforeClose.fb onDeactivate.fb": function(e, instance) {
4899 var SlideShow = instance && instance.SlideShow;
4900
4901 if (SlideShow) {
4902 SlideShow.stop();
4903 }
4904 }
4905 });
4906
4907 // Page Visibility API to pause slideshow when window is not active
4908 $(document).on("visibilitychange", function() {
4909 var instance = $.fancybox.getInstance(),
4910 SlideShow = instance && instance.SlideShow;
4911
4912 if (SlideShow && SlideShow.isActive) {
4913 if (document.hidden) {
4914 SlideShow.clear();
4915 } else {
4916 SlideShow.set();
4917 }
4918 }
4919 });
4920})(document, jQuery);
4921
4922// ==========================================================================
4923//
4924// FullScreen
4925// Adds fullscreen functionality
4926//
4927// ==========================================================================
4928(function(document, $) {
4929 "use strict";
4930
4931 // Collection of methods supported by user browser
4932 var fn = (function() {
4933 var fnMap = [
4934 ["requestFullscreen", "exitFullscreen", "fullscreenElement", "fullscreenEnabled", "fullscreenchange", "fullscreenerror"],
4935 // new WebKit
4936 [
4937 "webkitRequestFullscreen",
4938 "webkitExitFullscreen",
4939 "webkitFullscreenElement",
4940 "webkitFullscreenEnabled",
4941 "webkitfullscreenchange",
4942 "webkitfullscreenerror"
4943 ],
4944 // old WebKit (Safari 5.1)
4945 [
4946 "webkitRequestFullScreen",
4947 "webkitCancelFullScreen",
4948 "webkitCurrentFullScreenElement",
4949 "webkitCancelFullScreen",
4950 "webkitfullscreenchange",
4951 "webkitfullscreenerror"
4952 ],
4953 [
4954 "mozRequestFullScreen",
4955 "mozCancelFullScreen",
4956 "mozFullScreenElement",
4957 "mozFullScreenEnabled",
4958 "mozfullscreenchange",
4959 "mozfullscreenerror"
4960 ],
4961 ["msRequestFullscreen", "msExitFullscreen", "msFullscreenElement", "msFullscreenEnabled", "MSFullscreenChange", "MSFullscreenError"]
4962 ];
4963
4964 var ret = {};
4965
4966 for (var i = 0; i < fnMap.length; i++) {
4967 var val = fnMap[i];
4968
4969 if (val && val[1] in document) {
4970 for (var j = 0; j < val.length; j++) {
4971 ret[fnMap[0][j]] = val[j];
4972 }
4973
4974 return ret;
4975 }
4976 }
4977
4978 return false;
4979 })();
4980
4981 if (fn) {
4982 var FullScreen = {
4983 request: function(elem) {
4984 elem = elem || document.documentElement;
4985
4986 elem[fn.requestFullscreen](elem.ALLOW_KEYBOARD_INPUT);
4987 },
4988 exit: function() {
4989 document[fn.exitFullscreen]();
4990 },
4991 toggle: function(elem) {
4992 elem = elem || document.documentElement;
4993
4994 if (this.isFullscreen()) {
4995 this.exit();
4996 } else {
4997 this.request(elem);
4998 }
4999 },
5000 isFullscreen: function() {
5001 return Boolean(document[fn.fullscreenElement]);
5002 },
5003 enabled: function() {
5004 return Boolean(document[fn.fullscreenEnabled]);
5005 }
5006 };
5007
5008 $.extend(true, $.fancybox.defaults, {
5009 btnTpl: {
5010 fullScreen:
5011 '<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fsenter" title="{{FULL_SCREEN}}">' +
5012 '<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>' +
5013 '<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>' +
5014 "</button>"
5015 },
5016 fullScreen: {
5017 autoStart: false
5018 }
5019 });
5020
5021 $(document).on(fn.fullscreenchange, function() {
5022 var isFullscreen = FullScreen.isFullscreen(),
5023 instance = $.fancybox.getInstance();
5024
5025 if (instance) {
5026 // If image is zooming, then force to stop and reposition properly
5027 if (instance.current && instance.current.type === "image" && instance.isAnimating) {
5028 instance.current.$content.css("transition", "none");
5029
5030 instance.isAnimating = false;
5031
5032 instance.update(true, true, 0);
5033 }
5034
5035 instance.trigger("onFullscreenChange", isFullscreen);
5036
5037 instance.$refs.container.toggleClass("fancybox-is-fullscreen", isFullscreen);
5038
5039 instance.$refs.toolbar
5040 .find("[data-fancybox-fullscreen]")
5041 .toggleClass("fancybox-button--fsenter", !isFullscreen)
5042 .toggleClass("fancybox-button--fsexit", isFullscreen);
5043 }
5044 });
5045 }
5046
5047 $(document).on({
5048 "onInit.fb": function(e, instance) {
5049 var $container;
5050
5051 if (!fn) {
5052 instance.$refs.toolbar.find("[data-fancybox-fullscreen]").remove();
5053
5054 return;
5055 }
5056
5057 if (instance && instance.group[instance.currIndex].opts.fullScreen) {
5058 $container = instance.$refs.container;
5059
5060 $container.on("click.fb-fullscreen", "[data-fancybox-fullscreen]", function(e) {
5061 e.stopPropagation();
5062 e.preventDefault();
5063
5064 FullScreen.toggle();
5065 });
5066
5067 if (instance.opts.fullScreen && instance.opts.fullScreen.autoStart === true) {
5068 FullScreen.request();
5069 }
5070
5071 // Expose API
5072 instance.FullScreen = FullScreen;
5073 } else if (instance) {
5074 instance.$refs.toolbar.find("[data-fancybox-fullscreen]").hide();
5075 }
5076 },
5077
5078 "afterKeydown.fb": function(e, instance, current, keypress, keycode) {
5079 // "F"
5080 if (instance && instance.FullScreen && keycode === 70) {
5081 keypress.preventDefault();
5082
5083 instance.FullScreen.toggle();
5084 }
5085 },
5086
5087 "beforeClose.fb": function(e, instance) {
5088 if (instance && instance.FullScreen && instance.$refs.container.hasClass("fancybox-is-fullscreen")) {
5089 FullScreen.exit();
5090 }
5091 }
5092 });
5093})(document, jQuery);
5094
5095// ==========================================================================
5096//
5097// Thumbs
5098// Displays thumbnails in a grid
5099//
5100// ==========================================================================
5101(function(document, $) {
5102 "use strict";
5103
5104 var CLASS = "fancybox-thumbs",
5105 CLASS_ACTIVE = CLASS + "-active";
5106
5107 // Make sure there are default values
5108 $.fancybox.defaults = $.extend(
5109 true,
5110 {
5111 btnTpl: {
5112 thumbs:
5113 '<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="{{THUMBS}}">' +
5114 '<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>' +
5115 "</button>"
5116 },
5117 thumbs: {
5118 autoStart: false, // Display thumbnails on opening
5119 hideOnClose: true, // Hide thumbnail grid when closing animation starts
5120 parentEl: ".fancybox-container", // Container is injected into this element
5121 axis: "y" // Vertical (y) or horizontal (x) scrolling
5122 }
5123 },
5124 $.fancybox.defaults
5125 );
5126
5127 var FancyThumbs = function(instance) {
5128 this.init(instance);
5129 };
5130
5131 $.extend(FancyThumbs.prototype, {
5132 $button: null,
5133 $grid: null,
5134 $list: null,
5135 isVisible: false,
5136 isActive: false,
5137
5138 init: function(instance) {
5139 var self = this,
5140 group = instance.group,
5141 enabled = 0;
5142
5143 self.instance = instance;
5144 self.opts = group[instance.currIndex].opts.thumbs;
5145
5146 instance.Thumbs = self;
5147
5148 self.$button = instance.$refs.toolbar.find("[data-fancybox-thumbs]");
5149
5150 // Enable thumbs if at least two group items have thumbnails
5151 for (var i = 0, len = group.length; i < len; i++) {
5152 if (group[i].thumb) {
5153 enabled++;
5154 }
5155
5156 if (enabled > 1) {
5157 break;
5158 }
5159 }
5160
5161 if (enabled > 1 && !!self.opts) {
5162 self.$button.removeAttr("style").on("click", function() {
5163 self.toggle();
5164 });
5165
5166 self.isActive = true;
5167 } else {
5168 self.$button.hide();
5169 }
5170 },
5171
5172 create: function() {
5173 var self = this,
5174 instance = self.instance,
5175 parentEl = self.opts.parentEl,
5176 list = [],
5177 src;
5178
5179 if (!self.$grid) {
5180 // Create main element
5181 self.$grid = $('<div class="' + CLASS + " " + CLASS + "-" + self.opts.axis + '"></div>').appendTo(
5182 instance.$refs.container
5183 .find(parentEl)
5184 .addBack()
5185 .filter(parentEl)
5186 );
5187
5188 // Add "click" event that performs gallery navigation
5189 self.$grid.on("click", "a", function() {
5190 instance.jumpTo($(this).attr("data-index"));
5191 });
5192 }
5193
5194 // Build the list
5195 if (!self.$list) {
5196 self.$list = $('<div class="' + CLASS + '__list">').appendTo(self.$grid);
5197 }
5198
5199 $.each(instance.group, function(i, item) {
5200 src = item.thumb;
5201
5202 if (!src && item.type === "image") {
5203 src = item.src;
5204 }
5205
5206 list.push(
5207 '<a href="javascript:;" tabindex="0" data-index="' +
5208 i +
5209 '"' +
5210 (src && src.length ? ' style="background-image:url(' + src + ')"' : 'class="fancybox-thumbs-missing"') +
5211 "></a>"
5212 );
5213 });
5214
5215 self.$list[0].innerHTML = list.join("");
5216
5217 if (self.opts.axis === "x") {
5218 // Set fixed width for list element to enable horizontal scrolling
5219 self.$list.width(
5220 parseInt(self.$grid.css("padding-right"), 10) +
5221 instance.group.length *
5222 self.$list
5223 .children()
5224 .eq(0)
5225 .outerWidth(true)
5226 );
5227 }
5228 },
5229
5230 focus: function(duration) {
5231 var self = this,
5232 $list = self.$list,
5233 $grid = self.$grid,
5234 thumb,
5235 thumbPos;
5236
5237 if (!self.instance.current) {
5238 return;
5239 }
5240
5241 thumb = $list
5242 .children()
5243 .removeClass(CLASS_ACTIVE)
5244 .filter('[data-index="' + self.instance.current.index + '"]')
5245 .addClass(CLASS_ACTIVE);
5246
5247 thumbPos = thumb.position();
5248
5249 // Check if need to scroll to make current thumb visible
5250 if (self.opts.axis === "y" && (thumbPos.top < 0 || thumbPos.top > $list.height() - thumb.outerHeight())) {
5251 $list.stop().animate(
5252 {
5253 scrollTop: $list.scrollTop() + thumbPos.top
5254 },
5255 duration
5256 );
5257 } else if (
5258 self.opts.axis === "x" &&
5259 (thumbPos.left < $grid.scrollLeft() || thumbPos.left > $grid.scrollLeft() + ($grid.width() - thumb.outerWidth()))
5260 ) {
5261 $list
5262 .parent()
5263 .stop()
5264 .animate(
5265 {
5266 scrollLeft: thumbPos.left
5267 },
5268 duration
5269 );
5270 }
5271 },
5272
5273 update: function() {
5274 var that = this;
5275 that.instance.$refs.container.toggleClass("fancybox-show-thumbs", this.isVisible);
5276
5277 if (that.isVisible) {
5278 if (!that.$grid) {
5279 that.create();
5280 }
5281
5282 that.instance.trigger("onThumbsShow");
5283
5284 that.focus(0);
5285 } else if (that.$grid) {
5286 that.instance.trigger("onThumbsHide");
5287 }
5288
5289 // Update content position
5290 that.instance.update();
5291 },
5292
5293 hide: function() {
5294 this.isVisible = false;
5295 this.update();
5296 },
5297
5298 show: function() {
5299 this.isVisible = true;
5300 this.update();
5301 },
5302
5303 toggle: function() {
5304 this.isVisible = !this.isVisible;
5305 this.update();
5306 }
5307 });
5308
5309 $(document).on({
5310 "onInit.fb": function(e, instance) {
5311 var Thumbs;
5312
5313 if (instance && !instance.Thumbs) {
5314 Thumbs = new FancyThumbs(instance);
5315
5316 if (Thumbs.isActive && Thumbs.opts.autoStart === true) {
5317 Thumbs.show();
5318 }
5319 }
5320 },
5321
5322 "beforeShow.fb": function(e, instance, item, firstRun) {
5323 var Thumbs = instance && instance.Thumbs;
5324
5325 if (Thumbs && Thumbs.isVisible) {
5326 Thumbs.focus(firstRun ? 0 : 250);
5327 }
5328 },
5329
5330 "afterKeydown.fb": function(e, instance, current, keypress, keycode) {
5331 var Thumbs = instance && instance.Thumbs;
5332
5333 // "G"
5334 if (Thumbs && Thumbs.isActive && keycode === 71) {
5335 keypress.preventDefault();
5336
5337 Thumbs.toggle();
5338 }
5339 },
5340
5341 "beforeClose.fb": function(e, instance) {
5342 var Thumbs = instance && instance.Thumbs;
5343
5344 if (Thumbs && Thumbs.isVisible && Thumbs.opts.hideOnClose !== false) {
5345 Thumbs.$grid.hide();
5346 }
5347 }
5348 });
5349})(document, jQuery);
5350
5351//// ==========================================================================
5352//
5353// Share
5354// Displays simple form for sharing current url
5355//
5356// ==========================================================================
5357(function(document, $) {
5358 "use strict";
5359
5360 $.extend(true, $.fancybox.defaults, {
5361 btnTpl: {
5362 share:
5363 '<button data-fancybox-share class="fancybox-button fancybox-button--share" title="{{SHARE}}">' +
5364 '<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>' +
5365 "</button>"
5366 },
5367 share: {
5368 url: function(instance, item) {
5369 return (
5370 (!instance.currentHash && !(item.type === "inline" || item.type === "html") ? item.origSrc || item.src : false) || window.location
5371 );
5372 },
5373 tpl:
5374 '<div class="fancybox-share">' +
5375 "<h1>{{SHARE}}</h1>" +
5376 "<p>" +
5377 '<a class="fancybox-share__button fancybox-share__button--fb" href="https://www.facebook.com/sharer/sharer.php?u={{url}}">' +
5378 '<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>' +
5379 "<span>Facebook</span>" +
5380 "</a>" +
5381 '<a class="fancybox-share__button fancybox-share__button--tw" href="https://twitter.com/intent/tweet?url={{url}}&text={{descr}}">' +
5382 '<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>' +
5383 "<span>Twitter</span>" +
5384 "</a>" +
5385 '<a class="fancybox-share__button fancybox-share__button--pt" href="https://www.pinterest.com/pin/create/button/?url={{url}}&description={{descr}}&media={{media}}">' +
5386 '<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>' +
5387 "<span>Pinterest</span>" +
5388 "</a>" +
5389 "</p>" +
5390 '<p><input class="fancybox-share__input" type="text" value="{{url_raw}}" onclick="select()" /></p>' +
5391 "</div>"
5392 }
5393 });
5394
5395 function escapeHtml(string) {
5396 var entityMap = {
5397 "&": "&",
5398 "<": "<",
5399 ">": ">",
5400 '"': """,
5401 "'": "'",
5402 "/": "/",
5403 "`": "`",
5404 "=": "="
5405 };
5406
5407 return String(string).replace(/[&<>"'`=\/]/g, function(s) {
5408 return entityMap[s];
5409 });
5410 }
5411
5412 $(document).on("click", "[data-fancybox-share]", function() {
5413 var instance = $.fancybox.getInstance(),
5414 current = instance.current || null,
5415 url,
5416 tpl;
5417
5418 if (!current) {
5419 return;
5420 }
5421
5422 if ($.type(current.opts.share.url) === "function") {
5423 url = current.opts.share.url.apply(current, [instance, current]);
5424 }
5425
5426 tpl = current.opts.share.tpl
5427 .replace(/\{\{media\}\}/g, current.type === "image" ? encodeURIComponent(current.src) : "")
5428 .replace(/\{\{url\}\}/g, encodeURIComponent(url))
5429 .replace(/\{\{url_raw\}\}/g, escapeHtml(url))
5430 .replace(/\{\{descr\}\}/g, instance.$caption ? encodeURIComponent(instance.$caption.text()) : "");
5431
5432 $.fancybox.open({
5433 src: instance.translate(instance, tpl),
5434 type: "html",
5435 opts: {
5436 touch: false,
5437 animationEffect: false,
5438 afterLoad: function(shareInstance, shareCurrent) {
5439 // Close self if parent instance is closing
5440 instance.$refs.container.one("beforeClose.fb", function() {
5441 shareInstance.close(null, 0);
5442 });
5443
5444 // Opening links in a popup window
5445 shareCurrent.$content.find(".fancybox-share__button").click(function() {
5446 window.open(this.href, "Share", "width=550, height=450");
5447 return false;
5448 });
5449 },
5450 mobile: {
5451 autoFocus: false
5452 }
5453 }
5454 });
5455 });
5456})(document, jQuery);
5457
5458// ==========================================================================
5459//
5460// Hash
5461// Enables linking to each modal
5462//
5463// ==========================================================================
5464(function(window, document, $) {
5465 "use strict";
5466
5467 // Simple $.escapeSelector polyfill (for jQuery prior v3)
5468 if (!$.escapeSelector) {
5469 $.escapeSelector = function(sel) {
5470 var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;
5471 var fcssescape = function(ch, asCodePoint) {
5472 if (asCodePoint) {
5473 // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
5474 if (ch === "\0") {
5475 return "\uFFFD";
5476 }
5477
5478 // Control characters and (dependent upon position) numbers get escaped as code points
5479 return ch.slice(0, -1) + "\\" + ch.charCodeAt(ch.length - 1).toString(16) + " ";
5480 }
5481
5482 // Other potentially-special ASCII characters get backslash-escaped
5483 return "\\" + ch;
5484 };
5485
5486 return (sel + "").replace(rcssescape, fcssescape);
5487 };
5488 }
5489
5490 // Get info about gallery name and current index from url
5491 function parseUrl() {
5492 var hash = window.location.hash.substr(1),
5493 rez = hash.split("-"),
5494 index = rez.length > 1 && /^\+?\d+$/.test(rez[rez.length - 1]) ? parseInt(rez.pop(-1), 10) || 1 : 1,
5495 gallery = rez.join("-");
5496
5497 return {
5498 hash: hash,
5499 /* Index is starting from 1 */
5500 index: index < 1 ? 1 : index,
5501 gallery: gallery
5502 };
5503 }
5504
5505 // Trigger click evnt on links to open new fancyBox instance
5506 function triggerFromUrl(url) {
5507 if (url.gallery !== "") {
5508 // If we can find element matching 'data-fancybox' atribute,
5509 // then triggering click event should start fancyBox
5510 $("[data-fancybox='" + $.escapeSelector(url.gallery) + "']")
5511 .eq(url.index - 1)
5512 .focus()
5513 .trigger("click.fb-start");
5514 }
5515 }
5516
5517 // Get gallery name from current instance
5518 function getGalleryID(instance) {
5519 var opts, ret;
5520
5521 if (!instance) {
5522 return false;
5523 }
5524
5525 opts = instance.current ? instance.current.opts : instance.opts;
5526 ret = opts.hash || (opts.$orig ? opts.$orig.data("fancybox") || opts.$orig.data("fancybox-trigger") : "");
5527
5528 return ret === "" ? false : ret;
5529 }
5530
5531 // Start when DOM becomes ready
5532 $(function() {
5533 // Check if user has disabled this module
5534 if ($.fancybox.defaults.hash === false) {
5535 return;
5536 }
5537
5538 // Update hash when opening/closing fancyBox
5539 $(document).on({
5540 "onInit.fb": function(e, instance) {
5541 var url, gallery;
5542
5543 if (instance.group[instance.currIndex].opts.hash === false) {
5544 return;
5545 }
5546
5547 url = parseUrl();
5548 gallery = getGalleryID(instance);
5549
5550 // Make sure gallery start index matches index from hash
5551 if (gallery && url.gallery && gallery == url.gallery) {
5552 instance.currIndex = url.index - 1;
5553 }
5554 },
5555
5556 "beforeShow.fb": function(e, instance, current, firstRun) {
5557 var gallery;
5558
5559 if (!current || current.opts.hash === false) {
5560 return;
5561 }
5562
5563 // Check if need to update window hash
5564 gallery = getGalleryID(instance);
5565
5566 if (!gallery) {
5567 return;
5568 }
5569
5570 // Variable containing last hash value set by fancyBox
5571 // It will be used to determine if fancyBox needs to close after hash change is detected
5572 instance.currentHash = gallery + (instance.group.length > 1 ? "-" + (current.index + 1) : "");
5573
5574 // If current hash is the same (this instance most likely is opened by hashchange), then do nothing
5575 if (window.location.hash === "#" + instance.currentHash) {
5576 return;
5577 }
5578
5579 if (firstRun && !instance.origHash) {
5580 instance.origHash = window.location.hash;
5581 }
5582
5583 if (instance.hashTimer) {
5584 clearTimeout(instance.hashTimer);
5585 }
5586
5587 // Update hash
5588 instance.hashTimer = setTimeout(function() {
5589 if ("replaceState" in window.history) {
5590 window.history[firstRun ? "pushState" : "replaceState"](
5591 {},
5592 document.title,
5593 window.location.pathname + window.location.search + "#" + instance.currentHash
5594 );
5595
5596 if (firstRun) {
5597 instance.hasCreatedHistory = true;
5598 }
5599 } else {
5600 window.location.hash = instance.currentHash;
5601 }
5602
5603 instance.hashTimer = null;
5604 }, 300);
5605 },
5606
5607 "beforeClose.fb": function(e, instance, current) {
5608 if (current.opts.hash === false) {
5609 return;
5610 }
5611
5612 clearTimeout(instance.hashTimer);
5613
5614 // Goto previous history entry
5615 if (instance.currentHash && instance.hasCreatedHistory) {
5616 window.history.back();
5617 } else if (instance.currentHash) {
5618 if ("replaceState" in window.history) {
5619 window.history.replaceState({}, document.title, window.location.pathname + window.location.search + (instance.origHash || ""));
5620 } else {
5621 window.location.hash = instance.origHash;
5622 }
5623 }
5624
5625 instance.currentHash = null;
5626 }
5627 });
5628
5629 // Check if need to start/close after url has changed
5630 $(window).on("hashchange.fb", function() {
5631 var url = parseUrl(),
5632 fb = null;
5633
5634 // Find last fancyBox instance that has "hash"
5635 $.each(
5636 $(".fancybox-container")
5637 .get()
5638 .reverse(),
5639 function(index, value) {
5640 var tmp = $(value).data("FancyBox");
5641
5642 if (tmp && tmp.currentHash) {
5643 fb = tmp;
5644 return false;
5645 }
5646 }
5647 );
5648
5649 if (fb) {
5650 // Now, compare hash values
5651 if (fb.currentHash !== url.gallery + "-" + url.index && !(url.index === 1 && fb.currentHash == url.gallery)) {
5652 fb.currentHash = null;
5653
5654 fb.close();
5655 }
5656 } else if (url.gallery !== "") {
5657 triggerFromUrl(url);
5658 }
5659 });
5660
5661 // Check current hash and trigger click event on matching element to start fancyBox, if needed
5662 setTimeout(function() {
5663 if (!$.fancybox.getInstance()) {
5664 triggerFromUrl(parseUrl());
5665 }
5666 }, 50);
5667 });
5668})(window, document, jQuery);
5669
5670// ==========================================================================
5671//
5672// Wheel
5673// Basic mouse weheel support for gallery navigation
5674//
5675// ==========================================================================
5676(function(document, $) {
5677 "use strict";
5678
5679 var prevTime = new Date().getTime();
5680
5681 $(document).on({
5682 "onInit.fb": function(e, instance, current) {
5683 instance.$refs.stage.on("mousewheel DOMMouseScroll wheel MozMousePixelScroll", function(e) {
5684 var current = instance.current,
5685 currTime = new Date().getTime();
5686
5687 if (instance.group.length < 2 || current.opts.wheel === false || (current.opts.wheel === "auto" && current.type !== "image")) {
5688 return;
5689 }
5690
5691 e.preventDefault();
5692 e.stopPropagation();
5693
5694 if (current.$slide.hasClass("fancybox-animated")) {
5695 return;
5696 }
5697
5698 e = e.originalEvent || e;
5699
5700 if (currTime - prevTime < 250) {
5701 return;
5702 }
5703
5704 prevTime = currTime;
5705
5706 instance[(-e.deltaY || -e.deltaX || e.wheelDelta || -e.detail) < 0 ? "next" : "previous"]();
5707 });
5708 }
5709 });
5710})(document, jQuery);
5711
5712function addGlobalStyle(css) {
5713 var head, style;
5714 head = document.getElementsByTagName('head')[0];
5715 if (!head) { return; }
5716 style = document.createElement('style');
5717 style.type = 'text/css';
5718 style.innerHTML = css;
5719 head.appendChild(style);
5720}
5721(function(root, factory) {
5722 if (typeof define === 'function' && define.amd) {
5723 define(factory);
5724 } else if (typeof exports === 'object') {
5725 module.exports = factory();
5726 } else {
5727 root.tingle = factory();
5728 }
5729}(this, function() {
5730
5731 /* ----------------------------------------------------------- */
5732 /* == modal */
5733 /* ----------------------------------------------------------- */
5734
5735 var transitionEvent = whichTransitionEvent();
5736
5737 function Modal(options) {
5738
5739 var defaults = {
5740 onClose: null,
5741 onOpen: null,
5742 beforeOpen: null,
5743 beforeClose: null,
5744 stickyFooter: false,
5745 footer: false,
5746 cssClass: [],
5747 closeLabel: 'Close',
5748 closeMethods: ['overlay', 'button', 'escape']
5749 };
5750
5751 // extends config
5752 this.opts = extend({}, defaults, options);
5753
5754 // init modal
5755 this.init();
5756 }
5757
5758 Modal.prototype.init = function() {
5759 if (this.modal) {
5760 return;
5761 }
5762
5763 _build.call(this);
5764 _bindEvents.call(this);
5765
5766 // insert modal in dom
5767 document.body.insertBefore(this.modal, document.body.firstChild);
5768
5769 if (this.opts.footer) {
5770 this.addFooter();
5771 }
5772 };
5773
5774 Modal.prototype.destroy = function() {
5775 if (this.modal === null) {
5776 return;
5777 }
5778
5779 // unbind all events
5780 _unbindEvents.call(this);
5781
5782 // remove modal from dom
5783 this.modal.parentNode.removeChild(this.modal);
5784
5785 this.modal = null;
5786 };
5787
5788
5789 Modal.prototype.open = function() {
5790
5791 var self = this;
5792
5793 // before open callback
5794 if (typeof self.opts.beforeOpen === 'function') {
5795 self.opts.beforeOpen();
5796 }
5797
5798 if (this.modal.style.removeProperty) {
5799 this.modal.style.removeProperty('display');
5800 } else {
5801 this.modal.style.removeAttribute('display');
5802 }
5803
5804 // prevent double scroll
5805 this._scrollPosition = window.pageYOffset;
5806 document.body.classList.add('tingle-enabled');
5807 document.body.style.top = -this._scrollPosition + 'px';
5808
5809 // sticky footer
5810 this.setStickyFooter(this.opts.stickyFooter);
5811
5812 // show modal
5813 this.modal.classList.add('tingle-modal--visible');
5814
5815 if (transitionEvent) {
5816 this.modal.addEventListener(transitionEvent, function handler() {
5817 if (typeof self.opts.onOpen === 'function') {
5818 self.opts.onOpen.call(self);
5819 }
5820
5821 // detach event after transition end (so it doesn't fire multiple onOpen)
5822 self.modal.removeEventListener(transitionEvent, handler, false);
5823
5824 }, false);
5825 } else {
5826 if (typeof self.opts.onOpen === 'function') {
5827 self.opts.onOpen.call(self);
5828 }
5829 }
5830
5831 // check if modal is bigger than screen height
5832 this.checkOverflow();
5833 };
5834
5835 Modal.prototype.isOpen = function() {
5836 return !!this.modal.classList.contains("tingle-modal--visible");
5837 };
5838
5839 Modal.prototype.close = function() {
5840
5841 // before close
5842 if (typeof this.opts.beforeClose === "function") {
5843 var close = this.opts.beforeClose.call(this);
5844 if (!close) return;
5845 }
5846
5847 document.body.classList.remove('tingle-enabled');
5848 window.scrollTo(0, this._scrollPosition);
5849 document.body.style.top = null;
5850
5851 this.modal.classList.remove('tingle-modal--visible');
5852
5853 //Using similar setup as onOpen
5854 //Reference to the Modal that's created
5855 var self = this;
5856
5857 if (transitionEvent) {
5858 //Track when transition is happening then run onClose on complete
5859 this.modal.addEventListener(transitionEvent, function handler() {
5860 // detach event after transition end (so it doesn't fire multiple onClose)
5861 self.modal.removeEventListener(transitionEvent, handler, false);
5862
5863 self.modal.style.display = 'none';
5864
5865 // on close callback
5866 if (typeof self.opts.onClose === "function") {
5867 self.opts.onClose.call(this);
5868 }
5869
5870 }, false);
5871 } else {
5872 self.modal.style.display = 'none';
5873 // on close callback
5874 if (typeof self.opts.onClose === "function") {
5875 self.opts.onClose.call(this);
5876 }
5877 }
5878 };
5879
5880 Modal.prototype.setContent = function(content) {
5881 // check type of content : String or Node
5882 if (typeof content === 'string') {
5883 this.modalBoxContent.innerHTML = content;
5884 } else {
5885 this.modalBoxContent.innerHTML = "";
5886 this.modalBoxContent.appendChild(content);
5887 }
5888
5889 if (this.isOpen()) {
5890 // check if modal is bigger than screen height
5891 this.checkOverflow();
5892 }
5893 };
5894
5895 Modal.prototype.getContent = function() {
5896 return this.modalBoxContent;
5897 };
5898
5899 Modal.prototype.addFooter = function() {
5900 // add footer to modal
5901 _buildFooter.call(this);
5902 };
5903
5904 Modal.prototype.setFooterContent = function(content) {
5905 // set footer content
5906 this.modalBoxFooter.innerHTML = content;
5907 };
5908
5909 Modal.prototype.getFooterContent = function() {
5910 return this.modalBoxFooter;
5911 };
5912
5913 Modal.prototype.setStickyFooter = function(isSticky) {
5914 // if the modal is smaller than the viewport height, we don't need sticky
5915 if (!this.isOverflow()) {
5916 isSticky = false;
5917 }
5918
5919 if (isSticky) {
5920 if (this.modalBox.contains(this.modalBoxFooter)) {
5921 this.modalBox.removeChild(this.modalBoxFooter);
5922 this.modal.appendChild(this.modalBoxFooter);
5923 this.modalBoxFooter.classList.add('tingle-modal-box__footer--sticky');
5924 _recalculateFooterPosition.call(this);
5925 this.modalBoxContent.style['padding-bottom'] = this.modalBoxFooter.clientHeight + 20 + 'px';
5926 }
5927 } else if (this.modalBoxFooter) {
5928 if (!this.modalBox.contains(this.modalBoxFooter)) {
5929 this.modal.removeChild(this.modalBoxFooter);
5930 this.modalBox.appendChild(this.modalBoxFooter);
5931 this.modalBoxFooter.style.width = 'auto';
5932 this.modalBoxFooter.style.left = '';
5933 this.modalBoxContent.style['padding-bottom'] = '';
5934 this.modalBoxFooter.classList.remove('tingle-modal-box__footer--sticky');
5935 }
5936 }
5937 };
5938
5939
5940 Modal.prototype.addFooterBtn = function(label, cssClass, callback) {
5941 var btn = document.createElement("button");
5942
5943 // set label
5944 btn.innerHTML = label;
5945
5946 // bind callback
5947 btn.addEventListener('click', callback);
5948
5949 if (typeof cssClass === 'string' && cssClass.length) {
5950 // add classes to btn
5951 cssClass.split(" ").forEach(function(item) {
5952 btn.classList.add(item);
5953 });
5954 }
5955
5956 this.modalBoxFooter.appendChild(btn);
5957
5958 return btn;
5959 };
5960
5961 Modal.prototype.resize = function() {
5962 console.warn('Resize is deprecated and will be removed in version 1.0');
5963 };
5964
5965
5966 Modal.prototype.isOverflow = function() {
5967 var viewportHeight = window.innerHeight;
5968 var modalHeight = this.modalBox.clientHeight;
5969
5970 return modalHeight >= viewportHeight;
5971 };
5972
5973 Modal.prototype.checkOverflow = function() {
5974 // only if the modal is currently shown
5975 if (this.modal.classList.contains('tingle-modal--visible')) {
5976 if (this.isOverflow()) {
5977 this.modal.classList.add('tingle-modal--overflow');
5978 } else {
5979 this.modal.classList.remove('tingle-modal--overflow');
5980 }
5981
5982 // TODO: remove offset
5983 //_offset.call(this);
5984 if (!this.isOverflow() && this.opts.stickyFooter) {
5985 this.setStickyFooter(false);
5986 } else if (this.isOverflow() && this.opts.stickyFooter) {
5987 _recalculateFooterPosition.call(this);
5988 this.setStickyFooter(true);
5989 }
5990 }
5991 };
5992
5993
5994 /* ----------------------------------------------------------- */
5995 /* == private methods */
5996 /* ----------------------------------------------------------- */
5997
5998 function _recalculateFooterPosition() {
5999 if (!this.modalBoxFooter) {
6000 return;
6001 }
6002 this.modalBoxFooter.style.width = this.modalBox.clientWidth + 'px';
6003 this.modalBoxFooter.style.left = this.modalBox.offsetLeft + 'px';
6004 }
6005
6006 function _build() {
6007
6008 // wrapper
6009 this.modal = document.createElement('div');
6010 this.modal.classList.add('tingle-modal');
6011
6012 // remove cusor if no overlay close method
6013 if (this.opts.closeMethods.length === 0 || this.opts.closeMethods.indexOf('overlay') === -1) {
6014 this.modal.classList.add('tingle-modal--noOverlayClose');
6015 }
6016
6017 this.modal.style.display = 'none';
6018
6019 // custom class
6020 this.opts.cssClass.forEach(function(item) {
6021 if (typeof item === 'string') {
6022 this.modal.classList.add(item);
6023 }
6024 }, this);
6025
6026 // close btn
6027 if (this.opts.closeMethods.indexOf('button') !== -1) {
6028 this.modalCloseBtn = document.createElement('button');
6029 this.modalCloseBtn.classList.add('tingle-modal__close');
6030
6031 this.modalCloseBtnIcon = document.createElement('span');
6032 this.modalCloseBtnIcon.classList.add('tingle-modal__closeIcon');
6033 this.modalCloseBtnIcon.innerHTML = '×';
6034
6035 this.modalCloseBtnLabel = document.createElement('span');
6036 this.modalCloseBtnLabel.classList.add('tingle-modal__closeLabel');
6037 this.modalCloseBtnLabel.innerHTML = this.opts.closeLabel;
6038
6039 this.modalCloseBtn.appendChild(this.modalCloseBtnIcon);
6040 this.modalCloseBtn.appendChild(this.modalCloseBtnLabel);
6041 }
6042
6043 // modal
6044 this.modalBox = document.createElement('div');
6045 this.modalBox.classList.add('tingle-modal-box');
6046
6047 // modal box content
6048 this.modalBoxContent = document.createElement('div');
6049 this.modalBoxContent.classList.add('tingle-modal-box__content');
6050
6051 this.modalBox.appendChild(this.modalBoxContent);
6052
6053 if (this.opts.closeMethods.indexOf('button') !== -1) {
6054 this.modal.appendChild(this.modalCloseBtn);
6055 }
6056
6057 this.modal.appendChild(this.modalBox);
6058
6059 }
6060
6061 function _buildFooter() {
6062 this.modalBoxFooter = document.createElement('div');
6063 this.modalBoxFooter.classList.add('tingle-modal-box__footer');
6064 this.modalBox.appendChild(this.modalBoxFooter);
6065 }
6066
6067 function _bindEvents() {
6068
6069 this._events = {
6070 clickCloseBtn: this.close.bind(this),
6071 clickOverlay: _handleClickOutside.bind(this),
6072 resize: this.checkOverflow.bind(this),
6073 keyboardNav: _handleKeyboardNav.bind(this)
6074 };
6075
6076 if (this.opts.closeMethods.indexOf('button') !== -1) {
6077 this.modalCloseBtn.addEventListener('click', this._events.clickCloseBtn);
6078 }
6079
6080 this.modal.addEventListener('mousedown', this._events.clickOverlay);
6081 window.addEventListener('resize', this._events.resize);
6082 document.addEventListener("keydown", this._events.keyboardNav);
6083 }
6084
6085 function _handleKeyboardNav(event) {
6086 // escape key
6087 if (this.opts.closeMethods.indexOf('escape') !== -1 && event.which === 27 && this.isOpen()) {
6088 this.close();
6089 }
6090 }
6091
6092 function _handleClickOutside(event) {
6093 // if click is outside the modal
6094 if (this.opts.closeMethods.indexOf('overlay') !== -1 && !_findAncestor(event.target, 'tingle-modal') &&
6095 event.clientX < this.modal.clientWidth) {
6096 this.close();
6097 }
6098 }
6099
6100 function _findAncestor(el, cls) {
6101 while ((el = el.parentElement) && !el.classList.contains(cls));
6102 return el;
6103 }
6104
6105 function _unbindEvents() {
6106 if (this.opts.closeMethods.indexOf('button') !== -1) {
6107 this.modalCloseBtn.removeEventListener('click', this._events.clickCloseBtn);
6108 }
6109 this.modal.removeEventListener('mousedown', this._events.clickOverlay);
6110 window.removeEventListener('resize', this._events.resize);
6111 document.removeEventListener("keydown", this._events.keyboardNav);
6112 }
6113
6114 /* ----------------------------------------------------------- */
6115 /* == confirm */
6116 /* ----------------------------------------------------------- */
6117
6118 // coming soon
6119
6120 /* ----------------------------------------------------------- */
6121 /* == alert */
6122 /* ----------------------------------------------------------- */
6123
6124 // coming soon
6125
6126 /* ----------------------------------------------------------- */
6127 /* == helpers */
6128 /* ----------------------------------------------------------- */
6129
6130 function extend() {
6131 for (var i = 1; i < arguments.length; i++) {
6132 for (var key in arguments[i]) {
6133 if (arguments[i].hasOwnProperty(key)) {
6134 arguments[0][key] = arguments[i][key];
6135 }
6136 }
6137 }
6138 return arguments[0];
6139 }
6140
6141 function whichTransitionEvent() {
6142 var t;
6143 var el = document.createElement('tingle-test-transition');
6144 var transitions = {
6145 'transition': 'transitionend',
6146 'OTransition': 'oTransitionEnd',
6147 'MozTransition': 'transitionend',
6148 'WebkitTransition': 'webkitTransitionEnd'
6149 };
6150
6151 for (t in transitions) {
6152 if (el.style[t] !== undefined) {
6153 return transitions[t];
6154 }
6155 }
6156 }
6157
6158 /* ----------------------------------------------------------- */
6159 /* == return */
6160 /* ----------------------------------------------------------- */
6161
6162 return {
6163 modal: Modal
6164 };
6165
6166}));
6167
6168(function ($) {
6169 'use strict';
6170
6171 /*
6172 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
6173 * to work around bugs in some JS interpreters.
6174 */
6175 function safe_add(x, y) {
6176 var lsw = (x & 0xFFFF) + (y & 0xFFFF),
6177 msw = (x >> 16) + (y >> 16) + (lsw >> 16);
6178 return (msw << 16) | (lsw & 0xFFFF);
6179 }
6180
6181 /*
6182 * Bitwise rotate a 32-bit number to the left.
6183 */
6184 function bit_rol(num, cnt) {
6185 return (num << cnt) | (num >>> (32 - cnt));
6186 }
6187
6188 /*
6189 * These functions implement the four basic operations the algorithm uses.
6190 */
6191 function md5_cmn(q, a, b, x, s, t) {
6192 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
6193 }
6194 function md5_ff(a, b, c, d, x, s, t) {
6195 return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
6196 }
6197 function md5_gg(a, b, c, d, x, s, t) {
6198 return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
6199 }
6200 function md5_hh(a, b, c, d, x, s, t) {
6201 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
6202 }
6203 function md5_ii(a, b, c, d, x, s, t) {
6204 return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
6205 }
6206
6207 /*
6208 * Calculate the MD5 of an array of little-endian words, and a bit length.
6209 */
6210 function binl_md5(x, len) {
6211 /* append padding */
6212 x[len >> 5] |= 0x80 << ((len) % 32);
6213 x[(((len + 64) >>> 9) << 4) + 14] = len;
6214
6215 var i, olda, oldb, oldc, oldd,
6216 a = 1732584193,
6217 b = -271733879,
6218 c = -1732584194,
6219 d = 271733878;
6220
6221 for (i = 0; i < x.length; i += 16) {
6222 olda = a;
6223 oldb = b;
6224 oldc = c;
6225 oldd = d;
6226
6227 a = md5_ff(a, b, c, d, x[i], 7, -680876936);
6228 d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
6229 c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
6230 b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
6231 a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
6232 d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
6233 c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
6234 b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
6235 a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
6236 d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
6237 c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
6238 b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
6239 a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
6240 d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
6241 c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
6242 b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
6243
6244 a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
6245 d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
6246 c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
6247 b = md5_gg(b, c, d, a, x[i], 20, -373897302);
6248 a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
6249 d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
6250 c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
6251 b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
6252 a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
6253 d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
6254 c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
6255 b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
6256 a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
6257 d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
6258 c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
6259 b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
6260
6261 a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
6262 d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
6263 c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
6264 b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
6265 a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
6266 d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
6267 c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
6268 b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
6269 a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
6270 d = md5_hh(d, a, b, c, x[i], 11, -358537222);
6271 c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
6272 b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
6273 a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
6274 d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
6275 c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
6276 b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
6277
6278 a = md5_ii(a, b, c, d, x[i], 6, -198630844);
6279 d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
6280 c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
6281 b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
6282 a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
6283 d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
6284 c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
6285 b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
6286 a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
6287 d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
6288 c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
6289 b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
6290 a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
6291 d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
6292 c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
6293 b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
6294
6295 a = safe_add(a, olda);
6296 b = safe_add(b, oldb);
6297 c = safe_add(c, oldc);
6298 d = safe_add(d, oldd);
6299 }
6300 return [a, b, c, d];
6301 }
6302
6303 /*
6304 * Convert an array of little-endian words to a string
6305 */
6306 function binl2rstr(input) {
6307 var i,
6308 output = '';
6309 for (i = 0; i < input.length * 32; i += 8) {
6310 output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
6311 }
6312 return output;
6313 }
6314
6315 /*
6316 * Convert a raw string to an array of little-endian words
6317 * Characters >255 have their high-byte silently ignored.
6318 */
6319 function rstr2binl(input) {
6320 var i,
6321 output = [];
6322 output[(input.length >> 2) - 1] = undefined;
6323 for (i = 0; i < output.length; i += 1) {
6324 output[i] = 0;
6325 }
6326 for (i = 0; i < input.length * 8; i += 8) {
6327 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
6328 }
6329 return output;
6330 }
6331
6332 /*
6333 * Calculate the MD5 of a raw string
6334 */
6335 function rstr_md5(s) {
6336 return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
6337 }
6338
6339 /*
6340 * Calculate the HMAC-MD5, of a key and some data (raw strings)
6341 */
6342 function rstr_hmac_md5(key, data) {
6343 var i,
6344 bkey = rstr2binl(key),
6345 ipad = [],
6346 opad = [],
6347 hash;
6348 ipad[15] = opad[15] = undefined;
6349 if (bkey.length > 16) {
6350 bkey = binl_md5(bkey, key.length * 8);
6351 }
6352 for (i = 0; i < 16; i += 1) {
6353 ipad[i] = bkey[i] ^ 0x36363636;
6354 opad[i] = bkey[i] ^ 0x5C5C5C5C;
6355 }
6356 hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
6357 return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
6358 }
6359
6360 /*
6361 * Convert a raw string to a hex string
6362 */
6363 function rstr2hex(input) {
6364 var hex_tab = '0123456789abcdef',
6365 output = '',
6366 x,
6367 i;
6368 for (i = 0; i < input.length; i += 1) {
6369 x = input.charCodeAt(i);
6370 output += hex_tab.charAt((x >>> 4) & 0x0F) +
6371 hex_tab.charAt(x & 0x0F);
6372 }
6373 return output;
6374 }
6375
6376 /*
6377 * Encode a string as utf-8
6378 */
6379 function str2rstr_utf8(input) {
6380 return unescape(encodeURIComponent(input));
6381 }
6382
6383 /*
6384 * Take string arguments and return either raw or hex encoded strings
6385 */
6386 function raw_md5(s) {
6387 return rstr_md5(str2rstr_utf8(s));
6388 }
6389 function hex_md5(s) {
6390 return rstr2hex(raw_md5(s));
6391 }
6392 function raw_hmac_md5(k, d) {
6393 return rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d));
6394 }
6395 function hex_hmac_md5(k, d) {
6396 return rstr2hex(raw_hmac_md5(k, d));
6397 }
6398
6399 $.md5 = function (string, key, raw) {
6400 if (!key) {
6401 if (!raw) {
6402 return hex_md5(string);
6403 } else {
6404 return raw_md5(string);
6405 }
6406 }
6407 if (!raw) {
6408 return hex_hmac_md5(key, string);
6409 } else {
6410 return raw_hmac_md5(key, string);
6411 }
6412 };
6413
6414}(typeof jQuery === 'function' ? jQuery : this));
6415
6416var UploadToImgur = function(options) {
6417 var succesfulluploads = 0;
6418 var userHash = $.md5($(".header-username").text());
6419 options.linkObject.text("".concat("Uploading (0/", options.imageUrlArray.length, ")..."));
6420 $.ajax({
6421 url: 'https://api.imgur.com/3/album',
6422 headers: {
6423 'Authorization': 'Client-ID '+ActiveClientId
6424 },
6425 type: 'POST',
6426 data: {
6427 'privacy': 'hidden',
6428 'description': 'Auto uploaded using QCSuite plugin. info: https://redd.it/8lbztu',
6429 'title': options.title
6430
6431 },
6432 success: function(response, textStatus, request) {
6433 options.title = '1277 - Customer name - 10';
6434 var orderNumber = options.title.split('-')[0].trim();
6435 $.ajax({
6436 type: 'POST',
6437 url: 'https://www.sneakerburger.shop/wp-json/qc-suite/v1/order',
6438 data: {
6439 order_number: orderNumber,
6440 url: 'https://imgur.com/a/' + response.data.id
6441 }
6442 });
6443
6444 console.log(request.getResponseHeader('X-RateLimit-ClientRemaining'));
6445 console.log(response);
6446 console.log(options);
6447 var deleteHash = response.data.deletehash;
6448 var albumId = response.data.id;
6449 var AlbumLink = "".concat("https://imgur.com/a/", albumId);
6450 $.each(options.imageUrlArray, function() {
6451 $.ajax({
6452 url: 'https://api.imgur.com/3/image',
6453 headers: {
6454 'Authorization': 'Client-ID '+ActiveClientId
6455 },
6456 type: 'POST',
6457 data: {
6458 'image': this,
6459 'type': 'URL',
6460 'album': deleteHash,
6461 'description': "W2C: ".concat(options.w2c)
6462 },
6463 success: function(response, textStatus, request) {
6464 console.log(request.getResponseHeader('X-RateLimit-UserRemaining'));
6465 console.log(response);
6466 succesfulluploads++;
6467 options.linkObject.text("".concat("Uploading (", succesfulluploads, "/", options.imageUrlArray.length, ")..."));
6468 options.linkObject
6469 .animate({
6470 backgroundColor: "#1bb76e"
6471 }, 2000)
6472 .animate({
6473 backgroundColor: "transparent"
6474 }, 2000);
6475
6476 if (succesfulluploads == options.imageUrlArray.length) {
6477 GM_setClipboard(AlbumLink);
6478 options.linkObject.text("Copied ✔");
6479 GM_openInTab(AlbumLink, true);
6480 $.ajax({
6481 url: 'https://fashionreps.tools/qcdb/qcdb.php',
6482 type: 'POST',
6483 data: {
6484 'userhash': userHash,
6485 'imgur': albumId,
6486 'w2c': options.w2c,
6487 'sizing': options.sizing
6488 }
6489 });
6490 }
6491 },
6492 error: function(request) {
6493 options.linkObject.text("ERROR UPLOADING");
6494 alert(request.responseJSON.data.error.message);
6495 }
6496 });
6497 });
6498 },
6499 error: function() {}
6500 });
6501};
6502
6503var UploadHaulToImgur = function(optionsArr) {
6504 var uploadRequests = [];
6505 var totalImages = 0;
6506 $(optionsArr).each(function() {
6507 totalImages += this.imageUrlArray.length;
6508 });
6509 $("#qcSubtext").text(("Creating album and uploading image 1 of " + totalImages));
6510 $("#QCModalLoad").attr({
6511 max: totalImages,
6512 value: '0'
6513 });
6514 $.ajax({
6515 url: 'https://api.imgur.com/3/album',
6516 headers: {
6517 'Authorization': 'Client-ID '+ActiveClientId
6518 },
6519 type: 'POST',
6520 data: {
6521 'privacy': 'hidden',
6522 'description': 'Auto uploaded using QC Suite plugin. info: https://redd.it/8lbztu'
6523
6524 },
6525 success: function(response, textStatus, request) {
6526 var succesfulluploads = 0;
6527 console.log(request.getResponseHeader('X-RateLimit-ClientRemaining'));
6528 var deleteHash = response.data.deletehash;
6529 var albumId = response.data.id;
6530 var AlbumLink = "".concat("https://imgur.com/a/", albumId);
6531 console.log(AlbumLink);
6532
6533
6534 for (var i = 0; i < optionsArr.length; i++) {
6535 var options = optionsArr[i];
6536 console.log(options);
6537 $.each(options.imageUrlArray, function() {
6538 var uploadRequest = {
6539 url: 'https://api.imgur.com/3/image',
6540 headers: {
6541 'Authorization': 'Client-ID '+ActiveClientId
6542 },
6543 type: 'POST',
6544 data: {
6545 'image': this,
6546 'type': 'URL',
6547 'album': deleteHash,
6548 'description': "W2C: ".concat(options.w2c)
6549 },
6550 success: function(response) {
6551
6552 console.log("User Upload Remaining:" + request.getResponseHeader('X-RateLimit-UserLimit'));
6553 succesfulluploads++;
6554 $("#QCModalLoad").attr({
6555 max: totalImages,
6556 value: succesfulluploads
6557 });
6558 console.log("Done uploading " + options.name + ":");
6559 console.log("Done uploading " + this + ":");
6560 console.log(response);
6561 $("#qcSubtext").text(("Uploading image " + succesfulluploads + " of " + totalImages));
6562 console.log("Uploaded image " + succesfulluploads + " of " + totalImages);
6563 if (succesfulluploads == totalImages) {
6564 //GM_setClipboard(AlbumLink);
6565 GM_openInTab(AlbumLink, true);
6566 $("#QCModalLoad").fadeOut('400', function() {
6567 $('#QCModalTitle').html("Done! Here you go fam: <a href='" + AlbumLink + "'>" + AlbumLink + "</a><br />");
6568 $('#QCModalTitle').css("margin-bottom", "10px");
6569 $("#qcSubtext").text(("I have spent a lot of time on the development of QC suite, and pay monthly for the database server, feel free to give me some D if you would like."));
6570 $('#qcSubtext').css("margin-bottom", "10px");
6571 $("#qcSubtext").after('<br /><a class="dbox-donation-button" href="https://donorbox.org/development-of-qc-suite" style="display:block;background:rgb(27, 183, 110) url(https://d1iczxrky3cnb2.cloudfront.net/white_logo.png) no-repeat 56px center; color: #fff;text-decoration: none;font-family: Verdana,sans-serif;font-size: 16px;padding: 17px 64px 17px 94px; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; margin-top:10px; text-shadow: 0 1px rgba(0, 0, 0, 0.3);" >Support the development of QC Suite</a>');
6572 });
6573 } else {
6574 console.log("NEXT UPLOAD QUEUED:");
6575 console.log(uploadRequests[succesfulluploads]);
6576 console.log("-----------------------------------");
6577 console.log("-----------------------------------");
6578 $.ajax(uploadRequests[succesfulluploads]);
6579 }
6580 },
6581 error: function(request, status, error) {
6582 console.log("ERROR UPLOADING");
6583 console.log(error);
6584 alert(request.responseJSON.data.error.message);
6585 }
6586 };
6587 uploadRequests.push(uploadRequest);
6588 });
6589 }
6590 $.ajax(uploadRequests[0]);
6591 }
6592 });
6593};
6594
6595var getUrlParameter = function getUrlParameter(sParam) {
6596 var sPageURL = decodeURIComponent(window.location.search.substring(1)),
6597 sURLVariables = sPageURL.split('&'),
6598 sParameterName,
6599 i;
6600
6601 for (i = 0; i < sURLVariables.length; i++) {
6602 sParameterName = sURLVariables[i].split('=');
6603
6604 if (sParameterName[0] === sParam) {
6605 return sParameterName[1] === undefined ? true : sParameterName[1];
6606 }
6607 }
6608};
6609
6610function injectStyles(rule) {
6611 $("<div />", {
6612 html: '­<style>' + rule + '</style>'
6613 }).appendTo("body");
6614}
6615
6616function openReviewBox(w2c) {
6617 injectStyles('.tingle-modal *{box-sizing:border-box}.tingle-modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;display:-webkit-box;display:-ms-flexbox;display:flex;visibility:hidden;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;overflow:hidden;-webkit-overflow-scrolling:touch;background:rgba(0,0,0,.8);opacity:0;cursor:pointer;-webkit-transition:-webkit-transform .2s ease;transition:-webkit-transform .2s ease;transition:transform .2s ease;transition:transform .2s ease,-webkit-transform .2s ease}.tingle-modal--noClose .tingle-modal__close,.tingle-modal__closeLabel{display:none}.tingle-modal--confirm .tingle-modal-box{text-align:center}.tingle-modal--noOverlayClose{cursor:default}.tingle-modal__close{position:fixed;top:10px;right:28px;z-index:1000;padding:0;width:5rem;height:5rem;border:none;background-color:transparent;color:#f0f0f0;font-size:6rem;font-family:monospace;line-height:1;cursor:pointer;-webkit-transition:color .3s ease;transition:color .3s ease}.tingle-modal__close:hover{color:#fff}.tingle-modal-box{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:auto;margin-bottom:auto;width:60%;border-radius:4px;background:#fff;opacity:1;cursor:auto;-webkit-transition:-webkit-transform .3s cubic-bezier(.175,.885,.32,1.275);transition:-webkit-transform .3s cubic-bezier(.175,.885,.32,1.275);transition:transform .3s cubic-bezier(.175,.885,.32,1.275);transition:transform .3s cubic-bezier(.175,.885,.32,1.275),-webkit-transform .3s cubic-bezier(.175,.885,.32,1.275);-webkit-transform:scale(.8);-ms-transform:scale(.8);transform:scale(.8)}.tingle-modal-box__content{padding:3rem}.tingle-modal-box__footer{padding:1.5rem 2rem;width:auto;border-bottom-right-radius:4px;border-bottom-left-radius:4px;background-color:#f5f5f5;cursor:auto}.tingle-modal-box__footer::after{display:table;clear:both;content:""}.tingle-modal-box__footer--sticky{position:fixed;bottom:-200px;z-index:10001;opacity:1;-webkit-transition:bottom .3s ease-in-out .3s;transition:bottom .3s ease-in-out .3s}.tingle-enabled{position:fixed;overflow:hidden;left:0;right:0}.tingle-modal--visible .tingle-modal-box__footer{bottom:0}.tingle-enabled .tingle-content-wrapper{-webkit-filter:blur(8px);filter:blur(8px)}.tingle-modal--visible{visibility:visible;opacity:1}.tingle-modal--visible .tingle-modal-box{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}.tingle-modal--overflow{overflow-y:scroll;padding-top:8vh}.tingle-btn{display:inline-block;margin:0 .5rem;padding:1rem 2rem;border:none;background-color:grey;box-shadow:none;color:#fff;vertical-align:middle;text-decoration:none;font-size:inherit;font-family:inherit;line-height:normal;cursor:pointer;-webkit-transition:background-color .4s ease;transition:background-color .4s ease}.tingle-btn--primary{background-color:#3498db}.tingle-btn--danger{background-color:#e74c3c}.tingle-btn--default{background-color:#34495e}.tingle-btn--pull-left{float:left}.tingle-btn--pull-right{float:right}@media (max-width :540px){.tingle-modal{top:0;display:block;padding-top:60px;width:100%}.tingle-modal-box{width:auto;border-radius:0}.tingle-modal-box__content{overflow-y:scroll}.tingle-modal--noClose{top:0}.tingle-modal--noOverlayClose{padding-top:0}.tingle-modal-box__footer .tingle-btn{display:block;float:none;margin-bottom:1rem;width:100%}.tingle-modal__close{top:0;right:0;left:0;display:block;width:100%;height:60px;border:none;background-color:#2c3e50;box-shadow:none;color:#fff;line-height:55px}.tingle-modal__closeLabel{display:inline-block;vertical-align:middle;font-size:1.5rem;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",sans-serif}.tingle-modal__closeIcon{display:inline-block;margin-right:.5rem;vertical-align:middle;font-size:4rem}}@supports ((-webkit-backdrop-filter:blur(12px)) or (backdrop-filter:blur(12px))){.tingle-modal{-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px)}@media (max-width :540px){.tingle-modal{-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}}.tingle-enabled .tingle-content-wrapper{-webkit-filter:none;filter:none}}');
6618 injectStyles('.reviewModal .tingle-modal-box{width:650px}');
6619 var QCModal = new tingle.modal({
6620 footer: false,
6621 stickyFooter: false,
6622 closeMethods: ['overlay', 'button', 'escape'],
6623 closeLabel: "Close",
6624 cssClass: ['reviewModal'],
6625 onOpen: function() {
6626 QCModal.setContent("<iframe style='width:550px;height:900px;border:none' src='https://fashionreps.tools/qcdb/reviewForm.php?w2c="+encodeURIComponent(w2c)+"&uid="+$.md5($(".header-username").text())+"'></iframe>");
6627 },
6628 onClose: function() {
6629 console.log('modal closed');
6630 },
6631 beforeClose: function() {
6632 // here's goes some logic
6633 // e.g. save content before closing the modal
6634 return true; // close the modal
6635 }
6636 });
6637 QCModal.open();
6638}
6639function SetupUploadAll() {
6640 injectStyles('.tingle-modal *{box-sizing:border-box}.tingle-modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;display:-webkit-box;display:-ms-flexbox;display:flex;visibility:hidden;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;overflow:hidden;-webkit-overflow-scrolling:touch;background:rgba(0,0,0,.8);opacity:0;cursor:pointer;-webkit-transition:-webkit-transform .2s ease;transition:-webkit-transform .2s ease;transition:transform .2s ease;transition:transform .2s ease,-webkit-transform .2s ease}.tingle-modal--noClose .tingle-modal__close,.tingle-modal__closeLabel{display:none}.tingle-modal--confirm .tingle-modal-box{text-align:center}.tingle-modal--noOverlayClose{cursor:default}.tingle-modal__close{position:fixed;top:10px;right:28px;z-index:1000;padding:0;width:5rem;height:5rem;border:none;background-color:transparent;color:#f0f0f0;font-size:6rem;font-family:monospace;line-height:1;cursor:pointer;-webkit-transition:color .3s ease;transition:color .3s ease}.tingle-modal__close:hover{color:#fff}.tingle-modal-box{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:auto;margin-bottom:auto;width:60%;border-radius:4px;background:#fff;opacity:1;cursor:auto;-webkit-transition:-webkit-transform .3s cubic-bezier(.175,.885,.32,1.275);transition:-webkit-transform .3s cubic-bezier(.175,.885,.32,1.275);transition:transform .3s cubic-bezier(.175,.885,.32,1.275);transition:transform .3s cubic-bezier(.175,.885,.32,1.275),-webkit-transform .3s cubic-bezier(.175,.885,.32,1.275);-webkit-transform:scale(.8);-ms-transform:scale(.8);transform:scale(.8)}.tingle-modal-box__content{padding:3rem}.tingle-modal-box__footer{padding:1.5rem 2rem;width:auto;border-bottom-right-radius:4px;border-bottom-left-radius:4px;background-color:#f5f5f5;cursor:auto}.tingle-modal-box__footer::after{display:table;clear:both;content:""}.tingle-modal-box__footer--sticky{position:fixed;bottom:-200px;z-index:10001;opacity:1;-webkit-transition:bottom .3s ease-in-out .3s;transition:bottom .3s ease-in-out .3s}.tingle-enabled{position:fixed;overflow:hidden;left:0;right:0}.tingle-modal--visible .tingle-modal-box__footer{bottom:0}.tingle-enabled .tingle-content-wrapper{-webkit-filter:blur(8px);filter:blur(8px)}.tingle-modal--visible{visibility:visible;opacity:1}.tingle-modal--visible .tingle-modal-box{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}.tingle-modal--overflow{overflow-y:scroll;padding-top:8vh}.tingle-btn{display:inline-block;margin:0 .5rem;padding:1rem 2rem;border:none;background-color:grey;box-shadow:none;color:#fff;vertical-align:middle;text-decoration:none;font-size:inherit;font-family:inherit;line-height:normal;cursor:pointer;-webkit-transition:background-color .4s ease;transition:background-color .4s ease}.tingle-btn--primary{background-color:#3498db}.tingle-btn--danger{background-color:#e74c3c}.tingle-btn--default{background-color:#34495e}.tingle-btn--pull-left{float:left}.tingle-btn--pull-right{float:right}@media (max-width :540px){.tingle-modal{top:0;display:block;padding-top:60px;width:100%}.tingle-modal-box{width:auto;border-radius:0}.tingle-modal-box__content{overflow-y:scroll}.tingle-modal--noClose{top:0}.tingle-modal--noOverlayClose{padding-top:0}.tingle-modal-box__footer .tingle-btn{display:block;float:none;margin-bottom:1rem;width:100%}.tingle-modal__close{top:0;right:0;left:0;display:block;width:100%;height:60px;border:none;background-color:#2c3e50;box-shadow:none;color:#fff;line-height:55px}.tingle-modal__closeLabel{display:inline-block;vertical-align:middle;font-size:1.5rem;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",sans-serif}.tingle-modal__closeIcon{display:inline-block;margin-right:.5rem;vertical-align:middle;font-size:4rem}}@supports ((-webkit-backdrop-filter:blur(12px)) or (backdrop-filter:blur(12px))){.tingle-modal{-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px)}@media (max-width :540px){.tingle-modal{-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}}.tingle-enabled .tingle-content-wrapper{-webkit-filter:none;filter:none}}');
6641 var imgurUploadLink = $('<a/>');
6642 imgurUploadLink.text("Multi Upload QC");
6643 imgurUploadLink.attr("id", "MultiUploadQC");
6644 imgurUploadLink.attr("href", "_multi-upload");
6645 imgurUploadLink.addClass("input_send");
6646 imgurUploadLink.css({
6647 "padding": "0 8px",
6648 "height": "auto",
6649 "line-height": "20px",
6650 "background-color": "#1bb76e",
6651 "margin-top": "10px"
6652 });
6653 var superbuyPurchasesLink = $('<a/>');
6654 superbuyPurchasesLink.text("Purchase Tracker");
6655 superbuyPurchasesLink.attr("href", "https://fashionreps.tools/qcdb/sb/");
6656 superbuyPurchasesLink.attr("target", "_BLANK");
6657 superbuyPurchasesLink.addClass("input_send");
6658 superbuyPurchasesLink.css({
6659 "padding": "0 8px",
6660 "height": "auto",
6661 "line-height": "20px",
6662 "background-color": "#fdcc52",
6663 "margin-top": "10px",
6664 "text-align ": "right"
6665 });
6666 $(".usercenter_ordersearch").first().append(imgurUploadLink);
6667 $(".usercenter_ordersearch").first().append( superbuyPurchasesLink);
6668 imgurUploadLink.before($('<b>QC Suite Tools: </b>'));
6669 imgurUploadLink.after($("<span style='margin-left:10px'/>").load("https://fashionreps.tools/qcdb/version.php?version=" + _version));
6670 var QCModal = new tingle.modal({
6671 footer: true,
6672 stickyFooter: false,
6673 closeMethods: ['overlay', 'button', 'escape'],
6674 closeLabel: "Close",
6675 cssClass: ['custom-class-1', 'custom-class-2'],
6676 onOpen: function() {
6677 var LastPage = 1
6678 if($(".pagination .last").length > 0){
6679 LastPage = $(".pagination .last").attr("href").replace(/^\D+/g, "");
6680 }
6681 var QCModalContent = ('<h1 id="QCModalTitle">Gathering all your reps from ' + LastPage + ' page(s)...</h1>');
6682 QCModalContent += ('<progress id="QCModalLoad" name="total" max="' + LastPage + '" value="0" style="margin-top:10px; width:100%" ></progress>' +
6683 "<div id='CopContainer' style='height:400px;overflow-y:scroll;width:100%;display:block;margin:10px'/>")
6684 QCModal.setContent(QCModalContent);
6685 $("#CopContainer").hide();
6686 var pagesDone = 0;
6687 var infoArray = new Array();
6688
6689 for (var q = LastPage - 1; q >= 0; q--) {
6690 $.ajax({
6691 url: ("/order?page=" + (q + 1)),
6692 type: 'GET',
6693 success: function(res) {
6694
6695 var products = $(res).find('a.js-item-title');
6696 for (var i = products.length - 1; i >= 0; i--) {
6697
6698 if ($(products[i]).parent().parent().find(".j_checked_pic").length > 0) {
6699 var product = {
6700 images: new Array(),
6701 name: '',
6702 link: ''
6703 };
6704 var picturesForProduct = $(products[i]).parent().parent().find(".lookPic");
6705 picturesForProduct.each(function() {
6706 product.images.push($(this).attr("href"));
6707 });
6708 product.name = $(products[i]).text();
6709 product.link = $(products[i]).attr("href");
6710 infoArray.push(product);
6711
6712 $('#CopContainer').append(
6713 $(('<div class="cop" data-info-id="' + (infoArray.length - 1) + '" data-copid="' +
6714 (parseInt((this.url.replace(/^\D+/g, "")) * 100 + products.length - i)) +
6715 '" style="background-size:cover;background-image:url(\'' +
6716 $(products[i]).parent().find(".js-item-img").attr('src') +
6717 '\');border-radius:10px;border:2px solid #f0f0f0;cursor:pointer;box-shadow: -1px 3px 4px 0px #0000004a;display:block;float:left;width:120px;height:120px;overflow:hidden;text-align:center;padding:5px; margin:5px">' +
6718 '<b class="copName" style="font-size: 9px;background-color: #ffffffc2;margin: -5px;padding: 5px;display: block;line-height: 10px;margin-bottom: 10px;font-weight: normal;color: black;">' + $(products[i]).text() + '</b><div>')));
6719 }
6720
6721 }
6722 pagesDone++;
6723 $("#QCModalLoad").attr("value", pagesDone);
6724 if (pagesDone == LastPage) {
6725
6726 $("#QCModalLoad").fadeOut('200', function() {
6727
6728 var divList = $("#CopContainer .cop");
6729 $('#CopContainer').html($($(divList).toArray().sort(function(a, b) {
6730 var aVal = parseInt(a.getAttribute('data-copid')),
6731 bVal = parseInt(b.getAttribute('data-copid'));
6732 return aVal - bVal;
6733 })));
6734 $('#QCModalTitle').text("Select everything you want to upload to an album.");
6735 $('#CopContainer').slideDown('400').after("<span id='qcSubtext'>You have a total of " + divList.length + " reps. Nice!</span>");
6736 $('#CopContainer').after($("<a id='doMultiUpload' class='tingle-btn tingle-btn--primary tingle-btn--pull-right'>Upload to album</a>"));
6737 $("#doMultiUpload").hide();
6738 $("#CopContainer .cop").each(function() {
6739
6740 $(this).click(function() {
6741 console.log(infoArray[$(this).attr("data-info-id")]);
6742 if ($(this).hasClass('selected')) {
6743 $(this).removeClass('selected');
6744 $(this).css("box-shadow", "-1px 3px 4px 0px #0000004a");
6745 $(this).css("border", "2px solid #f0f0f0");
6746 } else {
6747 $(this).addClass('selected');
6748 $(this).css("box-shadow", "-1px 3px 4px 0px rgba(27, 183, 110, 0.33)");
6749 $(this).css("border", "2px solid rgb(27, 183, 110)");
6750 }
6751 if ($(".cop.selected").length) {
6752 $("#doMultiUpload").fadeIn().text(("Upload " + $(".cop.selected").length + " rep haul to Imgur"));
6753 } else {
6754 $("#doMultiUpload").fadeOut();
6755 }
6756 });
6757
6758 });
6759
6760 $("#doMultiUpload").click(function() {
6761 if ($(".cop.selected").length > 0) {
6762 var optionsArr = [];
6763 $(".cop.selected").each(function() {
6764 nodeOptions = infoArray[$(this).attr("data-info-id")];
6765 var options = {
6766 imageUrlArray: nodeOptions.images,
6767 w2c: nodeOptions.link,
6768 name: nodeOptions.name
6769 };
6770 optionsArr.push(options);
6771 $('#CopContainer').slideUp('400');
6772 $("#QCModalLoad").fadeIn('400').attr({
6773 max: '1',
6774 value: '0'
6775 });
6776 $('#QCModalTitle').text("Uploading, please browse reddit for a hot sec...");
6777 });
6778
6779 UploadHaulToImgur(optionsArr);
6780 $("#doMultiUpload").hide();
6781 }
6782 return false;
6783 });
6784
6785 });
6786
6787 }
6788 }
6789 });
6790 }
6791
6792 },
6793 onClose: function() {
6794 console.log('modal closed');
6795 },
6796 beforeClose: function() {
6797 // here's goes some logic
6798 // e.g. save content before closing the modal
6799 return true; // close the modal
6800 }
6801 });
6802 // set content
6803
6804 $("#MultiUploadQC").click(function() {
6805 // open modal
6806 QCModal.open();
6807 return false;
6808 });
6809
6810}
6811//QC Upload Button
6812if ((window.location.href.indexOf("superbuy") > -1)||(window.location.href.indexOf("wegobuy") > -1)) {
6813 //Set up our upload all button
6814 SetupUploadAll();
6815
6816 $(".j_checked_pic").each(function() {
6817 var imgurUploadLink = $('<a/>');
6818 imgurUploadLink.text("QC to Imgur");
6819
6820 imgurUploadLink.attr("href", "_upload");
6821 imgurUploadLink.addClass("input_send");
6822 imgurUploadLink.css({
6823 "padding": "0 8px",
6824 "height": "auto",
6825 "line-height": "20px",
6826 "background-color": "#1bb76e",
6827 "margin-top": "10px"
6828 });
6829 var reviewLink = $('<a/>');
6830 reviewLink.text("Review Item");
6831
6832 reviewLink.attr("href", "_upload");
6833 reviewLink.addClass("input_send");
6834 reviewLink.css({
6835 "padding": "0 8px",
6836 "height": "auto",
6837 "line-height": "20px",
6838 "background-color": "#1b9bb7",
6839 "margin-top": "10px"
6840 });
6841 var qcLink = $(this);
6842 var w2cLink = $(this).parent().parent().parent().find(".js-item-title").attr('href');
6843 var CloudServer = "https://fashionreps.tools/qcdb/qcExists.php";
6844 $.getJSON(CloudServer, {
6845 'w2c': w2cLink
6846 }).done(function(data) {
6847 if (data.exists === '0') {
6848 imgurUploadLink.after($('<span>No QC in database, please upload.</span>'));
6849 }
6850 });
6851
6852 CloudServer = "https://fashionreps.tools/qcdb/reviewExists.php";
6853 $.getJSON(CloudServer, {
6854 'w2c': w2cLink
6855 }).done(function(data) {
6856 if (data.exists === '0') {
6857 reviewLink.after($('<span>No reviews in database, please review.</span>'));
6858 }
6859 });
6860
6861 var sizingInfo = "One Size";
6862 if ($(this).parent().parent().parent().find(".user_orderlist_txt").size() > 0) {
6863 sizingInfo = $(this).parent().parent().parent().find(".user_orderlist_txt").first().text();
6864 }
6865
6866
6867 imgurUploadLink.click(function(event) {
6868 event.preventDefault();
6869 qcLink.click();
6870 $(".pic-boxs").css({
6871 "visibility": "hidden"
6872 });
6873 var PicsToUpload = [];
6874 qcLink.parent().find(".lookPic").each(function() {
6875 var imageLink = $(this).attr("href");
6876 if (PicsToUpload.indexOf(imageLink) === -1) PicsToUpload.push(imageLink);
6877 });
6878 if (PicsToUpload.length != 0) {
6879 var options = {
6880 imageUrlArray: PicsToUpload,
6881 linkObject: imgurUploadLink,
6882 w2c: w2cLink,
6883 sizing: $.trim(sizingInfo),
6884 title: qcLink.closest('tr').find('.js-item-title').text()
6885 };
6886 UploadToImgur(options);
6887 }
6888 });
6889
6890 reviewLink.click(function(event) {
6891 openReviewBox(w2cLink);
6892 event.preventDefault();
6893 });
6894
6895 $(this).before(imgurUploadLink);
6896 });
6897} else {
6898 addGlobalStyle('body.compensate-for-scrollbar{overflow:hidden}.fancybox-active{height:auto}.fancybox-is-hidden{left:-9999px;margin:0;position:absolute!important;top:-9999px;visibility:hidden}.fancybox-container{-webkit-backface-visibility:hidden;backface-visibility:hidden;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;height:100%;left:0;position:fixed;-webkit-tap-highlight-color:transparent;top:0;-webkit-transform:translateZ(0);transform:translateZ(0);width:100%;z-index:99992}.fancybox-container *{box-sizing:border-box}.fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-stage{bottom:0;left:0;position:absolute;right:0;top:0}.fancybox-outer{-webkit-overflow-scrolling:touch;overflow-y:auto}.fancybox-bg{background:#1e1e1e;opacity:0;transition-duration:inherit;transition-property:opacity;transition-timing-function:cubic-bezier(.47,0,.74,.71)}.fancybox-is-open .fancybox-bg{opacity:.87;transition-timing-function:cubic-bezier(.22,.61,.36,1)}.fancybox-caption,.fancybox-infobar,.fancybox-navigation .fancybox-button,.fancybox-toolbar{direction:ltr;opacity:0;position:absolute;transition:opacity .25s,visibility 0s linear .25s;visibility:hidden;z-index:99997}.fancybox-show-caption .fancybox-caption,.fancybox-show-infobar .fancybox-infobar,.fancybox-show-nav .fancybox-navigation .fancybox-button,.fancybox-show-toolbar .fancybox-toolbar{opacity:1;transition:opacity .25s,visibility 0s;visibility:visible}.fancybox-infobar{color:#ccc;font-size:13px;-webkit-font-smoothing:subpixel-antialiased;height:44px;left:0;line-height:44px;min-width:44px;mix-blend-mode:difference;padding:0 10px;pointer-events:none;text-align:center;top:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fancybox-toolbar{right:0;top:0}.fancybox-stage{direction:ltr;overflow:visible;-webkit-transform:translateZ(0);z-index:99994}.fancybox-is-open .fancybox-stage{overflow:hidden}.fancybox-slide{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:none;height:100%;left:0;outline:none;overflow:auto;-webkit-overflow-scrolling:touch;padding:44px;position:absolute;text-align:center;top:0;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;white-space:normal;width:100%;z-index:99994}.fancybox-slide:before{content:"";display:inline-block;height:100%;margin-right:-.25em;vertical-align:middle;width:0}.fancybox-is-sliding .fancybox-slide,.fancybox-slide--current,.fancybox-slide--next,.fancybox-slide--previous{display:block}.fancybox-slide--next{z-index:99995}.fancybox-slide--image{overflow:visible;padding:44px 0}.fancybox-slide--image:before{display:none}.fancybox-slide--html{padding:6px 6px 0}.fancybox-slide--iframe{padding:44px 44px 0}.fancybox-content{background:#fff;display:inline-block;margin:0 0 6px;max-width:100%;overflow:auto;padding:0;padding:24px;position:relative;text-align:left;vertical-align:middle}.fancybox-slide--image .fancybox-content{-webkit-animation-timing-function:cubic-bezier(.5,0,.14,1);animation-timing-function:cubic-bezier(.5,0,.14,1);-webkit-backface-visibility:hidden;backface-visibility:hidden;background:transparent;background-repeat:no-repeat;background-size:100% 100%;left:0;margin:0;max-width:none;overflow:visible;padding:0;position:absolute;top:0;-webkit-transform-origin:top left;transform-origin:top left;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:99995}.fancybox-can-zoomOut .fancybox-content{cursor:zoom-out}.fancybox-can-zoomIn .fancybox-content{cursor:zoom-in}.fancybox-can-drag .fancybox-content{cursor:-webkit-grab;cursor:grab}.fancybox-is-dragging .fancybox-content{cursor:-webkit-grabbing;cursor:grabbing}.fancybox-container [data-selectable=true]{cursor:text}.fancybox-image,.fancybox-spaceball{background:transparent;border:0;height:100%;left:0;margin:0;max-height:none;max-width:none;padding:0;position:absolute;top:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:100%}.fancybox-spaceball{z-index:1}.fancybox-slide--html .fancybox-content{margin-bottom:6px}.fancybox-slide--iframe .fancybox-content,.fancybox-slide--map .fancybox-content,.fancybox-slide--video .fancybox-content{height:100%;margin:0;overflow:visible;padding:0;width:100%}.fancybox-slide--video .fancybox-content{background:#000}.fancybox-slide--map .fancybox-content{background:#e5e3df}.fancybox-slide--iframe .fancybox-content{background:#fff;height:calc(100% - 44px);margin-bottom:44px}.fancybox-iframe,.fancybox-video{background:transparent;border:0;height:100%;margin:0;overflow:hidden;padding:0;width:100%}.fancybox-iframe{vertical-align:top}.fancybox-error{background:#fff;cursor:default;max-width:400px;padding:40px;width:100%}.fancybox-error p{color:#444;font-size:16px;line-height:20px;margin:0;padding:0}.fancybox-button{background:rgba(30,30,30,.6);border:0;border-radius:0;cursor:pointer;display:inline-block;height:44px;margin:0;outline:none;padding:10px;transition:color .2s;vertical-align:top;width:44px}.fancybox-button,.fancybox-button:link,.fancybox-button:visited{color:#ccc}.fancybox-button:focus,.fancybox-button:hover{color:#fff}.fancybox-button.disabled,.fancybox-button.disabled:hover,.fancybox-button[disabled],.fancybox-button[disabled]:hover{color:#888;cursor:default}.fancybox-button svg{display:block;overflow:visible;position:relative;shape-rendering:geometricPrecision}.fancybox-button svg path{fill:transparent;stroke:currentColor;stroke-linejoin:round;stroke-width:3}.fancybox-button--pause svg path:nth-child(1),.fancybox-button--play svg path:nth-child(2){display:none}.fancybox-button--play svg path,.fancybox-button--share svg path,.fancybox-button--thumbs svg path{fill:currentColor}.fancybox-button--share svg path{stroke-width:1}.fancybox-navigation .fancybox-button{height:38px;opacity:0;padding:6px;position:absolute;top:50%;width:38px}.fancybox-show-nav .fancybox-navigation .fancybox-button{transition:opacity .25s,visibility 0s,color .25s}.fancybox-navigation .fancybox-button:after{content:"";left:-25px;padding:50px;position:absolute;top:-25px}.fancybox-navigation .fancybox-button--arrow_left{left:6px}.fancybox-navigation .fancybox-button--arrow_right{right:6px}.fancybox-close-small{background:transparent;border:0;border-radius:0;color:#555;cursor:pointer;height:44px;margin:0;padding:6px;position:absolute;right:0;top:0;width:44px;z-index:10}.fancybox-close-small svg{fill:transparent;opacity:.8;stroke:currentColor;stroke-width:1.5;transition:stroke .1s}.fancybox-close-small:focus{outline:none}.fancybox-close-small:hover svg{opacity:1}.fancybox-slide--iframe .fancybox-close-small,.fancybox-slide--image .fancybox-close-small,.fancybox-slide--video .fancybox-close-small{color:#ccc;padding:5px;right:-12px;top:-44px}.fancybox-slide--iframe .fancybox-close-small:hover svg,.fancybox-slide--image .fancybox-close-small:hover svg,.fancybox-slide--video .fancybox-close-small:hover svg{background:transparent;color:#fff}.fancybox-is-scaling .fancybox-close-small,.fancybox-is-zoomable.fancybox-can-drag .fancybox-close-small{display:none}.fancybox-caption{bottom:0;color:#fff;font-size:14px;font-weight:400;left:0;line-height:1.5;padding:25px 44px;right:0}.fancybox-caption:before{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAEtCAQAAABjBcL7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAHRJREFUKM+Vk8EOgDAIQ0vj/3+xBw8qIZZueFnIKC90MCAI8DlrkHGeqqGIU6lVigrBtpCWqeRWoHDNqs0F7VNVBVxmHRlvoVqjaYkdnDIaivH2HqZ5+oZj3JUzWB+cOz4G48Bg+tsJ/tqu4dLC/4Xb+0GcF5BwBC0AA53qAAAAAElFTkSuQmCC);background-repeat:repeat-x;background-size:contain;bottom:0;content:"";display:block;left:0;pointer-events:none;position:absolute;right:0;top:-25px;z-index:-1}.fancybox-caption:after{border-bottom:1px solid hsla(0,0%,100%,.3);content:"";display:block;left:44px;position:absolute;right:44px;top:0}.fancybox-caption a,.fancybox-caption a:link,.fancybox-caption a:visited{color:#ccc;text-decoration:none}.fancybox-caption a:hover{color:#fff;text-decoration:underline}.fancybox-loading{-webkit-animation:a .8s infinite linear;animation:a .8s infinite linear;background:transparent;border:6px solid hsla(0,0%,39%,.5);border-radius:100%;border-top-color:#fff;height:60px;left:50%;margin:-30px 0 0 -30px;opacity:.6;padding:0;position:absolute;top:50%;width:60px;z-index:99999}@-webkit-keyframes a{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes a{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fancybox-animated{transition-timing-function:cubic-bezier(0,0,.25,1)}.fancybox-fx-slide.fancybox-slide--previous{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.fancybox-fx-slide.fancybox-slide--next{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.fancybox-fx-slide.fancybox-slide--current{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}.fancybox-fx-fade.fancybox-slide--next,.fancybox-fx-fade.fancybox-slide--previous{opacity:0;transition-timing-function:cubic-bezier(.19,1,.22,1)}.fancybox-fx-fade.fancybox-slide--current{opacity:1}.fancybox-fx-zoom-in-out.fancybox-slide--previous{opacity:0;-webkit-transform:scale3d(1.5,1.5,1.5);transform:scale3d(1.5,1.5,1.5)}.fancybox-fx-zoom-in-out.fancybox-slide--next{opacity:0;-webkit-transform:scale3d(.5,.5,.5);transform:scale3d(.5,.5,.5)}.fancybox-fx-zoom-in-out.fancybox-slide--current{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}.fancybox-fx-rotate.fancybox-slide--previous{opacity:0;-webkit-transform:rotate(-1turn);transform:rotate(-1turn)}.fancybox-fx-rotate.fancybox-slide--next{opacity:0;-webkit-transform:rotate(1turn);transform:rotate(1turn)}.fancybox-fx-rotate.fancybox-slide--current{opacity:1;-webkit-transform:rotate(0deg);transform:rotate(0deg)}.fancybox-fx-circular.fancybox-slide--previous{opacity:0;-webkit-transform:scale3d(0,0,0) translate3d(-100%,0,0);transform:scale3d(0,0,0) translate3d(-100%,0,0)}.fancybox-fx-circular.fancybox-slide--next{opacity:0;-webkit-transform:scale3d(0,0,0) translate3d(100%,0,0);transform:scale3d(0,0,0) translate3d(100%,0,0)}.fancybox-fx-circular.fancybox-slide--current{opacity:1;-webkit-transform:scaleX(1) translateZ(0);transform:scaleX(1) translateZ(0)}.fancybox-fx-tube.fancybox-slide--previous{-webkit-transform:translate3d(-100%,0,0) scale(.1) skew(-10deg);transform:translate3d(-100%,0,0) scale(.1) skew(-10deg)}.fancybox-fx-tube.fancybox-slide--next{-webkit-transform:translate3d(100%,0,0) scale(.1) skew(10deg);transform:translate3d(100%,0,0) scale(.1) skew(10deg)}.fancybox-fx-tube.fancybox-slide--current{-webkit-transform:translateZ(0) scale(1);transform:translateZ(0) scale(1)}.fancybox-share{background:#f4f4f4;border-radius:3px;max-width:90%;padding:30px;text-align:center}.fancybox-share h1{color:#222;font-size:35px;font-weight:700;margin:0 0 20px}.fancybox-share p{margin:0;padding:0}.fancybox-share__button{border:0;border-radius:3px;display:inline-block;font-size:14px;font-weight:700;line-height:40px;margin:0 5px 10px;min-width:130px;padding:0 15px;text-decoration:none;transition:all .2s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;white-space:nowrap}.fancybox-share__button:link,.fancybox-share__button:visited{color:#fff}.fancybox-share__button:hover{text-decoration:none}.fancybox-share__button--fb{background:#3b5998}.fancybox-share__button--fb:hover{background:#344e86}.fancybox-share__button--pt{background:#bd081d}.fancybox-share__button--pt:hover{background:#aa0719}.fancybox-share__button--tw{background:#1da1f2}.fancybox-share__button--tw:hover{background:#0d95e8}.fancybox-share__button svg{height:25px;margin-right:7px;position:relative;top:-1px;vertical-align:middle;width:25px}.fancybox-share__button svg path{fill:#fff}.fancybox-share__input{background:transparent;border:0;border-bottom:1px solid #d7d7d7;border-radius:0;color:#5d5b5b;font-size:14px;margin:10px 0 0;outline:none;padding:10px 15px;width:100%}.fancybox-thumbs{background:#fff;bottom:0;display:none;margin:0;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;padding:2px 2px 4px;position:absolute;right:0;-webkit-tap-highlight-color:transparent;top:0;width:212px;z-index:99995}.fancybox-thumbs-x{overflow-x:auto;overflow-y:hidden}.fancybox-show-thumbs .fancybox-thumbs{display:block}.fancybox-show-thumbs .fancybox-inner{right:212px}.fancybox-thumbs>ul{font-size:0;height:100%;list-style:none;margin:0;overflow-x:hidden;overflow-y:auto;padding:0;position:absolute;position:relative;white-space:nowrap;width:100%}.fancybox-thumbs-x>ul{overflow:hidden}.fancybox-thumbs-y>ul::-webkit-scrollbar{width:7px}.fancybox-thumbs-y>ul::-webkit-scrollbar-track{background:#fff;border-radius:10px;box-shadow:inset 0 0 6px rgba(0,0,0,.3)}.fancybox-thumbs-y>ul::-webkit-scrollbar-thumb{background:#2a2a2a;border-radius:10px}.fancybox-thumbs>ul>li{-webkit-backface-visibility:hidden;backface-visibility:hidden;cursor:pointer;float:left;height:75px;margin:2px;max-height:calc(100% - 8px);max-width:calc(50% - 4px);outline:none;overflow:hidden;padding:0;position:relative;-webkit-tap-highlight-color:transparent;width:100px}.fancybox-thumbs-loading{background:rgba(0,0,0,.1)}.fancybox-thumbs>ul>li{background-position:50%;background-repeat:no-repeat;background-size:cover}.fancybox-thumbs>ul>li:before{border:4px solid #4ea7f9;bottom:0;content:"";left:0;opacity:0;position:absolute;right:0;top:0;transition:all .2s cubic-bezier(.25,.46,.45,.94);z-index:99991}.fancybox-thumbs .fancybox-thumbs-active:before{opacity:1}@media (max-width:800px){.fancybox-thumbs{width:110px}.fancybox-show-thumbs .fancybox-inner{right:110px}.fancybox-thumbs>ul>li{max-width:calc(100% - 10px)}}');
6899 var qcIFrame = $('<iframe id="qciframe" width="770px" style="border:0px" src="https://fashionreps.tools/qcdb/qcview.php?id=' + getUrlParameter("id") + '" />');
6900 $("#description").prepend(qcIFrame);
6901 $('#qciframe').iFrameResize({
6902 bodyMargin: 10,
6903 bodyPadding: 10
6904 });
6905}
6906
6907window.addEventListener("message", function(e) {
6908 var messageData = e.data.split(",")
6909 if (messageData[0] == 'FancyBox') {
6910 var fancyboxData = [];
6911 for (var i = 1; i < messageData.length; i++) {
6912 messageData[i]
6913 var fbData = {
6914 'src': messageData[i],
6915 opts: {
6916 thumb: messageData[i]
6917 }
6918 }
6919 fancyboxData.push(fbData);
6920 };
6921 $.fancybox.open(fancyboxData, {
6922 loop: true,
6923 thumbs: {
6924 autoStart: true
6925 }
6926 });
6927 $(".fancybox-container").css("z-index", "99999999999");
6928 }
6929}, false);