From: Anthony Talarico Date: Wed, 20 Sep 2017 19:34:31 +0000 (-0400) Subject: adding fancybox for the detail images X-Git-Url: http://cvs2.gaslightmedia.com/gitweb/index.cgi?a=commitdiff_plain;h=f0dc1d9fb7f902c8bc24eef12c4f79c65d46bae1;p=WP-Plugins%2Fglm-member-db-for-sale.git adding fancybox for the detail images setting up lightbox effect for the detail page images --- diff --git a/css/admin.css b/css/admin.css index 8f82374..6545966 100644 --- a/css/admin.css +++ b/css/admin.css @@ -1,3 +1,3 @@ -#glm-admin-content-container .admin-item-label,#glm-admin-content-container .admin-item-input,#glm-admin-content-container .admin-item-list{padding:0}.itemTitle-record{float:left}.admin-new-item{margin-bottom:15px}.admin-new-item .admin-new-item-link{padding:5px;background:#3D84D0;color:white;text-decoration:none;border-radius:4px}.no-padding{padding:0}.no-margin{margin:0}#glm-admin-content-container .admin-item-list-header{margin-bottom:20px;padding-bottom:2px;border-bottom:1px solid lightgray}.admin-item-list-row{padding-bottom:5px}.item-company-list-header,.item-date-list-header,.item-title-list-header{font-weight:bold;font-size:16px}.admin-item-list-row:nth-child(odd){background:#e3e3e5}.item-image{display:block}.item-image-row{border-bottom:1px solid lightgray;padding-bottom:10px} +#glm-admin-content-container .admin-item-label,#glm-admin-content-container .admin-item-input,#glm-admin-content-container .admin-item-list{padding:0}.itemTitle-record{float:left}.admin-new-item{margin-bottom:15px}.admin-new-item .admin-new-item-link{padding:5px;background:#3D84D0;color:white;text-decoration:none;border-radius:4px}.no-padding{padding:0}.no-margin{margin:0}#glm-admin-content-container .admin-item-list-header{margin-bottom:20px;padding-bottom:2px;border-bottom:1px solid lightgray}.admin-item-list-row{padding-bottom:5px}.item-company-list-header,.item-date-list-header,.item-title-list-header{font-weight:bold;font-size:16px}.admin-item-list-row:nth-child(odd){background:#e3e3e5}.item-image{display:block;max-width:200px}.item-image-row{border-bottom:1px solid lightgray;padding-bottom:10px} /*# sourceMappingURL=admin.css.map */ diff --git a/css/admin.css.map b/css/admin.css.map index 8b32b22..e42d51d 100644 --- a/css/admin.css.map +++ b/css/admin.css.map @@ -1 +1 @@ -{"version":3,"file":"admin.css","sources":["admin.scss"],"sourcesContent":["#glm-admin-content-container .admin-item-label,\n#glm-admin-content-container .admin-item-input,\n#glm-admin-content-container .admin-item-list{\n padding: 0;\n}\n.itemTitle-record{\n float: left;\n}\n.admin-item-input textarea{\n \n}\n.admin-new-item{\n margin-bottom: 15px;\n}\n.admin-new-item .admin-new-item-link{\n padding: 5px;\n background: #3D84D0;\n color: white;\n text-decoration: none;\n border-radius: 4px;\n}\n.no-padding{\n padding: 0;\n}\n.no-margin{\n margin: 0;\n}\n#glm-admin-content-container .admin-item-list-header{\n margin-bottom: 20px;\n padding-bottom: 2px;\n border-bottom: 1px solid lightgray;\n}\n.admin-item-list-row{\n padding-bottom: 5px;\n/* border-bottom: 1px solid lightgray;*/\n}\n.item-company-list-header,.item-date-list-header, .item-title-list-header{\n font-weight: bold;\n font-size: 16px;\n}\n.admin-item-list-row:nth-child(odd){\n background: #e3e3e5;\n}\n.item-image{\n display: block;\n}\n.item-image-row{\n border-bottom: 1px solid lightgray;\n padding-bottom: 10px;\n}\n"],"names":[],"mappings":"AAAA,AAA6B,4BAAD,CAAC,iBAAiB,CAC9C,AAA6B,4BAAD,CAAC,iBAAiB,CAC9C,AAA6B,4BAAD,CAAC,gBAAgB,AAAA,CACzC,OAAO,CAAE,CAAC,CACb,AACD,AAAA,iBAAiB,AAAA,CACb,KAAK,CAAE,IAAI,CACd,AAID,AAAA,eAAe,AAAA,CACX,aAAa,CAAE,IAAI,CACtB,AACD,AAAgB,eAAD,CAAC,oBAAoB,AAAA,CAChC,OAAO,CAAE,GAAG,CACZ,UAAU,CAAE,OAAO,CACnB,KAAK,CAAE,KAAK,CACZ,eAAe,CAAE,IAAI,CACrB,aAAa,CAAE,GAAG,CACrB,AACD,AAAA,WAAW,AAAA,CACP,OAAO,CAAE,CAAC,CACb,AACD,AAAA,UAAU,AAAA,CACN,MAAM,CAAE,CAAC,CACZ,AACD,AAA6B,4BAAD,CAAC,uBAAuB,AAAA,CAChD,aAAa,CAAE,IAAI,CACnB,cAAc,CAAE,GAAG,CACnB,aAAa,CAAE,mBAAmB,CACrC,AACD,AAAA,oBAAoB,AAAA,CAChB,cAAc,CAAE,GAAG,CAEtB,AACD,AAAA,yBAAyB,CAAC,AAAA,sBAAsB,CAAE,AAAA,uBAAuB,AAAA,CACrE,WAAW,CAAE,IAAI,CACjB,SAAS,CAAE,IAAI,CAClB,AACD,AAAA,oBAAoB,AAAA,UAAW,CAAA,AAAA,GAAG,CAAC,CAC/B,UAAU,CAAE,OAAO,CACtB,AACD,AAAA,WAAW,AAAA,CACP,OAAO,CAAE,KAAK,CACjB,AACD,AAAA,eAAe,AAAA,CACX,aAAa,CAAE,mBAAmB,CAClC,cAAc,CAAE,IAAI,CACvB"} \ No newline at end of file +{"version":3,"file":"admin.css","sources":["admin.scss"],"sourcesContent":["#glm-admin-content-container .admin-item-label,\n#glm-admin-content-container .admin-item-input,\n#glm-admin-content-container .admin-item-list{\n padding: 0;\n}\n.itemTitle-record{\n float: left;\n}\n.admin-item-input textarea{\n \n}\n.admin-new-item{\n margin-bottom: 15px;\n}\n.admin-new-item .admin-new-item-link{\n padding: 5px;\n background: #3D84D0;\n color: white;\n text-decoration: none;\n border-radius: 4px;\n}\n.no-padding{\n padding: 0;\n}\n.no-margin{\n margin: 0;\n}\n#glm-admin-content-container .admin-item-list-header{\n margin-bottom: 20px;\n padding-bottom: 2px;\n border-bottom: 1px solid lightgray;\n}\n.admin-item-list-row{\n padding-bottom: 5px;\n/* border-bottom: 1px solid lightgray;*/\n}\n.item-company-list-header,.item-date-list-header, .item-title-list-header{\n font-weight: bold;\n font-size: 16px;\n}\n.admin-item-list-row:nth-child(odd){\n background: #e3e3e5;\n}\n.item-image{\n display: block;\n max-width: 200px;\n}\n.item-image-row{\n border-bottom: 1px solid lightgray;\n padding-bottom: 10px;\n}\n"],"names":[],"mappings":"AAAA,AAA6B,4BAAD,CAAC,iBAAiB,CAC9C,AAA6B,4BAAD,CAAC,iBAAiB,CAC9C,AAA6B,4BAAD,CAAC,gBAAgB,AAAA,CACzC,OAAO,CAAE,CAAC,CACb,AACD,AAAA,iBAAiB,AAAA,CACb,KAAK,CAAE,IAAI,CACd,AAID,AAAA,eAAe,AAAA,CACX,aAAa,CAAE,IAAI,CACtB,AACD,AAAgB,eAAD,CAAC,oBAAoB,AAAA,CAChC,OAAO,CAAE,GAAG,CACZ,UAAU,CAAE,OAAO,CACnB,KAAK,CAAE,KAAK,CACZ,eAAe,CAAE,IAAI,CACrB,aAAa,CAAE,GAAG,CACrB,AACD,AAAA,WAAW,AAAA,CACP,OAAO,CAAE,CAAC,CACb,AACD,AAAA,UAAU,AAAA,CACN,MAAM,CAAE,CAAC,CACZ,AACD,AAA6B,4BAAD,CAAC,uBAAuB,AAAA,CAChD,aAAa,CAAE,IAAI,CACnB,cAAc,CAAE,GAAG,CACnB,aAAa,CAAE,mBAAmB,CACrC,AACD,AAAA,oBAAoB,AAAA,CAChB,cAAc,CAAE,GAAG,CAEtB,AACD,AAAA,yBAAyB,CAAC,AAAA,sBAAsB,CAAE,AAAA,uBAAuB,AAAA,CACrE,WAAW,CAAE,IAAI,CACjB,SAAS,CAAE,IAAI,CAClB,AACD,AAAA,oBAAoB,AAAA,UAAW,CAAA,AAAA,GAAG,CAAC,CAC/B,UAAU,CAAE,OAAO,CACtB,AACD,AAAA,WAAW,AAAA,CACP,OAAO,CAAE,KAAK,CACd,SAAS,CAAE,KAAK,CACnB,AACD,AAAA,eAAe,AAAA,CACX,aAAa,CAAE,mBAAmB,CAClC,cAAc,CAAE,IAAI,CACvB"} \ No newline at end of file diff --git a/css/front.css b/css/front.css index 9691ea0..9f06b2c 100644 --- a/css/front.css +++ b/css/front.css @@ -1,3 +1,3 @@ -.no-padding{padding:0}.front-item-list-wrapper span{font-weight:bold}.front-item-list-wrapper .front-list-item{padding:10px 0;border-bottom:1px solid lightgray}.front-item-list-wrapper div{line-height:1.1;padding:5px}.front-item-list-wrapper .front-list-topic{padding:5px;width:100px;text-align:center;color:white;border-radius:5px;margin-bottom:10px}.front-item-list-wrapper .item-wanted{background:green}.front-item-list-wrapper .item-sale{background:red} +.no-padding{padding:0}.front-item-list-wrapper span{font-weight:bold}.front-item-list-wrapper .front-list-item{padding:10px 0;border-bottom:1px solid lightgray}.front-item-list-wrapper div{line-height:1.1;padding:3px 0 3px 0}.front-item-list-wrapper .front-list-topic{padding:5px;width:100px;text-align:center;color:white;border-radius:5px;margin:0 auto 10px auto}.front-item-list-wrapper .item-wanted{background:green}.front-item-list-wrapper .item-sale{background:red}.front-item-list-wrapper .item-detail-link{display:inline-block;padding:5px;background:blue;color:white;text-decoration:none;margin-top:5px}.front-item-detail-wrapper .item-detail-images{padding-top:20px} /*# sourceMappingURL=front.css.map */ diff --git a/css/front.css.map b/css/front.css.map index 928c7a6..6158f39 100644 --- a/css/front.css.map +++ b/css/front.css.map @@ -1 +1 @@ -{"version":3,"file":"front.css","sources":["front.scss"],"sourcesContent":[".no-padding{\n padding: 0;\n}\n.front-item-list-wrapper{\n span{\n font-weight: bold;\n }\n .front-list-item{\n padding: 10px 0;\n border-bottom: 1px solid lightgray;\n }\n div{\n line-height: 1.1;\n padding: 5px;\n }\n .front-list-topic{\n padding: 5px;\n width: 100px;\n text-align: center;\n color: white;\n border-radius: 5px;\n margin-bottom: 10px;\n }\n .item-wanted{\n background: green;\n }\n .item-sale{\n background: red;\n }\n}"],"names":[],"mappings":"AAAA,AAAA,WAAW,AAAA,CACP,OAAO,CAAE,CAAC,CACb,AACD,AACI,wBADoB,CACpB,IAAI,AAAA,CACA,WAAW,CAAE,IAAI,CACpB,AAHL,AAII,wBAJoB,CAIpB,gBAAgB,AAAA,CACZ,OAAO,CAAE,MAAM,CACf,aAAa,CAAE,mBAAmB,CACrC,AAPL,AAQI,wBARoB,CAQpB,GAAG,AAAA,CACC,WAAW,CAAE,GAAG,CAChB,OAAO,CAAE,GAAG,CACf,AAXL,AAYI,wBAZoB,CAYpB,iBAAiB,AAAA,CACb,OAAO,CAAE,GAAG,CACZ,KAAK,CAAE,KAAK,CACZ,UAAU,CAAE,MAAM,CAClB,KAAK,CAAE,KAAK,CACZ,aAAa,CAAE,GAAG,CAClB,aAAa,CAAE,IAAI,CACtB,AAnBL,AAoBI,wBApBoB,CAoBpB,YAAY,AAAA,CACR,UAAU,CAAE,KAAK,CACpB,AAtBL,AAuBI,wBAvBoB,CAuBpB,UAAU,AAAA,CACN,UAAU,CAAE,GAAG,CAClB"} \ No newline at end of file +{"version":3,"file":"front.css","sources":["front.scss"],"sourcesContent":[".no-padding{\n padding: 0;\n}\n.front-item-list-wrapper{\n span{\n font-weight: bold;\n }\n .front-list-item{\n padding: 10px 0;\n border-bottom: 1px solid lightgray;\n }\n div{\n line-height: 1.1;\n padding: 3px 0 3px 0;\n }\n .front-list-topic{\n padding: 5px;\n width: 100px;\n text-align: center;\n color: white;\n border-radius: 5px;\n margin: 0 auto 10px auto;\n }\n .item-wanted{\n background: green;\n }\n .item-sale{\n background: red;\n }\n .item-detail-link{\n display: inline-block;\n padding: 5px;\n background: blue;\n color: white;\n text-decoration: none;\n margin-top: 5px;\n }\n}\n.front-item-detail-wrapper{\n .item-detail-images{\n padding-top: 20px;\n }\n}"],"names":[],"mappings":"AAAA,AAAA,WAAW,AAAA,CACP,OAAO,CAAE,CAAC,CACb,AACD,AACI,wBADoB,CACpB,IAAI,AAAA,CACA,WAAW,CAAE,IAAI,CACpB,AAHL,AAII,wBAJoB,CAIpB,gBAAgB,AAAA,CACZ,OAAO,CAAE,MAAM,CACf,aAAa,CAAE,mBAAmB,CACrC,AAPL,AAQI,wBARoB,CAQpB,GAAG,AAAA,CACC,WAAW,CAAE,GAAG,CAChB,OAAO,CAAE,WAAW,CACvB,AAXL,AAYI,wBAZoB,CAYpB,iBAAiB,AAAA,CACb,OAAO,CAAE,GAAG,CACZ,KAAK,CAAE,KAAK,CACZ,UAAU,CAAE,MAAM,CAClB,KAAK,CAAE,KAAK,CACZ,aAAa,CAAE,GAAG,CAClB,MAAM,CAAE,gBAAgB,CAC3B,AAnBL,AAoBI,wBApBoB,CAoBpB,YAAY,AAAA,CACR,UAAU,CAAE,KAAK,CACpB,AAtBL,AAuBI,wBAvBoB,CAuBpB,UAAU,AAAA,CACN,UAAU,CAAE,GAAG,CAClB,AAzBL,AA0BI,wBA1BoB,CA0BpB,iBAAiB,AAAA,CACb,OAAO,CAAE,YAAY,CACrB,OAAO,CAAE,GAAG,CACZ,UAAU,CAAE,IAAI,CAChB,KAAK,CAAE,KAAK,CACZ,eAAe,CAAE,IAAI,CACrB,UAAU,CAAE,GAAG,CAClB,AAEL,AACI,0BADsB,CACtB,mBAAmB,AAAA,CACf,WAAW,CAAE,IAAI,CACpB"} \ No newline at end of file diff --git a/fancybox/README.md b/fancybox/README.md new file mode 100644 index 0000000..983a304 --- /dev/null +++ b/fancybox/README.md @@ -0,0 +1,62 @@ +# fancyBox + +jQuery lightbox script for displaying images, videos and more. +Touch enabled, responsive and fully customizable. + +See the [project page](http://fancyapps.com/fancybox/3/) for documentation and a demonstration. + +Follow [@thefancyapps](//twitter.com/thefancyapps) for updates. + + +## Quick start + +1\. Add latest jQuery and fancyBox files + +```html + + + + +``` + + +2\. Create links + +```html + + + + + + + +``` + + +3\. Enjoy! + + +## License + +fancyBox is licensed under the [GPLv3](http://choosealicense.com/licenses/gpl-3.0) license for all open source applications. +A commercial license is required for all commercial applications (including sites, themes and apps you plan to sell). + +[Read more about fancyBox license](http://fancyapps.com/fancybox/#license). + +## Bugs and feature requests + +If you find a bug, please report it [here on Github](https://github.com/fancyapps/fancybox/issues). + +Guidelines for bug reports: + +1. Use the GitHub issue search — check if the issue has already been reported. +2. Check if the issue has been fixed — try to reproduce it using the latest master or development branch in the repository. +3. Isolate the problem — create a reduced test case and a live example. You can use CodePen to fork any demo found on documentation to use it as a template. + +A good bug report shouldn't leave others needing to chase you up for more information. +Please try to be as detailed as possible in your report. + + +Feature requests are welcome. Please look for existing ones and use GitHub's "reactions" feature to vote. + +Please do not use the issue tracker for personal support requests - use Stack Overflow ([fancybox-3](http://stackoverflow.com/questions/tagged/fancybox-3) tag) instead. diff --git a/fancybox/bower.json b/fancybox/bower.json new file mode 100644 index 0000000..dc50191 --- /dev/null +++ b/fancybox/bower.json @@ -0,0 +1,23 @@ +{ + "name": "fancybox", + "description": "Touch enabled, responsive and fully customizable jQuery lightbox script", + "keywords": [ + "touch", + "responsive", + "lightbox", + "fancybox", + "gallery", + "jQuery", + "plugin" + ], + "homepage": "http://fancyapps.com/fancybox/", + "license": "GPL-3.0", + "moduleType": "globals", + "main": [ + "dist/jquery.fancybox.min.css", + "dist/jquery.fancybox.min.js" + ], + "dependencies": { + "jquery": ">=1.9.0" + } +} diff --git a/fancybox/dist/jquery.fancybox.css b/fancybox/dist/jquery.fancybox.css new file mode 100644 index 0000000..0ed0785 --- /dev/null +++ b/fancybox/dist/jquery.fancybox.css @@ -0,0 +1,740 @@ +@charset "UTF-8"; +.fancybox-enabled { + overflow: hidden; } + +.fancybox-enabled body { + overflow: visible; + height: 100%; } + +.fancybox-is-hidden { + position: absolute; + top: -9999px; + left: -9999px; + visibility: hidden; } + +.fancybox-container { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 99993; + -webkit-tap-highlight-color: transparent; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-transform: translateZ(0); + transform: translateZ(0); } + +/* Make sure that the first one is on the top */ +.fancybox-container ~ .fancybox-container { + z-index: 99992; } + +.fancybox-outer, +.fancybox-inner, +.fancybox-bg, +.fancybox-stage { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; } + +.fancybox-outer { + overflow-y: auto; + -webkit-overflow-scrolling: touch; } + +.fancybox-bg { + background: #1e1e1e; + opacity: 0; + transition-duration: inherit; + transition-property: opacity; + transition-timing-function: cubic-bezier(0.47, 0, 0.74, 0.71); } + +.fancybox-is-open .fancybox-bg { + opacity: 0.87; + transition-timing-function: cubic-bezier(0.22, 0.61, 0.36, 1); } + +.fancybox-infobar, +.fancybox-toolbar, +.fancybox-caption-wrap { + position: absolute; + direction: ltr; + z-index: 99997; + opacity: 0; + visibility: hidden; + transition: opacity .25s, visibility 0s linear .25s; + box-sizing: border-box; } + +.fancybox-show-infobar .fancybox-infobar, +.fancybox-show-toolbar .fancybox-toolbar, +.fancybox-show-caption .fancybox-caption-wrap { + opacity: 1; + visibility: visible; + transition: opacity .25s, visibility 0s; } + +.fancybox-infobar { + top: 0; + left: 50%; + margin-left: -79px; } + +.fancybox-infobar__body { + display: inline-block; + width: 70px; + line-height: 44px; + font-size: 13px; + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; + text-align: center; + color: #ddd; + background-color: rgba(30, 30, 30, 0.7); + pointer-events: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-touch-callout: none; + -webkit-tap-highlight-color: transparent; + -webkit-font-smoothing: subpixel-antialiased; } + +.fancybox-toolbar { + top: 0; + right: 0; } + +.fancybox-stage { + overflow: hidden; + direction: ltr; + z-index: 99994; + -webkit-transform: translate3d(0, 0, 0); } + +.fancybox-slide { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: auto; + outline: none; + white-space: normal; + box-sizing: border-box; + text-align: center; + z-index: 99994; + -webkit-overflow-scrolling: touch; + display: none; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + transition-property: opacity, -webkit-transform; + transition-property: transform, opacity; + transition-property: transform, opacity, -webkit-transform; + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + +.fancybox-slide::before { + content: ''; + display: inline-block; + vertical-align: middle; + height: 100%; + width: 0; } + +.fancybox-is-sliding .fancybox-slide, +.fancybox-slide--previous, +.fancybox-slide--current, +.fancybox-slide--next { + display: block; } + +.fancybox-slide--image { + overflow: visible; } + +.fancybox-slide--image::before { + display: none; } + +.fancybox-slide--video .fancybox-content, +.fancybox-slide--video iframe { + background: #000; } + +.fancybox-slide--map .fancybox-content, +.fancybox-slide--map iframe { + background: #E5E3DF; } + +.fancybox-slide--next { + z-index: 99995; } + +.fancybox-slide > * { + display: inline-block; + position: relative; + padding: 24px; + margin: 44px 0 44px; + border-width: 0; + vertical-align: middle; + text-align: left; + background-color: #fff; + overflow: auto; + box-sizing: border-box; } + +.fancybox-slide .fancybox-image-wrap { + position: absolute; + top: 0; + left: 0; + margin: 0; + padding: 0; + border: 0; + z-index: 99995; + background: transparent; + cursor: default; + overflow: visible; + -webkit-transform-origin: top left; + -ms-transform-origin: top left; + transform-origin: top left; + background-size: 100% 100%; + background-repeat: no-repeat; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; } + +.fancybox-can-zoomOut .fancybox-image-wrap { + cursor: -webkit-zoom-out; + cursor: zoom-out; } + +.fancybox-can-zoomIn .fancybox-image-wrap { + cursor: -webkit-zoom-in; + cursor: zoom-in; } + +.fancybox-can-drag .fancybox-image-wrap { + cursor: -webkit-grab; + cursor: grab; } + +.fancybox-is-dragging .fancybox-image-wrap { + cursor: -webkit-grabbing; + cursor: grabbing; } + +.fancybox-image, +.fancybox-spaceball { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + border: 0; + max-width: none; + max-height: none; } + +.fancybox-spaceball { + z-index: 1; } + +.fancybox-slide--iframe .fancybox-content { + padding: 0; + width: 80%; + height: 80%; + max-width: calc(100% - 100px); + max-height: calc(100% - 88px); + overflow: visible; + background: #fff; } + +.fancybox-iframe { + display: block; + margin: 0; + padding: 0; + border: 0; + width: 100%; + height: 100%; + background: #fff; } + +.fancybox-error { + margin: 0; + padding: 40px; + width: 100%; + max-width: 380px; + background: #fff; + cursor: default; } + +.fancybox-error p { + margin: 0; + padding: 0; + color: #444; + font: 16px/20px "Helvetica Neue",Helvetica,Arial,sans-serif; } + +.fancybox-close-small { + position: absolute; + top: 0; + right: 0; + width: 44px; + height: 44px; + padding: 0; + margin: 0; + border: 0; + border-radius: 0; + outline: none; + background: transparent; + z-index: 10; + cursor: pointer; } + +.fancybox-close-small:after { + content: '×'; + position: absolute; + top: 5px; + right: 5px; + width: 30px; + height: 30px; + font: 20px/30px Arial,"Helvetica Neue",Helvetica,sans-serif; + color: #888; + font-weight: 300; + text-align: center; + border-radius: 50%; + border-width: 0; + background: #fff; + transition: background .25s; + box-sizing: border-box; + z-index: 2; } + +.fancybox-close-small:focus:after { + outline: 1px dotted #888; } + +.fancybox-close-small:hover:after { + color: #555; + background: #eee; } + +.fancybox-slide--iframe .fancybox-close-small { + top: 0; + right: -44px; } + +.fancybox-slide--iframe .fancybox-close-small:after { + background: transparent; + font-size: 35px; + color: #aaa; } + +.fancybox-slide--iframe .fancybox-close-small:hover:after { + color: #fff; } + +/* Caption */ +.fancybox-caption-wrap { + bottom: 0; + left: 0; + right: 0; + padding: 60px 30px 0 30px; + background: linear-gradient(to bottom, transparent 0%, rgba(0, 0, 0, 0.1) 20%, rgba(0, 0, 0, 0.2) 40%, rgba(0, 0, 0, 0.6) 80%, rgba(0, 0, 0, 0.8) 100%); + pointer-events: none; } + +.fancybox-caption { + padding: 30px 0; + border-top: 1px solid rgba(255, 255, 255, 0.4); + font-size: 14px; + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; + color: #fff; + line-height: 20px; + -webkit-text-size-adjust: none; } + +.fancybox-caption a, +.fancybox-caption button, +.fancybox-caption select { + pointer-events: all; } + +.fancybox-caption a { + color: #fff; + text-decoration: underline; } + +/* Buttons */ +.fancybox-button { + display: inline-block; + position: relative; + margin: 0; + padding: 0; + border: 0; + width: 44px; + height: 44px; + line-height: 44px; + text-align: center; + background: transparent; + color: #ddd; + border-radius: 0; + cursor: pointer; + vertical-align: top; + outline: none; } + +.fancybox-button[disabled] { + cursor: default; + pointer-events: none; } + +.fancybox-infobar__body, .fancybox-button { + background: rgba(30, 30, 30, 0.6); } + +.fancybox-button:hover:not([disabled]) { + color: #fff; + background: rgba(0, 0, 0, 0.8); } + +.fancybox-button::before, +.fancybox-button::after { + content: ''; + pointer-events: none; + position: absolute; + background-color: currentColor; + color: currentColor; + opacity: 0.9; + box-sizing: border-box; + display: inline-block; } + +.fancybox-button[disabled]::before, +.fancybox-button[disabled]::after { + opacity: 0.3; } + +.fancybox-button--left::after, +.fancybox-button--right::after { + top: 18px; + width: 6px; + height: 6px; + background: transparent; + border-top: solid 2px currentColor; + border-right: solid 2px currentColor; } + +.fancybox-button--left::after { + left: 20px; + -webkit-transform: rotate(-135deg); + -ms-transform: rotate(-135deg); + transform: rotate(-135deg); } + +.fancybox-button--right::after { + right: 20px; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); } + +.fancybox-button--left { + border-bottom-left-radius: 5px; } + +.fancybox-button--right { + border-bottom-right-radius: 5px; } + +.fancybox-button--close::before, .fancybox-button--close::after { + content: ''; + display: inline-block; + position: absolute; + height: 2px; + width: 16px; + top: calc(50% - 1px); + left: calc(50% - 8px); } + +.fancybox-button--close::before { + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); } + +.fancybox-button--close::after { + -webkit-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + transform: rotate(-45deg); } + +/* Navigation arrows */ +.fancybox-arrow { + position: absolute; + top: 50%; + margin: -50px 0 0 0; + height: 100px; + width: 54px; + padding: 0; + border: 0; + outline: none; + background: none; + cursor: pointer; + z-index: 99995; + opacity: 0; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + transition: opacity .25s; } + +.fancybox-arrow::after { + content: ''; + position: absolute; + top: 28px; + width: 44px; + height: 44px; + background-color: rgba(30, 30, 30, 0.8); + background-image: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiBoZWlnaHQ9IjQ4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSI0OCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4gICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPiAgICA8cGF0aCBkPSJNMTIgNGwtMS40MSAxLjQxTDE2LjE3IDExSDR2MmgxMi4xN2wtNS41OCA1LjU5TDEyIDIwbDgtOHoiLz48L3N2Zz4=); + background-repeat: no-repeat; + background-position: center center; + background-size: 24px 24px; } + +.fancybox-arrow--right { + right: 0; } + +.fancybox-arrow--left { + left: 0; + -webkit-transform: scaleX(-1); + -ms-transform: scaleX(-1); + transform: scaleX(-1); } + +.fancybox-arrow--right::after, +.fancybox-arrow--left::after { + left: 0; } + +.fancybox-show-nav .fancybox-arrow { + opacity: 0.6; } + +.fancybox-show-nav .fancybox-arrow[disabled] { + opacity: 0.3; } + +/* Loading indicator */ +.fancybox-loading { + border: 6px solid rgba(100, 100, 100, 0.4); + border-top: 6px solid rgba(255, 255, 255, 0.6); + border-radius: 100%; + height: 50px; + width: 50px; + -webkit-animation: fancybox-rotate .8s infinite linear; + animation: fancybox-rotate .8s infinite linear; + background: transparent; + position: absolute; + top: 50%; + left: 50%; + margin-top: -25px; + margin-left: -25px; + z-index: 99999; } + +@-webkit-keyframes fancybox-rotate { + from { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } + to { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); } } + +@keyframes fancybox-rotate { + from { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } + to { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); } } + +/* Transition effects */ +.fancybox-animated { + transition-timing-function: cubic-bezier(0, 0, 0.25, 1); } + +/* transitionEffect: slide */ +.fancybox-fx-slide.fancybox-slide--previous { + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + opacity: 0; } + +.fancybox-fx-slide.fancybox-slide--next { + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + opacity: 0; } + +.fancybox-fx-slide.fancybox-slide--current { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + opacity: 1; } + +/* transitionEffect: fade */ +.fancybox-fx-fade.fancybox-slide--previous, +.fancybox-fx-fade.fancybox-slide--next { + opacity: 0; + transition-timing-function: cubic-bezier(0.19, 1, 0.22, 1); } + +.fancybox-fx-fade.fancybox-slide--current { + opacity: 1; } + +/* transitionEffect: zoom-in-out */ +.fancybox-fx-zoom-in-out.fancybox-slide--previous { + -webkit-transform: scale3d(1.5, 1.5, 1.5); + transform: scale3d(1.5, 1.5, 1.5); + opacity: 0; } + +.fancybox-fx-zoom-in-out.fancybox-slide--next { + -webkit-transform: scale3d(0.5, 0.5, 0.5); + transform: scale3d(0.5, 0.5, 0.5); + opacity: 0; } + +.fancybox-fx-zoom-in-out.fancybox-slide--current { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + opacity: 1; } + +/* transitionEffect: rotate */ +.fancybox-fx-rotate.fancybox-slide--previous { + -webkit-transform: rotate(-360deg); + -ms-transform: rotate(-360deg); + transform: rotate(-360deg); + opacity: 0; } + +.fancybox-fx-rotate.fancybox-slide--next { + -webkit-transform: rotate(360deg); + -ms-transform: rotate(360deg); + transform: rotate(360deg); + opacity: 0; } + +.fancybox-fx-rotate.fancybox-slide--current { + -webkit-transform: rotate(0deg); + -ms-transform: rotate(0deg); + transform: rotate(0deg); + opacity: 1; } + +/* transitionEffect: circular */ +.fancybox-fx-circular.fancybox-slide--previous { + -webkit-transform: scale3d(0, 0, 0) translate3d(-100%, 0, 0); + transform: scale3d(0, 0, 0) translate3d(-100%, 0, 0); + opacity: 0; } + +.fancybox-fx-circular.fancybox-slide--next { + -webkit-transform: scale3d(0, 0, 0) translate3d(100%, 0, 0); + transform: scale3d(0, 0, 0) translate3d(100%, 0, 0); + opacity: 0; } + +.fancybox-fx-circular.fancybox-slide--current { + -webkit-transform: scale3d(1, 1, 1) translate3d(0, 0, 0); + transform: scale3d(1, 1, 1) translate3d(0, 0, 0); + opacity: 1; } + +/* transitionEffect: tube */ +.fancybox-fx-tube.fancybox-slide--previous { + -webkit-transform: translate3d(-100%, 0, 0) scale(0.1) skew(-10deg); + transform: translate3d(-100%, 0, 0) scale(0.1) skew(-10deg); } + +.fancybox-fx-tube.fancybox-slide--next { + -webkit-transform: translate3d(100%, 0, 0) scale(0.1) skew(10deg); + transform: translate3d(100%, 0, 0) scale(0.1) skew(10deg); } + +.fancybox-fx-tube.fancybox-slide--current { + -webkit-transform: translate3d(0, 0, 0) scale(1); + transform: translate3d(0, 0, 0) scale(1); } + +/* Styling for Small-Screen Devices */ +@media all and (max-width: 800px) { + .fancybox-infobar { + left: 0; + margin-left: 0; } + .fancybox-button--left, + .fancybox-button--right { + display: none !important; } + .fancybox-caption { + padding: 20px 0; + margin: 0; } } + +/* Fullscreen */ +.fancybox-button--fullscreen::before { + width: 15px; + height: 11px; + left: calc(50% - 7px); + top: calc(50% - 6px); + border: 2px solid; + background: none; } + +/* Slideshow button */ +.fancybox-button--play::before, +.fancybox-button--pause::before { + top: calc(50% - 6px); + left: calc(50% - 4px); + background: transparent; } + +.fancybox-button--play::before { + width: 0; + height: 0; + border-top: 6px inset transparent; + border-bottom: 6px inset transparent; + border-left: 10px solid; + border-radius: 1px; } + +.fancybox-button--pause::before { + width: 7px; + height: 11px; + border-style: solid; + border-width: 0 2px 0 2px; } + +/* Thumbs */ +.fancybox-thumbs { + display: none; } + +.fancybox-button--thumbs { + display: none; } + +@media all and (min-width: 800px) { + .fancybox-button--thumbs { + display: inline-block; } + .fancybox-button--thumbs span { + font-size: 23px; } + .fancybox-button--thumbs::before { + width: 3px; + height: 3px; + top: calc(50% - 2px); + left: calc(50% - 2px); + box-shadow: 0 -4px 0, -4px -4px 0, 4px -4px 0, 0 0 0 32px inset, -4px 0 0, 4px 0 0, 0 4px 0, -4px 4px 0, 4px 4px 0; } + .fancybox-thumbs { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: auto; + width: 220px; + margin: 0; + padding: 5px 5px 0 0; + background: #fff; + word-break: normal; + -webkit-tap-highlight-color: transparent; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + box-sizing: border-box; + z-index: 99995; } + .fancybox-show-thumbs .fancybox-thumbs { + display: block; } + .fancybox-show-thumbs .fancybox-inner { + right: 220px; } + .fancybox-thumbs > ul { + list-style: none; + position: absolute; + position: relative; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow-x: hidden; + overflow-y: auto; + font-size: 0; } + .fancybox-thumbs > ul > li { + float: left; + overflow: hidden; + max-width: 50%; + padding: 0; + margin: 0; + width: 105px; + height: 75px; + position: relative; + cursor: pointer; + outline: none; + border: 5px solid transparent; + border-top-width: 0; + border-right-width: 0; + -webkit-tap-highlight-color: transparent; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + box-sizing: border-box; } + li.fancybox-thumbs-loading { + background: rgba(0, 0, 0, 0.1); } + .fancybox-thumbs > ul > li > img { + position: absolute; + top: 0; + left: 0; + min-width: 100%; + min-height: 100%; + max-width: none; + max-height: none; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + .fancybox-thumbs > ul > li:before { + content: ''; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + border-radius: 2px; + border: 4px solid #4ea7f9; + z-index: 99991; + opacity: 0; + transition: all 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94); } + .fancybox-thumbs > ul > li.fancybox-thumbs-active:before { + opacity: 1; } } diff --git a/fancybox/dist/jquery.fancybox.js b/fancybox/dist/jquery.fancybox.js new file mode 100644 index 0000000..f5ed361 --- /dev/null +++ b/fancybox/dist/jquery.fancybox.js @@ -0,0 +1,4936 @@ +// ================================================== +// fancyBox v3.1.24 +// +// Licensed GPLv3 for open source use +// or fancyBox Commercial License for commercial use +// +// http://fancyapps.com/fancybox/ +// Copyright 2017 fancyApps +// +// ================================================== +;(function (window, document, $, undefined) { + 'use strict'; + + // If there's no jQuery, fancyBox can't work + // ========================================= + + if ( !$ ) { + return; + } + + // Check if fancyBox is already initialized + // ======================================== + + if ( $.fn.fancybox ) { + + $.error('fancyBox already initialized'); + + return; + } + + // Private default settings + // ======================== + + var defaults = { + + // Enable infinite gallery navigation + loop : false, + + // Space around image, ignored if zoomed-in or viewport smaller than 800px + margin : [44, 0], + + // Horizontal space between slides + gutter : 50, + + // Enable keyboard navigation + keyboard : true, + + // Should display navigation arrows at the screen edges + arrows : true, + + // Should display infobar (counter and arrows at the top) + infobar : false, + + // Should display toolbar (buttons at the top) + toolbar : true, + + // What buttons should appear in the top right corner. + // Buttons will be created using templates from `btnTpl` option + // and they will be placed into toolbar (class="fancybox-toolbar"` element) + buttons : [ + 'slideShow', + 'fullScreen', + 'thumbs', + 'close' + ], + + // Detect "idle" time in seconds + idleTime : 4, + + // Should display buttons at top right corner of the content + // If 'auto' - they will be created for content having type 'html', 'inline' or 'ajax' + // Use template from `btnTpl.smallBtn` for customization + smallBtn : 'auto', + + // Disable right-click and use simple image protection for images + protect : false, + + // Shortcut to make content "modal" - disable keyboard navigtion, hide buttons, etc + modal : false, + + image : { + + // Wait for images to load before displaying + // Requires predefined image dimensions + // If 'auto' - will zoom in thumbnail if 'width' and 'height' attributes are found + preload : "auto", + + }, + + ajax : { + + // Object containing settings for ajax request + settings : { + + // This helps to indicate that request comes from the modal + // Feel free to change naming + data : { + fancybox : true + } + } + + }, + + iframe : { + + // Iframe template + tpl : '', + + // Preload iframe before displaying it + // This allows to calculate iframe content width and height + // (note: Due to "Same Origin Policy", you can't get cross domain data). + preload : true, + + // Custom CSS styling for iframe wrapping element + // You can use this to set custom iframe dimensions + css : {}, + + // Iframe tag attributes + attr : { + scrolling : 'auto' + } + + }, + + // Open/close animation type + // Possible values: + // false - disable + // "zoom" - zoom images from/to thumbnail + // "fade" + // "zoom-in-out" + // + animationEffect : "zoom", + + // Duration in ms for open/close animation + animationDuration : 366, + + // Should image change opacity while zooming + // If opacity is 'auto', then opacity will be changed if image and thumbnail have different aspect ratios + zoomOpacity : 'auto', + + // Transition effect between slides + // + // Possible values: + // false - disable + // "fade' + // "slide' + // "circular' + // "tube' + // "zoom-in-out' + // "rotate' + // + transitionEffect : "fade", + + // Duration in ms for transition animation + transitionDuration : 366, + + // Custom CSS class for slide element + slideClass : '', + + // Custom CSS class for layout + baseClass : '', + + // Base template for layout + baseTpl : + '', + + // Loading indicator template + spinnerTpl : '
', + + // Error message template + errorTpl : '

{{ERROR}}

', + + btnTpl : { + slideShow : '', + fullScreen : '', + thumbs : '', + close : '', + + // This small close button will be appended to your html/inline/ajax content by default, + // if "smallBtn" option is not set to false + smallBtn : '' + }, + + // Container is injected into this element + parentEl : 'body', + + + // Focus handling + // ============== + + // Try to focus on the first focusable element after opening + autoFocus : true, + + // Put focus back to active element after closing + backFocus : true, + + // Do not let user to focus on element outside modal content + trapFocus : true, + + + // Module specific options + // ======================= + + fullScreen : { + autoStart : false, + }, + + touch : { + vertical : true, // Allow to drag content vertically + momentum : true // Continue movement after releasing mouse/touch when panning + }, + + // Hash value when initializing manually, + // set `false` to disable hash change + hash : null, + + // Customize or add new media types + // Example: + /* + media : { + youtube : { + params : { + autoplay : 0 + } + } + } + */ + media : {}, + + slideShow : { + autoStart : false, + speed : 4000 + }, + + thumbs : { + autoStart : false, // Display thumbnails on opening + hideOnClose : true // Hide thumbnail grid when closing animation starts + }, + + // Callbacks + //========== + + // See Documentation/API/Events for more information + // Example: + /* + afterShow: function( instance, current ) { + console.info( 'Clicked element:' ); + console.info( current.opts.$orig ); + } + */ + + onInit : $.noop, // When instance has been initialized + + beforeLoad : $.noop, // Before the content of a slide is being loaded + afterLoad : $.noop, // When the content of a slide is done loading + + beforeShow : $.noop, // Before open animation starts + afterShow : $.noop, // When content is done loading and animating + + beforeClose : $.noop, // Before the instance attempts to close. Return false to cancel the close. + afterClose : $.noop, // After instance has been closed + + onActivate : $.noop, // When instance is brought to front + onDeactivate : $.noop, // When other instance has been activated + + + // Interaction + // =========== + + // Use options below to customize taken action when user clicks or double clicks on the fancyBox area, + // each option can be string or method that returns value. + // + // Possible values: + // "close" - close instance + // "next" - move to next gallery item + // "nextOrClose" - move to next gallery item or close if gallery has only one item + // "toggleControls" - show/hide controls + // "zoom" - zoom image (if loaded) + // false - do nothing + + // Clicked on the content + clickContent : function( current, event ) { + return current.type === 'image' ? 'zoom' : false; + }, + + // Clicked on the slide + clickSlide : 'close', + + // Clicked on the background (backdrop) element + clickOutside : 'close', + + // Same as previous two, but for double click + dblclickContent : false, + dblclickSlide : false, + dblclickOutside : false, + + + // Custom options when mobile device is detected + // ============================================= + + mobile : { + clickContent : function( current, event ) { + return current.type === 'image' ? 'toggleControls' : false; + }, + clickSlide : function( current, event ) { + return current.type === 'image' ? 'toggleControls' : "close"; + }, + dblclickContent : function( current, event ) { + return current.type === 'image' ? 'zoom' : false; + }, + dblclickSlide : function( current, event ) { + return current.type === 'image' ? 'zoom' : false; + } + }, + + + // Internationalization + // ============ + + lang : 'en', + i18n : { + 'en' : { + CLOSE : 'Close', + NEXT : 'Next', + PREV : 'Previous', + ERROR : 'The requested content cannot be loaded.
Please try again later.', + PLAY_START : 'Start slideshow', + PLAY_STOP : 'Pause slideshow', + FULL_SCREEN : 'Full screen', + THUMBS : 'Thumbnails' + }, + 'de' : { + CLOSE : 'Schliessen', + NEXT : 'Weiter', + PREV : 'Zurück', + ERROR : 'Die angeforderten Daten konnten nicht geladen werden.
Bitte versuchen Sie es später nochmal.', + PLAY_START : 'Diaschau starten', + PLAY_STOP : 'Diaschau beenden', + FULL_SCREEN : 'Vollbild', + THUMBS : 'Vorschaubilder' + } + } + + }; + + // Few useful variables and methods + // ================================ + + var $W = $(window); + var $D = $(document); + + var called = 0; + + + // Check if an object is a jQuery object and not a native JavaScript object + // ======================================================================== + + var isQuery = function ( obj ) { + return obj && obj.hasOwnProperty && obj instanceof $; + }; + + + // Handle multiple browsers for "requestAnimationFrame" and "cancelAnimationFrame" + // =============================================================================== + + var requestAFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + // if all else fails, use setTimeout + function (callback) { + return window.setTimeout(callback, 1000 / 60); + }; + })(); + + + // Detect the supported transition-end event property name + // ======================================================= + + var transitionEnd = (function () { + var t, el = document.createElement("fakeelement"); + + var transitions = { + "transition" : "transitionend", + "OTransition" : "oTransitionEnd", + "MozTransition" : "transitionend", + "WebkitTransition": "webkitTransitionEnd" + }; + + for (t in transitions) { + if (el.style[t] !== undefined){ + return transitions[t]; + } + } + })(); + + + // Force redraw on an element. + // This helps in cases where the browser doesn't redraw an updated element properly. + // ================================================================================= + + var forceRedraw = function( $el ) { + return ( $el && $el.length && $el[0].offsetHeight ); + }; + + + // Class definition + // ================ + + var FancyBox = function( content, opts, index ) { + var self = this; + + self.opts = $.extend( true, { index : index }, defaults, opts || {} ); + + // Exclude buttons option from deep merging + if ( opts && $.isArray( opts.buttons ) ) { + self.opts.buttons = opts.buttons; + } + + self.id = self.opts.id || ++called; + self.group = []; + + self.currIndex = parseInt( self.opts.index, 10 ) || 0; + self.prevIndex = null; + + self.prevPos = null; + self.currPos = 0; + + self.firstRun = null; + + // Create group elements from original item collection + self.createGroup( content ); + + if ( !self.group.length ) { + return; + } + + // Save last active element and current scroll position + self.$lastFocus = $(document.activeElement).blur(); + + // Collection of gallery objects + self.slides = {}; + + self.init( content ); + + }; + + $.extend(FancyBox.prototype, { + + // Create DOM structure + // ==================== + + init : function() { + var self = this; + + var testWidth, $container, buttonStr; + + var firstItemOpts = self.group[ self.currIndex ].opts; + + self.scrollTop = $D.scrollTop(); + self.scrollLeft = $D.scrollLeft(); + + + // Hide scrollbars + // =============== + + if ( !$.fancybox.getInstance() && !$.fancybox.isMobile && $( 'body' ).css('overflow') !== 'hidden' ) { + testWidth = $( 'body' ).width(); + + $( 'html' ).addClass( 'fancybox-enabled' ); + + // Compare body width after applying "overflow: hidden" + testWidth = $( 'body' ).width() - testWidth; + + // If width has changed - compensate missing scrollbars by adding right margin + if ( testWidth > 1 ) { + $( 'head' ).append( '' ); + } + } + + + // Build html markup and set references + // ==================================== + + // Build html code for buttons and insert into main template + buttonStr = ''; + + $.each( firstItemOpts.buttons, function( index, value ) { + buttonStr += ( firstItemOpts.btnTpl[ value ] || '' ); + }); + + // Create markup from base template, it will be initially hidden to + // avoid unnecessary work like painting while initializing is not complete + $container = $( self.translate( self, firstItemOpts.baseTpl.replace( '\{\{BUTTONS\}\}', buttonStr ) ) ) + .addClass( 'fancybox-is-hidden' ) + .attr('id', 'fancybox-container-' + self.id) + .addClass( firstItemOpts.baseClass ) + .data( 'FancyBox', self ) + .prependTo( firstItemOpts.parentEl ); + + // Create object holding references to jQuery wrapped nodes + self.$refs = { + container : $container + }; + + [ 'bg', 'inner', 'infobar', 'toolbar', 'stage', 'caption' ].forEach(function(item) { + self.$refs[ item ] = $container.find( '.fancybox-' + item ); + }); + + // Check for redundant elements + if ( !firstItemOpts.arrows || self.group.length < 2 ) { + $container.find('.fancybox-navigation').remove(); + } + + if ( !firstItemOpts.infobar ) { + self.$refs.infobar.remove(); + } + + if ( !firstItemOpts.toolbar ) { + self.$refs.toolbar.remove(); + } + + self.trigger( 'onInit' ); + + // Bring to front and enable events + self.activate(); + + // Build slides, load and reveal content + self.jumpTo( self.currIndex ); + }, + + + // Simple i18n support - replaces object keys found in template + // with corresponding values + // ============================================================ + + translate : function( obj, str ) { + var arr = obj.opts.i18n[ obj.opts.lang ]; + + return str.replace(/\{\{(\w+)\}\}/g, function(match, n) { + var value = arr[n]; + + if ( value === undefined ) { + return match; + } + + return value; + }); + }, + + // Create array of gally item objects + // Check if each object has valid type and content + // =============================================== + + createGroup : function ( content ) { + var self = this; + var items = $.makeArray( content ); + + $.each(items, function( i, item ) { + var obj = {}, + opts = {}, + data = [], + $item, + type, + src, + srcParts; + + // Step 1 - Make sure we have an object + // ==================================== + + if ( $.isPlainObject( item ) ) { + + // We probably have manual usage here, something like + // $.fancybox.open( [ { src : "image.jpg", type : "image" } ] ) + + obj = item; + opts = item.opts || item; + + } else if ( $.type( item ) === 'object' && $( item ).length ) { + + // Here we propbably have jQuery collection returned by some selector + + $item = $( item ); + data = $item.data(); + + opts = 'options' in data ? data.options : {}; + opts = $.type( opts ) === 'object' ? opts : {}; + + obj.src = 'src' in data ? data.src : ( opts.src || $item.attr( 'href' ) ); + + [ 'width', 'height', 'thumb', 'type', 'filter' ].forEach(function(item) { + if ( item in data ) { + opts[ item ] = data[ item ]; + } + }); + + if ( 'srcset' in data ) { + opts.image = { srcset : data.srcset }; + } + + opts.$orig = $item; + + if ( !obj.type && !obj.src ) { + obj.type = 'inline'; + obj.src = item; + } + + } else { + + // Assume we have a simple html code, for example: + // $.fancybox.open( '

Hi!

' ); + + obj = { + type : 'html', + src : item + '' + }; + + } + + // Each gallery object has full collection of options + obj.opts = $.extend( true, {}, self.opts, opts ); + + if ( $.fancybox.isMobile ) { + obj.opts = $.extend( true, {}, obj.opts, obj.opts.mobile ); + } + + + // Step 2 - Make sure we have content type, if not - try to guess + // ============================================================== + + type = obj.type || obj.opts.type; + src = obj.src || ''; + + if ( !type && src ) { + if ( src.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i) ) { + type = 'image'; + + } else if ( src.match(/\.(pdf)((\?|#).*)?$/i) ) { + type = 'pdf'; + + } else if ( src.charAt(0) === '#' ) { + type = 'inline'; + } + } + + obj.type = type; + + + // Step 3 - Some adjustments + // ========================= + + obj.index = self.group.length; + + // Check if $orig and $thumb objects exist + if ( obj.opts.$orig && !obj.opts.$orig.length ) { + delete obj.opts.$orig; + } + + if ( !obj.opts.$thumb && obj.opts.$orig ) { + obj.opts.$thumb = obj.opts.$orig.find( 'img:first' ); + } + + if ( obj.opts.$thumb && !obj.opts.$thumb.length ) { + delete obj.opts.$thumb; + } + + // Caption is a "special" option, it can be passed as a method + if ( $.type( obj.opts.caption ) === 'function' ) { + obj.opts.caption = obj.opts.caption.apply( item, [ self, obj ] ); + + } else if ( 'caption' in data ) { + obj.opts.caption = data.caption; + } + + // Make sure we have caption as a string + obj.opts.caption = obj.opts.caption === undefined ? '' : obj.opts.caption + ''; + + // Check if url contains "filter" used to filter the content + // Example: "ajax.html #something" + if ( type === 'ajax' ) { + srcParts = src.split(/\s+/, 2); + + if ( srcParts.length > 1 ) { + obj.src = srcParts.shift(); + + obj.opts.filter = srcParts.shift(); + } + } + + if ( obj.opts.smallBtn == 'auto' ) { + + if ( $.inArray( type, ['html', 'inline', 'ajax'] ) > -1 ) { + obj.opts.toolbar = false; + obj.opts.smallBtn = true; + + } else { + obj.opts.smallBtn = false; + } + + } + + // If the type is "pdf", then simply load file into iframe + if ( type === 'pdf' ) { + obj.type = 'iframe'; + + obj.opts.iframe.preload = false; + } + + // Hide all buttons and disable interactivity for modal items + if ( obj.opts.modal ) { + + obj.opts = $.extend(true, obj.opts, { + // Remove buttons + infobar : 0, + toolbar : 0, + + smallBtn : 0, + + // Disable keyboard navigation + keyboard : 0, + + // Disable some modules + slideShow : 0, + fullScreen : 0, + thumbs : 0, + touch : 0, + + // Disable click event handlers + clickContent : false, + clickSlide : false, + clickOutside : false, + dblclickContent : false, + dblclickSlide : false, + dblclickOutside : false + }); + + } + + // Step 4 - Add processed object to group + // ====================================== + + self.group.push( obj ); + + }); + + }, + + + // Attach an event handler functions for: + // - navigation buttons + // - browser scrolling, resizing; + // - focusing + // - keyboard + // - detect idle + // ====================================== + + addEvents : function() { + var self = this; + + self.removeEvents(); + + // Make navigation elements clickable + self.$refs.container.on('click.fb-close', '[data-fancybox-close]', function(e) { + e.stopPropagation(); + e.preventDefault(); + + self.close( e ); + + }).on( 'click.fb-prev touchend.fb-prev', '[data-fancybox-prev]', function(e) { + e.stopPropagation(); + e.preventDefault(); + + self.previous(); + + }).on( 'click.fb-next touchend.fb-next', '[data-fancybox-next]', function(e) { + e.stopPropagation(); + e.preventDefault(); + + self.next(); + + }); + + + // Handle page scrolling and browser resizing + $W.on('orientationchange.fb resize.fb', function(e) { + + if ( e && e.originalEvent && e.originalEvent.type === "resize" ) { + + requestAFrame(function() { + self.update(); + }); + + } else { + + self.$refs.stage.hide(); + + setTimeout(function() { + self.$refs.stage.show(); + + self.update(); + }, 500); + + } + + }); + + // Trap keyboard focus inside of the modal, so the user does not accidentally tab outside of the modal + // (a.k.a. "escaping the modal") + $D.on('focusin.fb', function(e) { + var instance = $.fancybox ? $.fancybox.getInstance() : null; + + if ( instance.isClosing || !instance.current || !instance.current.opts.trapFocus || $( e.target ).hasClass( 'fancybox-container' ) || $( e.target ).is( document ) ) { + return; + } + + if ( instance && $( e.target ).css( 'position' ) !== 'fixed' && !instance.$refs.container.has( e.target ).length ) { + e.stopPropagation(); + + instance.focus(); + + // Sometimes page gets scrolled, set it back + $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft ); + } + }); + + + // Enable keyboard navigation + $D.on('keydown.fb', function (e) { + var current = self.current, + keycode = e.keyCode || e.which; + + if ( !current || !current.opts.keyboard ) { + return; + } + + if ( $(e.target).is('input') || $(e.target).is('textarea') ) { + return; + } + + // Backspace and Esc keys + if ( keycode === 8 || keycode === 27 ) { + e.preventDefault(); + + self.close( e ); + + return; + } + + // Left arrow and Up arrow + if ( keycode === 37 || keycode === 38 ) { + e.preventDefault(); + + self.previous(); + + return; + } + + // Righ arrow and Down arrow + if ( keycode === 39 || keycode === 40 ) { + e.preventDefault(); + + self.next(); + + return; + } + + self.trigger('afterKeydown', e, keycode); + }); + + + // Hide controls after some inactivity period + if ( self.group[ self.currIndex ].opts.idleTime ) { + self.idleSecondsCounter = 0; + + $D.on('mousemove.fb-idle mouseenter.fb-idle mouseleave.fb-idle mousedown.fb-idle touchstart.fb-idle touchmove.fb-idle scroll.fb-idle keydown.fb-idle', function() { + self.idleSecondsCounter = 0; + + if ( self.isIdle ) { + self.showControls(); + } + + self.isIdle = false; + }); + + self.idleInterval = window.setInterval(function() { + + self.idleSecondsCounter++; + + if ( self.idleSecondsCounter >= self.group[ self.currIndex ].opts.idleTime ) { + self.isIdle = true; + self.idleSecondsCounter = 0; + + self.hideControls(); + } + + }, 1000); + } + + }, + + + // Remove events added by the core + // =============================== + + removeEvents : function () { + var self = this; + + $W.off( 'orientationchange.fb resize.fb' ); + $D.off( 'focusin.fb keydown.fb .fb-idle' ); + + this.$refs.container.off( '.fb-close .fb-prev .fb-next' ); + + if ( self.idleInterval ) { + window.clearInterval( self.idleInterval ); + + self.idleInterval = null; + } + }, + + + // Change to previous gallery item + // =============================== + + previous : function( duration ) { + return this.jumpTo( this.currPos - 1, duration ); + }, + + + // Change to next gallery item + // =========================== + + next : function( duration ) { + return this.jumpTo( this.currPos + 1, duration ); + }, + + + // Switch to selected gallery item + // =============================== + + jumpTo : function ( pos, duration, slide ) { + var self = this, + firstRun, + loop, + current, + previous, + canvasWidth, + currentPos, + transitionProps; + + var groupLen = self.group.length; + + if ( self.isSliding || self.isClosing || ( self.isAnimating && self.firstRun ) ) { + return; + } + + pos = parseInt( pos, 10 ); + loop = self.current ? self.current.opts.loop : self.opts.loop; + + if ( !loop && ( pos < 0 || pos >= groupLen ) ) { + return false; + } + + firstRun = self.firstRun = ( self.firstRun === null ); + + if ( groupLen < 2 && !firstRun && !!self.isSliding ) { + return; + } + + previous = self.current; + + self.prevIndex = self.currIndex; + self.prevPos = self.currPos; + + // Create slides + current = self.createSlide( pos ); + + if ( groupLen > 1 ) { + if ( loop || current.index > 0 ) { + self.createSlide( pos - 1 ); + } + + if ( loop || current.index < groupLen - 1 ) { + self.createSlide( pos + 1 ); + } + } + + self.current = current; + self.currIndex = current.index; + self.currPos = current.pos; + + self.trigger( 'beforeShow', firstRun ); + + self.updateControls(); + + currentPos = $.fancybox.getTranslate( current.$slide ); + + current.isMoved = ( currentPos.left !== 0 || currentPos.top !== 0 ) && !current.$slide.hasClass( 'fancybox-animated' ); + current.forcedDuration = undefined; + + if ( $.isNumeric( duration ) ) { + current.forcedDuration = duration; + } else { + duration = current.opts[ firstRun ? 'animationDuration' : 'transitionDuration' ]; + } + + duration = parseInt( duration, 10 ); + + // Fresh start - reveal container, current slide and start loading content + if ( firstRun ) { + + if ( current.opts.animationEffect && duration ) { + self.$refs.container.css( 'transition-duration', duration + 'ms' ); + } + + self.$refs.container.removeClass( 'fancybox-is-hidden' ); + + forceRedraw( self.$refs.container ); + + self.$refs.container.addClass( 'fancybox-is-open' ); + + // Make first slide visible (to display loading icon, if needed) + current.$slide.addClass( 'fancybox-slide--current' ); + + self.loadSlide( current ); + + self.preload(); + + return; + } + + // Clean up + $.each(self.slides, function( index, slide ) { + $.fancybox.stop( slide.$slide ); + }); + + // Make current that slide is visible even if content is still loading + current.$slide.removeClass( 'fancybox-slide--next fancybox-slide--previous' ).addClass( 'fancybox-slide--current' ); + + // If slides have been dragged, animate them to correct position + if ( current.isMoved ) { + canvasWidth = Math.round( current.$slide.width() ); + + $.each(self.slides, function( index, slide ) { + var pos = slide.pos - current.pos; + + $.fancybox.animate( slide.$slide, { + top : 0, + left : ( pos * canvasWidth ) + ( pos * slide.opts.gutter ) + }, duration, function() { + + slide.$slide.removeAttr('style').removeClass( 'fancybox-slide--next fancybox-slide--previous' ); + + if ( slide.pos === self.currPos ) { + current.isMoved = false; + + self.complete(); + } + }); + }); + + } else { + self.$refs.stage.children().removeAttr( 'style' ); + } + + // Start transition that reveals current content + // or wait when it will be loaded + + if ( current.isLoaded ) { + self.revealContent( current ); + + } else { + self.loadSlide( current ); + } + + self.preload(); + + if ( previous.pos === current.pos ) { + return; + } + + // Handle previous slide + // ===================== + + transitionProps = 'fancybox-slide--' + ( previous.pos > current.pos ? 'next' : 'previous' ); + + previous.$slide.removeClass( 'fancybox-slide--complete fancybox-slide--current fancybox-slide--next fancybox-slide--previous' ); + + previous.isComplete = false; + + if ( !duration || ( !current.isMoved && !current.opts.transitionEffect ) ) { + return; + } + + if ( current.isMoved ) { + previous.$slide.addClass( transitionProps ); + + } else { + + transitionProps = 'fancybox-animated ' + transitionProps + ' fancybox-fx-' + current.opts.transitionEffect; + + $.fancybox.animate( previous.$slide, transitionProps, duration, function() { + previous.$slide.removeClass( transitionProps ).removeAttr( 'style' ); + }); + + } + + }, + + + // Create new "slide" element + // These are gallery items that are actually added to DOM + // ======================================================= + + createSlide : function( pos ) { + + var self = this; + var $slide; + var index; + + index = pos % self.group.length; + index = index < 0 ? self.group.length + index : index; + + if ( !self.slides[ pos ] && self.group[ index ] ) { + $slide = $('
').appendTo( self.$refs.stage ); + + self.slides[ pos ] = $.extend( true, {}, self.group[ index ], { + pos : pos, + $slide : $slide, + isLoaded : false, + }); + + self.updateSlide( self.slides[ pos ] ); + } + + return self.slides[ pos ]; + }, + + + // Scale image to the actual size of the image + // =========================================== + + scaleToActual : function( x, y, duration ) { + + var self = this; + + var current = self.current; + var $what = current.$content; + + var imgPos, posX, posY, scaleX, scaleY; + + var canvasWidth = parseInt( current.$slide.width(), 10 ); + var canvasHeight = parseInt( current.$slide.height(), 10 ); + + var newImgWidth = current.width; + var newImgHeight = current.height; + + if ( !( current.type == 'image' && !current.hasError) || !$what || self.isAnimating) { + return; + } + + $.fancybox.stop( $what ); + + self.isAnimating = true; + + x = x === undefined ? canvasWidth * 0.5 : x; + y = y === undefined ? canvasHeight * 0.5 : y; + + imgPos = $.fancybox.getTranslate( $what ); + + scaleX = newImgWidth / imgPos.width; + scaleY = newImgHeight / imgPos.height; + + // Get center position for original image + posX = ( canvasWidth * 0.5 - newImgWidth * 0.5 ); + posY = ( canvasHeight * 0.5 - newImgHeight * 0.5 ); + + // Make sure image does not move away from edges + if ( newImgWidth > canvasWidth ) { + posX = imgPos.left * scaleX - ( ( x * scaleX ) - x ); + + if ( posX > 0 ) { + posX = 0; + } + + if ( posX < canvasWidth - newImgWidth ) { + posX = canvasWidth - newImgWidth; + } + } + + if ( newImgHeight > canvasHeight) { + posY = imgPos.top * scaleY - ( ( y * scaleY ) - y ); + + if ( posY > 0 ) { + posY = 0; + } + + if ( posY < canvasHeight - newImgHeight ) { + posY = canvasHeight - newImgHeight; + } + } + + self.updateCursor( newImgWidth, newImgHeight ); + + $.fancybox.animate( $what, { + top : posY, + left : posX, + scaleX : scaleX, + scaleY : scaleY + }, duration || 330, function() { + self.isAnimating = false; + }); + + // Stop slideshow + if ( self.SlideShow && self.SlideShow.isActive ) { + self.SlideShow.stop(); + } + }, + + + // Scale image to fit inside parent element + // ======================================== + + scaleToFit : function( duration ) { + + var self = this; + + var current = self.current; + var $what = current.$content; + var end; + + if ( !( current.type == 'image' && !current.hasError) || !$what || self.isAnimating ) { + return; + } + + $.fancybox.stop( $what ); + + self.isAnimating = true; + + end = self.getFitPos( current ); + + self.updateCursor( end.width, end.height ); + + $.fancybox.animate( $what, { + top : end.top, + left : end.left, + scaleX : end.width / $what.width(), + scaleY : end.height / $what.height() + }, duration || 330, function() { + self.isAnimating = false; + }); + + }, + + // Calculate image size to fit inside viewport + // =========================================== + + getFitPos : function( slide ) { + var self = this; + var $what = slide.$content; + + var imgWidth = slide.width; + var imgHeight = slide.height; + + var margin = slide.opts.margin; + + var canvasWidth, canvasHeight, minRatio, width, height; + + if ( !$what || !$what.length || ( !imgWidth && !imgHeight) ) { + return false; + } + + // Convert "margin to CSS style: [ top, right, bottom, left ] + if ( $.type( margin ) === "number" ) { + margin = [ margin, margin ]; + } + + if ( margin.length == 2 ) { + margin = [ margin[0], margin[1], margin[0], margin[1] ]; + } + + if ( $W.width() < 800 ) { + margin = [ 0, 0, 0, 0 ]; + } + + // We can not use $slide width here, because it can have different diemensions while in transiton + canvasWidth = parseInt( self.$refs.stage.width(), 10 ) - ( margin[ 1 ] + margin[ 3 ] ); + canvasHeight = parseInt( self.$refs.stage.height(), 10 ) - ( margin[ 0 ] + margin[ 2 ] ); + + minRatio = Math.min(1, canvasWidth / imgWidth, canvasHeight / imgHeight ); + + width = Math.floor( minRatio * imgWidth ); + height = Math.floor( minRatio * imgHeight ); + + // Use floor rounding to make sure it really fits + return { + top : Math.floor( ( canvasHeight - height ) * 0.5 ) + margin[ 0 ], + left : Math.floor( ( canvasWidth - width ) * 0.5 ) + margin[ 3 ], + width : width, + height : height + }; + + }, + + + // Update position and content of all slides + // ========================================= + + update : function() { + + var self = this; + + $.each( self.slides, function( key, slide ) { + self.updateSlide( slide ); + }); + + }, + + + // Update slide position and scale content to fit + // ============================================== + + updateSlide : function( slide ) { + + var self = this; + var $what = slide.$content; + + if ( $what && ( slide.width || slide.height ) ) { + $.fancybox.stop( $what ); + + $.fancybox.setTranslate( $what, self.getFitPos( slide ) ); + + if ( slide.pos === self.currPos ) { + self.updateCursor(); + } + } + + slide.$slide.trigger( 'refresh' ); + + self.trigger( 'onUpdate', slide ); + + }, + + // Update cursor style depending if content can be zoomed + // ====================================================== + + updateCursor : function( nextWidth, nextHeight ) { + + var self = this; + var isScaledDown; + + var $container = self.$refs.container.removeClass('fancybox-is-zoomable fancybox-can-zoomIn fancybox-can-drag fancybox-can-zoomOut'); + + if ( !self.current || self.isClosing ) { + return; + } + + if ( self.isZoomable() ) { + + $container.addClass( 'fancybox-is-zoomable' ); + + if ( nextWidth !== undefined && nextHeight !== undefined ) { + isScaledDown = nextWidth < self.current.width && nextHeight < self.current.height; + + } else { + isScaledDown = self.isScaledDown(); + } + + if ( isScaledDown ) { + + // If image is scaled down, then, obviously, it can be zoomed to full size + $container.addClass('fancybox-can-zoomIn'); + + } else { + + if ( self.current.opts.touch ) { + + // If image size ir largen than available available and touch module is not disable, + // then user can do panning + $container.addClass('fancybox-can-drag'); + + } else { + $container.addClass('fancybox-can-zoomOut'); + } + + } + + } else if ( self.current.opts.touch ) { + $container.addClass('fancybox-can-drag'); + } + + }, + + + // Check if current slide is zoomable + // ================================== + + isZoomable : function() { + + var self = this; + + var current = self.current; + var fitPos; + + if ( !current || self.isClosing ) { + return; + } + + // Assume that slide is zoomable if + // - image is loaded successfuly + // - click action is "zoom" + // - actual size of the image is smaller than available area + if ( current.type === 'image' && current.isLoaded && !current.hasError && + ( current.opts.clickContent === 'zoom' || ( $.isFunction( current.opts.clickContent ) && current.opts.clickContent( current ) === "zoom" ) ) + ) { + + fitPos = self.getFitPos( current ); + + if ( current.width > fitPos.width || current.height > fitPos.height ) { + return true; + } + + } + + return false; + + }, + + + // Check if current image dimensions are smaller than actual + // ========================================================= + + isScaledDown : function() { + + var self = this; + + var current = self.current; + var $what = current.$content; + + var rez = false; + + if ( $what ) { + rez = $.fancybox.getTranslate( $what ); + rez = rez.width < current.width || rez.height < current.height; + } + + return rez; + + }, + + + // Check if image dimensions exceed parent element + // =============================================== + + canPan : function() { + + var self = this; + + var current = self.current; + var $what = current.$content; + + var rez = false; + + if ( $what ) { + rez = self.getFitPos( current ); + rez = Math.abs( $what.width() - rez.width ) > 1 || Math.abs( $what.height() - rez.height ) > 1; + + } + + return rez; + + }, + + + // Load content into the slide + // =========================== + + loadSlide : function( slide ) { + + var self = this, type, $slide; + var ajaxLoad; + + if ( slide.isLoading ) { + return; + } + + if ( slide.isLoaded ) { + return; + } + + slide.isLoading = true; + + self.trigger( 'beforeLoad', slide ); + + type = slide.type; + $slide = slide.$slide; + + $slide + .off( 'refresh' ) + .trigger( 'onReset' ) + .addClass( 'fancybox-slide--' + ( type || 'unknown' ) ) + .addClass( slide.opts.slideClass ); + + // Create content depending on the type + + switch ( type ) { + + case 'image': + + self.setImage( slide ); + + break; + + case 'iframe': + + self.setIframe( slide ); + + break; + + case 'html': + + self.setContent( slide, slide.src || slide.content ); + + break; + + case 'inline': + + if ( $( slide.src ).length ) { + self.setContent( slide, $( slide.src ) ); + + } else { + self.setError( slide ); + } + + break; + + case 'ajax': + + self.showLoading( slide ); + + ajaxLoad = $.ajax( $.extend( {}, slide.opts.ajax.settings, { + url : slide.src, + success : function ( data, textStatus ) { + + if ( textStatus === 'success' ) { + self.setContent( slide, data ); + } + + }, + error : function ( jqXHR, textStatus ) { + + if ( jqXHR && textStatus !== 'abort' ) { + self.setError( slide ); + } + + } + })); + + $slide.one( 'onReset', function () { + ajaxLoad.abort(); + }); + + break; + + default: + + self.setError( slide ); + + break; + + } + + return true; + + }, + + + // Use thumbnail image, if possible + // ================================ + + setImage : function( slide ) { + + var self = this; + var srcset = slide.opts.image.srcset; + + var found, temp, pxRatio, windowWidth; + + // If we have "srcset", then we need to find matching "src" value. + // This is necessary, because when you set an src attribute, the browser will preload the image + // before any javascript or even CSS is applied. + if ( srcset ) { + pxRatio = window.devicePixelRatio || 1; + windowWidth = window.innerWidth * pxRatio; + + temp = srcset.split(',').map(function ( el ) { + var ret = {}; + + el.trim().split(/\s+/).forEach(function ( el, i ) { + var value = parseInt( el.substring(0, el.length - 1), 10 ); + + if ( i === 0 ) { + return ( ret.url = el ); + } + + if ( value ) { + ret.value = value; + ret.postfix = el[ el.length - 1 ]; + } + + }); + + return ret; + }); + + // Sort by value + temp.sort(function (a, b) { + return a.value - b.value; + }); + + // Ok, now we have an array of all srcset values + for ( var j = 0; j < temp.length; j++ ) { + var el = temp[ j ]; + + if ( ( el.postfix === 'w' && el.value >= windowWidth ) || ( el.postfix === 'x' && el.value >= pxRatio ) ) { + found = el; + break; + } + } + + // If not found, take the last one + if ( !found && temp.length ) { + found = temp[ temp.length - 1 ]; + } + + if ( found ) { + slide.src = found.url; + + // If we have default width/height values, we can calculate height for matching source + if ( slide.width && slide.height && found.postfix == 'w' ) { + slide.height = ( slide.width / slide.height ) * found.value; + slide.width = found.value; + } + } + } + + // This will be wrapper containing both ghost and actual image + slide.$content = $('
') + .addClass( 'fancybox-is-hidden' ) + .appendTo( slide.$slide ); + + + // If we have a thumbnail, we can display it while actual image is loading + // Users will not stare at black screen and actual image will appear gradually + if ( slide.opts.preload !== false && slide.opts.width && slide.opts.height && ( slide.opts.thumb || slide.opts.$thumb ) ) { + + slide.width = slide.opts.width; + slide.height = slide.opts.height; + + slide.$ghost = $('') + .one('error', function() { + + $(this).remove(); + + slide.$ghost = null; + + self.setBigImage( slide ); + + }) + .one('load', function() { + + self.afterLoad( slide ); + + self.setBigImage( slide ); + + }) + .addClass( 'fancybox-image' ) + .appendTo( slide.$content ) + .attr( 'src', slide.opts.thumb || slide.opts.$thumb.attr( 'src' ) ); + + } else { + + self.setBigImage( slide ); + + } + + }, + + + // Create full-size image + // ====================== + + setBigImage : function ( slide ) { + var self = this; + var $img = $(''); + + slide.$image = $img + .one('error', function() { + + self.setError( slide ); + + }) + .one('load', function() { + + // Clear timeout that checks if loading icon needs to be displayed + clearTimeout( slide.timouts ); + + slide.timouts = null; + + if ( self.isClosing ) { + return; + } + + slide.width = this.naturalWidth; + slide.height = this.naturalHeight; + + if ( slide.opts.image.srcset ) { + $img.attr( 'sizes', '100vw' ).attr( 'srcset', slide.opts.image.srcset ); + } + + self.hideLoading( slide ); + + if ( slide.$ghost ) { + + slide.timouts = setTimeout(function() { + slide.timouts = null; + + slide.$ghost.hide(); + + }, Math.min( 300, Math.max( 1000, slide.height / 1600 ) ) ); + + } else { + self.afterLoad( slide ); + } + + }) + .addClass( 'fancybox-image' ) + .attr('src', slide.src) + .appendTo( slide.$content ); + + if ( $img[0].complete ) { + $img.trigger( 'load' ); + + } else if( $img[0].error ) { + $img.trigger( 'error' ); + + } else { + + slide.timouts = setTimeout(function() { + if ( !$img[0].complete && !slide.hasError ) { + self.showLoading( slide ); + } + + }, 100); + + } + + }, + + + // Create iframe wrapper, iframe and bindings + // ========================================== + + setIframe : function( slide ) { + var self = this, + opts = slide.opts.iframe, + $slide = slide.$slide, + $iframe; + + slide.$content = $('
') + .css( opts.css ) + .appendTo( $slide ); + + $iframe = $( opts.tpl.replace(/\{rnd\}/g, new Date().getTime()) ) + .attr( opts.attr ) + .appendTo( slide.$content ); + + if ( opts.preload ) { + + self.showLoading( slide ); + + // Unfortunately, it is not always possible to determine if iframe is successfully loaded + // (due to browser security policy) + + $iframe.on('load.fb error.fb', function(e) { + this.isReady = 1; + + slide.$slide.trigger( 'refresh' ); + + self.afterLoad( slide ); + }); + + // Recalculate iframe content size + // =============================== + + $slide.on('refresh.fb', function() { + var $wrap = slide.$content, + $contents, + $body, + scrollWidth, + frameWidth, + frameHeight; + + if ( $iframe[0].isReady !== 1 ) { + return; + } + + // Check if content is accessible, + // it will fail if frame is not with the same origin + + try { + $contents = $iframe.contents(); + $body = $contents.find('body'); + + } catch (ignore) {} + + // Calculate dimensions for the wrapper + if ( $body && $body.length && !( opts.css.width !== undefined && opts.css.height !== undefined ) ) { + + scrollWidth = $iframe[0].contentWindow.document.documentElement.scrollWidth; + + frameWidth = Math.ceil( $body.outerWidth(true) + ( $wrap.width() - scrollWidth ) ); + frameHeight = Math.ceil( $body.outerHeight(true) ); + + // Resize wrapper to fit iframe content + $wrap.css({ + 'width' : opts.css.width === undefined ? frameWidth + ( $wrap.outerWidth() - $wrap.innerWidth() ) : opts.css.width, + 'height' : opts.css.height === undefined ? frameHeight + ( $wrap.outerHeight() - $wrap.innerHeight() ) : opts.css.height + }); + + } + + $wrap.removeClass( 'fancybox-is-hidden' ); + + }); + + } else { + + this.afterLoad( slide ); + + } + + $iframe.attr( 'src', slide.src ); + + if ( slide.opts.smallBtn === true ) { + slide.$content.prepend( self.translate( slide, slide.opts.btnTpl.smallBtn ) ); + } + + // Remove iframe if closing or changing gallery item + $slide.one( 'onReset', function () { + + // This helps IE not to throw errors when closing + try { + + $( this ).find( 'iframe' ).hide().attr( 'src', '//about:blank' ); + + } catch ( ignore ) {} + + $( this ).empty(); + + slide.isLoaded = false; + + }); + + }, + + + // Wrap and append content to the slide + // ====================================== + + setContent : function ( slide, content ) { + + var self = this; + + if ( self.isClosing ) { + return; + } + + self.hideLoading( slide ); + + slide.$slide.empty(); + + if ( isQuery( content ) && content.parent().length ) { + + // If content is a jQuery object, then it will be moved to the slide. + // The placeholder is created so we will know where to put it back. + // If user is navigating gallery fast, then the content might be already inside fancyBox + // ===================================================================================== + + // Make sure content is not already moved to fancyBox + content.parent( '.fancybox-slide--inline' ).trigger( 'onReset' ); + + // Create temporary element marking original place of the content + slide.$placeholder = $( '
' ).hide().insertAfter( content ); + + // Make sure content is visible + content.css('display', 'inline-block'); + + } else if ( !slide.hasError ) { + + // If content is just a plain text, try to convert it to html + if ( $.type( content ) === 'string' ) { + content = $('
').append( $.trim( content ) ).contents(); + + // If we have text node, then add wrapping element to make vertical alignment work + if ( content[0].nodeType === 3 ) { + content = $('
').html( content ); + } + } + + // If "filter" option is provided, then filter content + if ( slide.opts.filter ) { + content = $('
').html( content ).find( slide.opts.filter ); + } + + } + + slide.$slide.one('onReset', function () { + + // Put content back + if ( slide.$placeholder ) { + slide.$placeholder.after( content.hide() ).remove(); + + slide.$placeholder = null; + } + + // Remove custom close button + if ( slide.$smallBtn ) { + slide.$smallBtn.remove(); + + slide.$smallBtn = null; + } + + // Remove content and mark slide as not loaded + if ( !slide.hasError ) { + $(this).empty(); + + slide.isLoaded = false; + } + + }); + + slide.$content = $( content ).appendTo( slide.$slide ); + + if ( slide.opts.smallBtn && !slide.$smallBtn ) { + slide.$smallBtn = $( self.translate( slide, slide.opts.btnTpl.smallBtn ) ).appendTo( slide.$content ); + } + + this.afterLoad( slide ); + }, + + // Display error message + // ===================== + + setError : function ( slide ) { + + slide.hasError = true; + + slide.$slide.removeClass( 'fancybox-slide--' + slide.type ); + + this.setContent( slide, this.translate( slide, slide.opts.errorTpl ) ); + + }, + + + // Show loading icon inside the slide + // ================================== + + showLoading : function( slide ) { + + var self = this; + + slide = slide || self.current; + + if ( slide && !slide.$spinner ) { + slide.$spinner = $( self.opts.spinnerTpl ).appendTo( slide.$slide ); + } + + }, + + // Remove loading icon from the slide + // ================================== + + hideLoading : function( slide ) { + + var self = this; + + slide = slide || self.current; + + if ( slide && slide.$spinner ) { + slide.$spinner.remove(); + + delete slide.$spinner; + } + + }, + + + // Adjustments after slide content has been loaded + // =============================================== + + afterLoad : function( slide ) { + + var self = this; + + if ( self.isClosing ) { + return; + } + + slide.isLoading = false; + slide.isLoaded = true; + + self.trigger( 'afterLoad', slide ); + + self.hideLoading( slide ); + + if ( slide.opts.protect && slide.$content && !slide.hasError ) { + + // Disable right click + slide.$content.on( 'contextmenu.fb', function( e ) { + if ( e.button == 2 ) { + e.preventDefault(); + } + + return true; + }); + + // Add fake element on top of the image + // This makes a bit harder for user to select image + if ( slide.type === 'image' ) { + $( '
' ).appendTo( slide.$content ); + } + + } + + self.revealContent( slide ); + + }, + + + // Make content visible + // This method is called right after content has been loaded or + // user navigates gallery and transition should start + // ============================================================ + + revealContent : function( slide ) { + + var self = this; + var $slide = slide.$slide; + + var effect, effectClassName, duration, opacity, end, start = false; + + effect = slide.opts[ self.firstRun ? 'animationEffect' : 'transitionEffect' ]; + duration = slide.opts[ self.firstRun ? 'animationDuration' : 'transitionDuration' ]; + + duration = parseInt( slide.forcedDuration === undefined ? duration : slide.forcedDuration, 10 ); + + if ( slide.isMoved || slide.pos !== self.currPos || !duration ) { + effect = false; + } + + // Check if can zoom + if ( effect === 'zoom' && !( slide.pos === self.currPos && duration && slide.type === 'image' && !slide.hasError && ( start = self.getThumbPos( slide ) ) ) ) { + effect = 'fade'; + } + + + // Zoom animation + // ============== + + if ( effect === 'zoom' ) { + end = self.getFitPos( slide ); + + end.scaleX = end.width / start.width; + end.scaleY = end.height / start.height; + + delete end.width; + delete end.height; + + // Check if we need to animate opacity + opacity = slide.opts.zoomOpacity; + + if ( opacity == 'auto' ) { + opacity = Math.abs( slide.width / slide.height - start.width / start.height ) > 0.1; + } + + if ( opacity ) { + start.opacity = 0.1; + end.opacity = 1; + } + + // Draw image at start position + $.fancybox.setTranslate( slide.$content.removeClass( 'fancybox-is-hidden' ), start ); + + forceRedraw( slide.$content ); + + // Start animation + $.fancybox.animate( slide.$content, end, duration, function() { + self.complete(); + }); + + return; + } + + + self.updateSlide( slide ); + + + // Simply show content + // =================== + + if ( !effect ) { + forceRedraw( $slide ); + + slide.$content.removeClass( 'fancybox-is-hidden' ); + + if ( slide.pos === self.currPos ) { + self.complete(); + } + + return; + } + + $.fancybox.stop( $slide ); + + effectClassName = 'fancybox-animated fancybox-slide--' + ( slide.pos > self.prevPos ? 'next' : 'previous' ) + ' fancybox-fx-' + effect; + + $slide.removeAttr( 'style' ).removeClass( 'fancybox-slide--current fancybox-slide--next fancybox-slide--previous' ).addClass( effectClassName ); + + slide.$content.removeClass( 'fancybox-is-hidden' ); + + //Force reflow for CSS3 transitions + forceRedraw( $slide ); + + $.fancybox.animate( $slide, 'fancybox-slide--current', duration, function(e) { + $slide.removeClass( effectClassName ).removeAttr( 'style' ); + + if ( slide.pos === self.currPos ) { + self.complete(); + } + + }, true); + + }, + + + // Check if we can and have to zoom from thumbnail + //================================================ + + getThumbPos : function( slide ) { + + var self = this; + var rez = false; + + // Check if element is inside the viewport by at least 1 pixel + var isElementVisible = function( $el ) { + var element = $el[0]; + + var elementRect = element.getBoundingClientRect(); + var parentRects = []; + + var visibleInAllParents; + + while ( element.parentElement !== null ) { + if ( $(element.parentElement).css('overflow') === 'hidden' || $(element.parentElement).css('overflow') === 'auto' ) { + parentRects.push(element.parentElement.getBoundingClientRect()); + } + + element = element.parentElement; + } + + visibleInAllParents = parentRects.every(function(parentRect){ + var visiblePixelX = Math.min(elementRect.right, parentRect.right) - Math.max(elementRect.left, parentRect.left); + var visiblePixelY = Math.min(elementRect.bottom, parentRect.bottom) - Math.max(elementRect.top, parentRect.top); + + return visiblePixelX > 0 && visiblePixelY > 0; + }); + + return visibleInAllParents && + elementRect.bottom > 0 && elementRect.right > 0 && + elementRect.left < $(window).width() && elementRect.top < $(window).height(); + }; + + var $thumb = slide.opts.$thumb; + var thumbPos = $thumb ? $thumb.offset() : 0; + var slidePos; + + if ( thumbPos && $thumb[0].ownerDocument === document && isElementVisible( $thumb ) ) { + slidePos = self.$refs.stage.offset(); + + rez = { + top : thumbPos.top - slidePos.top + parseFloat( $thumb.css( "border-top-width" ) || 0 ), + left : thumbPos.left - slidePos.left + parseFloat( $thumb.css( "border-left-width" ) || 0 ), + width : $thumb.width(), + height : $thumb.height(), + scaleX : 1, + scaleY : 1 + }; + } + + return rez; + }, + + + // Final adjustments after current gallery item is moved to position + // and it`s content is loaded + // ================================================================== + + complete : function() { + + var self = this; + + var current = self.current; + var slides = {}; + + if ( current.isMoved || !current.isLoaded || current.isComplete ) { + return; + } + + current.isComplete = true; + + current.$slide.siblings().trigger( 'onReset' ); + + // Trigger any CSS3 transiton inside the slide + forceRedraw( current.$slide ); + + current.$slide.addClass( 'fancybox-slide--complete' ); + + // Remove unnecessary slides + $.each( self.slides, function( key, slide ) { + if ( slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1 ) { + slides[ slide.pos ] = slide; + + } else if ( slide ) { + + $.fancybox.stop( slide.$slide ); + + slide.$slide.unbind().remove(); + } + }); + + self.slides = slides; + + self.updateCursor(); + + self.trigger( 'afterShow' ); + + // Try to focus on the first focusable element + if ( $( document.activeElement ).is( '[disabled]' ) || ( current.opts.autoFocus && !( current.type == 'image' || current.type === 'iframe' ) ) ) { + self.focus(); + } + + }, + + + // Preload next and previous slides + // ================================ + + preload : function() { + var self = this; + var next, prev; + + if ( self.group.length < 2 ) { + return; + } + + next = self.slides[ self.currPos + 1 ]; + prev = self.slides[ self.currPos - 1 ]; + + if ( next && next.type === 'image' ) { + self.loadSlide( next ); + } + + if ( prev && prev.type === 'image' ) { + self.loadSlide( prev ); + } + + }, + + + // Try to find and focus on the first focusable element + // ==================================================== + + focus : function() { + var current = this.current; + var $el; + + if ( this.isClosing ) { + return; + } + + // Skip for images and iframes + $el = current && current.isComplete ? current.$slide.find('button,:input,[tabindex],a').filter(':not([disabled]):visible:first') : null; + $el = $el && $el.length ? $el : this.$refs.container; + + $el.focus(); + }, + + + // Activates current instance - brings container to the front and enables keyboard, + // notifies other instances about deactivating + // ================================================================================= + + activate : function () { + var self = this; + + // Deactivate all instances + $( '.fancybox-container' ).each(function () { + var instance = $(this).data( 'FancyBox' ); + + // Skip self and closing instances + if (instance && instance.uid !== self.uid && !instance.isClosing) { + instance.trigger( 'onDeactivate' ); + } + + }); + + if ( self.current ) { + if ( self.$refs.container.index() > 0 ) { + self.$refs.container.prependTo( document.body ); + } + + self.updateControls(); + } + + self.trigger( 'onActivate' ); + + self.addEvents(); + + }, + + + // Start closing procedure + // This will start "zoom-out" animation if needed and clean everything up afterwards + // ================================================================================= + + close : function( e, d ) { + + var self = this; + var current = self.current; + + var effect, duration; + var $what, opacity, start, end; + + var done = function() { + self.cleanUp( e ); + }; + + if ( self.isClosing ) { + return false; + } + + self.isClosing = true; + + // If beforeClose callback prevents closing, make sure content is centered + if ( self.trigger( 'beforeClose', e ) === false ) { + self.isClosing = false; + + requestAFrame(function() { + self.update(); + }); + + return false; + } + + // Remove all events + // If there are multiple instances, they will be set again by "activate" method + self.removeEvents(); + + if ( current.timouts ) { + clearTimeout( current.timouts ); + } + + $what = current.$content; + effect = current.opts.animationEffect; + duration = $.isNumeric( d ) ? d : ( effect ? current.opts.animationDuration : 0 ); + + // Remove other slides + current.$slide.off( transitionEnd ).removeClass( 'fancybox-slide--complete fancybox-slide--next fancybox-slide--previous fancybox-animated' ); + + current.$slide.siblings().trigger( 'onReset' ).remove(); + + // Trigger animations + if ( duration ) { + self.$refs.container.removeClass( 'fancybox-is-open' ).addClass( 'fancybox-is-closing' ); + } + + // Clean up + self.hideLoading( current ); + + self.hideControls(); + + self.updateCursor(); + + // Check if possible to zoom-out + if ( effect === 'zoom' && !( e !== true && $what && duration && current.type === 'image' && !current.hasError && ( end = self.getThumbPos( current ) ) ) ) { + effect = 'fade'; + } + + if ( effect === 'zoom' ) { + $.fancybox.stop( $what ); + + start = $.fancybox.getTranslate( $what ); + + start.width = start.width * start.scaleX; + start.height = start.height * start.scaleY; + + // Check if we need to animate opacity + opacity = current.opts.zoomOpacity; + + if ( opacity == 'auto' ) { + opacity = Math.abs( current.width / current.height - end.width / end.height ) > 0.1; + } + + if ( opacity ) { + end.opacity = 0; + } + + start.scaleX = start.width / end.width; + start.scaleY = start.height / end.height; + + start.width = end.width; + start.height = end.height; + + $.fancybox.setTranslate( current.$content, start ); + + $.fancybox.animate( current.$content, end, duration, done ); + + return true; + } + + if ( effect && duration ) { + + // If skip animation + if ( e === true ) { + setTimeout( done, duration ); + + } else { + $.fancybox.animate( current.$slide.removeClass( 'fancybox-slide--current' ), 'fancybox-animated fancybox-slide--previous fancybox-fx-' + effect, duration, done ); + } + + } else { + done(); + } + + return true; + }, + + + // Final adjustments after removing the instance + // ============================================= + + cleanUp : function( e ) { + var self = this, + instance; + + self.current.$slide.trigger( 'onReset' ); + + self.$refs.container.empty().remove(); + + self.trigger( 'afterClose', e ); + + // Place back focus + if ( self.$lastFocus && !!self.current.opts.backFocus ) { + self.$lastFocus.focus(); + } + + self.current = null; + + // Check if there are other instances + instance = $.fancybox.getInstance(); + + if ( instance ) { + instance.activate(); + + } else { + + $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft ); + + $( 'html' ).removeClass( 'fancybox-enabled' ); + + $( '#fancybox-style-noscroll' ).remove(); + } + + }, + + + // Call callback and trigger an event + // ================================== + + trigger : function( name, slide ) { + var args = Array.prototype.slice.call(arguments, 1), + self = this, + obj = slide && slide.opts ? slide : self.current, + rez; + + if ( obj ) { + args.unshift( obj ); + + } else { + obj = self; + } + + args.unshift( self ); + + if ( $.isFunction( obj.opts[ name ] ) ) { + rez = obj.opts[ name ].apply( obj, args ); + } + + if ( rez === false ) { + return rez; + } + + if ( name === 'afterClose' ) { + $D.trigger( name + '.fb', args ); + + } else { + self.$refs.container.trigger( name + '.fb', args ); + } + + }, + + + // Update infobar values, navigation button states and reveal caption + // ================================================================== + + updateControls : function ( force ) { + + var self = this; + + var current = self.current; + var index = current.index; + var opts = current.opts; + var caption = opts.caption; + var $caption = self.$refs.caption; + + // Recalculate content dimensions + current.$slide.trigger( 'refresh' ); + + self.$caption = caption && caption.length ? $caption.html( caption ) : null; + + if ( !self.isHiddenControls ) { + self.showControls(); + } + + // Update info and navigation elements + $('[data-fancybox-count]').html( self.group.length ); + $('[data-fancybox-index]').html( index + 1 ); + + $('[data-fancybox-prev]').prop('disabled', ( !opts.loop && index <= 0 ) ); + $('[data-fancybox-next]').prop('disabled', ( !opts.loop && index >= self.group.length - 1 ) ); + + }, + + // Hide toolbar and caption + // ======================== + + hideControls : function () { + + this.isHiddenControls = true; + + this.$refs.container.removeClass('fancybox-show-infobar fancybox-show-toolbar fancybox-show-caption fancybox-show-nav'); + + }, + + showControls : function() { + + var self = this; + var opts = self.current ? self.current.opts : self.opts; + var $container = self.$refs.container; + + self.isHiddenControls = false; + self.idleSecondsCounter = 0; + + $container + .toggleClass('fancybox-show-toolbar', !!( opts.toolbar && opts.buttons ) ) + .toggleClass('fancybox-show-infobar', !!( opts.infobar && self.group.length > 1 ) ) + .toggleClass('fancybox-show-nav', !!( opts.arrows && self.group.length > 1 ) ) + .toggleClass('fancybox-is-modal', !!opts.modal ); + + if ( self.$caption ) { + $container.addClass( 'fancybox-show-caption '); + + } else { + $container.removeClass( 'fancybox-show-caption' ); + } + + }, + + + // Toggle toolbar and caption + // ========================== + + toggleControls : function() { + + if ( this.isHiddenControls ) { + this.showControls(); + + } else { + this.hideControls(); + } + + }, + + + }); + + + $.fancybox = { + + version : "3.1.24", + defaults : defaults, + + + // Get current instance and execute a command. + // + // Examples of usage: + // + // $instance = $.fancybox.getInstance(); + // $.fancybox.getInstance().jumpTo( 1 ); + // $.fancybox.getInstance( 'jumpTo', 1 ); + // $.fancybox.getInstance( function() { + // console.info( this.currIndex ); + // }); + // ====================================================== + + getInstance : function ( command ) { + var instance = $('.fancybox-container:not(".fancybox-is-closing"):first').data( 'FancyBox' ); + var args = Array.prototype.slice.call(arguments, 1); + + if ( instance instanceof FancyBox ) { + + if ( $.type( command ) === 'string' ) { + instance[ command ].apply( instance, args ); + + } else if ( $.type( command ) === 'function' ) { + command.apply( instance, args ); + + } + + return instance; + } + + return false; + + }, + + + // Create new instance + // =================== + + open : function ( items, opts, index ) { + return new FancyBox( items, opts, index ); + }, + + + // Close current or all instances + // ============================== + + close : function ( all ) { + var instance = this.getInstance(); + + if ( instance ) { + instance.close(); + + // Try to find and close next instance + + if ( all === true ) { + this.close(); + } + } + + }, + + // Close instances and unbind all events + // ============================== + + destroy : function() { + + this.close( true ); + + $D.off( 'click.fb-start' ); + + }, + + + // Try to detect mobile devices + // ============================ + + isMobile : document.createTouch !== undefined && /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent), + + + // Detect if 'translate3d' support is available + // ============================================ + + use3d : (function() { + var div = document.createElement('div'); + + return window.getComputedStyle && window.getComputedStyle( div ).getPropertyValue('transform') && !(document.documentMode && document.documentMode < 11); + }()), + + + // Helper function to get current visual state of an element + // returns array[ top, left, horizontal-scale, vertical-scale, opacity ] + // ===================================================================== + + getTranslate : function( $el ) { + var matrix; + + if ( !$el || !$el.length ) { + return false; + } + + matrix = $el.eq( 0 ).css('transform'); + + if ( matrix && matrix.indexOf( 'matrix' ) !== -1 ) { + matrix = matrix.split('(')[1]; + matrix = matrix.split(')')[0]; + matrix = matrix.split(','); + } else { + matrix = []; + } + + if ( matrix.length ) { + + // If IE + if ( matrix.length > 10 ) { + matrix = [ matrix[13], matrix[12], matrix[0], matrix[5] ]; + + } else { + matrix = [ matrix[5], matrix[4], matrix[0], matrix[3]]; + } + + matrix = matrix.map(parseFloat); + + } else { + matrix = [ 0, 0, 1, 1 ]; + + var transRegex = /\.*translate\((.*)px,(.*)px\)/i; + var transRez = transRegex.exec( $el.eq( 0 ).attr('style') ); + + if ( transRez ) { + matrix[ 0 ] = parseFloat( transRez[2] ); + matrix[ 1 ] = parseFloat( transRez[1] ); + } + } + + return { + top : matrix[ 0 ], + left : matrix[ 1 ], + scaleX : matrix[ 2 ], + scaleY : matrix[ 3 ], + opacity : parseFloat( $el.css('opacity') ), + width : $el.width(), + height : $el.height() + }; + + }, + + + // Shortcut for setting "translate3d" properties for element + // Can set be used to set opacity, too + // ======================================================== + + setTranslate : function( $el, props ) { + var str = ''; + var css = {}; + + if ( !$el || !props ) { + return; + } + + if ( props.left !== undefined || props.top !== undefined ) { + str = ( props.left === undefined ? $el.position().left : props.left ) + 'px, ' + ( props.top === undefined ? $el.position().top : props.top ) + 'px'; + + if ( this.use3d ) { + str = 'translate3d(' + str + ', 0px)'; + + } else { + str = 'translate(' + str + ')'; + } + } + + if ( props.scaleX !== undefined && props.scaleY !== undefined ) { + str = (str.length ? str + ' ' : '') + 'scale(' + props.scaleX + ', ' + props.scaleY + ')'; + } + + if ( str.length ) { + css.transform = str; + } + + if ( props.opacity !== undefined ) { + css.opacity = props.opacity; + } + + if ( props.width !== undefined ) { + css.width = props.width; + } + + if ( props.height !== undefined ) { + css.height = props.height; + } + + return $el.css( css ); + }, + + + // Simple CSS transition handler + // ============================= + + animate : function ( $el, to, duration, callback, leaveAnimationName ) { + var event = transitionEnd || 'transitionend'; + + if ( $.isFunction( duration ) ) { + callback = duration; + duration = null; + } + + if ( !$.isPlainObject( to ) ) { + $el.removeAttr('style'); + } + + $el.on( event, function(e) { + + // Skip events from child elements and z-index change + if ( e && e.originalEvent && ( !$el.is( e.originalEvent.target ) || e.originalEvent.propertyName == 'z-index' ) ) { + return; + } + + $el.off( event ); + + if ( $.isPlainObject( to ) ) { + + if ( to.scaleX !== undefined && to.scaleY !== undefined ) { + $el.css( 'transition-duration', '0ms' ); + + to.width = Math.round( $el.width() * to.scaleX ); + to.height = Math.round( $el.height() * to.scaleY ); + + to.scaleX = 1; + to.scaleY = 1; + + $.fancybox.setTranslate( $el, to ); + } + + } else if ( leaveAnimationName !== true ) { + $el.removeClass( to ); + } + + if ( $.isFunction( callback ) ) { + callback( e ); + } + + }); + + if ( $.isNumeric( duration ) ) { + $el.css( 'transition-duration', duration + 'ms' ); + } + + if ( $.isPlainObject( to ) ) { + $.fancybox.setTranslate( $el, to ); + + } else { + $el.addClass( to ); + } + + $el.data("timer", setTimeout(function() { + $el.trigger( 'transitionend' ); + }, duration + 16)); + + }, + + stop : function( $el ) { + clearTimeout( $el.data("timer") ); + + $el.off( transitionEnd ); + } + + }; + + + // Default click handler for "fancyboxed" links + // ============================================ + + function _run( e ) { + var target = e.currentTarget, + opts = e.data ? e.data.options : {}, + items = opts.selector ? $( opts.selector ) : ( e.data ? e.data.items : [] ), + value = $(target).attr( 'data-fancybox' ) || '', + index = 0, + active = $.fancybox.getInstance(); + + e.preventDefault(); + e.stopPropagation(); + + // Avoid opening multiple times + if ( active && active.current.opts.$orig.is( target ) ) { + return; + } + + // Get all related items and find index for clicked one + if ( value ) { + items = items.length ? items.filter( '[data-fancybox="' + value + '"]' ) : $( '[data-fancybox="' + value + '"]' ); + index = items.index( target ); + + // Sometimes current item can not be found + // (for example, when slider clones items) + if ( index < 0 ) { + index = 0; + } + + } else { + items = [ target ]; + } + + $.fancybox.open( items, opts, index ); + } + + + // Create a jQuery plugin + // ====================== + + $.fn.fancybox = function (options) { + var selector; + + options = options || {}; + selector = options.selector || false; + + if ( selector ) { + + $( 'body' ).off( 'click.fb-start', selector ).on( 'click.fb-start', selector, { + options : options + }, _run ); + + } else { + + this.off( 'click.fb-start' ).on( 'click.fb-start', { + items : this, + options : options + }, _run); + + } + + return this; + }; + + + // Self initializing plugin + // ======================== + + $D.on( 'click.fb-start', '[data-fancybox]', _run ); + +}( window, document, window.jQuery )); + +// ========================================================================== +// +// Media +// Adds additional media type support +// +// ========================================================================== +;(function ($) { + + 'use strict'; + + // Formats matching url to final form + + var format = function (url, rez, params) { + if ( !url ) { + return; + } + + params = params || ''; + + if ( $.type(params) === "object" ) { + params = $.param(params, true); + } + + $.each(rez, function (key, value) { + url = url.replace('$' + key, value || ''); + }); + + if (params.length) { + url += (url.indexOf('?') > 0 ? '&' : '?') + params; + } + + return url; + }; + + // Object containing properties for each media type + + var defaults = { + youtube : { + matcher : /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i, + params : { + autoplay : 1, + autohide : 1, + fs : 1, + rel : 0, + hd : 1, + wmode : 'transparent', + enablejsapi : 1, + html5 : 1 + }, + paramPlace : 8, + type : 'iframe', + url : '//www.youtube.com/embed/$4', + thumb : '//img.youtube.com/vi/$4/hqdefault.jpg' + }, + + vimeo : { + matcher : /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/, + params : { + autoplay : 1, + hd : 1, + show_title : 1, + show_byline : 1, + show_portrait : 0, + fullscreen : 1, + api : 1 + }, + paramPlace : 3, + type : 'iframe', + url : '//player.vimeo.com/video/$2' + }, + + metacafe : { + matcher : /metacafe.com\/watch\/(\d+)\/(.*)?/, + type : 'iframe', + url : '//www.metacafe.com/embed/$1/?ap=1' + }, + + dailymotion : { + matcher : /dailymotion.com\/video\/(.*)\/?(.*)/, + params : { + additionalInfos : 0, + autoStart : 1 + }, + type : 'iframe', + url : '//www.dailymotion.com/embed/video/$1' + }, + + vine : { + matcher : /vine.co\/v\/([a-zA-Z0-9\?\=\-]+)/, + type : 'iframe', + url : '//vine.co/v/$1/embed/simple' + }, + + instagram : { + matcher : /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i, + type : 'image', + url : '//$1/p/$2/media/?size=l' + }, + + // Examples: + // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16 + // https://www.google.com/maps/@37.7852006,-122.4146355,14.65z + // https://www.google.com/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572 + gmap_place : { + matcher : /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i, + type : 'iframe', + url : function (rez) { + return '//maps.google.' + rez[2] + '/?ll=' + ( rez[9] ? rez[9] + '&z=' + Math.floor( rez[10] ) + ( rez[12] ? rez[12].replace(/^\//, "&") : '' ) : rez[12] ) + '&output=' + ( rez[12] && rez[12].indexOf('layer=c') > 0 ? 'svembed' : 'embed' ); + } + }, + + // Examples: + // https://www.google.com/maps/search/Empire+State+Building/ + // https://www.google.com/maps/search/?api=1&query=centurylink+field + // https://www.google.com/maps/search/?api=1&query=47.5951518,-122.3316393 + gmap_search : { + matcher : /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(maps\/search\/)(.*)/i, + type : 'iframe', + url : function (rez) { + return '//maps.google.' + rez[2] + '/maps?q=' + rez[5].replace('query=', 'q=').replace('api=1', '') + '&output=embed'; + } + } + }; + + $(document).on('onInit.fb', function (e, instance) { + + $.each(instance.group, function( i, item ) { + + var url = item.src || '', + type = false, + media, + thumb, + rez, + params, + urlParams, + o, + provider; + + // Skip items that already have content type + if ( item.type ) { + return; + } + + media = $.extend( true, {}, defaults, item.opts.media ); + + // Look for any matching media type + $.each(media, function ( n, el ) { + rez = url.match(el.matcher); + o = {}; + provider = n; + + if (!rez) { + return; + } + + type = el.type; + + if ( el.paramPlace && rez[ el.paramPlace ] ) { + urlParams = rez[ el.paramPlace ]; + + if ( urlParams[ 0 ] == '?' ) { + urlParams = urlParams.substring(1); + } + + urlParams = urlParams.split('&'); + + for ( var m = 0; m < urlParams.length; ++m ) { + var p = urlParams[ m ].split('=', 2); + + if ( p.length == 2 ) { + o[ p[0] ] = decodeURIComponent( p[1].replace(/\+/g, " ") ); + } + } + } + + params = $.extend( true, {}, el.params, item.opts[ n ], o ); + + url = $.type(el.url) === "function" ? el.url.call(this, rez, params, item) : format(el.url, rez, params); + thumb = $.type(el.thumb) === "function" ? el.thumb.call(this, rez, params, item) : format(el.thumb, rez); + + if ( provider === 'vimeo' ) { + url = url.replace('&%23', '#'); + } + + return false; + }); + + // If it is found, then change content type and update the url + + if ( type ) { + item.src = url; + item.type = type; + + if ( !item.opts.thumb && !( item.opts.$thumb && item.opts.$thumb.length ) ) { + item.opts.thumb = thumb; + } + + if ( type === 'iframe' ) { + $.extend(true, item.opts, { + iframe : { + preload : false, + attr : { + scrolling : "no" + } + } + }); + + item.contentProvider = provider; + + item.opts.slideClass += ' fancybox-slide--' + ( provider == 'gmap_place' || provider == 'gmap_search' ? 'map' : 'video' ); + } + + } else { + + // If no content type is found, then set it to `image` as fallback + item.type = 'image'; + } + + }); + + }); + +}(window.jQuery)); + +// ========================================================================== +// +// Guestures +// Adds touch guestures, handles click and tap events +// +// ========================================================================== +;(function (window, document, $) { + 'use strict'; + + var requestAFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + // if all else fails, use setTimeout + function (callback) { + return window.setTimeout(callback, 1000 / 60); + }; + })(); + + + var cancelAFrame = (function () { + return window.cancelAnimationFrame || + window.webkitCancelAnimationFrame || + window.mozCancelAnimationFrame || + window.oCancelAnimationFrame || + function (id) { + window.clearTimeout(id); + }; + })(); + + + var pointers = function( e ) { + var result = []; + + e = e.originalEvent || e || window.e; + e = e.touches && e.touches.length ? e.touches : ( e.changedTouches && e.changedTouches.length ? e.changedTouches : [ e ] ); + + for ( var key in e ) { + + if ( e[ key ].pageX ) { + result.push( { x : e[ key ].pageX, y : e[ key ].pageY } ); + + } else if ( e[ key ].clientX ) { + result.push( { x : e[ key ].clientX, y : e[ key ].clientY } ); + } + } + + return result; + }; + + var distance = function( point2, point1, what ) { + if ( !point1 || !point2 ) { + return 0; + } + + if ( what === 'x' ) { + return point2.x - point1.x; + + } else if ( what === 'y' ) { + return point2.y - point1.y; + } + + return Math.sqrt( Math.pow( point2.x - point1.x, 2 ) + Math.pow( point2.y - point1.y, 2 ) ); + }; + + var isClickable = function( $el ) { + if ( $el.is('a,button,input,select,textarea') || $.isFunction( $el.get(0).onclick ) ) { + return true; + } + + // Check for attributes like data-fancybox-next or data-fancybox-close + for ( var i = 0, atts = $el[0].attributes, n = atts.length; i < n; i++ ) { + if ( atts[i].nodeName.substr(0, 14) === 'data-fancybox-' ) { + return true; + } + } + + return false; + }; + + var hasScrollbars = function( el ) { + var overflowY = window.getComputedStyle( el )['overflow-y']; + var overflowX = window.getComputedStyle( el )['overflow-x']; + + var vertical = (overflowY === 'scroll' || overflowY === 'auto') && el.scrollHeight > el.clientHeight; + var horizontal = (overflowX === 'scroll' || overflowX === 'auto') && el.scrollWidth > el.clientWidth; + + return vertical || horizontal; + }; + + var isScrollable = function ( $el ) { + var rez = false; + + while ( true ) { + rez = hasScrollbars( $el.get(0) ); + + if ( rez ) { + break; + } + + $el = $el.parent(); + + if ( !$el.length || $el.hasClass( 'fancybox-stage' ) || $el.is( 'body' ) ) { + break; + } + } + + return rez; + }; + + + var Guestures = function ( instance ) { + var self = this; + + self.instance = instance; + + self.$bg = instance.$refs.bg; + self.$stage = instance.$refs.stage; + self.$container = instance.$refs.container; + + self.destroy(); + + self.$container.on( 'touchstart.fb.touch mousedown.fb.touch', $.proxy(self, 'ontouchstart') ); + }; + + Guestures.prototype.destroy = function() { + this.$container.off( '.fb.touch' ); + }; + + Guestures.prototype.ontouchstart = function( e ) { + var self = this; + + var $target = $( e.target ); + var instance = self.instance; + var current = instance.current; + var $content = current.$content; + + var isTouchDevice = ( e.type == 'touchstart' ); + + // Do not respond to both events + if ( isTouchDevice ) { + self.$container.off( 'mousedown.fb.touch' ); + } + + // Ignore clicks while zooming or closing + if ( !current || self.instance.isAnimating || self.instance.isClosing ) { + e.stopPropagation(); + e.preventDefault(); + + return; + } + + // Ignore right click + if ( e.originalEvent && e.originalEvent.button == 2 ) { + return; + } + + // Ignore taping on links, buttons, input elements + if ( !$target.length || isClickable( $target ) || isClickable( $target.parent() ) ) { + return; + } + + // Ignore clicks on the scrollbar + if ( e.originalEvent.clientX > $target[0].clientWidth + $target.offset().left ) { + return; + } + + self.startPoints = pointers( e ); + + // Prevent zooming if already swiping + if ( !self.startPoints || ( self.startPoints.length > 1 && instance.isSliding ) ) { + return; + } + + self.$target = $target; + self.$content = $content; + self.canTap = true; + + $(document).off( '.fb.touch' ); + + $(document).on( isTouchDevice ? 'touchend.fb.touch touchcancel.fb.touch' : 'mouseup.fb.touch mouseleave.fb.touch', $.proxy(self, "ontouchend")); + $(document).on( isTouchDevice ? 'touchmove.fb.touch' : 'mousemove.fb.touch', $.proxy(self, "ontouchmove")); + + e.stopPropagation(); + + if ( !(instance.current.opts.touch || instance.canPan() ) || !( $target.is( self.$stage ) || self.$stage.find( $target ).length ) ) { + + // Prevent ghosting + if ( $target.is('img') ) { + e.preventDefault(); + } + + return; + } + + if ( !( $.fancybox.isMobile && ( isScrollable( self.$target ) || isScrollable( self.$target.parent() ) ) ) ) { + e.preventDefault(); + } + + self.canvasWidth = Math.round( current.$slide[0].clientWidth ); + self.canvasHeight = Math.round( current.$slide[0].clientHeight ); + + self.startTime = new Date().getTime(); + self.distanceX = self.distanceY = self.distance = 0; + + self.isPanning = false; + self.isSwiping = false; + self.isZooming = false; + + self.sliderStartPos = self.sliderLastPos || { top: 0, left: 0 }; + self.contentStartPos = $.fancybox.getTranslate( self.$content ); + self.contentLastPos = null; + + if ( self.startPoints.length === 1 && !self.isZooming ) { + self.canTap = !instance.isSliding; + + if ( current.type === 'image' && ( self.contentStartPos.width > self.canvasWidth + 1 || self.contentStartPos.height > self.canvasHeight + 1 ) ) { + + $.fancybox.stop( self.$content ); + + self.$content.css( 'transition-duration', '0ms' ); + + self.isPanning = true; + + } else { + + self.isSwiping = true; + } + + self.$container.addClass('fancybox-controls--isGrabbing'); + } + + if ( self.startPoints.length === 2 && !instance.isAnimating && !current.hasError && current.type === 'image' && ( current.isLoaded || current.$ghost ) ) { + self.isZooming = true; + + self.isSwiping = false; + self.isPanning = false; + + $.fancybox.stop( self.$content ); + + self.$content.css( 'transition-duration', '0ms' ); + + self.centerPointStartX = ( ( self.startPoints[0].x + self.startPoints[1].x ) * 0.5 ) - $(window).scrollLeft(); + self.centerPointStartY = ( ( self.startPoints[0].y + self.startPoints[1].y ) * 0.5 ) - $(window).scrollTop(); + + self.percentageOfImageAtPinchPointX = ( self.centerPointStartX - self.contentStartPos.left ) / self.contentStartPos.width; + self.percentageOfImageAtPinchPointY = ( self.centerPointStartY - self.contentStartPos.top ) / self.contentStartPos.height; + + self.startDistanceBetweenFingers = distance( self.startPoints[0], self.startPoints[1] ); + } + + }; + + Guestures.prototype.ontouchmove = function( e ) { + + var self = this; + + self.newPoints = pointers( e ); + + if ( $.fancybox.isMobile && ( isScrollable( self.$target ) || isScrollable( self.$target.parent() ) ) ) { + e.stopPropagation(); + + self.canTap = false; + + return; + } + + if ( !( self.instance.current.opts.touch || self.instance.canPan() ) || !self.newPoints || !self.newPoints.length ) { + return; + } + + self.distanceX = distance( self.newPoints[0], self.startPoints[0], 'x' ); + self.distanceY = distance( self.newPoints[0], self.startPoints[0], 'y' ); + + self.distance = distance( self.newPoints[0], self.startPoints[0] ); + + // Skip false ontouchmove events (Chrome) + if ( self.distance > 0 ) { + + if ( !( self.$target.is( self.$stage ) || self.$stage.find( self.$target ).length ) ) { + return; + } + + e.stopPropagation(); + e.preventDefault(); + + if ( self.isSwiping ) { + self.onSwipe(); + + } else if ( self.isPanning ) { + self.onPan(); + + } else if ( self.isZooming ) { + self.onZoom(); + } + + } + + }; + + Guestures.prototype.onSwipe = function() { + + var self = this; + + var swiping = self.isSwiping; + var left = self.sliderStartPos.left || 0; + var angle; + + if ( swiping === true ) { + + if ( Math.abs( self.distance ) > 10 ) { + + self.canTap = false; + + if ( self.instance.group.length < 2 && self.instance.opts.touch.vertical ) { + self.isSwiping = 'y'; + + } else if ( self.instance.isSliding || self.instance.opts.touch.vertical === false || ( self.instance.opts.touch.vertical === 'auto' && $( window ).width() > 800 ) ) { + self.isSwiping = 'x'; + + } else { + angle = Math.abs( Math.atan2( self.distanceY, self.distanceX ) * 180 / Math.PI ); + + self.isSwiping = ( angle > 45 && angle < 135 ) ? 'y' : 'x'; + } + + self.instance.isSliding = self.isSwiping; + + // Reset points to avoid jumping, because we dropped first swipes to calculate the angle + self.startPoints = self.newPoints; + + $.each(self.instance.slides, function( index, slide ) { + $.fancybox.stop( slide.$slide ); + + slide.$slide.css( 'transition-duration', '0ms' ); + + slide.inTransition = false; + + if ( slide.pos === self.instance.current.pos ) { + self.sliderStartPos.left = $.fancybox.getTranslate( slide.$slide ).left; + } + }); + + //self.instance.current.isMoved = true; + + // Stop slideshow + if ( self.instance.SlideShow && self.instance.SlideShow.isActive ) { + self.instance.SlideShow.stop(); + } + } + + } else { + + if ( swiping == 'x' ) { + + // Sticky edges + if ( self.distanceX > 0 && ( self.instance.group.length < 2 || ( self.instance.current.index === 0 && !self.instance.current.opts.loop ) ) ) { + left = left + Math.pow( self.distanceX, 0.8 ); + + } else if ( self.distanceX < 0 && ( self.instance.group.length < 2 || ( self.instance.current.index === self.instance.group.length - 1 && !self.instance.current.opts.loop ) ) ) { + left = left - Math.pow( -self.distanceX, 0.8 ); + + } else { + left = left + self.distanceX; + } + + } + + self.sliderLastPos = { + top : swiping == 'x' ? 0 : self.sliderStartPos.top + self.distanceY, + left : left + }; + + if ( self.requestId ) { + cancelAFrame( self.requestId ); + + self.requestId = null; + } + + self.requestId = requestAFrame(function() { + + if ( self.sliderLastPos ) { + $.each(self.instance.slides, function( index, slide ) { + var pos = slide.pos - self.instance.currPos; + + $.fancybox.setTranslate( slide.$slide, { + top : self.sliderLastPos.top, + left : self.sliderLastPos.left + ( pos * self.canvasWidth ) + ( pos * slide.opts.gutter ) + }); + }); + + self.$container.addClass( 'fancybox-is-sliding' ); + } + + }); + + } + + }; + + Guestures.prototype.onPan = function() { + + var self = this; + + var newOffsetX, newOffsetY, newPos; + + self.canTap = false; + + if ( self.contentStartPos.width > self.canvasWidth ) { + newOffsetX = self.contentStartPos.left + self.distanceX; + + } else { + newOffsetX = self.contentStartPos.left; + } + + newOffsetY = self.contentStartPos.top + self.distanceY; + + newPos = self.limitMovement( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height ); + + newPos.scaleX = self.contentStartPos.scaleX; + newPos.scaleY = self.contentStartPos.scaleY; + + self.contentLastPos = newPos; + + if ( self.requestId ) { + cancelAFrame( self.requestId ); + + self.requestId = null; + } + + self.requestId = requestAFrame(function() { + $.fancybox.setTranslate( self.$content, self.contentLastPos ); + }); + }; + + // Make panning sticky to the edges + Guestures.prototype.limitMovement = function( newOffsetX, newOffsetY, newWidth, newHeight ) { + + var self = this; + + var minTranslateX, minTranslateY, maxTranslateX, maxTranslateY; + + var canvasWidth = self.canvasWidth; + var canvasHeight = self.canvasHeight; + + var currentOffsetX = self.contentStartPos.left; + var currentOffsetY = self.contentStartPos.top; + + var distanceX = self.distanceX; + var distanceY = self.distanceY; + + // Slow down proportionally to traveled distance + + minTranslateX = Math.max(0, canvasWidth * 0.5 - newWidth * 0.5 ); + minTranslateY = Math.max(0, canvasHeight * 0.5 - newHeight * 0.5 ); + + maxTranslateX = Math.min( canvasWidth - newWidth, canvasWidth * 0.5 - newWidth * 0.5 ); + maxTranslateY = Math.min( canvasHeight - newHeight, canvasHeight * 0.5 - newHeight * 0.5 ); + + if ( newWidth > canvasWidth ) { + + // -> + if ( distanceX > 0 && newOffsetX > minTranslateX ) { + newOffsetX = minTranslateX - 1 + Math.pow( -minTranslateX + currentOffsetX + distanceX, 0.8 ) || 0; + } + + // <- + if ( distanceX < 0 && newOffsetX < maxTranslateX ) { + newOffsetX = maxTranslateX + 1 - Math.pow( maxTranslateX - currentOffsetX - distanceX, 0.8 ) || 0; + } + + } + + if ( newHeight > canvasHeight ) { + + // \/ + if ( distanceY > 0 && newOffsetY > minTranslateY ) { + newOffsetY = minTranslateY - 1 + Math.pow(-minTranslateY + currentOffsetY + distanceY, 0.8 ) || 0; + } + + // /\ + if ( distanceY < 0 && newOffsetY < maxTranslateY ) { + newOffsetY = maxTranslateY + 1 - Math.pow ( maxTranslateY - currentOffsetY - distanceY, 0.8 ) || 0; + } + + } + + return { + top : newOffsetY, + left : newOffsetX + }; + + }; + + + Guestures.prototype.limitPosition = function( newOffsetX, newOffsetY, newWidth, newHeight ) { + + var self = this; + + var canvasWidth = self.canvasWidth; + var canvasHeight = self.canvasHeight; + + if ( newWidth > canvasWidth ) { + newOffsetX = newOffsetX > 0 ? 0 : newOffsetX; + newOffsetX = newOffsetX < canvasWidth - newWidth ? canvasWidth - newWidth : newOffsetX; + + } else { + + // Center horizontally + newOffsetX = Math.max( 0, canvasWidth / 2 - newWidth / 2 ); + + } + + if ( newHeight > canvasHeight ) { + newOffsetY = newOffsetY > 0 ? 0 : newOffsetY; + newOffsetY = newOffsetY < canvasHeight - newHeight ? canvasHeight - newHeight : newOffsetY; + + } else { + + // Center vertically + newOffsetY = Math.max( 0, canvasHeight / 2 - newHeight / 2 ); + + } + + return { + top : newOffsetY, + left : newOffsetX + }; + + }; + + Guestures.prototype.onZoom = function() { + + var self = this; + + // Calculate current distance between points to get pinch ratio and new width and height + + var currentWidth = self.contentStartPos.width; + var currentHeight = self.contentStartPos.height; + + var currentOffsetX = self.contentStartPos.left; + var currentOffsetY = self.contentStartPos.top; + + var endDistanceBetweenFingers = distance( self.newPoints[0], self.newPoints[1] ); + + var pinchRatio = endDistanceBetweenFingers / self.startDistanceBetweenFingers; + + var newWidth = Math.floor( currentWidth * pinchRatio ); + var newHeight = Math.floor( currentHeight * pinchRatio ); + + // This is the translation due to pinch-zooming + var translateFromZoomingX = (currentWidth - newWidth) * self.percentageOfImageAtPinchPointX; + var translateFromZoomingY = (currentHeight - newHeight) * self.percentageOfImageAtPinchPointY; + + //Point between the two touches + + var centerPointEndX = ((self.newPoints[0].x + self.newPoints[1].x) / 2) - $(window).scrollLeft(); + var centerPointEndY = ((self.newPoints[0].y + self.newPoints[1].y) / 2) - $(window).scrollTop(); + + // And this is the translation due to translation of the centerpoint + // between the two fingers + + var translateFromTranslatingX = centerPointEndX - self.centerPointStartX; + var translateFromTranslatingY = centerPointEndY - self.centerPointStartY; + + // The new offset is the old/current one plus the total translation + + var newOffsetX = currentOffsetX + ( translateFromZoomingX + translateFromTranslatingX ); + var newOffsetY = currentOffsetY + ( translateFromZoomingY + translateFromTranslatingY ); + + var newPos = { + top : newOffsetY, + left : newOffsetX, + scaleX : self.contentStartPos.scaleX * pinchRatio, + scaleY : self.contentStartPos.scaleY * pinchRatio + }; + + self.canTap = false; + + self.newWidth = newWidth; + self.newHeight = newHeight; + + self.contentLastPos = newPos; + + if ( self.requestId ) { + cancelAFrame( self.requestId ); + + self.requestId = null; + } + + self.requestId = requestAFrame(function() { + $.fancybox.setTranslate( self.$content, self.contentLastPos ); + }); + + }; + + Guestures.prototype.ontouchend = function( e ) { + + var self = this; + var dMs = Math.max( (new Date().getTime() ) - self.startTime, 1); + + var swiping = self.isSwiping; + var panning = self.isPanning; + var zooming = self.isZooming; + + self.endPoints = pointers( e ); + + self.$container.removeClass( 'fancybox-controls--isGrabbing' ); + + $(document).off( '.fb.touch' ); + + if ( self.requestId ) { + cancelAFrame( self.requestId ); + + self.requestId = null; + } + + self.isSwiping = false; + self.isPanning = false; + self.isZooming = false; + + if ( self.canTap ) { + return self.onTap( e ); + } + + self.speed = 366; + + // Speed in px/ms + self.velocityX = self.distanceX / dMs * 0.5; + self.velocityY = self.distanceY / dMs * 0.5; + + self.speedX = Math.max( self.speed * 0.5, Math.min( self.speed * 1.5, ( 1 / Math.abs( self.velocityX ) ) * self.speed ) ); + + if ( panning ) { + self.endPanning(); + + } else if ( zooming ) { + self.endZooming(); + + } else { + self.endSwiping( swiping ); + } + + return; + }; + + Guestures.prototype.endSwiping = function( swiping ) { + + var self = this; + var ret = false; + + self.instance.isSliding = false; + self.sliderLastPos = null; + + // Close if swiped vertically / navigate if horizontally + if ( swiping == 'y' && Math.abs( self.distanceY ) > 50 ) { + + // Continue vertical movement + $.fancybox.animate( self.instance.current.$slide, { + top : self.sliderStartPos.top + self.distanceY + ( self.velocityY * 150 ), + opacity : 0 + }, 150 ); + + ret = self.instance.close( true, 300 ); + + } else if ( swiping == 'x' && self.distanceX > 50 && self.instance.group.length > 1 ) { + ret = self.instance.previous( self.speedX ); + + } else if ( swiping == 'x' && self.distanceX < -50 && self.instance.group.length > 1 ) { + ret = self.instance.next( self.speedX ); + } + + if ( ret === false && ( swiping == 'x' || swiping == 'y' ) ) { + self.instance.jumpTo( self.instance.current.index, 150 ); + } + + self.$container.removeClass( 'fancybox-is-sliding' ); + + }; + + // Limit panning from edges + // ======================== + + Guestures.prototype.endPanning = function() { + + var self = this; + var newOffsetX, newOffsetY, newPos; + + if ( !self.contentLastPos ) { + return; + } + + if ( self.instance.current.opts.touch.momentum === false ) { + newOffsetX = self.contentLastPos.left; + newOffsetY = self.contentLastPos.top; + + } else { + + // Continue movement + newOffsetX = self.contentLastPos.left + ( self.velocityX * self.speed ); + newOffsetY = self.contentLastPos.top + ( self.velocityY * self.speed ); + } + + newPos = self.limitPosition( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height ); + + newPos.width = self.contentStartPos.width; + newPos.height = self.contentStartPos.height; + + $.fancybox.animate( self.$content, newPos, 330 ); + }; + + + Guestures.prototype.endZooming = function() { + + var self = this; + + var current = self.instance.current; + + var newOffsetX, newOffsetY, newPos, reset; + + var newWidth = self.newWidth; + var newHeight = self.newHeight; + + if ( !self.contentLastPos ) { + return; + } + + newOffsetX = self.contentLastPos.left; + newOffsetY = self.contentLastPos.top; + + reset = { + top : newOffsetY, + left : newOffsetX, + width : newWidth, + height : newHeight, + scaleX : 1, + scaleY : 1 + }; + + // Reset scalex/scaleY values; this helps for perfomance and does not break animation + $.fancybox.setTranslate( self.$content, reset ); + + if ( newWidth < self.canvasWidth && newHeight < self.canvasHeight ) { + self.instance.scaleToFit( 150 ); + + } else if ( newWidth > current.width || newHeight > current.height ) { + self.instance.scaleToActual( self.centerPointStartX, self.centerPointStartY, 150 ); + + } else { + + newPos = self.limitPosition( newOffsetX, newOffsetY, newWidth, newHeight ); + + // Switch from scale() to width/height or animation will not work correctly + $.fancybox.setTranslate( self.content, $.fancybox.getTranslate( self.$content ) ); + + $.fancybox.animate( self.$content, newPos, 150 ); + } + + }; + + Guestures.prototype.onTap = function(e) { + var self = this; + var $target = $( e.target ); + + var instance = self.instance; + var current = instance.current; + + var endPoints = ( e && pointers( e ) ) || self.startPoints; + + var tapX = endPoints[0] ? endPoints[0].x - self.$stage.offset().left : 0; + var tapY = endPoints[0] ? endPoints[0].y - self.$stage.offset().top : 0; + + var where; + + var process = function ( prefix ) { + + var action = current.opts[ prefix ]; + + if ( $.isFunction( action ) ) { + action = action.apply( instance, [ current, e ] ); + } + + if ( !action) { + return; + } + + switch ( action ) { + + case "close" : + + instance.close( self.startEvent ); + + break; + + case "toggleControls" : + + instance.toggleControls( true ); + + break; + + case "next" : + + instance.next(); + + break; + + case "nextOrClose" : + + if ( instance.group.length > 1 ) { + instance.next(); + + } else { + instance.close( self.startEvent ); + } + + break; + + case "zoom" : + + if ( current.type == 'image' && ( current.isLoaded || current.$ghost ) ) { + + if ( instance.canPan() ) { + instance.scaleToFit(); + + } else if ( instance.isScaledDown() ) { + instance.scaleToActual( tapX, tapY ); + + } else if ( instance.group.length < 2 ) { + instance.close( self.startEvent ); + } + } + + break; + } + + }; + + // Ignore right click + if ( e.originalEvent && e.originalEvent.button == 2 ) { + return; + } + + // Skip if current slide is not in the center + if ( instance.isSliding ) { + return; + } + + // Skip if clicked on the scrollbar + if ( tapX > $target[0].clientWidth + $target.offset().left ) { + return; + } + + // Check where is clicked + if ( $target.is( '.fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-container' ) ) { + where = 'Outside'; + + } else if ( $target.is( '.fancybox-slide' ) ) { + where = 'Slide'; + + } else if ( instance.current.$content && instance.current.$content.has( e.target ).length ) { + where = 'Content'; + + } else { + return; + } + + // Check if this is a double tap + if ( self.tapped ) { + + // Stop previously created single tap + clearTimeout( self.tapped ); + self.tapped = null; + + // Skip if distance between taps is too big + if ( Math.abs( tapX - self.tapX ) > 50 || Math.abs( tapY - self.tapY ) > 50 || instance.isSliding ) { + return this; + } + + // OK, now we assume that this is a double-tap + process( 'dblclick' + where ); + + } else { + + // Single tap will be processed if user has not clicked second time within 300ms + // or there is no need to wait for double-tap + self.tapX = tapX; + self.tapY = tapY; + + if ( current.opts[ 'dblclick' + where ] && current.opts[ 'dblclick' + where ] !== current.opts[ 'click' + where ] ) { + self.tapped = setTimeout(function() { + self.tapped = null; + + process( 'click' + where ); + + }, 300); + + } else { + process( 'click' + where ); + } + + } + + return this; + }; + + $(document).on('onActivate.fb', function (e, instance) { + if ( instance && !instance.Guestures ) { + instance.Guestures = new Guestures( instance ); + } + }); + + $(document).on('beforeClose.fb', function (e, instance) { + if ( instance && instance.Guestures ) { + instance.Guestures.destroy(); + } + }); + + +}(window, document, window.jQuery)); + +// ========================================================================== +// +// SlideShow +// Enables slideshow functionality +// +// Example of usage: +// $.fancybox.getInstance().SlideShow.start() +// +// ========================================================================== +;(function (document, $) { + 'use strict'; + + var SlideShow = function( instance ) { + this.instance = instance; + this.init(); + }; + + $.extend( SlideShow.prototype, { + timer : null, + isActive : false, + $button : null, + speed : 3000, + + init : function() { + var self = this; + + self.$button = self.instance.$refs.toolbar.find('[data-fancybox-play]').on('click', function() { + self.toggle(); + }); + + if ( self.instance.group.length < 2 || !self.instance.group[ self.instance.currIndex ].opts.slideShow ) { + self.$button.hide(); + } + }, + + set : function() { + var self = this; + + // Check if reached last element + if ( self.instance && self.instance.current && (self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1 )) { + self.timer = setTimeout(function() { + self.instance.next(); + + }, self.instance.current.opts.slideShow.speed || self.speed); + + } else { + self.stop(); + self.instance.idleSecondsCounter = 0; + self.instance.showControls(); + } + + }, + + clear : function() { + var self = this; + + clearTimeout( self.timer ); + + self.timer = null; + }, + + start : function() { + var self = this; + var current = self.instance.current; + + if ( self.instance && current && ( current.opts.loop || current.index < self.instance.group.length - 1 )) { + + self.isActive = true; + + self.$button + .attr( 'title', current.opts.i18n[ current.opts.lang ].PLAY_STOP ) + .addClass( 'fancybox-button--pause' ); + + if ( current.isComplete ) { + self.set(); + } + } + }, + + stop : function() { + var self = this; + var current = self.instance.current; + + self.clear(); + + self.$button + .attr( 'title', current.opts.i18n[ current.opts.lang ].PLAY_START ) + .removeClass( 'fancybox-button--pause' ); + + self.isActive = false; + }, + + toggle : function() { + var self = this; + + if ( self.isActive ) { + self.stop(); + + } else { + self.start(); + } + } + + }); + + $(document).on({ + 'onInit.fb' : function(e, instance) { + if ( instance && !instance.SlideShow ) { + instance.SlideShow = new SlideShow( instance ); + } + }, + + 'beforeShow.fb' : function(e, instance, current, firstRun) { + var SlideShow = instance && instance.SlideShow; + + if ( firstRun ) { + + if ( SlideShow && current.opts.slideShow.autoStart ) { + SlideShow.start(); + } + + } else if ( SlideShow && SlideShow.isActive ) { + SlideShow.clear(); + } + }, + + 'afterShow.fb' : function(e, instance, current) { + var SlideShow = instance && instance.SlideShow; + + if ( SlideShow && SlideShow.isActive ) { + SlideShow.set(); + } + }, + + 'afterKeydown.fb' : function(e, instance, current, keypress, keycode) { + var SlideShow = instance && instance.SlideShow; + + // "P" or Spacebar + if ( SlideShow && current.opts.slideShow && ( keycode === 80 || keycode === 32 ) && !$(document.activeElement).is( 'button,a,input' ) ) { + keypress.preventDefault(); + + SlideShow.toggle(); + } + }, + + 'beforeClose.fb onDeactivate.fb' : function(e, instance) { + var SlideShow = instance && instance.SlideShow; + + if ( SlideShow ) { + SlideShow.stop(); + } + } + }); + + // Page Visibility API to pause slideshow when window is not active + $(document).on("visibilitychange", function() { + var instance = $.fancybox.getInstance(); + var SlideShow = instance && instance.SlideShow; + + if ( SlideShow && SlideShow.isActive ) { + if ( document.hidden ) { + SlideShow.clear(); + + } else { + SlideShow.set(); + } + } + }); + +}(document, window.jQuery)); + +// ========================================================================== +// +// FullScreen +// Adds fullscreen functionality +// +// ========================================================================== +;(function (document, $) { + 'use strict'; + + // Collection of methods supported by user browser + var fn = (function () { + + var fnMap = [ + [ + 'requestFullscreen', + 'exitFullscreen', + 'fullscreenElement', + 'fullscreenEnabled', + 'fullscreenchange', + 'fullscreenerror' + ], + // new WebKit + [ + 'webkitRequestFullscreen', + 'webkitExitFullscreen', + 'webkitFullscreenElement', + 'webkitFullscreenEnabled', + 'webkitfullscreenchange', + 'webkitfullscreenerror' + + ], + // old WebKit (Safari 5.1) + [ + 'webkitRequestFullScreen', + 'webkitCancelFullScreen', + 'webkitCurrentFullScreenElement', + 'webkitCancelFullScreen', + 'webkitfullscreenchange', + 'webkitfullscreenerror' + + ], + [ + 'mozRequestFullScreen', + 'mozCancelFullScreen', + 'mozFullScreenElement', + 'mozFullScreenEnabled', + 'mozfullscreenchange', + 'mozfullscreenerror' + ], + [ + 'msRequestFullscreen', + 'msExitFullscreen', + 'msFullscreenElement', + 'msFullscreenEnabled', + 'MSFullscreenChange', + 'MSFullscreenError' + ] + ]; + + var val; + var ret = {}; + var i, j; + + for ( i = 0; i < fnMap.length; i++ ) { + val = fnMap[ i ]; + + if ( val && val[ 1 ] in document ) { + for ( j = 0; j < val.length; j++ ) { + ret[ fnMap[ 0 ][ j ] ] = val[ j ]; + } + + return ret; + } + } + + return false; + })(); + + // If browser does not have Full Screen API, then simply unset default button template and stop + if ( !fn ) { + $.fancybox.defaults.btnTpl.fullScreen = false; + + return; + } + + var FullScreen = { + request : function ( elem ) { + + elem = elem || document.documentElement; + + elem[ fn.requestFullscreen ]( elem.ALLOW_KEYBOARD_INPUT ); + + }, + exit : function () { + + document[ fn.exitFullscreen ](); + + }, + toggle : function ( elem ) { + + elem = elem || document.documentElement; + + if ( this.isFullscreen() ) { + this.exit(); + + } else { + this.request( elem ); + } + + }, + isFullscreen : function() { + + return Boolean( document[ fn.fullscreenElement ] ); + + }, + enabled : function() { + + return Boolean( document[ fn.fullscreenEnabled ] ); + + } + }; + + $(document).on({ + 'onInit.fb' : function(e, instance) { + var $container; + + var $button = instance.$refs.toolbar.find('[data-fancybox-fullscreen]'); + + if ( instance && !instance.FullScreen && instance.group[ instance.currIndex ].opts.fullScreen ) { + $container = instance.$refs.container; + + $container.on('click.fb-fullscreen', '[data-fancybox-fullscreen]', function(e) { + + e.stopPropagation(); + e.preventDefault(); + + FullScreen.toggle( $container[ 0 ] ); + + }); + + if ( instance.opts.fullScreen && instance.opts.fullScreen.autoStart === true ) { + FullScreen.request( $container[ 0 ] ); + } + + // Expose API + instance.FullScreen = FullScreen; + + } else { + $button.hide(); + } + + }, + + 'afterKeydown.fb' : function(e, instance, current, keypress, keycode) { + + // "P" or Spacebar + if ( instance && instance.FullScreen && keycode === 70 ) { + keypress.preventDefault(); + + instance.FullScreen.toggle( instance.$refs.container[ 0 ] ); + } + + }, + + 'beforeClose.fb' : function( instance ) { + if ( instance && instance.FullScreen ) { + FullScreen.exit(); + } + } + }); + + $(document).on(fn.fullscreenchange, function() { + var instance = $.fancybox.getInstance(); + + // If image is zooming, then force to stop and reposition properly + if ( instance.current && instance.current.type === 'image' && instance.isAnimating ) { + instance.current.$content.css( 'transition', 'none' ); + + instance.isAnimating = false; + + instance.update( true, true, 0 ); + } + + }); + +}(document, window.jQuery)); + +// ========================================================================== +// +// Thumbs +// Displays thumbnails in a grid +// +// ========================================================================== +;(function (document, $) { + 'use strict'; + + var FancyThumbs = function( instance ) { + this.instance = instance; + this.init(); + }; + + $.extend( FancyThumbs.prototype, { + + $button : null, + $grid : null, + $list : null, + isVisible : false, + + init : function() { + var self = this; + + var first = self.instance.group[0], + second = self.instance.group[1]; + + self.$button = self.instance.$refs.toolbar.find( '[data-fancybox-thumbs]' ); + + if ( self.instance.group.length > 1 && self.instance.group[ self.instance.currIndex ].opts.thumbs && ( + ( first.type == 'image' || first.opts.thumb || first.opts.$thumb ) && + ( second.type == 'image' || second.opts.thumb || second.opts.$thumb ) + )) { + + self.$button.on('click', function() { + self.toggle(); + }); + + self.isActive = true; + + } else { + self.$button.hide(); + + self.isActive = false; + } + + }, + + create : function() { + var instance = this.instance, + list, + src; + + this.$grid = $('
').appendTo( instance.$refs.container ); + + list = '
    '; + + $.each(instance.group, function( i, item ) { + + src = item.opts.thumb || ( item.opts.$thumb ? item.opts.$thumb.attr('src') : null ); + + if ( !src && item.type === 'image' ) { + src = item.src; + } + + if ( src && src.length ) { + list += '
  • '; + } + + }); + + list += '
'; + + this.$list = $( list ).appendTo( this.$grid ).on('click', 'li', function() { + instance.jumpTo( $(this).data('index') ); + }); + + this.$list.find('img').hide().one('load', function() { + + var $parent = $(this).parent().removeClass('fancybox-thumbs-loading'), + thumbWidth = $parent.outerWidth(), + thumbHeight = $parent.outerHeight(), + width, + height, + widthRatio, + heightRatio; + + width = this.naturalWidth || this.width; + height = this.naturalHeight || this.height; + + //Calculate thumbnail width/height and center it + + widthRatio = width / thumbWidth; + heightRatio = height / thumbHeight; + + if (widthRatio >= 1 && heightRatio >= 1) { + if (widthRatio > heightRatio) { + width = width / heightRatio; + height = thumbHeight; + + } else { + width = thumbWidth; + height = height / widthRatio; + } + } + + $(this).css({ + width : Math.floor(width), + height : Math.floor(height), + 'margin-top' : Math.min( 0, Math.floor(thumbHeight * 0.3 - height * 0.3 ) ), + 'margin-left' : Math.min( 0, Math.floor(thumbWidth * 0.5 - width * 0.5 ) ) + }).show(); + + }) + .each(function() { + this.src = $( this ).data( 'src' ); + }); + + }, + + focus : function() { + + if ( this.instance.current ) { + this.$list + .children() + .removeClass('fancybox-thumbs-active') + .filter('[data-index="' + this.instance.current.index + '"]') + .addClass('fancybox-thumbs-active') + .focus(); + } + + }, + + close : function() { + this.$grid.hide(); + }, + + update : function() { + + this.instance.$refs.container.toggleClass( 'fancybox-show-thumbs', this.isVisible ); + + if ( this.isVisible ) { + + if ( !this.$grid ) { + this.create(); + } + + this.instance.trigger( 'onThumbsShow' ); + + this.focus(); + + } else if ( this.$grid ) { + this.instance.trigger( 'onThumbsHide' ); + } + + // Update content position + this.instance.update(); + + }, + + hide : function() { + this.isVisible = false; + this.update(); + }, + + show : function() { + this.isVisible = true; + this.update(); + }, + + toggle : function() { + this.isVisible = !this.isVisible; + this.update(); + } + + }); + + $(document).on({ + + 'onInit.fb' : function(e, instance) { + if ( instance && !instance.Thumbs ) { + instance.Thumbs = new FancyThumbs( instance ); + } + }, + + 'beforeShow.fb' : function(e, instance, item, firstRun) { + var Thumbs = instance && instance.Thumbs; + + if ( !Thumbs || !Thumbs.isActive ) { + return; + } + + if ( item.modal ) { + Thumbs.$button.hide(); + + Thumbs.hide(); + + return; + } + + if ( firstRun && instance.opts.thumbs.autoStart === true ) { + Thumbs.show(); + } + + if ( Thumbs.isVisible ) { + Thumbs.focus(); + } + }, + + 'afterKeydown.fb' : function(e, instance, current, keypress, keycode) { + var Thumbs = instance && instance.Thumbs; + + // "G" + if ( Thumbs && Thumbs.isActive && keycode === 71 ) { + keypress.preventDefault(); + + Thumbs.toggle(); + } + }, + + 'beforeClose.fb' : function( e, instance ) { + var Thumbs = instance && instance.Thumbs; + + if ( Thumbs && Thumbs.isVisible && instance.opts.thumbs.hideOnClose !== false ) { + Thumbs.close(); + } + } + + }); + +}(document, window.jQuery)); + +// ========================================================================== +// +// Hash +// Enables linking to each modal +// +// ========================================================================== +;(function (document, window, $) { + 'use strict'; + + // Simple $.escapeSelector polyfill (for jQuery prior v3) + if ( !$.escapeSelector ) { + $.escapeSelector = function( sel ) { + var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; + var fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }; + + return ( sel + "" ).replace( rcssescape, fcssescape ); + }; + } + + // Create new history entry only once + var shouldCreateHistory = true; + + // Variable containing last hash value set by fancyBox + // It will be used to determine if fancyBox needs to close after hash change is detected + var currentHash = null; + + // Throttling the history change + var timerID = null; + + // Get info about gallery name and current index from url + function parseUrl() { + var hash = window.location.hash.substr( 1 ); + var rez = hash.split( '-' ); + var index = rez.length > 1 && /^\+?\d+$/.test( rez[ rez.length - 1 ] ) ? parseInt( rez.pop( -1 ), 10 ) || 1 : 1; + var gallery = rez.join( '-' ); + + // Index is starting from 1 + if ( index < 1 ) { + index = 1; + } + + return { + hash : hash, + index : index, + gallery : gallery + }; + } + + // Trigger click evnt on links to open new fancyBox instance + function triggerFromUrl( url ) { + var $el; + + if ( url.gallery !== '' ) { + + // If we can find element matching 'data-fancybox' atribute, then trigger click event for that .. + $el = $( "[data-fancybox='" + $.escapeSelector( url.gallery ) + "']" ).eq( url.index - 1 ); + + if ( !$el.length ) { + // .. if not, try finding element by ID + $el = $( "#" + $.escapeSelector( url.gallery ) + "" ); + } + + if ( $el.length ) { + shouldCreateHistory = false; + + $el.trigger( 'click' ); + } + + } + } + + // Get gallery name from current instance + function getGallery( instance ) { + var opts; + + if ( !instance ) { + return false; + } + + opts = instance.current ? instance.current.opts : instance.opts; + + return opts.$orig ? opts.$orig.data( 'fancybox' ) : ( opts.hash || '' ); + } + + // Star when DOM becomes ready + $(function() { + + // Small delay is used to allow other scripts to process "dom ready" event + setTimeout(function() { + + // Check if this module is not disabled + if ( $.fancybox.defaults.hash === false ) { + return; + } + + // Update hash when opening/closing fancyBox + $(document).on({ + 'onInit.fb' : function( e, instance ) { + var url, gallery; + + if ( instance.group[ instance.currIndex ].opts.hash === false ) { + return; + } + + url = parseUrl(); + gallery = getGallery( instance ); + + // Make sure gallery start index matches index from hash + if ( gallery && url.gallery && gallery == url.gallery ) { + instance.currIndex = url.index - 1; + } + + }, + + 'beforeShow.fb' : function( e, instance, current ) { + var gallery; + + if ( current.opts.hash === false ) { + return; + } + + gallery = getGallery( instance ); + + // Update window hash + if ( gallery && gallery !== '' ) { + + if ( window.location.hash.indexOf( gallery ) < 0 ) { + instance.opts.origHash = window.location.hash; + } + + currentHash = gallery + ( instance.group.length > 1 ? '-' + ( current.index + 1 ) : '' ); + + if ( 'replaceState' in window.history ) { + if ( timerID ) { + clearTimeout( timerID ); + } + + timerID = setTimeout(function() { + window.history[ shouldCreateHistory ? 'pushState' : 'replaceState' ]( {} , document.title, window.location.pathname + window.location.search + '#' + currentHash ); + + timerID = null; + + shouldCreateHistory = false; + + }, 300); + + } else { + window.location.hash = currentHash; + } + + } + + }, + + 'beforeClose.fb' : function( e, instance, current ) { + var gallery, origHash; + + if ( timerID ) { + clearTimeout( timerID ); + } + + if ( current.opts.hash === false ) { + return; + } + + gallery = getGallery( instance ); + origHash = instance && instance.opts.origHash ? instance.opts.origHash : ''; + + // Remove hash from location bar + if ( gallery && gallery !== '' ) { + + if ( 'replaceState' in history ) { + window.history.replaceState( {} , document.title, window.location.pathname + window.location.search + origHash ); + + } else { + window.location.hash = origHash; + + // Keep original scroll position + $( window ).scrollTop( instance.scrollTop ).scrollLeft( instance.scrollLeft ); + } + } + + currentHash = null; + } + }); + + // Check if need to close after url has changed + $(window).on('hashchange.fb', function() { + var url = parseUrl(); + + if ( $.fancybox.getInstance() ) { + if ( currentHash && currentHash !== url.gallery + '-' + url.index && !( url.index === 1 && currentHash == url.gallery ) ) { + currentHash = null; + + $.fancybox.close(); + + shouldCreateHistory = true; + } + + } else if ( url.gallery !== '' ) { + triggerFromUrl( url ); + } + }); + + // Check current hash and trigger click event on matching element to start fancyBox, if needed + triggerFromUrl( parseUrl() ); + + }, 50); + + }); + + +}(document, window, window.jQuery)); diff --git a/fancybox/dist/jquery.fancybox.min.css b/fancybox/dist/jquery.fancybox.min.css new file mode 100644 index 0000000..ab39c7a --- /dev/null +++ b/fancybox/dist/jquery.fancybox.min.css @@ -0,0 +1 @@ +@charset "UTF-8";.fancybox-enabled{overflow:hidden}.fancybox-enabled body{overflow:visible;height:100%}.fancybox-is-hidden{position:absolute;top:-9999px;left:-9999px;visibility:hidden}.fancybox-container{position:fixed;top:0;left:0;width:100%;height:100%;z-index:99993;-webkit-tap-highlight-color:transparent;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateZ(0);transform:translateZ(0)}.fancybox-container~.fancybox-container{z-index:99992}.fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-stage{position:absolute;top:0;right:0;bottom:0;left:0}.fancybox-outer{overflow-y:auto;-webkit-overflow-scrolling:touch}.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-wrap,.fancybox-infobar,.fancybox-toolbar{position:absolute;direction:ltr;z-index:99997;opacity:0;visibility:hidden;transition:opacity .25s,visibility 0s linear .25s;box-sizing:border-box}.fancybox-show-caption .fancybox-caption-wrap,.fancybox-show-infobar .fancybox-infobar,.fancybox-show-toolbar .fancybox-toolbar{opacity:1;visibility:visible;transition:opacity .25s,visibility 0s}.fancybox-infobar{top:0;left:50%;margin-left:-79px}.fancybox-infobar__body{display:inline-block;width:70px;line-height:44px;font-size:13px;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;text-align:center;color:#ddd;background-color:rgba(30,30,30,.7);pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent;-webkit-font-smoothing:subpixel-antialiased}.fancybox-toolbar{top:0;right:0}.fancybox-stage{overflow:hidden;direction:ltr;z-index:99994;-webkit-transform:translateZ(0)}.fancybox-slide{position:absolute;top:0;left:0;width:100%;height:100%;margin:0;padding:0;overflow:auto;outline:none;white-space:normal;box-sizing:border-box;text-align:center;z-index:99994;-webkit-overflow-scrolling:touch;display:none;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.fancybox-slide:before{content:"";display:inline-block;vertical-align:middle;height:100%;width:0}.fancybox-is-sliding .fancybox-slide,.fancybox-slide--current,.fancybox-slide--next,.fancybox-slide--previous{display:block}.fancybox-slide--image{overflow:visible}.fancybox-slide--image:before{display:none}.fancybox-slide--video .fancybox-content,.fancybox-slide--video iframe{background:#000}.fancybox-slide--map .fancybox-content,.fancybox-slide--map iframe{background:#e5e3df}.fancybox-slide--next{z-index:99995}.fancybox-slide>*{display:inline-block;position:relative;padding:24px;margin:44px 0;border-width:0;vertical-align:middle;text-align:left;background-color:#fff;overflow:auto;box-sizing:border-box}.fancybox-slide .fancybox-image-wrap{position:absolute;top:0;left:0;margin:0;padding:0;border:0;z-index:99995;background:transparent;cursor:default;overflow:visible;-webkit-transform-origin:top left;transform-origin:top left;background-size:100% 100%;background-repeat:no-repeat;-webkit-backface-visibility:hidden;backface-visibility:hidden}.fancybox-can-zoomOut .fancybox-image-wrap{cursor:zoom-out}.fancybox-can-zoomIn .fancybox-image-wrap{cursor:zoom-in}.fancybox-can-drag .fancybox-image-wrap{cursor:-webkit-grab;cursor:grab}.fancybox-is-dragging .fancybox-image-wrap{cursor:-webkit-grabbing;cursor:grabbing}.fancybox-image,.fancybox-spaceball{position:absolute;top:0;left:0;width:100%;height:100%;margin:0;padding:0;border:0;max-width:none;max-height:none}.fancybox-spaceball{z-index:1}.fancybox-slide--iframe .fancybox-content{padding:0;width:80%;height:80%;max-width:calc(100% - 100px);max-height:calc(100% - 88px);overflow:visible;background:#fff}.fancybox-iframe{display:block;padding:0;border:0;height:100%}.fancybox-error,.fancybox-iframe{margin:0;width:100%;background:#fff}.fancybox-error{padding:40px;max-width:380px;cursor:default}.fancybox-error p{margin:0;padding:0;color:#444;font:16px/20px Helvetica Neue,Helvetica,Arial,sans-serif}.fancybox-close-small{position:absolute;top:0;right:0;width:44px;height:44px;padding:0;margin:0;border:0;border-radius:0;outline:none;background:transparent;z-index:10;cursor:pointer}.fancybox-close-small:after{content:"×";position:absolute;top:5px;right:5px;width:30px;height:30px;font:20px/30px Arial,Helvetica Neue,Helvetica,sans-serif;color:#888;font-weight:300;text-align:center;border-radius:50%;border-width:0;background:#fff;transition:background .25s;box-sizing:border-box;z-index:2}.fancybox-close-small:focus:after{outline:1px dotted #888}.fancybox-close-small:hover:after{color:#555;background:#eee}.fancybox-slide--iframe .fancybox-close-small{top:0;right:-44px}.fancybox-slide--iframe .fancybox-close-small:after{background:transparent;font-size:35px;color:#aaa}.fancybox-slide--iframe .fancybox-close-small:hover:after{color:#fff}.fancybox-caption-wrap{bottom:0;left:0;right:0;padding:60px 30px 0;background:linear-gradient(180deg,transparent 0,rgba(0,0,0,.1) 20%,rgba(0,0,0,.2) 40%,rgba(0,0,0,.6) 80%,rgba(0,0,0,.8));pointer-events:none}.fancybox-caption{padding:30px 0;border-top:1px solid hsla(0,0%,100%,.4);font-size:14px;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;color:#fff;line-height:20px;-webkit-text-size-adjust:none}.fancybox-caption a,.fancybox-caption button,.fancybox-caption select{pointer-events:all}.fancybox-caption a{color:#fff;text-decoration:underline}.fancybox-button{display:inline-block;position:relative;margin:0;padding:0;border:0;width:44px;height:44px;line-height:44px;text-align:center;background:transparent;color:#ddd;border-radius:0;cursor:pointer;vertical-align:top;outline:none}.fancybox-button[disabled]{cursor:default;pointer-events:none}.fancybox-button,.fancybox-infobar__body{background:rgba(30,30,30,.6)}.fancybox-button:hover:not([disabled]){color:#fff;background:rgba(0,0,0,.8)}.fancybox-button:after,.fancybox-button:before{content:"";pointer-events:none;position:absolute;background-color:currentColor;color:currentColor;opacity:.9;box-sizing:border-box;display:inline-block}.fancybox-button[disabled]:after,.fancybox-button[disabled]:before{opacity:.3}.fancybox-button--left:after,.fancybox-button--right:after{top:18px;width:6px;height:6px;background:transparent;border-top:2px solid currentColor;border-right:2px solid currentColor}.fancybox-button--left:after{left:20px;-webkit-transform:rotate(-135deg);transform:rotate(-135deg)}.fancybox-button--right:after{right:20px;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.fancybox-button--left{border-bottom-left-radius:5px}.fancybox-button--right{border-bottom-right-radius:5px}.fancybox-button--close:after,.fancybox-button--close:before{content:"";display:inline-block;position:absolute;height:2px;width:16px;top:calc(50% - 1px);left:calc(50% - 8px)}.fancybox-button--close:before{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.fancybox-button--close:after{-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.fancybox-arrow{position:absolute;top:50%;margin:-50px 0 0;height:100px;width:54px;padding:0;border:0;outline:none;background:none;cursor:pointer;z-index:99995;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;transition:opacity .25s}.fancybox-arrow:after{content:"";position:absolute;top:28px;width:44px;height:44px;background-color:rgba(30,30,30,.8);background-image:url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiBoZWlnaHQ9IjQ4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSI0OCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4gICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPiAgICA8cGF0aCBkPSJNMTIgNGwtMS40MSAxLjQxTDE2LjE3IDExSDR2MmgxMi4xN2wtNS41OCA1LjU5TDEyIDIwbDgtOHoiLz48L3N2Zz4=);background-repeat:no-repeat;background-position:50%;background-size:24px 24px}.fancybox-arrow--right{right:0}.fancybox-arrow--left{left:0;-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fancybox-arrow--left:after,.fancybox-arrow--right:after{left:0}.fancybox-show-nav .fancybox-arrow{opacity:.6}.fancybox-show-nav .fancybox-arrow[disabled]{opacity:.3}.fancybox-loading{border:6px solid hsla(0,0%,39%,.4);border-top:6px solid hsla(0,0%,100%,.6);border-radius:100%;height:50px;width:50px;-webkit-animation:a .8s infinite linear;animation:a .8s infinite linear;background:transparent;position:absolute;top:50%;left:50%;margin-top:-25px;margin-left:-25px;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{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);opacity:0}.fancybox-fx-slide.fancybox-slide--next{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);opacity:0}.fancybox-fx-slide.fancybox-slide--current{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}.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{-webkit-transform:scale3d(1.5,1.5,1.5);transform:scale3d(1.5,1.5,1.5);opacity:0}.fancybox-fx-zoom-in-out.fancybox-slide--next{-webkit-transform:scale3d(.5,.5,.5);transform:scale3d(.5,.5,.5);opacity:0}.fancybox-fx-zoom-in-out.fancybox-slide--current{-webkit-transform:scaleX(1);transform:scaleX(1);opacity:1}.fancybox-fx-rotate.fancybox-slide--previous{-webkit-transform:rotate(-1turn);transform:rotate(-1turn);opacity:0}.fancybox-fx-rotate.fancybox-slide--next{-webkit-transform:rotate(1turn);transform:rotate(1turn);opacity:0}.fancybox-fx-rotate.fancybox-slide--current{-webkit-transform:rotate(0deg);transform:rotate(0deg);opacity:1}.fancybox-fx-circular.fancybox-slide--previous{-webkit-transform:scale3d(0,0,0) translate3d(-100%,0,0);transform:scale3d(0,0,0) translate3d(-100%,0,0);opacity:0}.fancybox-fx-circular.fancybox-slide--next{-webkit-transform:scale3d(0,0,0) translate3d(100%,0,0);transform:scale3d(0,0,0) translate3d(100%,0,0);opacity:0}.fancybox-fx-circular.fancybox-slide--current{-webkit-transform:scaleX(1) translateZ(0);transform:scaleX(1) translateZ(0);opacity:1}.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)}@media (max-width:800px){.fancybox-infobar{left:0;margin-left:0}.fancybox-button--left,.fancybox-button--right{display:none!important}.fancybox-caption{padding:20px 0;margin:0}}.fancybox-button--fullscreen:before{width:15px;height:11px;left:calc(50% - 7px);top:calc(50% - 6px);border:2px solid;background:none}.fancybox-button--pause:before,.fancybox-button--play:before{top:calc(50% - 6px);left:calc(50% - 4px);background:transparent}.fancybox-button--play:before{width:0;height:0;border-top:6px inset transparent;border-bottom:6px inset transparent;border-left:10px solid;border-radius:1px}.fancybox-button--pause:before{width:7px;height:11px;border-style:solid;border-width:0 2px}.fancybox-button--thumbs,.fancybox-thumbs{display:none}@media (min-width:800px){.fancybox-button--thumbs{display:inline-block}.fancybox-button--thumbs span{font-size:23px}.fancybox-button--thumbs:before{width:3px;height:3px;top:calc(50% - 2px);left:calc(50% - 2px);box-shadow:0 -4px 0,-4px -4px 0,4px -4px 0,inset 0 0 0 32px,-4px 0 0,4px 0 0,0 4px 0,-4px 4px 0,4px 4px 0}.fancybox-thumbs{position:absolute;top:0;right:0;bottom:0;left:auto;width:220px;margin:0;padding:5px 5px 0 0;background:#fff;word-break:normal;-webkit-tap-highlight-color:transparent;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;box-sizing:border-box;z-index:99995}.fancybox-show-thumbs .fancybox-thumbs{display:block}.fancybox-show-thumbs .fancybox-inner{right:220px}.fancybox-thumbs>ul{list-style:none;position:absolute;position:relative;width:100%;height:100%;margin:0;padding:0;overflow-x:hidden;overflow-y:auto;font-size:0}.fancybox-thumbs>ul>li{float:left;overflow:hidden;max-width:50%;padding:0;margin:0;width:105px;height:75px;position:relative;cursor:pointer;outline:none;border:5px solid transparent;border-top-width:0;border-right-width:0;-webkit-tap-highlight-color:transparent;-webkit-backface-visibility:hidden;backface-visibility:hidden;box-sizing:border-box}li.fancybox-thumbs-loading{background:rgba(0,0,0,.1)}.fancybox-thumbs>ul>li>img{position:absolute;top:0;left:0;min-width:100%;min-height:100%;max-width:none;max-height:none;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fancybox-thumbs>ul>li:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;border-radius:2px;border:4px solid #4ea7f9;z-index:99991;opacity:0;transition:all .2s cubic-bezier(.25,.46,.45,.94)}.fancybox-thumbs>ul>li.fancybox-thumbs-active:before{opacity:1}} \ No newline at end of file diff --git a/fancybox/dist/jquery.fancybox.min.js b/fancybox/dist/jquery.fancybox.min.js new file mode 100644 index 0000000..ab95714 --- /dev/null +++ b/fancybox/dist/jquery.fancybox.min.js @@ -0,0 +1,12 @@ +// ================================================== +// fancyBox v3.1.24 +// +// Licensed GPLv3 for open source use +// or fancyBox Commercial License for commercial use +// +// http://fancyapps.com/fancybox/ +// Copyright 2017 fancyApps +// +// ================================================== +!function(t,e,n,o){"use strict";function i(t){var e=t.currentTarget,o=t.data?t.data.options:{},i=o.selector?n(o.selector):t.data?t.data.items:[],a=n(e).attr("data-fancybox")||"",s=0,r=n.fancybox.getInstance();t.preventDefault(),t.stopPropagation(),r&&r.current.opts.$orig.is(e)||(a?(i=i.length?i.filter('[data-fancybox="'+a+'"]'):n('[data-fancybox="'+a+'"]'),s=i.index(e),s<0&&(s=0)):i=[e],n.fancybox.open(i,o,s))}if(n){if(n.fn.fancybox)return void n.error("fancyBox already initialized");var a={loop:!1,margin:[44,0],gutter:50,keyboard:!0,arrows:!0,infobar:!1,toolbar:!0,buttons:["slideShow","fullScreen","thumbs","close"],idleTime:4,smallBtn:"auto",protect:!1,modal:!1,image:{preload:"auto"},ajax:{settings:{data:{fancybox:!0}}},iframe:{tpl:'',preload:!0,css:{},attr:{scrolling:"auto"}},animationEffect:"zoom",animationDuration:366,zoomOpacity:"auto",transitionEffect:"fade",transitionDuration:366,slideClass:"",baseClass:"",baseTpl:'',spinnerTpl:'
',errorTpl:'

{{ERROR}}

',btnTpl:{slideShow:'',fullScreen:'',thumbs:'',close:'',smallBtn:''},parentEl:"body",autoFocus:!0,backFocus:!0,trapFocus:!0,fullScreen:{autoStart:!1},touch:{vertical:!0,momentum:!0},hash:null,media:{},slideShow:{autoStart:!1,speed:4e3},thumbs:{autoStart:!1,hideOnClose:!0},onInit:n.noop,beforeLoad:n.noop,afterLoad:n.noop,beforeShow:n.noop,afterShow:n.noop,beforeClose:n.noop,afterClose:n.noop,onActivate:n.noop,onDeactivate:n.noop,clickContent:function(t,e){return"image"===t.type&&"zoom"},clickSlide:"close",clickOutside:"close",dblclickContent:!1,dblclickSlide:!1,dblclickOutside:!1,mobile:{clickContent:function(t,e){return"image"===t.type&&"toggleControls"},clickSlide:function(t,e){return"image"===t.type?"toggleControls":"close"},dblclickContent:function(t,e){return"image"===t.type&&"zoom"},dblclickSlide:function(t,e){return"image"===t.type&&"zoom"}},lang:"en",i18n:{en:{CLOSE:"Close",NEXT:"Next",PREV:"Previous",ERROR:"The requested content cannot be loaded.
Please try again later.",PLAY_START:"Start slideshow",PLAY_STOP:"Pause slideshow",FULL_SCREEN:"Full screen",THUMBS:"Thumbnails"},de:{CLOSE:"Schliessen",NEXT:"Weiter",PREV:"Zurück",ERROR:"Die angeforderten Daten konnten nicht geladen werden.
Bitte versuchen Sie es später nochmal.",PLAY_START:"Diaschau starten",PLAY_STOP:"Diaschau beenden",FULL_SCREEN:"Vollbild",THUMBS:"Vorschaubilder"}}},s=n(t),r=n(e),c=0,l=function(t){return t&&t.hasOwnProperty&&t instanceof n},u=function(){return t.requestAnimationFrame||t.webkitRequestAnimationFrame||t.mozRequestAnimationFrame||t.oRequestAnimationFrame||function(e){return t.setTimeout(e,1e3/60)}}(),d=function(){var t,n=e.createElement("fakeelement"),i={transition:"transitionend",OTransition:"oTransitionEnd",MozTransition:"transitionend",WebkitTransition:"webkitTransitionEnd"};for(t in i)if(n.style[t]!==o)return i[t]}(),f=function(t){return t&&t.length&&t[0].offsetHeight},h=function(t,o,i){var s=this;s.opts=n.extend(!0,{index:i},a,o||{}),o&&n.isArray(o.buttons)&&(s.opts.buttons=o.buttons),s.id=s.opts.id||++c,s.group=[],s.currIndex=parseInt(s.opts.index,10)||0,s.prevIndex=null,s.prevPos=null,s.currPos=0,s.firstRun=null,s.createGroup(t),s.group.length&&(s.$lastFocus=n(e.activeElement).blur(),s.slides={},s.init(t))};n.extend(h.prototype,{init:function(){var t,e,o,i=this,a=i.group[i.currIndex].opts;i.scrollTop=r.scrollTop(),i.scrollLeft=r.scrollLeft(),n.fancybox.getInstance()||n.fancybox.isMobile||"hidden"===n("body").css("overflow")||(t=n("body").width(),n("html").addClass("fancybox-enabled"),t=n("body").width()-t,t>1&&n("head").append('")),o="",n.each(a.buttons,function(t,e){o+=a.btnTpl[e]||""}),e=n(i.translate(i,a.baseTpl.replace("{{BUTTONS}}",o))).addClass("fancybox-is-hidden").attr("id","fancybox-container-"+i.id).addClass(a.baseClass).data("FancyBox",i).prependTo(a.parentEl),i.$refs={container:e},["bg","inner","infobar","toolbar","stage","caption"].forEach(function(t){i.$refs[t]=e.find(".fancybox-"+t)}),(!a.arrows||i.group.length<2)&&e.find(".fancybox-navigation").remove(),a.infobar||i.$refs.infobar.remove(),a.toolbar||i.$refs.toolbar.remove(),i.trigger("onInit"),i.activate(),i.jumpTo(i.currIndex)},translate:function(t,e){var n=t.opts.i18n[t.opts.lang];return e.replace(/\{\{(\w+)\}\}/g,function(t,e){var i=n[e];return i===o?t:i})},createGroup:function(t){var e=this,i=n.makeArray(t);n.each(i,function(t,i){var a,s,r,c,l={},u={},d=[];n.isPlainObject(i)?(l=i,u=i.opts||i):"object"===n.type(i)&&n(i).length?(a=n(i),d=a.data(),u="options"in d?d.options:{},u="object"===n.type(u)?u:{},l.src="src"in d?d.src:u.src||a.attr("href"),["width","height","thumb","type","filter"].forEach(function(t){t in d&&(u[t]=d[t])}),"srcset"in d&&(u.image={srcset:d.srcset}),u.$orig=a,l.type||l.src||(l.type="inline",l.src=i)):l={type:"html",src:i+""},l.opts=n.extend(!0,{},e.opts,u),n.fancybox.isMobile&&(l.opts=n.extend(!0,{},l.opts,l.opts.mobile)),s=l.type||l.opts.type,r=l.src||"",!s&&r&&(r.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i)?s="image":r.match(/\.(pdf)((\?|#).*)?$/i)?s="pdf":"#"===r.charAt(0)&&(s="inline")),l.type=s,l.index=e.group.length,l.opts.$orig&&!l.opts.$orig.length&&delete l.opts.$orig,!l.opts.$thumb&&l.opts.$orig&&(l.opts.$thumb=l.opts.$orig.find("img:first")),l.opts.$thumb&&!l.opts.$thumb.length&&delete l.opts.$thumb,"function"===n.type(l.opts.caption)?l.opts.caption=l.opts.caption.apply(i,[e,l]):"caption"in d&&(l.opts.caption=d.caption),l.opts.caption=l.opts.caption===o?"":l.opts.caption+"","ajax"===s&&(c=r.split(/\s+/,2),c.length>1&&(l.src=c.shift(),l.opts.filter=c.shift())),"auto"==l.opts.smallBtn&&(n.inArray(s,["html","inline","ajax"])>-1?(l.opts.toolbar=!1,l.opts.smallBtn=!0):l.opts.smallBtn=!1),"pdf"===s&&(l.type="iframe",l.opts.iframe.preload=!1),l.opts.modal&&(l.opts=n.extend(!0,l.opts,{infobar:0,toolbar:0,smallBtn:0,keyboard:0,slideShow:0,fullScreen:0,thumbs:0,touch:0,clickContent:!1,clickSlide:!1,clickOutside:!1,dblclickContent:!1,dblclickSlide:!1,dblclickOutside:!1})),e.group.push(l)})},addEvents:function(){var o=this;o.removeEvents(),o.$refs.container.on("click.fb-close","[data-fancybox-close]",function(t){t.stopPropagation(),t.preventDefault(),o.close(t)}).on("click.fb-prev touchend.fb-prev","[data-fancybox-prev]",function(t){t.stopPropagation(),t.preventDefault(),o.previous()}).on("click.fb-next touchend.fb-next","[data-fancybox-next]",function(t){t.stopPropagation(),t.preventDefault(),o.next()}),s.on("orientationchange.fb resize.fb",function(t){t&&t.originalEvent&&"resize"===t.originalEvent.type?u(function(){o.update()}):(o.$refs.stage.hide(),setTimeout(function(){o.$refs.stage.show(),o.update()},500))}),r.on("focusin.fb",function(t){var i=n.fancybox?n.fancybox.getInstance():null;i.isClosing||!i.current||!i.current.opts.trapFocus||n(t.target).hasClass("fancybox-container")||n(t.target).is(e)||i&&"fixed"!==n(t.target).css("position")&&!i.$refs.container.has(t.target).length&&(t.stopPropagation(),i.focus(),s.scrollTop(o.scrollTop).scrollLeft(o.scrollLeft))}),r.on("keydown.fb",function(t){var e=o.current,i=t.keyCode||t.which;if(e&&e.opts.keyboard&&!n(t.target).is("input")&&!n(t.target).is("textarea"))return 8===i||27===i?(t.preventDefault(),void o.close(t)):37===i||38===i?(t.preventDefault(),void o.previous()):39===i||40===i?(t.preventDefault(),void o.next()):void o.trigger("afterKeydown",t,i)}),o.group[o.currIndex].opts.idleTime&&(o.idleSecondsCounter=0,r.on("mousemove.fb-idle mouseenter.fb-idle mouseleave.fb-idle mousedown.fb-idle touchstart.fb-idle touchmove.fb-idle scroll.fb-idle keydown.fb-idle",function(){o.idleSecondsCounter=0,o.isIdle&&o.showControls(),o.isIdle=!1}),o.idleInterval=t.setInterval(function(){o.idleSecondsCounter++,o.idleSecondsCounter>=o.group[o.currIndex].opts.idleTime&&(o.isIdle=!0,o.idleSecondsCounter=0,o.hideControls())},1e3))},removeEvents:function(){var e=this;s.off("orientationchange.fb resize.fb"),r.off("focusin.fb keydown.fb .fb-idle"),this.$refs.container.off(".fb-close .fb-prev .fb-next"),e.idleInterval&&(t.clearInterval(e.idleInterval),e.idleInterval=null)},previous:function(t){return this.jumpTo(this.currPos-1,t)},next:function(t){return this.jumpTo(this.currPos+1,t)},jumpTo:function(t,e,i){var a,s,r,c,l,u,d,h=this,p=h.group.length;if(!(h.isSliding||h.isClosing||h.isAnimating&&h.firstRun)){if(t=parseInt(t,10),s=h.current?h.current.opts.loop:h.opts.loop,!s&&(t<0||t>=p))return!1;if(a=h.firstRun=null===h.firstRun,!(p<2&&!a&&h.isSliding)){if(c=h.current,h.prevIndex=h.currIndex,h.prevPos=h.currPos,r=h.createSlide(t),p>1&&((s||r.index>0)&&h.createSlide(t-1),(s||r.indexr.pos?"next":"previous"),c.$slide.removeClass("fancybox-slide--complete fancybox-slide--current fancybox-slide--next fancybox-slide--previous"),c.isComplete=!1,e&&(r.isMoved||r.opts.transitionEffect)&&(r.isMoved?c.$slide.addClass(d):(d="fancybox-animated "+d+" fancybox-fx-"+r.opts.transitionEffect,n.fancybox.animate(c.$slide,d,e,function(){c.$slide.removeClass(d).removeAttr("style")}))))}}},createSlide:function(t){var e,o,i=this;return o=t%i.group.length,o=o<0?i.group.length+o:o,!i.slides[t]&&i.group[o]&&(e=n('
').appendTo(i.$refs.stage),i.slides[t]=n.extend(!0,{},i.group[o],{pos:t,$slide:e,isLoaded:!1}),i.updateSlide(i.slides[t])),i.slides[t]},scaleToActual:function(t,e,i){var a,s,r,c,l,u=this,d=u.current,f=d.$content,h=parseInt(d.$slide.width(),10),p=parseInt(d.$slide.height(),10),g=d.width,b=d.height;"image"!=d.type||d.hasError||!f||u.isAnimating||(n.fancybox.stop(f),u.isAnimating=!0,t=t===o?.5*h:t,e=e===o?.5*p:e,a=n.fancybox.getTranslate(f),c=g/a.width,l=b/a.height,s=.5*h-.5*g,r=.5*p-.5*b,g>h&&(s=a.left*c-(t*c-t),s>0&&(s=0),sp&&(r=a.top*l-(e*l-e),r>0&&(r=0),rt.width||o.height>t.height))},isScaledDown:function(){var t=this,e=t.current,o=e.$content,i=!1;return o&&(i=n.fancybox.getTranslate(o),i=i.width1||Math.abs(n.height()-o.height)>1),o},loadSlide:function(t){var e,o,i,a=this;if(!t.isLoading&&!t.isLoaded){switch(t.isLoading=!0,a.trigger("beforeLoad",t),e=t.type,o=t.$slide,o.off("refresh").trigger("onReset").addClass("fancybox-slide--"+(e||"unknown")).addClass(t.opts.slideClass),e){case"image":a.setImage(t);break;case"iframe":a.setIframe(t);break;case"html":a.setContent(t,t.src||t.content);break;case"inline":n(t.src).length?a.setContent(t,n(t.src)):a.setError(t);break;case"ajax":a.showLoading(t),i=n.ajax(n.extend({},t.opts.ajax.settings,{url:t.src,success:function(e,n){"success"===n&&a.setContent(t,e)},error:function(e,n){e&&"abort"!==n&&a.setError(t)}})),o.one("onReset",function(){i.abort()});break;default:a.setError(t)}return!0}},setImage:function(e){var o,i,a,s,r=this,c=e.opts.image.srcset;if(c){a=t.devicePixelRatio||1,s=t.innerWidth*a,i=c.split(",").map(function(t){var e={};return t.trim().split(/\s+/).forEach(function(t,n){var o=parseInt(t.substring(0,t.length-1),10);return 0===n?e.url=t:void(o&&(e.value=o,e.postfix=t[t.length-1]))}),e}),i.sort(function(t,e){return t.value-e.value});for(var l=0;l=s||"x"===u.postfix&&u.value>=a){o=u;break}}!o&&i.length&&(o=i[i.length-1]),o&&(e.src=o.url,e.width&&e.height&&"w"==o.postfix&&(e.height=e.width/e.height*o.value,e.width=o.value))}e.$content=n('
').addClass("fancybox-is-hidden").appendTo(e.$slide),e.opts.preload!==!1&&e.opts.width&&e.opts.height&&(e.opts.thumb||e.opts.$thumb)?(e.width=e.opts.width,e.height=e.opts.height,e.$ghost=n("").one("error",function(){n(this).remove(),e.$ghost=null,r.setBigImage(e)}).one("load",function(){r.afterLoad(e),r.setBigImage(e)}).addClass("fancybox-image").appendTo(e.$content).attr("src",e.opts.thumb||e.opts.$thumb.attr("src"))):r.setBigImage(e)},setBigImage:function(t){var e=this,o=n("");t.$image=o.one("error",function(){e.setError(t)}).one("load",function(){clearTimeout(t.timouts),t.timouts=null,e.isClosing||(t.width=this.naturalWidth,t.height=this.naturalHeight,t.opts.image.srcset&&o.attr("sizes","100vw").attr("srcset",t.opts.image.srcset),e.hideLoading(t),t.$ghost?t.timouts=setTimeout(function(){t.timouts=null,t.$ghost.hide()},Math.min(300,Math.max(1e3,t.height/1600))):e.afterLoad(t))}).addClass("fancybox-image").attr("src",t.src).appendTo(t.$content),o[0].complete?o.trigger("load"):o[0].error?o.trigger("error"):t.timouts=setTimeout(function(){o[0].complete||t.hasError||e.showLoading(t)},100)},setIframe:function(t){var e,i=this,a=t.opts.iframe,s=t.$slide;t.$content=n('
').css(a.css).appendTo(s),e=n(a.tpl.replace(/\{rnd\}/g,(new Date).getTime())).attr(a.attr).appendTo(t.$content),a.preload?(i.showLoading(t),e.on("load.fb error.fb",function(e){this.isReady=1,t.$slide.trigger("refresh"),i.afterLoad(t)}),s.on("refresh.fb",function(){var n,i,s,r,c,l=t.$content;if(1===e[0].isReady){try{n=e.contents(),i=n.find("body")}catch(t){}i&&i.length&&(a.css.width===o||a.css.height===o)&&(s=e[0].contentWindow.document.documentElement.scrollWidth,r=Math.ceil(i.outerWidth(!0)+(l.width()-s)),c=Math.ceil(i.outerHeight(!0)),l.css({width:a.css.width===o?r+(l.outerWidth()-l.innerWidth()):a.css.width,height:a.css.height===o?c+(l.outerHeight()-l.innerHeight()):a.css.height})),l.removeClass("fancybox-is-hidden")}})):this.afterLoad(t),e.attr("src",t.src),t.opts.smallBtn===!0&&t.$content.prepend(i.translate(t,t.opts.btnTpl.smallBtn)),s.one("onReset",function(){try{n(this).find("iframe").hide().attr("src","//about:blank")}catch(t){}n(this).empty(),t.isLoaded=!1})},setContent:function(t,e){var o=this;o.isClosing||(o.hideLoading(t),t.$slide.empty(),l(e)&&e.parent().length?(e.parent(".fancybox-slide--inline").trigger("onReset"),t.$placeholder=n("
").hide().insertAfter(e),e.css("display","inline-block")):t.hasError||("string"===n.type(e)&&(e=n("
").append(n.trim(e)).contents(),3===e[0].nodeType&&(e=n("
").html(e))),t.opts.filter&&(e=n("
").html(e).find(t.opts.filter))),t.$slide.one("onReset",function(){t.$placeholder&&(t.$placeholder.after(e.hide()).remove(),t.$placeholder=null),t.$smallBtn&&(t.$smallBtn.remove(),t.$smallBtn=null),t.hasError||(n(this).empty(),t.isLoaded=!1)}),t.$content=n(e).appendTo(t.$slide),t.opts.smallBtn&&!t.$smallBtn&&(t.$smallBtn=n(o.translate(t,t.opts.btnTpl.smallBtn)).appendTo(t.$content)),this.afterLoad(t))},setError:function(t){t.hasError=!0,t.$slide.removeClass("fancybox-slide--"+t.type),this.setContent(t,this.translate(t,t.opts.errorTpl))},showLoading:function(t){var e=this;t=t||e.current,t&&!t.$spinner&&(t.$spinner=n(e.opts.spinnerTpl).appendTo(t.$slide))},hideLoading:function(t){var e=this;t=t||e.current,t&&t.$spinner&&(t.$spinner.remove(),delete t.$spinner)},afterLoad:function(t){var e=this;e.isClosing||(t.isLoading=!1,t.isLoaded=!0,e.trigger("afterLoad",t),e.hideLoading(t),t.opts.protect&&t.$content&&!t.hasError&&(t.$content.on("contextmenu.fb",function(t){return 2==t.button&&t.preventDefault(),!0}),"image"===t.type&&n('
').appendTo(t.$content)),e.revealContent(t))},revealContent:function(t){var e,i,a,s,r,c=this,l=t.$slide,u=!1;return e=t.opts[c.firstRun?"animationEffect":"transitionEffect"],a=t.opts[c.firstRun?"animationDuration":"transitionDuration"],a=parseInt(t.forcedDuration===o?a:t.forcedDuration,10),!t.isMoved&&t.pos===c.currPos&&a||(e=!1),"zoom"!==e||t.pos===c.currPos&&a&&"image"===t.type&&!t.hasError&&(u=c.getThumbPos(t))||(e="fade"),"zoom"===e?(r=c.getFitPos(t),r.scaleX=r.width/u.width,r.scaleY=r.height/u.height,delete r.width,delete r.height,s=t.opts.zoomOpacity,"auto"==s&&(s=Math.abs(t.width/t.height-u.width/u.height)>.1),s&&(u.opacity=.1,r.opacity=1),n.fancybox.setTranslate(t.$content.removeClass("fancybox-is-hidden"),u),f(t.$content),void n.fancybox.animate(t.$content,r,a,function(){c.complete()})):(c.updateSlide(t),e?(n.fancybox.stop(l),i="fancybox-animated fancybox-slide--"+(t.pos>c.prevPos?"next":"previous")+" fancybox-fx-"+e,l.removeAttr("style").removeClass("fancybox-slide--current fancybox-slide--next fancybox-slide--previous").addClass(i),t.$content.removeClass("fancybox-is-hidden"),f(l),void n.fancybox.animate(l,"fancybox-slide--current",a,function(e){l.removeClass(i).removeAttr("style"),t.pos===c.currPos&&c.complete()},!0)):(f(l),t.$content.removeClass("fancybox-is-hidden"),void(t.pos===c.currPos&&c.complete())))},getThumbPos:function(o){var i,a=this,s=!1,r=function(e){for(var o,i=e[0],a=i.getBoundingClientRect(),s=[];null!==i.parentElement;)"hidden"!==n(i.parentElement).css("overflow")&&"auto"!==n(i.parentElement).css("overflow")||s.push(i.parentElement.getBoundingClientRect()),i=i.parentElement;return o=s.every(function(t){var e=Math.min(a.right,t.right)-Math.max(a.left,t.left),n=Math.min(a.bottom,t.bottom)-Math.max(a.top,t.top);return e>0&&n>0}),o&&a.bottom>0&&a.right>0&&a.left=t.currPos-1&&o.pos<=t.currPos+1?i[o.pos]=o:o&&(n.fancybox.stop(o.$slide),o.$slide.unbind().remove())}),t.slides=i,t.updateCursor(),t.trigger("afterShow"),(n(e.activeElement).is("[disabled]")||o.opts.autoFocus&&"image"!=o.type&&"iframe"!==o.type)&&t.focus())},preload:function(){var t,e,n=this;n.group.length<2||(t=n.slides[n.currPos+1],e=n.slides[n.currPos-1],t&&"image"===t.type&&n.loadSlide(t),e&&"image"===e.type&&n.loadSlide(e))},focus:function(){var t,e=this.current;this.isClosing||(t=e&&e.isComplete?e.$slide.find("button,:input,[tabindex],a").filter(":not([disabled]):visible:first"):null,t=t&&t.length?t:this.$refs.container,t.focus())},activate:function(){var t=this;n(".fancybox-container").each(function(){var e=n(this).data("FancyBox");e&&e.uid!==t.uid&&!e.isClosing&&e.trigger("onDeactivate")}),t.current&&(t.$refs.container.index()>0&&t.$refs.container.prependTo(e.body),t.updateControls()),t.trigger("onActivate"),t.addEvents()},close:function(t,e){var o,i,a,s,r,c,l=this,f=l.current,h=function(){l.cleanUp(t)};return!l.isClosing&&(l.isClosing=!0,l.trigger("beforeClose",t)===!1?(l.isClosing=!1,u(function(){l.update()}),!1):(l.removeEvents(),f.timouts&&clearTimeout(f.timouts),a=f.$content,o=f.opts.animationEffect,i=n.isNumeric(e)?e:o?f.opts.animationDuration:0,f.$slide.off(d).removeClass("fancybox-slide--complete fancybox-slide--next fancybox-slide--previous fancybox-animated"),f.$slide.siblings().trigger("onReset").remove(),i&&l.$refs.container.removeClass("fancybox-is-open").addClass("fancybox-is-closing"),l.hideLoading(f),l.hideControls(),l.updateCursor(),"zoom"!==o||t!==!0&&a&&i&&"image"===f.type&&!f.hasError&&(c=l.getThumbPos(f))||(o="fade"),"zoom"===o?(n.fancybox.stop(a),r=n.fancybox.getTranslate(a),r.width=r.width*r.scaleX,r.height=r.height*r.scaleY,s=f.opts.zoomOpacity,"auto"==s&&(s=Math.abs(f.width/f.height-c.width/c.height)>.1),s&&(c.opacity=0),r.scaleX=r.width/c.width,r.scaleY=r.height/c.height,r.width=c.width,r.height=c.height,n.fancybox.setTranslate(f.$content,r),n.fancybox.animate(f.$content,c,i,h),!0):(o&&i?t===!0?setTimeout(h,i):n.fancybox.animate(f.$slide.removeClass("fancybox-slide--current"),"fancybox-animated fancybox-slide--previous fancybox-fx-"+o,i,h):h(),!0)))},cleanUp:function(t){var e,o=this;o.current.$slide.trigger("onReset"),o.$refs.container.empty().remove(),o.trigger("afterClose",t),o.$lastFocus&&o.current.opts.backFocus&&o.$lastFocus.focus(),o.current=null,e=n.fancybox.getInstance(),e?e.activate():(s.scrollTop(o.scrollTop).scrollLeft(o.scrollLeft),n("html").removeClass("fancybox-enabled"),n("#fancybox-style-noscroll").remove())},trigger:function(t,e){var o,i=Array.prototype.slice.call(arguments,1),a=this,s=e&&e.opts?e:a.current;return s?i.unshift(s):s=a,i.unshift(a),n.isFunction(s.opts[t])&&(o=s.opts[t].apply(s,i)),o===!1?o:void("afterClose"===t?r.trigger(t+".fb",i):a.$refs.container.trigger(t+".fb",i))},updateControls:function(t){var e=this,o=e.current,i=o.index,a=o.opts,s=a.caption,r=e.$refs.caption;o.$slide.trigger("refresh"),e.$caption=s&&s.length?r.html(s):null,e.isHiddenControls||e.showControls(),n("[data-fancybox-count]").html(e.group.length),n("[data-fancybox-index]").html(i+1),n("[data-fancybox-prev]").prop("disabled",!a.loop&&i<=0),n("[data-fancybox-next]").prop("disabled",!a.loop&&i>=e.group.length-1)},hideControls:function(){this.isHiddenControls=!0,this.$refs.container.removeClass("fancybox-show-infobar fancybox-show-toolbar fancybox-show-caption fancybox-show-nav")},showControls:function(){var t=this,e=t.current?t.current.opts:t.opts,n=t.$refs.container;t.isHiddenControls=!1,t.idleSecondsCounter=0,n.toggleClass("fancybox-show-toolbar",!(!e.toolbar||!e.buttons)).toggleClass("fancybox-show-infobar",!!(e.infobar&&t.group.length>1)).toggleClass("fancybox-show-nav",!!(e.arrows&&t.group.length>1)).toggleClass("fancybox-is-modal",!!e.modal),t.$caption?n.addClass("fancybox-show-caption "):n.removeClass("fancybox-show-caption")},toggleControls:function(){this.isHiddenControls?this.showControls():this.hideControls()}}),n.fancybox={version:"3.1.24",defaults:a,getInstance:function(t){var e=n('.fancybox-container:not(".fancybox-is-closing"):first').data("FancyBox"),o=Array.prototype.slice.call(arguments,1);return e instanceof h&&("string"===n.type(t)?e[t].apply(e,o):"function"===n.type(t)&&t.apply(e,o),e)},open:function(t,e,n){return new h(t,e,n)},close:function(t){var e=this.getInstance();e&&(e.close(),t===!0&&this.close())},destroy:function(){this.close(!0),r.off("click.fb-start")},isMobile:e.createTouch!==o&&/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent),use3d:function(){var n=e.createElement("div");return t.getComputedStyle&&t.getComputedStyle(n).getPropertyValue("transform")&&!(e.documentMode&&e.documentMode<11)}(),getTranslate:function(t){var e;if(!t||!t.length)return!1;if(e=t.eq(0).css("transform"),e&&e.indexOf("matrix")!==-1?(e=e.split("(")[1],e=e.split(")")[0],e=e.split(",")):e=[],e.length)e=e.length>10?[e[13],e[12],e[0],e[5]]:[e[5],e[4],e[0],e[3]],e=e.map(parseFloat);else{e=[0,0,1,1];var n=/\.*translate\((.*)px,(.*)px\)/i,o=n.exec(t.eq(0).attr("style"));o&&(e[0]=parseFloat(o[2]),e[1]=parseFloat(o[1]))}return{top:e[0],left:e[1],scaleX:e[2],scaleY:e[3],opacity:parseFloat(t.css("opacity")),width:t.width(),height:t.height()}},setTranslate:function(t,e){var n="",i={};if(t&&e)return e.left===o&&e.top===o||(n=(e.left===o?t.position().left:e.left)+"px, "+(e.top===o?t.position().top:e.top)+"px",n=this.use3d?"translate3d("+n+", 0px)":"translate("+n+")"),e.scaleX!==o&&e.scaleY!==o&&(n=(n.length?n+" ":"")+"scale("+e.scaleX+", "+e.scaleY+")"),n.length&&(i.transform=n),e.opacity!==o&&(i.opacity=e.opacity),e.width!==o&&(i.width=e.width),e.height!==o&&(i.height=e.height),t.css(i)},animate:function(t,e,i,a,s){var r=d||"transitionend";n.isFunction(i)&&(a=i,i=null),n.isPlainObject(e)||t.removeAttr("style"),t.on(r,function(i){(!i||!i.originalEvent||t.is(i.originalEvent.target)&&"z-index"!=i.originalEvent.propertyName)&&(t.off(r),n.isPlainObject(e)?e.scaleX!==o&&e.scaleY!==o&&(t.css("transition-duration","0ms"),e.width=Math.round(t.width()*e.scaleX),e.height=Math.round(t.height()*e.scaleY),e.scaleX=1,e.scaleY=1,n.fancybox.setTranslate(t,e)):s!==!0&&t.removeClass(e),n.isFunction(a)&&a(i))}),n.isNumeric(i)&&t.css("transition-duration",i+"ms"),n.isPlainObject(e)?n.fancybox.setTranslate(t,e):t.addClass(e),t.data("timer",setTimeout(function(){t.trigger("transitionend")},i+16))},stop:function(t){clearTimeout(t.data("timer")),t.off(d)}},n.fn.fancybox=function(t){var e;return t=t||{},e=t.selector||!1,e?n("body").off("click.fb-start",e).on("click.fb-start",e,{options:t},i):this.off("click.fb-start").on("click.fb-start",{items:this,options:t},i),this},r.on("click.fb-start","[data-fancybox]",i)}}(window,document,window.jQuery),function(t){"use strict";var e=function(e,n,o){if(e)return o=o||"","object"===t.type(o)&&(o=t.param(o,!0)),t.each(n,function(t,n){e=e.replace("$"+t,n||"")}),o.length&&(e+=(e.indexOf("?")>0?"&":"?")+o),e},n={youtube:{matcher:/(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i,params:{autoplay:1,autohide:1,fs:1,rel:0,hd:1,wmode:"transparent",enablejsapi:1,html5:1},paramPlace:8,type:"iframe",url:"//www.youtube.com/embed/$4",thumb:"//img.youtube.com/vi/$4/hqdefault.jpg"},vimeo:{matcher:/^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/,params:{autoplay:1,hd:1,show_title:1,show_byline:1,show_portrait:0,fullscreen:1,api:1},paramPlace:3,type:"iframe",url:"//player.vimeo.com/video/$2"},metacafe:{matcher:/metacafe.com\/watch\/(\d+)\/(.*)?/,type:"iframe",url:"//www.metacafe.com/embed/$1/?ap=1"},dailymotion:{matcher:/dailymotion.com\/video\/(.*)\/?(.*)/,params:{additionalInfos:0,autoStart:1},type:"iframe",url:"//www.dailymotion.com/embed/video/$1"},vine:{matcher:/vine.co\/v\/([a-zA-Z0-9\?\=\-]+)/,type:"iframe",url:"//vine.co/v/$1/embed/simple"},instagram:{matcher:/(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i,type:"image",url:"//$1/p/$2/media/?size=l"},gmap_place:{matcher:/(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i,type:"iframe",url:function(t){return"//maps.google."+t[2]+"/?ll="+(t[9]?t[9]+"&z="+Math.floor(t[10])+(t[12]?t[12].replace(/^\//,"&"):""):t[12])+"&output="+(t[12]&&t[12].indexOf("layer=c")>0?"svembed":"embed")}},gmap_search:{matcher:/(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(maps\/search\/)(.*)/i,type:"iframe",url:function(t){return"//maps.google."+t[2]+"/maps?q="+t[5].replace("query=","q=").replace("api=1","")+"&output=embed"}}};t(document).on("onInit.fb",function(o,i){t.each(i.group,function(o,i){var a,s,r,c,l,u,d,f=i.src||"",h=!1;i.type||(a=t.extend(!0,{},n,i.opts.media),t.each(a,function(n,o){if(r=f.match(o.matcher),u={},d=n,r){if(h=o.type,o.paramPlace&&r[o.paramPlace]){l=r[o.paramPlace],"?"==l[0]&&(l=l.substring(1)),l=l.split("&");for(var a=0;ae.clientHeight,a=("scroll"===o||"auto"===o)&&e.scrollWidth>e.clientWidth;return i||a},l=function(t){for(var e=!1;;){if(e=c(t.get(0)))break;if(t=t.parent(),!t.length||t.hasClass("fancybox-stage")||t.is("body"))break}return e},u=function(t){var e=this;e.instance=t,e.$bg=t.$refs.bg,e.$stage=t.$refs.stage,e.$container=t.$refs.container,e.destroy(),e.$container.on("touchstart.fb.touch mousedown.fb.touch",n.proxy(e,"ontouchstart"))};u.prototype.destroy=function(){this.$container.off(".fb.touch")},u.prototype.ontouchstart=function(o){var i=this,c=n(o.target),u=i.instance,d=u.current,f=d.$content,h="touchstart"==o.type;if(h&&i.$container.off("mousedown.fb.touch"),!d||i.instance.isAnimating||i.instance.isClosing)return o.stopPropagation(),void o.preventDefault();if((!o.originalEvent||2!=o.originalEvent.button)&&c.length&&!r(c)&&!r(c.parent())&&!(o.originalEvent.clientX>c[0].clientWidth+c.offset().left)&&(i.startPoints=a(o),i.startPoints&&!(i.startPoints.length>1&&u.isSliding))){if(i.$target=c,i.$content=f,i.canTap=!0,n(e).off(".fb.touch"),n(e).on(h?"touchend.fb.touch touchcancel.fb.touch":"mouseup.fb.touch mouseleave.fb.touch",n.proxy(i,"ontouchend")),n(e).on(h?"touchmove.fb.touch":"mousemove.fb.touch",n.proxy(i,"ontouchmove")),o.stopPropagation(),!u.current.opts.touch&&!u.canPan()||!c.is(i.$stage)&&!i.$stage.find(c).length)return void(c.is("img")&&o.preventDefault());n.fancybox.isMobile&&(l(i.$target)||l(i.$target.parent()))||o.preventDefault(),i.canvasWidth=Math.round(d.$slide[0].clientWidth),i.canvasHeight=Math.round(d.$slide[0].clientHeight),i.startTime=(new Date).getTime(),i.distanceX=i.distanceY=i.distance=0,i.isPanning=!1,i.isSwiping=!1,i.isZooming=!1,i.sliderStartPos=i.sliderLastPos||{top:0,left:0},i.contentStartPos=n.fancybox.getTranslate(i.$content),i.contentLastPos=null,1!==i.startPoints.length||i.isZooming||(i.canTap=!u.isSliding,"image"===d.type&&(i.contentStartPos.width>i.canvasWidth+1||i.contentStartPos.height>i.canvasHeight+1)?(n.fancybox.stop(i.$content),i.$content.css("transition-duration","0ms"),i.isPanning=!0):i.isSwiping=!0,i.$container.addClass("fancybox-controls--isGrabbing")),2!==i.startPoints.length||u.isAnimating||d.hasError||"image"!==d.type||!d.isLoaded&&!d.$ghost||(i.isZooming=!0,i.isSwiping=!1,i.isPanning=!1,n.fancybox.stop(i.$content),i.$content.css("transition-duration","0ms"),i.centerPointStartX=.5*(i.startPoints[0].x+i.startPoints[1].x)-n(t).scrollLeft(),i.centerPointStartY=.5*(i.startPoints[0].y+i.startPoints[1].y)-n(t).scrollTop(),i.percentageOfImageAtPinchPointX=(i.centerPointStartX-i.contentStartPos.left)/i.contentStartPos.width,i.percentageOfImageAtPinchPointY=(i.centerPointStartY-i.contentStartPos.top)/i.contentStartPos.height,i.startDistanceBetweenFingers=s(i.startPoints[0],i.startPoints[1]))}},u.prototype.ontouchmove=function(t){var e=this;if(e.newPoints=a(t),n.fancybox.isMobile&&(l(e.$target)||l(e.$target.parent())))return t.stopPropagation(),void(e.canTap=!1);if((e.instance.current.opts.touch||e.instance.canPan())&&e.newPoints&&e.newPoints.length&&(e.distanceX=s(e.newPoints[0],e.startPoints[0],"x"),e.distanceY=s(e.newPoints[0],e.startPoints[0],"y"),e.distance=s(e.newPoints[0],e.startPoints[0]),e.distance>0)){if(!e.$target.is(e.$stage)&&!e.$stage.find(e.$target).length)return;t.stopPropagation(),t.preventDefault(),e.isSwiping?e.onSwipe():e.isPanning?e.onPan():e.isZooming&&e.onZoom()}},u.prototype.onSwipe=function(){var e,a=this,s=a.isSwiping,r=a.sliderStartPos.left||0;s===!0?Math.abs(a.distance)>10&&(a.canTap=!1,a.instance.group.length<2&&a.instance.opts.touch.vertical?a.isSwiping="y":a.instance.isSliding||a.instance.opts.touch.vertical===!1||"auto"===a.instance.opts.touch.vertical&&n(t).width()>800?a.isSwiping="x":(e=Math.abs(180*Math.atan2(a.distanceY,a.distanceX)/Math.PI),a.isSwiping=e>45&&e<135?"y":"x"),a.instance.isSliding=a.isSwiping,a.startPoints=a.newPoints,n.each(a.instance.slides,function(t,e){n.fancybox.stop(e.$slide),e.$slide.css("transition-duration","0ms"),e.inTransition=!1,e.pos===a.instance.current.pos&&(a.sliderStartPos.left=n.fancybox.getTranslate(e.$slide).left)}),a.instance.SlideShow&&a.instance.SlideShow.isActive&&a.instance.SlideShow.stop()):("x"==s&&(a.distanceX>0&&(a.instance.group.length<2||0===a.instance.current.index&&!a.instance.current.opts.loop)?r+=Math.pow(a.distanceX,.8):a.distanceX<0&&(a.instance.group.length<2||a.instance.current.index===a.instance.group.length-1&&!a.instance.current.opts.loop)?r-=Math.pow(-a.distanceX,.8):r+=a.distanceX),a.sliderLastPos={top:"x"==s?0:a.sliderStartPos.top+a.distanceY,left:r},a.requestId&&(i(a.requestId),a.requestId=null),a.requestId=o(function(){a.sliderLastPos&&(n.each(a.instance.slides,function(t,e){var o=e.pos-a.instance.currPos;n.fancybox.setTranslate(e.$slide,{top:a.sliderLastPos.top,left:a.sliderLastPos.left+o*a.canvasWidth+o*e.opts.gutter})}),a.$container.addClass("fancybox-is-sliding"))}))},u.prototype.onPan=function(){var t,e,a,s=this;s.canTap=!1,t=s.contentStartPos.width>s.canvasWidth?s.contentStartPos.left+s.distanceX:s.contentStartPos.left,e=s.contentStartPos.top+s.distanceY,a=s.limitMovement(t,e,s.contentStartPos.width,s.contentStartPos.height),a.scaleX=s.contentStartPos.scaleX,a.scaleY=s.contentStartPos.scaleY,s.contentLastPos=a,s.requestId&&(i(s.requestId),s.requestId=null),s.requestId=o(function(){n.fancybox.setTranslate(s.$content,s.contentLastPos)})},u.prototype.limitMovement=function(t,e,n,o){var i,a,s,r,c=this,l=c.canvasWidth,u=c.canvasHeight,d=c.contentStartPos.left,f=c.contentStartPos.top,h=c.distanceX,p=c.distanceY;return i=Math.max(0,.5*l-.5*n),a=Math.max(0,.5*u-.5*o),s=Math.min(l-n,.5*l-.5*n),r=Math.min(u-o,.5*u-.5*o),n>l&&(h>0&&t>i&&(t=i-1+Math.pow(-i+d+h,.8)||0),h<0&&tu&&(p>0&&e>a&&(e=a-1+Math.pow(-a+f+p,.8)||0),p<0&&ea?(t=t>0?0:t,t=ts?(e=e>0?0:e,e=e50?(n.fancybox.animate(e.instance.current.$slide,{top:e.sliderStartPos.top+e.distanceY+150*e.velocityY,opacity:0},150),o=e.instance.close(!0,300)):"x"==t&&e.distanceX>50&&e.instance.group.length>1?o=e.instance.previous(e.speedX):"x"==t&&e.distanceX<-50&&e.instance.group.length>1&&(o=e.instance.next(e.speedX)),o!==!1||"x"!=t&&"y"!=t||e.instance.jumpTo(e.instance.current.index,150),e.$container.removeClass("fancybox-is-sliding")},u.prototype.endPanning=function(){var t,e,o,i=this;i.contentLastPos&&(i.instance.current.opts.touch.momentum===!1?(t=i.contentLastPos.left,e=i.contentLastPos.top):(t=i.contentLastPos.left+i.velocityX*i.speed,e=i.contentLastPos.top+i.velocityY*i.speed),o=i.limitPosition(t,e,i.contentStartPos.width,i.contentStartPos.height),o.width=i.contentStartPos.width,o.height=i.contentStartPos.height,n.fancybox.animate(i.$content,o,330))},u.prototype.endZooming=function(){var t,e,o,i,a=this,s=a.instance.current,r=a.newWidth,c=a.newHeight;a.contentLastPos&&(t=a.contentLastPos.left,e=a.contentLastPos.top,i={top:e,left:t,width:r,height:c,scaleX:1,scaleY:1},n.fancybox.setTranslate(a.$content,i),rs.width||c>s.height?a.instance.scaleToActual(a.centerPointStartX,a.centerPointStartY,150):(o=a.limitPosition(t,e,r,c),n.fancybox.setTranslate(a.content,n.fancybox.getTranslate(a.$content)),n.fancybox.animate(a.$content,o,150)))},u.prototype.onTap=function(t){var e,o=this,i=n(t.target),s=o.instance,r=s.current,c=t&&a(t)||o.startPoints,l=c[0]?c[0].x-o.$stage.offset().left:0,u=c[0]?c[0].y-o.$stage.offset().top:0,d=function(e){var i=r.opts[e];if(n.isFunction(i)&&(i=i.apply(s,[r,t])),i)switch(i){case"close":s.close(o.startEvent);break;case"toggleControls":s.toggleControls(!0);break;case"next":s.next();break;case"nextOrClose":s.group.length>1?s.next():s.close(o.startEvent);break;case"zoom":"image"==r.type&&(r.isLoaded||r.$ghost)&&(s.canPan()?s.scaleToFit():s.isScaledDown()?s.scaleToActual(l,u):s.group.length<2&&s.close(o.startEvent))}};if(!(t.originalEvent&&2==t.originalEvent.button||s.isSliding||l>i[0].clientWidth+i.offset().left)){if(i.is(".fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-container"))e="Outside";else if(i.is(".fancybox-slide"))e="Slide";else{if(!s.current.$content||!s.current.$content.has(t.target).length)return;e="Content"}if(o.tapped){if(clearTimeout(o.tapped),o.tapped=null,Math.abs(l-o.tapX)>50||Math.abs(u-o.tapY)>50||s.isSliding)return this;d("dblclick"+e)}else o.tapX=l,o.tapY=u,r.opts["dblclick"+e]&&r.opts["dblclick"+e]!==r.opts["click"+e]?o.tapped=setTimeout(function(){o.tapped=null,d("click"+e)},300):d("click"+e);return this}},n(e).on("onActivate.fb",function(t,e){e&&!e.Guestures&&(e.Guestures=new u(e))}),n(e).on("beforeClose.fb",function(t,e){e&&e.Guestures&&e.Guestures.destroy()})}(window,document,window.jQuery),function(t,e){"use strict";var n=function(t){this.instance=t,this.init()};e.extend(n.prototype,{timer:null,isActive:!1,$button:null,speed:3e3,init:function(){var t=this;t.$button=t.instance.$refs.toolbar.find("[data-fancybox-play]").on("click",function(){t.toggle()}),(t.instance.group.length<2||!t.instance.group[t.instance.currIndex].opts.slideShow)&&t.$button.hide()},set:function(){var t=this;t.instance&&t.instance.current&&(t.instance.current.opts.loop||t.instance.currIndex1&&t.instance.group[t.instance.currIndex].opts.thumbs&&("image"==e.type||e.opts.thumb||e.opts.$thumb)&&("image"==n.type||n.opts.thumb||n.opts.$thumb)?(t.$button.on("click",function(){t.toggle()}),t.isActive=!0):(t.$button.hide(),t.isActive=!1)},create:function(){var t,n,o=this.instance;this.$grid=e('
').appendTo(o.$refs.container),t="
    ",e.each(o.group,function(e,o){n=o.opts.thumb||(o.opts.$thumb?o.opts.$thumb.attr("src"):null),n||"image"!==o.type||(n=o.src),n&&n.length&&(t+='
  • ')}),t+="
",this.$list=e(t).appendTo(this.$grid).on("click","li",function(){o.jumpTo(e(this).data("index"))}),this.$list.find("img").hide().one("load",function(){var t,n,o,i,a=e(this).parent().removeClass("fancybox-thumbs-loading"),s=a.outerWidth(),r=a.outerHeight();t=this.naturalWidth||this.width,n=this.naturalHeight||this.height,o=t/s,i=n/r,o>=1&&i>=1&&(o>i?(t/=i,n=r):(t=s,n/=o)),e(this).css({width:Math.floor(t),height:Math.floor(n),"margin-top":Math.min(0,Math.floor(.3*r-.3*n)),"margin-left":Math.min(0,Math.floor(.5*s-.5*t))}).show()}).each(function(){this.src=e(this).data("src")})},focus:function(){this.instance.current&&this.$list.children().removeClass("fancybox-thumbs-active").filter('[data-index="'+this.instance.current.index+'"]').addClass("fancybox-thumbs-active").focus()},close:function(){this.$grid.hide()},update:function(){this.instance.$refs.container.toggleClass("fancybox-show-thumbs",this.isVisible),this.isVisible?(this.$grid||this.create(),this.instance.trigger("onThumbsShow"),this.focus()):this.$grid&&this.instance.trigger("onThumbsHide"),this.instance.update()},hide:function(){this.isVisible=!1,this.update()},show:function(){this.isVisible=!0,this.update()},toggle:function(){this.isVisible=!this.isVisible,this.update()}}),e(t).on({"onInit.fb":function(t,e){e&&!e.Thumbs&&(e.Thumbs=new n(e))},"beforeShow.fb":function(t,e,n,o){var i=e&&e.Thumbs;if(i&&i.isActive){if(n.modal)return i.$button.hide(),void i.hide();o&&e.opts.thumbs.autoStart===!0&&i.show(),i.isVisible&&i.focus()}},"afterKeydown.fb":function(t,e,n,o,i){var a=e&&e.Thumbs;a&&a.isActive&&71===i&&(o.preventDefault(),a.toggle())},"beforeClose.fb":function(t,e){var n=e&&e.Thumbs;n&&n.isVisible&&e.opts.thumbs.hideOnClose!==!1&&n.close()}})}(document,window.jQuery),function(t,e,n){"use strict";function o(){var t=e.location.hash.substr(1),n=t.split("-"),o=n.length>1&&/^\+?\d+$/.test(n[n.length-1])?parseInt(n.pop(-1),10)||1:1,i=n.join("-");return o<1&&(o=1),{hash:t,index:o,gallery:i}}function i(t){var e;""!==t.gallery&&(e=n("[data-fancybox='"+n.escapeSelector(t.gallery)+"']").eq(t.index-1),e.length||(e=n("#"+n.escapeSelector(t.gallery))),e.length&&(s=!1,e.trigger("click")))}function a(t){var e;return!!t&&(e=t.current?t.current.opts:t.opts,e.$orig?e.$orig.data("fancybox"):e.hash||"")}n.escapeSelector||(n.escapeSelector=function(t){var e=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g,n=function(t,e){return e?"\0"===t?"�":t.slice(0,-1)+"\\"+t.charCodeAt(t.length-1).toString(16)+" ":"\\"+t};return(t+"").replace(e,n)});var s=!0,r=null,c=null;n(function(){setTimeout(function(){n.fancybox.defaults.hash!==!1&&(n(t).on({"onInit.fb":function(t,e){var n,i;e.group[e.currIndex].opts.hash!==!1&&(n=o(),i=a(e),i&&n.gallery&&i==n.gallery&&(e.currIndex=n.index-1))},"beforeShow.fb":function(n,o,i){var l;i.opts.hash!==!1&&(l=a(o),l&&""!==l&&(e.location.hash.indexOf(l)<0&&(o.opts.origHash=e.location.hash),r=l+(o.group.length>1?"-"+(i.index+1):""),"replaceState"in e.history?(c&&clearTimeout(c),c=setTimeout(function(){e.history[s?"pushState":"replaceState"]({},t.title,e.location.pathname+e.location.search+"#"+r),c=null,s=!1},300)):e.location.hash=r))},"beforeClose.fb":function(o,i,s){var l,u;c&&clearTimeout(c),s.opts.hash!==!1&&(l=a(i),u=i&&i.opts.origHash?i.opts.origHash:"",l&&""!==l&&("replaceState"in history?e.history.replaceState({},t.title,e.location.pathname+e.location.search+u):(e.location.hash=u,n(e).scrollTop(i.scrollTop).scrollLeft(i.scrollLeft))),r=null)}}),n(e).on("hashchange.fb",function(){var t=o();n.fancybox.getInstance()?!r||r===t.gallery+"-"+t.index||1===t.index&&r==t.gallery||(r=null,n.fancybox.close(),s=!0):""!==t.gallery&&i(t)}),i(o()))},50)})}(document,window,window.jQuery); \ No newline at end of file diff --git a/fancybox/docs/index.html b/fancybox/docs/index.html new file mode 100644 index 0000000..bb5fc1d --- /dev/null +++ b/fancybox/docs/index.html @@ -0,0 +1,1827 @@ + + + + fancyBox - touch enabled, responsive and fully customizable lightbox script + + + + + + + + + + + + +
+
+

fancyBox3 Documentation

+ + +
+
+ +
+
+ + + +

Introduction

+ +

+ fancyBox is a JavaScript library used to present images, videos and any html content in an elegant way. + It has all features you would expect - touch enabled, responsive and fully customizable. +

+ +

+ +

+ Dependencies +

+ +

+ jQuery 3+ is preferred, but fancyBox works with jQuery 1.9.1+ and jQuery 2+ +

+ +

Compatibility

+ +

+ fancyBox includes support for touch gestures and even supports pinch gestures for zooming. + It is perfectly suited for both mobile and desktop browsers. +

+ +

+ fancyBox has been tested in following browsers/devices: +

+ +
    +
  • Chrome
  • +
  • Firefox
  • +
  • IE10/11
  • +
  • Edge
  • +
  • iOS Safari
  • +
  • Nexus 7 Chrome
  • +
+ +

Setup

+ +

+ You can install fancyBox by linking .css and .js to your html file. + + Make sure you also load the jQuery library. + Below is a basic HTML template to use as an example: +

+ +
<!DOCTYPE html>
+<html>
+<head>
+	<meta charset="utf-8">
+	<title>My page</title>
+
+	<!-- CSS -->
+	<link rel="stylesheet" type="text/css" href="jquery.fancybox.min.css">
+</head>
+<body>
+
+	<!-- Your HTML content goes here -->
+
+	<!-- JS -->
+	<script src="//code.jquery.com/jquery-3.2.1.min.js"></script>
+	<script src="jquery.fancybox.min.js"></script>
+</body>
+</html>
+
+ +

Important

+ +
    +
  • Make sure you add the jQuery library first
  • +
  • If you already have jQuery on your page, you shouldn't include it second time
  • +
  • Do not include both fancybox.js and fancybox.min.js files
  • +
  • + Some functionality (ajax, iframes, etc) will not work when you're opening local files on your computer, + the code must be running on a web server +
  • +
+ +

Download fancyBox

+ +

+ You can download the latest version of fancyBox on GitHub. +
+ Or just link directly to fancyBox files on cdnjs - https://cdnjs.com/libraries/fancybox. +

+ + +

Package Managers

+ +

+ fancyBox can also be installed via Bower or npm. +

+ +
# Bower
+bower install fancybox --save
+
+# NPM
+npm install @fancyapps/fancybox --save
+
+ + + +

How to Use

+ +

Initialize with data attributes

+ +

+ The most basic way to use fancyBox is by adding the data-fancybox attribute to a link. + A caption can be added using the data-caption attribute. Example: +

+ +
<a href="image.jpg" data-fancybox data-caption="My caption">
+	<img src="thumbnail.jpg" alt="" />
+</a>
+ +

+ If you choose this method, default settings will be applied. + See Options section for examples how to customize by changing defaults or using data-options attribute. +

+ +

Initialize with JavaScript

+ +

+ Select elements with a jQuery selector and call the fancybox method: +

+ +
<script type="text/javascript">
+	$("[data-fancybox]").fancybox({
+		// Options will go here
+	});
+</script>
+ + +

+ Using this method, click event handler is attached only to the currently selected elements. +
+ To attach click event listener for elements that exist now or in the future, use selector option. Example: +

+ +
$().fancybox({
+  selector : '[data-fancybox="images"]',
+  loop     : true
+});
+ +

+ View demo on CodePen +

+ +

Manual calling of fancyBox

+ +

+ fancyBox can be activated at any point within Javascript and therefore does not necessarily need a trigger element. + + Example of displaying a simple message: +

+ +
$.fancybox.open('<div class="message"><h2>Hello!</h2><p>You are awesome!</p></div>');
+ +

+ See API section for more information and examples. +

+ +

Grouping

+ +

+ If you have a group of items, you can use the same attribute data-fancybox value for each of them to create a gallery. + Each group should have a unique value: +

+ +
<a href="image_1.jpg" data-fancybox="group" data-caption="Caption #1">
+	<img src="thumbnail_1.jpg" alt="" />
+</a>
+
+<a href="image_2.jpg" data-fancybox="group" data-caption="Caption #2">
+	<img src="thumbnail_2.jpg" alt="" />
+</a>
+
+ + +

Important

+ +

+ fancyBox attempts to automatically detect the type of content based on the given url. + + If it cannot be detected, the type can also be set manually using data-type attribute: + +

<a href="images.php?id=123" data-type="image" data-caption="Caption">
+	Show image
+</a>
+

+ + + + +

Media types

+ + +

Images

+ +

+ The standard way of using fancyBox is with a number of thumbnail images that link to larger images: +

+ +
<a href="image.jpg" data-fancybox="images" data-caption="My caption">
+	<img src="thumbnail.jpg" alt="" />
+</a>
+ +

+ View demo on CodePen +

+ +

+ By default, fancyBox fully preloads an image before displaying it. + You can choose to display the image right away. + It will render and show the full size image while the data is being received. + To do so, some attributes are necessary: +

+ +
    +
  • data-width - the real width of the image
  • +
  • data-height - the real height of the image
  • +
+ +
<a href="image.jpg" data-fancybox="images" data-width="2048" data-height="1365">
+    <img src="thumbnail.jpg" />
+</a>
+

+ View demo on CodePen +

+ +

+ fancyBox supports "scrset" so I can display different images based on viewport width. You can use this to improve download times for mobile users and over time save bandwidth. + Example: +

+ +
<a href="medium.jpg" data-fancybox="images" data-srcset="large.jpg 1600w, medium.jpg 1200w, small.jpg 640w">
+	<img src="thumbnail.jpg" />
+</a>
+

+ View demo on CodePen +

+ +

+ It is also possible to protect images from downloading by right-click. + While this does not protect from truly determined users, it should discourage the vast majority from ripping off your files. +

+ +
$('[data-fancybox]').fancybox({
+	protect: true
+});
+ +

+ View demo on CodePen +

+ +

Inline HTML

+ +

+ For inline content, create a hidden element with unique id: +

+ +
<div style="display: none;" id="hidden-content">
+	<h2>Hello</h2>
+	<p>You are awesome.</p>
+</div>
+ +

+ And then simply create a link having data-src attribute that matches ID of the element you want to open (preceded by a hash mark (#); in this example - #hidden-content): +

+ +
<a data-fancybox data-src="#hidden-content" href="javascript:;">
+	Hidden div
+</a>
+

+ View demo on CodePen +

+ +

+ The script will append small close button (if you have not disabled by smallBtn:false) + and will not apply any styles except for centering. Therefore you can easily set custom dimensions using CSS. +

+ +

Ajax

+ +

+ To load content via AJAX, you need to add a data-type="ajax" attribute to your link: +

+ +
<a data-fancybox data-type="ajax" data-src="my_page.com/path/to/ajax/" href="javascript:;">
+	AJAX content
+</a>
+

+ View demo on CodePen +

+ +

+ Additionally it is possible to define a selector with the data-filter attribute to show only a part of the response. The selector can be any string, that is a valid jQuery selector: +

+ +
<a data-fancybox data-type="ajax" data-src="my_page.com/path/to/ajax/" data-filter="#two" href="javascript:;">
+	AJAX content
+</a>
+
+

+ View demo on CodePen +

+ +

Iframe

+ +

+ If the content can be shown on a page, and placement in an iframe is not blocked by script or security configuration of that page, + it can be presented in a fancyBox: +

+ +
<a data-fancybox data-type="iframe" data-src="http://codepen.io/fancyapps/full/jyEGGG/" href="javascript:;">
+	Webpage
+</a>
+
+<a data-fancybox data-type="iframe" data-src="https://mozilla.github.io/pdf.js/web/viewer.html" href="javascript:;">
+	Sample PDF
+</a>
+
+

+ View demo on CodePen +

+ + +

+ To access and control fancyBox in parent window from inside an iframe: +

+ +
// Adjust iframe height according to the contents
+parent.jQuery.fancybox.getInstance().update();
+
+// Close current fancyBox instance
+parent.jQuery.fancybox.getInstance().close();
+ + +

+ Iframe dimensions can be controlled by CSS: +

+ +
.fancybox-slide--iframe .fancybox-content {
+	width  : 800px;
+	height : 600px;
+	max-width  : 80%;
+	max-height : 80%;
+	margin: 0;
+}
+ + +

+ These CSS rules can be overridden by JS, if needed: +

+ +
$("[data-fancybox]").fancybox({
+	iframe : {
+		css : {
+			width : '600px'
+		}
+	}
+});
+ + +

+ If you have not disabled iframe preloading (using preload option), then the script will atempt to + calculate content dimensions and will adjust width/height of iframe to fit with content in it. + Keep in mind, that due to same origin policy, + there are some limitations. +

+ +

+ This example will disable iframe preloading and will display small close button next to iframe instead of the toolbar: +

+ +
$('[data-fancybox]').fancybox({
+	toolbar  : false,
+	smallBtn : true,
+	iframe : {
+		preload : false
+	}
+})
+
+

+ View demo on CodePen +

+ + + +

Embedding

+ +

+ Supported sites can be used with fancyBox by just providing the page URL: +

+ +
<a data-fancybox href="https://www.youtube.com/watch?v=_sI_Ps7JSEk">
+  YouTube video
+</a>
+
+<a data-fancybox href="https://vimeo.com/191947042">
+  Vimeo video
+</a>
+
+<a data-fancybox href="https://www.google.com/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572">
+	Google Map
+</a>
+
+<a data-fancybox href="https://www.instagram.com/p/BNXYW8-goPI/?taken-by=jamesrelfdyer" data-caption="<span title=&quot;Edited&quot;>balloon rides at dawn ✨🎈<br>was such a magical experience floating over napa valley as the golden light hit the hills.<br><a href=&quot;https://www.instagram.com/jamesrelfdyer/&quot;>@jamesrelfdyer</a></span>">
+	Instagram photo
+</a>
+

+ View demo on CodePen +

+ +

Video dimensions

+ +

+ Resize video display with the following CSS: +

+ +
.fancybox-slide--video .fancybox-content {
+	width  : 800px;
+	height : 600px;
+	max-width  : 80%;
+	max-height : 80%;
+}
+

+ View demo on CodePen +

+ +

+ Obviously, you can choose any size you like, any combination with min/max values. +
+ Aspect ratio lock for videos is not implemented yet, but if you wish, you can use this snippet. +

+ + + +

Video parameters

+ +

+ Controlling a video via URL parameters: +

+ +
<a data-fancybox href="https://www.youtube.com/watch?v=_sI_Ps7JSEk&amp;autoplay=1&amp;rel=0&amp;controls=0&amp;showinfo=0">
+  YouTube video - hide controls and info
+</a>
+
+<a data-fancybox href="https://vimeo.com/191947042?color=f00">
+  Vimeo video - custom color
+</a>
+

+ View demo on CodePen +

+ + +

+ Via JavaScript: +

+ +
$('[data-fancybox]').fancybox({
+	youtube : {
+		controls : 0,
+		showinfo : 0
+	},
+	vimeo : {
+		color : 'f00'
+	}
+});
+

+ View demo on CodePen +

+ + +

Options

+ +

+ Quick reference for all default options as defined in the source: +

+ +
defaults = {
+
+	// Enable infinite gallery navigation
+	loop : false,
+
+	// Space around image, ignored if zoomed-in or viewport smaller than 800px
+	margin : [44, 0],
+
+	// Horizontal space between slides
+	gutter : 50,
+
+	// Enable keyboard navigation
+	keyboard : true,
+
+	// Should display navigation arrows at the screen edges
+	arrows : true,
+
+	// Should display infobar (counter and arrows at the top)
+	infobar : false,
+
+	// Should display toolbar (buttons at the top)
+	toolbar : true,
+
+	// What buttons should appear in the top right corner.
+	// Buttons will be created using templates from `btnTpl` option
+	// and they will be placed into toolbar (class="fancybox-toolbar"` element)
+	buttons : [
+		'slideShow',
+		'fullScreen',
+		'thumbs',
+		'close'
+	],
+
+	// Detect "idle" time in seconds
+	idleTime : 4,
+
+	// Should display buttons at top right corner of the content
+	// If 'auto' - they will be created for content having type 'html', 'inline' or 'ajax'
+	// Use template from `btnTpl.smallBtn` for customization
+	smallBtn : 'auto',
+
+	// Disable right-click and use simple image protection for images
+	protect : false,
+
+	// Shortcut to make content "modal" - disable keyboard navigtion, hide buttons, etc
+	modal : false,
+
+	image : {
+
+		// Wait for images to load before displaying
+		// Requires predefined image dimensions
+		// If 'auto' - will zoom in thumbnail if 'width' and 'height' attributes are found
+		preload : "auto",
+
+	},
+
+	ajax : {
+
+		// Object containing settings for ajax request
+		settings : {
+
+			// This helps to indicate that request comes from the modal
+			// Feel free to change naming
+			data : {
+				fancybox : true
+			}
+		}
+
+	},
+
+	iframe : {
+
+		// Iframe template
+		tpl : '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen allowtransparency="true" src=""></iframe>',
+
+		// Preload iframe before displaying it
+		// This allows to calculate iframe content width and height
+		// (note: Due to "Same Origin Policy", you can't get cross domain data).
+		preload : true,
+
+		// Custom CSS styling for iframe wrapping element
+		// You can use this to set custom iframe dimensions
+		css : {},
+
+		// Iframe tag attributes
+		attr : {
+			scrolling : 'auto'
+		}
+
+	},
+
+	// Open/close animation type
+	// Possible values:
+	//   false            - disable
+	//   "zoom"           - zoom images from/to thumbnail
+	//   "fade"
+	//   "zoom-in-out"
+	//
+	animationEffect : "zoom",
+
+	// Duration in ms for open/close animation
+	animationDuration : 366,
+
+	// Should image change opacity while zooming
+	// If opacity is 'auto', then opacity will be changed if image and thumbnail have different aspect ratios
+	zoomOpacity : 'auto',
+
+	// Transition effect between slides
+	//
+	// Possible values:
+	//   false            - disable
+	//   "fade'
+	//   "slide'
+	//   "circular'
+	//   "tube'
+	//   "zoom-in-out'
+	//   "rotate'
+	//
+	transitionEffect : "fade",
+
+	// Duration in ms for transition animation
+	transitionDuration : 366,
+
+	// Custom CSS class for slide element
+	slideClass : '',
+
+	// Custom CSS class for layout
+	baseClass : '',
+
+	// Base template for layout
+	baseTpl	:
+		'<div class="fancybox-container" role="dialog" tabindex="-1">' +
+			'<div class="fancybox-bg"></div>' +
+			'<div class="fancybox-inner">' +
+				'<div class="fancybox-infobar">' +
+					'<button data-fancybox-prev title="{{PREV}}" class="fancybox-button fancybox-button--left"></button>' +
+					'<div class="fancybox-infobar__body">' +
+						'<span data-fancybox-index></span>&nbsp;/&nbsp;<span data-fancybox-count></span>' +
+					'</div>' +
+					'<button data-fancybox-next title="{{NEXT}}" class="fancybox-button fancybox-button--right"></button>' +
+				'</div>' +
+				'<div class="fancybox-toolbar">' +
+					'{{BUTTONS}}' +
+				'</div>' +
+				'<div class="fancybox-navigation">' +
+					'<button data-fancybox-prev title="{{PREV}}" class="fancybox-arrow fancybox-arrow--left" />' +
+					'<button data-fancybox-next title="{{NEXT}}" class="fancybox-arrow fancybox-arrow--right" />' +
+				'</div>' +
+				'<div class="fancybox-stage"></div>' +
+				'<div class="fancybox-caption-wrap">' +
+					'<div class="fancybox-caption"></div>' +
+				'</div>' +
+			'</div>' +
+		'</div>',
+
+	// Loading indicator template
+	spinnerTpl : '<div class="fancybox-loading"></div>',
+
+	// Error message template
+	errorTpl : '<div class="fancybox-error"><p>{{ERROR}}<p></div>',
+
+	btnTpl : {
+		slideShow  : '<button data-fancybox-play class="fancybox-button fancybox-button--play" title="{{PLAY_START}}"></button>',
+		fullScreen : '<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fullscreen" title="{{FULL_SCREEN}}"></button>',
+		thumbs     : '<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="{{THUMBS}}"></button>',
+		close      : '<button data-fancybox-close class="fancybox-button fancybox-button--close" title="{{CLOSE}}"></button>',
+
+		// This small close button will be appended to your html/inline/ajax content by default,
+		// if "smallBtn" option is not set to false
+		smallBtn   : '<button data-fancybox-close class="fancybox-close-small" title="{{CLOSE}}"></button>'
+	},
+
+	// Container is injected into this element
+	parentEl : 'body',
+
+
+	// Focus handling
+	// ==============
+
+	// Try to focus on the first focusable element after opening
+	autoFocus : true,
+
+	// Put focus back to active element after closing
+	backFocus : true,
+
+	// Do not let user to focus on element outside modal content
+	trapFocus : true,
+
+
+	// Module specific options
+	// =======================
+
+	fullScreen : {
+		autoStart : false,
+	},
+
+	touch : {
+		vertical : true,  // Allow to drag content vertically
+		momentum : true   // Continue movement after releasing mouse/touch when panning
+	},
+
+	// Hash value when initializing manually,
+	// set `false` to disable hash change
+	hash : null,
+
+	// Customize or add new media types
+	// Example:
+	/*
+	media : {
+		youtube : {
+			params : {
+				autoplay : 0
+			}
+		}
+	}
+	*/
+	media : {},
+
+	slideShow : {
+		autoStart : false,
+		speed     : 4000
+	},
+
+	thumbs : {
+		autoStart   : false,   // Display thumbnails on opening
+		hideOnClose : true     // Hide thumbnail grid when closing animation starts
+	},
+
+	// Callbacks
+	//==========
+
+	// See Documentation/API/Events for more information
+	// Example:
+	/*
+		afterShow: function( instance, current ) {
+			 console.info( 'Clicked element:' );
+			 console.info( current.opts.$orig );
+		}
+	*/
+
+	onInit       : $.noop,  // When instance has been initialized
+
+	beforeLoad   : $.noop,  // Before the content of a slide is being loaded
+	afterLoad    : $.noop,  // When the content of a slide is done loading
+
+	beforeShow   : $.noop,  // Before open animation starts
+	afterShow    : $.noop,  // When content is done loading and animating
+
+	beforeClose  : $.noop,  // Before the instance attempts to close. Return false to cancel the close.
+	afterClose   : $.noop,  // After instance has been closed
+
+	onActivate   : $.noop,  // When instance is brought to front
+	onDeactivate : $.noop,  // When other instance has been activated
+
+
+	// Interaction
+	// ===========
+
+	// Use options below to customize taken action when user clicks or double clicks on the fancyBox area,
+	// each option can be string or method that returns value.
+	//
+	// Possible values:
+	//   "close"           - close instance
+	//   "next"            - move to next gallery item
+	//   "nextOrClose"     - move to next gallery item or close if gallery has only one item
+	//   "toggleControls"  - show/hide controls
+	//   "zoom"            - zoom image (if loaded)
+	//   false             - do nothing
+
+	// Clicked on the content
+	clickContent : function( current, event ) {
+		return current.type === 'image' ? 'zoom' : false;
+	},
+
+	// Clicked on the slide
+	clickSlide : 'close',
+
+	// Clicked on the background (backdrop) element
+	clickOutside : 'close',
+
+	// Same as previous two, but for double click
+	dblclickContent : false,
+	dblclickSlide   : false,
+	dblclickOutside : false,
+
+
+	// Custom options when mobile device is detected
+	// =============================================
+
+	mobile : {
+		clickContent : function( current, event ) {
+			return current.type === 'image' ? 'toggleControls' : false;
+		},
+		clickSlide : function( current, event ) {
+			return current.type === 'image' ? 'toggleControls' : "close";
+		},
+		dblclickContent : function( current, event ) {
+			return current.type === 'image' ? 'zoom' : false;
+		},
+		dblclickSlide : function( current, event ) {
+			return current.type === 'image' ? 'zoom' : false;
+		}
+	},
+
+
+	// Internationalization
+	// ============
+
+	lang : 'en',
+	i18n : {
+		'en' : {
+			CLOSE       : 'Close',
+			NEXT        : 'Next',
+			PREV        : 'Previous',
+			ERROR       : 'The requested content cannot be loaded. <br/> Please try again later.',
+			PLAY_START  : 'Start slideshow',
+			PLAY_STOP   : 'Pause slideshow',
+			FULL_SCREEN : 'Full screen',
+			THUMBS      : 'Thumbnails'
+		},
+		'de' : {
+			CLOSE       : 'Schliessen',
+			NEXT        : 'Weiter',
+			PREV        : 'Zurück',
+			ERROR       : 'Die angeforderten Daten konnten nicht geladen werden. <br/> Bitte versuchen Sie es später nochmal.',
+			PLAY_START  : 'Diaschau starten',
+			PLAY_STOP   : 'Diaschau beenden',
+			FULL_SCREEN : 'Vollbild',
+			THUMBS      : 'Vorschaubilder'
+		}
+	}
+
+}
+
+ + +

+ Set instance options by passing a valid object to fancybox() method: +

+ +
$("[data-fancybox]").fancybox({
+	thumbs : {
+		autoStart : true
+	}
+});
+ + +

+ Plugin options / defaults are exposed in $.fancybox.defaults namespace so you can easily adjust them globally: + +

+ +
$.fancybox.defaults.animationEffect = "fade";
+ +

+ Custom options for each element individually can be set by adding a data-options + attribute to the element. + + This attribute should contain the properly formatted JSON object: +

+ +
<a data-fancybox data-options='{"caption" : "My caption", "src" : "https://codepen.io/about/", "type" : "iframe"}' href="javascript:;" class="btn">
+	Open external page
+</a>
+
+

+ View demo on CodePen +

+ + + + +

API

+ +

+ The fancyBox API offers a couple of methods to control fancyBox. + + This gives you the ability to extend the plugin and to integrate it with other web application components. +

+ +

Core methods

+ +

+ Core methods are methods which affect/handle instances: +

+ + +
// Close only the currently active or all fancyBox instances
+$.fancybox.close( all );
+
+// Open the fancyBox right away
+$.fancybox.open( items, opts, index );
+
+ + +

+ Gallery items can be collection of jQuery objects or array containing plain objects. This can be used, for example, to create content filter. +

+ +
var $links = $('.fancybox');
+
+$links.on('click', function() {
+
+	$.fancybox.open( $links, {
+		// Custom options
+	}, $links.index( this ) );
+
+	return false;
+});
+

+ View demo on CodePen +

+ + + +

+ When creating group objects manually, each item should follow this pattern: +

+ +
{
+	src  : '' // Source of the content
+	type : '' // Content type: image|inline|ajax|iframe|html (optional)
+	opts : {} // Object containing item options (optional)
+}
+
+ + +

+ Example of opening image gallery: +

+ +
$.fancybox.open([
+	{
+		src  : '1_b.jpg',
+		opts : {
+			caption : 'First caption'
+		}
+	},
+	{
+		src  : '2_b.jpg',
+		opts : {
+			caption : 'Second caption'
+		}
+	}
+], {
+	loop : false
+});
+

+ View demo on CodePen +

+ +

+ It is also possible to pass only one object. Example of opening inline content: +

+ +
$.fancybox.open({
+	src  : '#hidden-content',
+	type : 'inline',
+	opts : {
+		afterShow : function( instance, current ) {
+			console.info( 'done!' );
+		}
+	}
+});
+
+

+ View demo on CodePen +

+ +

+ If you wish to display some html content (for example, a message), then you can use a simpler syntax. + It is advised to use a wrapper around your content. +

+ +
$.fancybox.open('<div class="message"><h2>Hello!</h2><p>You are awesome!</p></div>');
+

+ View demo on CodePen +

+ + + +

Instance methods

+ +

+ In order to use these methods, you need an instance of the plugin's object. +

+ +
var instance = $.fancybox.open(
+	// Your content and options
+);
+ +

+ Get reference to currently active instance: +

+ +
var instance = $.fancybox.getInstance();
+ +

+ The first argument of the callback is reference to instance: +

+ +
$("[data-fancybox]").fancybox({
+	afterShow : function( instance, current ) {
+		console.info( instance );
+	}
+});
+

+ Once you have a reference to fancyBox instance the following methods are available: +

+ + +
// Go to next gallery item
+instance.next( duration );
+
+// Go to previous gallery item
+instance.previous( duration );
+
+// Switch to selected gallery item
+instance.jumpTo( index, duration );
+
+// Check if current image dimensions are smaller than actual
+instance.isScaledDown();
+
+// Scale image to the actual size of the image
+instance.scaleToActual( x, y, duration );
+
+// Check if image dimensions exceed parent element
+instance.canPan();
+
+// Scale image to fit inside parent element
+instance.scaleToFit( duration );
+
+// Update position and content of all slides
+instance.update();
+
+// Update slide position and scale content to fit
+instance.updateSlide( slide );
+
+// Update infobar values, navigation button states and reveal caption
+instance.updateControls( force );
+
+// Load custom content into the slide
+instance.setContent( slide, content );
+
+// Show loading icon inside the slide
+instance.showLoading( slide );
+
+// Remove loading icon from the slide
+instance.hideLoading( slide );
+
+// Try to find and focus on the first focusable element
+instance.focus();
+
+// Activates current instance, brings it to the front
+instance.activate();
+
+// Close instance
+instance.close();
+
+ + +

+ You can also do something like this: +

+ +
$.fancybox.getInstance().jumpTo(1);
+ +

+ or simply: +

+ +
$.fancybox.getInstance('jumpTo', 1);
+ + + + +

Events

+ +

+ fancyBox fires several events: +

+ +
beforeLoad   : Before the content of a slide is being loaded
+afterLoad    : When the content of a slide is done loading
+
+beforeShow   : Before open animation starts
+afterShow    : When content is done loading and animating
+
+beforeClose  : Before the instance attempts to close. Return false to cancel the close.
+afterClose   : After instance has been closed
+
+onInit       : When instance has been initialized
+onActivate   : When instance is brought to front
+onDeactivate : When other instance has been activated
+ + +

+ Event callbacks can be set as function properties of the options object passed to fancyBox initialization function: +

+ +
<script type="text/javascript">
+	$("[data-fancybox]").fancybox({
+		afterShow: function( instance, slide ) {
+
+			// Tip: Each event passes useful information within the event object:
+
+			// Object containing references to interface elements
+			// (background, buttons, caption, etc)
+			// console.info( instance.$refs );
+
+			// Current slide options
+			// console.info( slide.opts );
+
+			// Clicked element
+			// console.info( slide.opts.$orig );
+
+			// Reference to DOM element of the slide
+			// console.info( slide.$slide );
+
+		}
+	});
+</script>
+ +

+ Each callback receives two parameters - current fancyBox instance and current gallery object, if exists. +

+ +

+ It is also possible to attach event handler for all instances. + To prevent interfering with other scripts, these events have been namespaced to .fb. + These handlers receive 3 parameters - event, current fancyBox instance and current gallery object. +

+

+ Here is an example of binding to the afterShow event: +

+ +
$(document).on('afterShow.fb', function( e, instance, slide ) {
+	// Your code goes here
+});
+ + +

+ If you wish to prevent closing of the modal (for example, after form submit), you can use beforeClose + callback. Simply return false: +

+ +
beforeClose : function( instance, current, e ) {
+	if ( $('#my-field').val() == '' ) {
+		return false;
+	}
+}
+
+ +

Modules

+ +

+ fancyBox code is split into several files (modules) that extend core functionality. + You can build your own fancyBox version by excluding unnecessary modules, if needed. + Each one has their own js and/or css files. +

+ +

+ Some modules can be customized and controlled programmatically. + List of all possible options: +

+ +
fullScreen : {
+	autoStart : false,
+},
+
+touch : {
+	vertical : true,  // Allow to drag content vertically
+	momentum : true   // Continuous movement when panning
+},
+
+// Hash value when initializing manually,
+// set `false` to disable hash change
+hash : null,
+
+// Customize or add new media types
+// Example:
+/*
+media : {
+	youtube : {
+		params : {
+			autoplay : 0
+		}
+	}
+}
+*/
+media : {},
+
+slideShow : {
+	autoStart : false,
+	speed     : 4000
+},
+
+thumbs : {
+	autoStart   : false, // Display thumbnails on opening
+	hideOnClose : true   // Hide thumbnail grid when closing animation starts
+}
+
+
+ +

+ Example (show thumbnails on start): +

+ +
$('[data-fancybox="images"]').fancybox({
+	thumbs : {
+		autoStart : true
+	}
+})
+

+ View demo on CodePen +

+ +

+ If you would inspect fancyBox instance object, you would find that same keys ar captialized - these are references for each module object. + Also, you would notice that fancyBox uses common naming convention to prefix jQuery objects with $. +

+ +

+ This is how you, for example, can access thumbnail grid element: +

+ +
$.fancybox.getInstance().Thumbs.$grid
+ +

+ This example shows how to call method that toggles thumbnails: +

+ +
$.fancybox.getInstance().Thumbs.toggle();
+ +

+ List of available methods: +

+ +
Thumbs.focus()
+Thumbs.update();
+Thumbs.hide();
+Thumbs.show();
+Thumbs.toggle();
+
+FullScreen.request( elem );
+FullScreen.exit();
+FullScreen.toggle( elem );
+FullScreen.isFullscreen();
+FullScreen.enabled();
+
+SlideShow.start();
+SlideShow.stop();
+SlideShow.toggle();
+
+ +

+ If you wish to disable hash module, use this snippet (after including JS file): +

+ +
$.fancybox.defaults.hash = false;
+ + +

FAQ

+ +

Opening/closing causes fixed element to jump

+ +

+ Simply add compensate-for-scrollbar CSS class to your fixed positioned elements. + Example of using Bootstrap navbar component: +

+ +
<nav class="navbar navbar-inverse navbar-fixed-top compensate-for-scrollbar">
+	<div class="container">
+		..
+	</div>
+</nav>
+ +

+ The script measures width of the scrollbar and creates compensate-for-scrollbar CSS class + that uses this value for margin-right property. + Therefore, if your element has width:100%, you should positon it using left and right properties instead. Example: +

+ +
.navbar {
+	position: fixed;
+	top: 0;
+	left: 0;
+	right: 0;
+}
+ +

How to customize caption

+ +

+ You can use caption option that accepts a function and is called for each group element. Example of appending image download link: +

+ +
$( '[data-fancybox]' ).fancybox({
+	caption : function( instance, item ) {
+		var caption = $(this).data('caption') || '';
+
+		if ( item.type === 'image' ) {
+			caption = (caption.length ? caption + '<br />' : '') + '<a href="' + item.src + '">Download image</a>' ;
+		}
+
+		return caption;
+	}
+});
+

+ View demo on CodePen +

+ + +

+ Add current image index and image count (the total number of images in the gallery) right in the caption: +

+ +
$( '[data-fancybox]' ).fancybox({
+	caption : function( instance, item ) {
+		var caption = $(this).data('caption') || '';
+
+		return '(<span data-fancybox-index></span>/<span data-fancybox-count></span>)' + ( caption.length ? ' ' + caption : '' );
+	}
+});
+
+

+ View demo on CodePen +

+ + + +

+ Inside caption method, this refers to the clicked element. Example of using different source for caption: +

+ +
$( '[data-fancybox]' ).fancybox({
+	caption : function( instance, item ) {
+		return $(this).find('figcaption').html();
+	}
+});
+

+ View demo on CodePen +

+ + +

How to create custom button in the toolbar

+ +

+ Example of creating reusable button and changing href property dynamically +

+ +
// Create template for download button
+$.fancybox.defaults.btnTpl.download = '<a download class="fancybox-button fancybox-download"></a>';
+
+// Choose what buttons to display by default
+$.fancybox.defaults.buttons = [
+  'slideShow',
+  'fullScreen',
+  'thumbs',
+  'download',
+  'close'
+];
+
+// Dynamically update download url
+$( '[data-fancybox]' ).fancybox({
+	beforeShow : function( instance, current ) {
+		$('.fancybox-download').attr('href', current.src);
+	}
+});
+
+ +

+ View demo on CodePen +

+ +

How to reposition thumbnail grid

+ +

+ There is currenty no JS option to change thumbnail grid position. + But fancyBox is designed so that you can use CSS to change position or dimension for each block + (e.g., content area, caption or thumbnail grid). + This gives you freedom to completely change the look and feel of the modal window, if needed. + View demo on CodePen +

+ +
+ + +
+ + + + + + diff --git a/fancybox/gulpfile.js b/fancybox/gulpfile.js new file mode 100644 index 0000000..137dcfa --- /dev/null +++ b/fancybox/gulpfile.js @@ -0,0 +1,66 @@ +var gulp = require('gulp'), + livereload = require('gulp-livereload'), + concat = require('gulp-concat'), + uglify = require('gulp-uglify'), + rename = require('gulp-rename'), + cssnano = require('gulp-cssnano'), + sass = require('gulp-sass'), + autoprefixer = require('gulp-autoprefixer'), + header = require('gulp-header'), + replace = require('gulp-replace'), + gutil = require('gulp-util'); + +var pkg = require('./package.json'); +var banner = ['// ==================================================', + '// fancyBox v${pkg.version}', + '//', + '// Licensed GPLv3 for open source use', + '// or fancyBox Commercial License for commercial use', + '//', + '// http://fancyapps.com/fancybox/', + '// Copyright ${new Date().getFullYear()} fancyApps', + '//', + '// ==================================================', + ''].join('\n'); + +// Concatenate & Minify JS + +gulp.task('scripts', function() { + return gulp.src([ + 'src/js/core.js', + 'src/js/media.js', + 'src/js/guestures.js', + 'src/js/slideshow.js', + 'src/js/fullscreen.js', + 'src/js/thumbs.js', + 'src/js/hash.js', + ]) + .pipe(concat('jquery.fancybox.js')) + .pipe(replace(/({fancybox-version})/g, pkg.version)) + .pipe(header(banner, { pkg : pkg } )) + .pipe(gulp.dest('dist')) + .pipe(rename({suffix: '.min'})) + .pipe(uglify()) + .pipe(header(banner, { pkg : pkg } )) + .pipe(gulp.dest('dist')); + }); + + +// Compile CSS + +gulp.task('css', function() { + return gulp.src('src/css/*.css') // Gets all files src/css + .pipe(sass()) + .pipe(autoprefixer({ + browsers: ['last 5 versions'], + cascade: false + })) + .pipe(concat('jquery.fancybox.css')) + .pipe(gulp.dest('dist')) + .pipe(rename({suffix: '.min'})) + .pipe(cssnano({zindex: false})) + .pipe(gulp.dest('dist')); +}); + +// Default Task +gulp.task('default', ['scripts', 'css']); diff --git a/fancybox/package.json b/fancybox/package.json new file mode 100644 index 0000000..8344787 --- /dev/null +++ b/fancybox/package.json @@ -0,0 +1,51 @@ +{ + "name": "@fancyapps/fancybox", + "description": "Touch enabled, responsive and fully customizable jQuery lightbox script", + "version": "3.1.24", + "homepage": "http://fancyapps.com/fancybox/", + "main": "./dist/jquery.fancybox.min.js", + "author": "fancyApps", + "license": "GPL-3.0", + "repository": { + "type": "git", + "url": "git+https://github.com/fancyapps/fancybox.git" + }, + "peerDependencies": { + "jquery": ">=1.9.0" + }, + "devDependencies": { + "del": "^2.2.2", + "gulp": "^3.9.1", + "gulp-autoprefixer": "^3.1.1", + "gulp-concat": "^2.6.1", + "gulp-cssnano": "^2.1.2", + "gulp-header": "^1.8.8", + "gulp-jshint": "^2.0.4", + "gulp-livereload": "^3.8.1", + "gulp-notify": "^2.2.0", + "gulp-rename": "^1.2.2", + "gulp-replace": "^0.5.4", + "gulp-sass": "^3.0.0", + "gulp-uglify": "^2.0.0", + "gulp-util": "^3.0.8", + "jshint": "^2.9.4" + }, + "keywords": [ + "touch", + "responsive", + "lightbox", + "fancybox", + "gallery", + "jQuery", + "plugin" + ], + "bugs": { + "url": "https://github.com/fancyapps/fancybox/issues" + }, + "directories": { + "doc": "docs" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + } +} diff --git a/fancybox/src/css/core.css b/fancybox/src/css/core.css new file mode 100644 index 0000000..1060d13 --- /dev/null +++ b/fancybox/src/css/core.css @@ -0,0 +1,664 @@ +.fancybox-enabled { + overflow: hidden; +} + +.fancybox-enabled body { + overflow: visible; + height: 100%; +} + +.fancybox-is-hidden { + position: absolute; + top: -9999px; + left: -9999px; + visibility: hidden; +} + +.fancybox-container { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 99993; + -webkit-tap-highlight-color: rgba(0,0,0,0); + backface-visibility: hidden; + transform: translateZ(0); +} + +/* Make sure that the first one is on the top */ +.fancybox-container ~ .fancybox-container { + z-index: 99992; +} + +.fancybox-outer, +.fancybox-inner, +.fancybox-bg, +.fancybox-stage { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.fancybox-outer { + overflow-y: auto; + -webkit-overflow-scrolling: touch; +} + +.fancybox-bg { + background: rgb(30,30,30); + opacity: 0; + transition-duration: inherit; + transition-property: opacity; + transition-timing-function: cubic-bezier(.47,0,.74,.71); +} + +.fancybox-is-open .fancybox-bg { + opacity: 0.87; + transition-timing-function: cubic-bezier(.22,.61,.36,1); +} + +.fancybox-infobar, +.fancybox-toolbar, +.fancybox-caption-wrap { + position: absolute; + direction: ltr; + z-index: 99997; + opacity: 0; + visibility: hidden; + transition: opacity .25s, visibility 0s linear .25s; + box-sizing: border-box; +} + +.fancybox-show-infobar .fancybox-infobar, +.fancybox-show-toolbar .fancybox-toolbar, +.fancybox-show-caption .fancybox-caption-wrap { + opacity: 1; + visibility: visible; + transition: opacity .25s, visibility 0s; +} + +.fancybox-infobar { + top: 0; + left : 50%; + margin-left: -79px; +} + +.fancybox-infobar__body { + display: inline-block; + width: 70px; + line-height: 44px; + font-size: 13px; + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; + text-align: center; + color: #ddd; + background-color: rgba(30,30,30,0.7); + pointer-events: none; + user-select: none; + -webkit-touch-callout: none; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-font-smoothing: subpixel-antialiased; +} + +.fancybox-toolbar { + top: 0; + right: 0; +} + +.fancybox-stage { + overflow: hidden; + direction: ltr; + z-index: 99994; + -webkit-transform: translate3d(0, 0, 0); +} + +.fancybox-slide { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: auto; + outline: none; + white-space: normal; + box-sizing: border-box; + text-align: center; + z-index: 99994; + -webkit-overflow-scrolling: touch; + display: none; + backface-visibility: hidden; + transition-property: transform, opacity; + transform-style: preserve-3d; +} + +.fancybox-slide::before { + content: ''; + display: inline-block; + vertical-align: middle; + height: 100%; + width: 0; +} + +.fancybox-is-sliding .fancybox-slide, +.fancybox-slide--previous, +.fancybox-slide--current, +.fancybox-slide--next { + display: block; +} + +.fancybox-slide--image { + overflow: visible; +} + +.fancybox-slide--image::before { + display: none; +} + +.fancybox-slide--video .fancybox-content, +.fancybox-slide--video iframe { + background: #000; +} + +.fancybox-slide--map .fancybox-content, +.fancybox-slide--map iframe { + background: #E5E3DF; +} + +.fancybox-slide--next { + z-index: 99995; +} + +.fancybox-slide > * { + display: inline-block; + position: relative; + padding: 24px; + margin: 44px 0 44px; + border-width: 0; + vertical-align: middle; + text-align: left; + background-color: #fff; + overflow: auto; + box-sizing: border-box; +} + +.fancybox-slide .fancybox-image-wrap { + position: absolute; + top: 0; + left: 0; + margin: 0; + padding: 0; + border: 0; + z-index: 99995; + background: transparent; + cursor: default; + overflow: visible; + transform-origin: top left; + background-size: 100% 100%; + background-repeat: no-repeat; + backface-visibility: hidden; +} + +.fancybox-can-zoomOut .fancybox-image-wrap { + cursor: zoom-out; +} + +.fancybox-can-zoomIn .fancybox-image-wrap { + cursor: zoom-in; +} + +.fancybox-can-drag .fancybox-image-wrap { + cursor: grab; +} + +.fancybox-is-dragging .fancybox-image-wrap { + cursor: grabbing; +} + +.fancybox-image, +.fancybox-spaceball { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + border: 0; + max-width: none; + max-height: none; +} + + .fancybox-spaceball { + z-index: 1; +} + +.fancybox-slide--iframe .fancybox-content { + padding: 0; + width: 80%; + height: 80%; + max-width: calc(100% - 100px); + max-height: calc(100% - 88px); + overflow: visible; + background: #fff; +} + +.fancybox-iframe { + display: block; + margin: 0; + padding: 0; + border: 0; + width: 100%; + height: 100%; + background: #fff; +} + +.fancybox-error { + margin: 0; + padding: 40px; + width: 100%; + max-width: 380px; + background: #fff; + cursor: default; +} + +.fancybox-error p { + margin: 0; + padding: 0; + color: #444; + font: 16px/20px "Helvetica Neue",Helvetica,Arial,sans-serif; +} + +.fancybox-close-small { + position: absolute; + top: 0; + right: 0; + width: 44px; + height: 44px; + padding: 0; + margin: 0; + border: 0; + border-radius: 0; + outline: none; + background: transparent; + z-index: 10; + cursor: pointer; +} + +.fancybox-close-small:after { + content: '×'; + position: absolute; + top : 5px; + right: 5px; + width: 30px; + height: 30px; + font: 20px/30px Arial,"Helvetica Neue",Helvetica,sans-serif; + color: #888; + font-weight: 300; + text-align: center; + border-radius: 50%; + border-width: 0; + background: #fff; + transition: background .25s; + box-sizing: border-box; + z-index: 2; +} + +.fancybox-close-small:focus:after { + outline: 1px dotted #888; +} + +.fancybox-close-small:hover:after { + color: #555; + background: #eee; +} + +.fancybox-slide--iframe .fancybox-close-small { + top : 0; + right : -44px; +} + +.fancybox-slide--iframe .fancybox-close-small:after { + background: transparent; + font-size: 35px; + color: #aaa; +} + +.fancybox-slide--iframe .fancybox-close-small:hover:after { + color: #fff; +} + + +/* Caption */ + +.fancybox-caption-wrap { + bottom: 0; + left: 0; + right: 0; + padding: 60px 30px 0 30px; + background: linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,0.1) 20%, rgba(0,0,0,0.2) 40%,rgba(0,0,0,0.6) 80%, rgba(0,0,0,0.8) 100%); + pointer-events: none; +} + +.fancybox-caption { + padding: 30px 0; + border-top: 1px solid rgba(255,255,255,0.4); + font-size: 14px; + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; + color: #fff; + line-height: 20px; + -webkit-text-size-adjust: none; +} + +.fancybox-caption a, +.fancybox-caption button, +.fancybox-caption select { + pointer-events: all; +} + +.fancybox-caption a { + color: #fff; + text-decoration: underline; +} + + +/* Buttons */ + +.fancybox-button { + display: inline-block; + position: relative; + margin: 0; + padding: 0; + border: 0; + width: 44px; + height: 44px; + line-height: 44px; + text-align: center; + background: transparent; + color: #ddd; + border-radius: 0; + cursor: pointer; + vertical-align: top; + outline: none; +} + +.fancybox-button[disabled] { + cursor: default; + pointer-events: none; +} + +.fancybox-infobar__body, .fancybox-button { + background: rgba(30,30,30,0.6); +} + +.fancybox-button:hover:not([disabled]) { + color: #fff; + background: rgba(0,0,0,0.8); +} + +.fancybox-button::before, +.fancybox-button::after { + content: ''; + pointer-events: none; + position: absolute; + background-color: currentColor; + color: currentColor; + opacity: 0.9; + box-sizing: border-box; + display: inline-block; +} + +.fancybox-button[disabled]::before, +.fancybox-button[disabled]::after { + opacity: 0.3; +} + +.fancybox-button--left::after, +.fancybox-button--right::after { + top: 18px; + width: 6px; + height: 6px; + background: transparent; + border-top: solid 2px currentColor; + border-right: solid 2px currentColor; +} + +.fancybox-button--left::after { + left: 20px; + transform: rotate(-135deg); +} + +.fancybox-button--right::after { + right: 20px; + transform: rotate(45deg); +} + +.fancybox-button--left { + border-bottom-left-radius: 5px; +} + +.fancybox-button--right { + border-bottom-right-radius: 5px; +} + +.fancybox-button--close::before, .fancybox-button--close::after { + content: ''; + display: inline-block; + position: absolute; + height: 2px; + width: 16px; + top: calc(50% - 1px); + left: calc(50% - 8px); +} + +.fancybox-button--close::before { + transform: rotate(45deg); +} + +.fancybox-button--close::after { + transform: rotate(-45deg); +} + + +/* Navigation arrows */ + +.fancybox-arrow { + position: absolute; + top: 50%; + margin: -50px 0 0 0; + height: 100px; + width: 54px; + padding: 0; + border: 0; + outline: none; + background: none; + cursor: pointer; + z-index: 99995; + opacity: 0; + user-select: none; + transition: opacity .25s; +} + +.fancybox-arrow::after { + content : ''; + position: absolute; + top: 28px; + width: 44px; + height: 44px; + background-color: rgba(30,30,30,0.8); + background-image: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiBoZWlnaHQ9IjQ4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSI0OCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4gICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPiAgICA8cGF0aCBkPSJNMTIgNGwtMS40MSAxLjQxTDE2LjE3IDExSDR2MmgxMi4xN2wtNS41OCA1LjU5TDEyIDIwbDgtOHoiLz48L3N2Zz4=); + background-repeat: no-repeat; + background-position: center center; + background-size: 24px 24px; +} + +.fancybox-arrow--right { + right: 0; +} + +.fancybox-arrow--left { + left: 0; + transform: scaleX(-1); +} + +.fancybox-arrow--right::after, +.fancybox-arrow--left::after { + left: 0; +} + +.fancybox-show-nav .fancybox-arrow { + opacity: 0.6; +} + +.fancybox-show-nav .fancybox-arrow[disabled] { + opacity: 0.3; +} + + +/* Loading indicator */ + +.fancybox-loading { + border: 6px solid rgba(100, 100, 100, .4); + border-top: 6px solid rgba(255, 255, 255, .6); + border-radius: 100%; + height: 50px; + width: 50px; + animation: fancybox-rotate .8s infinite linear; + background: transparent; + position: absolute; + top: 50%; + left: 50%; + margin-top: -25px; + margin-left: -25px; + z-index: 99999; +} + +@keyframes fancybox-rotate { + from { transform: rotate( 0deg ); } + to { transform: rotate( 359deg ); } +} + + +/* Transition effects */ + +.fancybox-animated { + transition-timing-function: cubic-bezier(0, 0, 0.25, 1); +} + +/* transitionEffect: slide */ + +.fancybox-fx-slide.fancybox-slide--previous { + transform: translate3d(-100%, 0, 0); + opacity: 0; +} + +.fancybox-fx-slide.fancybox-slide--next { + transform: translate3d(100%, 0, 0); + opacity: 0; +} + +.fancybox-fx-slide.fancybox-slide--current { + transform: translate3d(0, 0, 0); + opacity: 1; +} + +/* transitionEffect: fade */ + +.fancybox-fx-fade.fancybox-slide--previous, +.fancybox-fx-fade.fancybox-slide--next { + opacity: 0; + transition-timing-function: cubic-bezier(.19,1,.22,1); +} + +.fancybox-fx-fade.fancybox-slide--current { + opacity: 1; +} + +/* transitionEffect: zoom-in-out */ + +.fancybox-fx-zoom-in-out.fancybox-slide--previous { + transform: scale3d(1.5, 1.5, 1.5); + opacity: 0; +} + +.fancybox-fx-zoom-in-out.fancybox-slide--next { + transform: scale3d(0.5, 0.5, 0.5); + opacity: 0; +} + +.fancybox-fx-zoom-in-out.fancybox-slide--current { + transform: scale3d(1, 1, 1); + opacity: 1; +} + +/* transitionEffect: rotate */ + +.fancybox-fx-rotate.fancybox-slide--previous { + transform: rotate(-360deg); + opacity: 0; +} + +.fancybox-fx-rotate.fancybox-slide--next { + transform: rotate(360deg); + opacity: 0; +} + +.fancybox-fx-rotate.fancybox-slide--current { + transform: rotate(0deg); + opacity: 1; +} + +/* transitionEffect: circular */ + +.fancybox-fx-circular.fancybox-slide--previous { + transform: scale3d(0, 0, 0) translate3d(-100%, 0, 0); + opacity: 0; +} + +.fancybox-fx-circular.fancybox-slide--next { + transform: scale3d(0, 0, 0) translate3d(100%, 0, 0); + opacity: 0; +} + +.fancybox-fx-circular.fancybox-slide--current { + transform: scale3d(1, 1, 1) translate3d(0, 0, 0); + opacity: 1; +} + +/* transitionEffect: tube */ + +.fancybox-fx-tube.fancybox-slide--previous { + transform: translate3d(-100%, 0, 0) scale(0.1) skew(-10deg); +} + +.fancybox-fx-tube.fancybox-slide--next { + transform: translate3d(100%, 0, 0) scale(0.1) skew(10deg); +} + +.fancybox-fx-tube.fancybox-slide--current { + transform: translate3d(0, 0, 0) scale(1); +} + + +/* Styling for Small-Screen Devices */ + +@media all and (max-width: 800px) { + + .fancybox-infobar { + left: 0; + margin-left: 0; + } + + .fancybox-button--left, + .fancybox-button--right { + display: none !important; + } + + .fancybox-caption { + padding: 20px 0; + margin: 0; + } +} diff --git a/fancybox/src/css/fullscreen.css b/fancybox/src/css/fullscreen.css new file mode 100644 index 0000000..a4bd316 --- /dev/null +++ b/fancybox/src/css/fullscreen.css @@ -0,0 +1,11 @@ + +/* Fullscreen */ + +.fancybox-button--fullscreen::before { + width: 15px; + height: 11px; + left: calc(50% - 7px); + top: calc(50% - 6px); + border: 2px solid; + background: none; +} diff --git a/fancybox/src/css/slideshow.css b/fancybox/src/css/slideshow.css new file mode 100644 index 0000000..b663619 --- /dev/null +++ b/fancybox/src/css/slideshow.css @@ -0,0 +1,26 @@ +/* Slideshow button */ + +.fancybox-button--play {} + +.fancybox-button--play::before, +.fancybox-button--pause::before { + top: calc(50% - 6px); + left: calc(50% - 4px); + background: transparent; +} + +.fancybox-button--play::before { + width: 0; + height: 0; + border-top: 6px inset transparent; + border-bottom: 6px inset transparent; + border-left: 10px solid; + border-radius: 1px; +} + +.fancybox-button--pause::before { + width: 7px; + height: 11px; + border-style: solid; + border-width: 0 2px 0 2px; +} diff --git a/fancybox/src/css/thumbs.css b/fancybox/src/css/thumbs.css new file mode 100644 index 0000000..215e5f7 --- /dev/null +++ b/fancybox/src/css/thumbs.css @@ -0,0 +1,122 @@ + +/* Thumbs */ + +.fancybox-thumbs { + display: none; +} + +.fancybox-button--thumbs { + display: none; +} + +@media all and (min-width: 800px) { + + .fancybox-button--thumbs { + display: inline-block; + } + + .fancybox-button--thumbs span { + font-size: 23px; + } + + .fancybox-button--thumbs::before { + width: 3px; + height: 3px; + top: calc(50% - 2px); + left: calc(50% - 2px); + box-shadow: 0 -4px 0, -4px -4px 0, 4px -4px 0, 0 0 0 32px inset, -4px 0 0, 4px 0 0, 0 4px 0, -4px 4px 0, 4px 4px 0; + } + + .fancybox-thumbs { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: auto; + width: 220px; + margin: 0; + padding: 5px 5px 0 0; + background: #fff; + word-break: normal; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + box-sizing: border-box; + z-index: 99995; + } + + .fancybox-show-thumbs .fancybox-thumbs { + display: block; + } + + .fancybox-show-thumbs .fancybox-inner { + right: 220px; + } + + .fancybox-thumbs > ul { + list-style: none; + position: absolute; + position: relative; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow-x: hidden; + overflow-y: auto; + font-size: 0; + } + + .fancybox-thumbs > ul > li { + float: left; + overflow: hidden; + max-width: 50%; + padding: 0; + margin: 0; + width: 105px; + height: 75px; + position: relative; + cursor: pointer; + outline: none; + border: 5px solid transparent; + border-top-width: 0; + border-right-width: 0; + -webkit-tap-highlight-color: transparent; + backface-visibility: hidden; + box-sizing: border-box; + } + + li.fancybox-thumbs-loading { + background: rgba(0,0,0,0.1); + } + + .fancybox-thumbs > ul > li > img { + position: absolute; + top: 0; + left: 0; + min-width: 100%; + min-height: 100%; + max-width: none; + max-height: none; + -webkit-touch-callout: none; + user-select: none; + } + + .fancybox-thumbs > ul > li:before { + content:''; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + border-radius: 2px; + border: 4px solid #4ea7f9; + z-index: 99991; + opacity: 0; + transition: all 0.2s cubic-bezier(.25,.46,.45,.94); + } + + .fancybox-thumbs > ul > li.fancybox-thumbs-active:before { + opacity: 1; + } + +} diff --git a/fancybox/src/js/core.js b/fancybox/src/js/core.js new file mode 100644 index 0000000..b381c61 --- /dev/null +++ b/fancybox/src/js/core.js @@ -0,0 +1,2964 @@ +;(function (window, document, $, undefined) { + 'use strict'; + + // If there's no jQuery, fancyBox can't work + // ========================================= + + if ( !$ ) { + return; + } + + // Check if fancyBox is already initialized + // ======================================== + + if ( $.fn.fancybox ) { + + $.error('fancyBox already initialized'); + + return; + } + + // Private default settings + // ======================== + + var defaults = { + + // Enable infinite gallery navigation + loop : false, + + // Space around image, ignored if zoomed-in or viewport smaller than 800px + margin : [44, 0], + + // Horizontal space between slides + gutter : 50, + + // Enable keyboard navigation + keyboard : true, + + // Should display navigation arrows at the screen edges + arrows : true, + + // Should display infobar (counter and arrows at the top) + infobar : false, + + // Should display toolbar (buttons at the top) + toolbar : true, + + // What buttons should appear in the top right corner. + // Buttons will be created using templates from `btnTpl` option + // and they will be placed into toolbar (class="fancybox-toolbar"` element) + buttons : [ + 'slideShow', + 'fullScreen', + 'thumbs', + 'close' + ], + + // Detect "idle" time in seconds + idleTime : 4, + + // Should display buttons at top right corner of the content + // If 'auto' - they will be created for content having type 'html', 'inline' or 'ajax' + // Use template from `btnTpl.smallBtn` for customization + smallBtn : 'auto', + + // Disable right-click and use simple image protection for images + protect : false, + + // Shortcut to make content "modal" - disable keyboard navigtion, hide buttons, etc + modal : false, + + image : { + + // Wait for images to load before displaying + // Requires predefined image dimensions + // If 'auto' - will zoom in thumbnail if 'width' and 'height' attributes are found + preload : "auto", + + }, + + ajax : { + + // Object containing settings for ajax request + settings : { + + // This helps to indicate that request comes from the modal + // Feel free to change naming + data : { + fancybox : true + } + } + + }, + + iframe : { + + // Iframe template + tpl : '', + + // Preload iframe before displaying it + // This allows to calculate iframe content width and height + // (note: Due to "Same Origin Policy", you can't get cross domain data). + preload : true, + + // Custom CSS styling for iframe wrapping element + // You can use this to set custom iframe dimensions + css : {}, + + // Iframe tag attributes + attr : { + scrolling : 'auto' + } + + }, + + // Open/close animation type + // Possible values: + // false - disable + // "zoom" - zoom images from/to thumbnail + // "fade" + // "zoom-in-out" + // + animationEffect : "zoom", + + // Duration in ms for open/close animation + animationDuration : 366, + + // Should image change opacity while zooming + // If opacity is 'auto', then opacity will be changed if image and thumbnail have different aspect ratios + zoomOpacity : 'auto', + + // Transition effect between slides + // + // Possible values: + // false - disable + // "fade' + // "slide' + // "circular' + // "tube' + // "zoom-in-out' + // "rotate' + // + transitionEffect : "fade", + + // Duration in ms for transition animation + transitionDuration : 366, + + // Custom CSS class for slide element + slideClass : '', + + // Custom CSS class for layout + baseClass : '', + + // Base template for layout + baseTpl : + '', + + // Loading indicator template + spinnerTpl : '
', + + // Error message template + errorTpl : '

{{ERROR}}

', + + btnTpl : { + slideShow : '', + fullScreen : '', + thumbs : '', + close : '', + + // This small close button will be appended to your html/inline/ajax content by default, + // if "smallBtn" option is not set to false + smallBtn : '' + }, + + // Container is injected into this element + parentEl : 'body', + + + // Focus handling + // ============== + + // Try to focus on the first focusable element after opening + autoFocus : true, + + // Put focus back to active element after closing + backFocus : true, + + // Do not let user to focus on element outside modal content + trapFocus : true, + + + // Module specific options + // ======================= + + fullScreen : { + autoStart : false, + }, + + touch : { + vertical : true, // Allow to drag content vertically + momentum : true // Continue movement after releasing mouse/touch when panning + }, + + // Hash value when initializing manually, + // set `false` to disable hash change + hash : null, + + // Customize or add new media types + // Example: + /* + media : { + youtube : { + params : { + autoplay : 0 + } + } + } + */ + media : {}, + + slideShow : { + autoStart : false, + speed : 4000 + }, + + thumbs : { + autoStart : false, // Display thumbnails on opening + hideOnClose : true // Hide thumbnail grid when closing animation starts + }, + + // Callbacks + //========== + + // See Documentation/API/Events for more information + // Example: + /* + afterShow: function( instance, current ) { + console.info( 'Clicked element:' ); + console.info( current.opts.$orig ); + } + */ + + onInit : $.noop, // When instance has been initialized + + beforeLoad : $.noop, // Before the content of a slide is being loaded + afterLoad : $.noop, // When the content of a slide is done loading + + beforeShow : $.noop, // Before open animation starts + afterShow : $.noop, // When content is done loading and animating + + beforeClose : $.noop, // Before the instance attempts to close. Return false to cancel the close. + afterClose : $.noop, // After instance has been closed + + onActivate : $.noop, // When instance is brought to front + onDeactivate : $.noop, // When other instance has been activated + + + // Interaction + // =========== + + // Use options below to customize taken action when user clicks or double clicks on the fancyBox area, + // each option can be string or method that returns value. + // + // Possible values: + // "close" - close instance + // "next" - move to next gallery item + // "nextOrClose" - move to next gallery item or close if gallery has only one item + // "toggleControls" - show/hide controls + // "zoom" - zoom image (if loaded) + // false - do nothing + + // Clicked on the content + clickContent : function( current, event ) { + return current.type === 'image' ? 'zoom' : false; + }, + + // Clicked on the slide + clickSlide : 'close', + + // Clicked on the background (backdrop) element + clickOutside : 'close', + + // Same as previous two, but for double click + dblclickContent : false, + dblclickSlide : false, + dblclickOutside : false, + + + // Custom options when mobile device is detected + // ============================================= + + mobile : { + clickContent : function( current, event ) { + return current.type === 'image' ? 'toggleControls' : false; + }, + clickSlide : function( current, event ) { + return current.type === 'image' ? 'toggleControls' : "close"; + }, + dblclickContent : function( current, event ) { + return current.type === 'image' ? 'zoom' : false; + }, + dblclickSlide : function( current, event ) { + return current.type === 'image' ? 'zoom' : false; + } + }, + + + // Internationalization + // ============ + + lang : 'en', + i18n : { + 'en' : { + CLOSE : 'Close', + NEXT : 'Next', + PREV : 'Previous', + ERROR : 'The requested content cannot be loaded.
Please try again later.', + PLAY_START : 'Start slideshow', + PLAY_STOP : 'Pause slideshow', + FULL_SCREEN : 'Full screen', + THUMBS : 'Thumbnails' + }, + 'de' : { + CLOSE : 'Schliessen', + NEXT : 'Weiter', + PREV : 'Zurück', + ERROR : 'Die angeforderten Daten konnten nicht geladen werden.
Bitte versuchen Sie es später nochmal.', + PLAY_START : 'Diaschau starten', + PLAY_STOP : 'Diaschau beenden', + FULL_SCREEN : 'Vollbild', + THUMBS : 'Vorschaubilder' + } + } + + }; + + // Few useful variables and methods + // ================================ + + var $W = $(window); + var $D = $(document); + + var called = 0; + + + // Check if an object is a jQuery object and not a native JavaScript object + // ======================================================================== + + var isQuery = function ( obj ) { + return obj && obj.hasOwnProperty && obj instanceof $; + }; + + + // Handle multiple browsers for "requestAnimationFrame" and "cancelAnimationFrame" + // =============================================================================== + + var requestAFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + // if all else fails, use setTimeout + function (callback) { + return window.setTimeout(callback, 1000 / 60); + }; + })(); + + + // Detect the supported transition-end event property name + // ======================================================= + + var transitionEnd = (function () { + var t, el = document.createElement("fakeelement"); + + var transitions = { + "transition" : "transitionend", + "OTransition" : "oTransitionEnd", + "MozTransition" : "transitionend", + "WebkitTransition": "webkitTransitionEnd" + }; + + for (t in transitions) { + if (el.style[t] !== undefined){ + return transitions[t]; + } + } + })(); + + + // Force redraw on an element. + // This helps in cases where the browser doesn't redraw an updated element properly. + // ================================================================================= + + var forceRedraw = function( $el ) { + return ( $el && $el.length && $el[0].offsetHeight ); + }; + + + // Class definition + // ================ + + var FancyBox = function( content, opts, index ) { + var self = this; + + self.opts = $.extend( true, { index : index }, defaults, opts || {} ); + + // Exclude buttons option from deep merging + if ( opts && $.isArray( opts.buttons ) ) { + self.opts.buttons = opts.buttons; + } + + self.id = self.opts.id || ++called; + self.group = []; + + self.currIndex = parseInt( self.opts.index, 10 ) || 0; + self.prevIndex = null; + + self.prevPos = null; + self.currPos = 0; + + self.firstRun = null; + + // Create group elements from original item collection + self.createGroup( content ); + + if ( !self.group.length ) { + return; + } + + // Save last active element and current scroll position + self.$lastFocus = $(document.activeElement).blur(); + + // Collection of gallery objects + self.slides = {}; + + self.init( content ); + + }; + + $.extend(FancyBox.prototype, { + + // Create DOM structure + // ==================== + + init : function() { + var self = this; + + var testWidth, $container, buttonStr; + + var firstItemOpts = self.group[ self.currIndex ].opts; + + self.scrollTop = $D.scrollTop(); + self.scrollLeft = $D.scrollLeft(); + + + // Hide scrollbars + // =============== + + if ( !$.fancybox.getInstance() && !$.fancybox.isMobile && $( 'body' ).css('overflow') !== 'hidden' ) { + testWidth = $( 'body' ).width(); + + $( 'html' ).addClass( 'fancybox-enabled' ); + + // Compare body width after applying "overflow: hidden" + testWidth = $( 'body' ).width() - testWidth; + + // If width has changed - compensate missing scrollbars by adding right margin + if ( testWidth > 1 ) { + $( 'head' ).append( '' ); + } + } + + + // Build html markup and set references + // ==================================== + + // Build html code for buttons and insert into main template + buttonStr = ''; + + $.each( firstItemOpts.buttons, function( index, value ) { + buttonStr += ( firstItemOpts.btnTpl[ value ] || '' ); + }); + + // Create markup from base template, it will be initially hidden to + // avoid unnecessary work like painting while initializing is not complete + $container = $( self.translate( self, firstItemOpts.baseTpl.replace( '\{\{BUTTONS\}\}', buttonStr ) ) ) + .addClass( 'fancybox-is-hidden' ) + .attr('id', 'fancybox-container-' + self.id) + .addClass( firstItemOpts.baseClass ) + .data( 'FancyBox', self ) + .prependTo( firstItemOpts.parentEl ); + + // Create object holding references to jQuery wrapped nodes + self.$refs = { + container : $container + }; + + [ 'bg', 'inner', 'infobar', 'toolbar', 'stage', 'caption' ].forEach(function(item) { + self.$refs[ item ] = $container.find( '.fancybox-' + item ); + }); + + // Check for redundant elements + if ( !firstItemOpts.arrows || self.group.length < 2 ) { + $container.find('.fancybox-navigation').remove(); + } + + if ( !firstItemOpts.infobar ) { + self.$refs.infobar.remove(); + } + + if ( !firstItemOpts.toolbar ) { + self.$refs.toolbar.remove(); + } + + self.trigger( 'onInit' ); + + // Bring to front and enable events + self.activate(); + + // Build slides, load and reveal content + self.jumpTo( self.currIndex ); + }, + + + // Simple i18n support - replaces object keys found in template + // with corresponding values + // ============================================================ + + translate : function( obj, str ) { + var arr = obj.opts.i18n[ obj.opts.lang ]; + + return str.replace(/\{\{(\w+)\}\}/g, function(match, n) { + var value = arr[n]; + + if ( value === undefined ) { + return match; + } + + return value; + }); + }, + + // Create array of gally item objects + // Check if each object has valid type and content + // =============================================== + + createGroup : function ( content ) { + var self = this; + var items = $.makeArray( content ); + + $.each(items, function( i, item ) { + var obj = {}, + opts = {}, + data = [], + $item, + type, + src, + srcParts; + + // Step 1 - Make sure we have an object + // ==================================== + + if ( $.isPlainObject( item ) ) { + + // We probably have manual usage here, something like + // $.fancybox.open( [ { src : "image.jpg", type : "image" } ] ) + + obj = item; + opts = item.opts || item; + + } else if ( $.type( item ) === 'object' && $( item ).length ) { + + // Here we propbably have jQuery collection returned by some selector + + $item = $( item ); + data = $item.data(); + + opts = 'options' in data ? data.options : {}; + opts = $.type( opts ) === 'object' ? opts : {}; + + obj.src = 'src' in data ? data.src : ( opts.src || $item.attr( 'href' ) ); + + [ 'width', 'height', 'thumb', 'type', 'filter' ].forEach(function(item) { + if ( item in data ) { + opts[ item ] = data[ item ]; + } + }); + + if ( 'srcset' in data ) { + opts.image = { srcset : data.srcset }; + } + + opts.$orig = $item; + + if ( !obj.type && !obj.src ) { + obj.type = 'inline'; + obj.src = item; + } + + } else { + + // Assume we have a simple html code, for example: + // $.fancybox.open( '

Hi!

' ); + + obj = { + type : 'html', + src : item + '' + }; + + } + + // Each gallery object has full collection of options + obj.opts = $.extend( true, {}, self.opts, opts ); + + if ( $.fancybox.isMobile ) { + obj.opts = $.extend( true, {}, obj.opts, obj.opts.mobile ); + } + + + // Step 2 - Make sure we have content type, if not - try to guess + // ============================================================== + + type = obj.type || obj.opts.type; + src = obj.src || ''; + + if ( !type && src ) { + if ( src.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i) ) { + type = 'image'; + + } else if ( src.match(/\.(pdf)((\?|#).*)?$/i) ) { + type = 'pdf'; + + } else if ( src.charAt(0) === '#' ) { + type = 'inline'; + } + } + + obj.type = type; + + + // Step 3 - Some adjustments + // ========================= + + obj.index = self.group.length; + + // Check if $orig and $thumb objects exist + if ( obj.opts.$orig && !obj.opts.$orig.length ) { + delete obj.opts.$orig; + } + + if ( !obj.opts.$thumb && obj.opts.$orig ) { + obj.opts.$thumb = obj.opts.$orig.find( 'img:first' ); + } + + if ( obj.opts.$thumb && !obj.opts.$thumb.length ) { + delete obj.opts.$thumb; + } + + // Caption is a "special" option, it can be passed as a method + if ( $.type( obj.opts.caption ) === 'function' ) { + obj.opts.caption = obj.opts.caption.apply( item, [ self, obj ] ); + + } else if ( 'caption' in data ) { + obj.opts.caption = data.caption; + } + + // Make sure we have caption as a string + obj.opts.caption = obj.opts.caption === undefined ? '' : obj.opts.caption + ''; + + // Check if url contains "filter" used to filter the content + // Example: "ajax.html #something" + if ( type === 'ajax' ) { + srcParts = src.split(/\s+/, 2); + + if ( srcParts.length > 1 ) { + obj.src = srcParts.shift(); + + obj.opts.filter = srcParts.shift(); + } + } + + if ( obj.opts.smallBtn == 'auto' ) { + + if ( $.inArray( type, ['html', 'inline', 'ajax'] ) > -1 ) { + obj.opts.toolbar = false; + obj.opts.smallBtn = true; + + } else { + obj.opts.smallBtn = false; + } + + } + + // If the type is "pdf", then simply load file into iframe + if ( type === 'pdf' ) { + obj.type = 'iframe'; + + obj.opts.iframe.preload = false; + } + + // Hide all buttons and disable interactivity for modal items + if ( obj.opts.modal ) { + + obj.opts = $.extend(true, obj.opts, { + // Remove buttons + infobar : 0, + toolbar : 0, + + smallBtn : 0, + + // Disable keyboard navigation + keyboard : 0, + + // Disable some modules + slideShow : 0, + fullScreen : 0, + thumbs : 0, + touch : 0, + + // Disable click event handlers + clickContent : false, + clickSlide : false, + clickOutside : false, + dblclickContent : false, + dblclickSlide : false, + dblclickOutside : false + }); + + } + + // Step 4 - Add processed object to group + // ====================================== + + self.group.push( obj ); + + }); + + }, + + + // Attach an event handler functions for: + // - navigation buttons + // - browser scrolling, resizing; + // - focusing + // - keyboard + // - detect idle + // ====================================== + + addEvents : function() { + var self = this; + + self.removeEvents(); + + // Make navigation elements clickable + self.$refs.container.on('click.fb-close', '[data-fancybox-close]', function(e) { + e.stopPropagation(); + e.preventDefault(); + + self.close( e ); + + }).on( 'click.fb-prev touchend.fb-prev', '[data-fancybox-prev]', function(e) { + e.stopPropagation(); + e.preventDefault(); + + self.previous(); + + }).on( 'click.fb-next touchend.fb-next', '[data-fancybox-next]', function(e) { + e.stopPropagation(); + e.preventDefault(); + + self.next(); + + }); + + + // Handle page scrolling and browser resizing + $W.on('orientationchange.fb resize.fb', function(e) { + + if ( e && e.originalEvent && e.originalEvent.type === "resize" ) { + + requestAFrame(function() { + self.update(); + }); + + } else { + + self.$refs.stage.hide(); + + setTimeout(function() { + self.$refs.stage.show(); + + self.update(); + }, 500); + + } + + }); + + // Trap keyboard focus inside of the modal, so the user does not accidentally tab outside of the modal + // (a.k.a. "escaping the modal") + $D.on('focusin.fb', function(e) { + var instance = $.fancybox ? $.fancybox.getInstance() : null; + + if ( instance.isClosing || !instance.current || !instance.current.opts.trapFocus || $( e.target ).hasClass( 'fancybox-container' ) || $( e.target ).is( document ) ) { + return; + } + + if ( instance && $( e.target ).css( 'position' ) !== 'fixed' && !instance.$refs.container.has( e.target ).length ) { + e.stopPropagation(); + + instance.focus(); + + // Sometimes page gets scrolled, set it back + $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft ); + } + }); + + + // Enable keyboard navigation + $D.on('keydown.fb', function (e) { + var current = self.current, + keycode = e.keyCode || e.which; + + if ( !current || !current.opts.keyboard ) { + return; + } + + if ( $(e.target).is('input') || $(e.target).is('textarea') ) { + return; + } + + // Backspace and Esc keys + if ( keycode === 8 || keycode === 27 ) { + e.preventDefault(); + + self.close( e ); + + return; + } + + // Left arrow and Up arrow + if ( keycode === 37 || keycode === 38 ) { + e.preventDefault(); + + self.previous(); + + return; + } + + // Righ arrow and Down arrow + if ( keycode === 39 || keycode === 40 ) { + e.preventDefault(); + + self.next(); + + return; + } + + self.trigger('afterKeydown', e, keycode); + }); + + + // Hide controls after some inactivity period + if ( self.group[ self.currIndex ].opts.idleTime ) { + self.idleSecondsCounter = 0; + + $D.on('mousemove.fb-idle mouseenter.fb-idle mouseleave.fb-idle mousedown.fb-idle touchstart.fb-idle touchmove.fb-idle scroll.fb-idle keydown.fb-idle', function() { + self.idleSecondsCounter = 0; + + if ( self.isIdle ) { + self.showControls(); + } + + self.isIdle = false; + }); + + self.idleInterval = window.setInterval(function() { + + self.idleSecondsCounter++; + + if ( self.idleSecondsCounter >= self.group[ self.currIndex ].opts.idleTime ) { + self.isIdle = true; + self.idleSecondsCounter = 0; + + self.hideControls(); + } + + }, 1000); + } + + }, + + + // Remove events added by the core + // =============================== + + removeEvents : function () { + var self = this; + + $W.off( 'orientationchange.fb resize.fb' ); + $D.off( 'focusin.fb keydown.fb .fb-idle' ); + + this.$refs.container.off( '.fb-close .fb-prev .fb-next' ); + + if ( self.idleInterval ) { + window.clearInterval( self.idleInterval ); + + self.idleInterval = null; + } + }, + + + // Change to previous gallery item + // =============================== + + previous : function( duration ) { + return this.jumpTo( this.currPos - 1, duration ); + }, + + + // Change to next gallery item + // =========================== + + next : function( duration ) { + return this.jumpTo( this.currPos + 1, duration ); + }, + + + // Switch to selected gallery item + // =============================== + + jumpTo : function ( pos, duration, slide ) { + var self = this, + firstRun, + loop, + current, + previous, + canvasWidth, + currentPos, + transitionProps; + + var groupLen = self.group.length; + + if ( self.isSliding || self.isClosing || ( self.isAnimating && self.firstRun ) ) { + return; + } + + pos = parseInt( pos, 10 ); + loop = self.current ? self.current.opts.loop : self.opts.loop; + + if ( !loop && ( pos < 0 || pos >= groupLen ) ) { + return false; + } + + firstRun = self.firstRun = ( self.firstRun === null ); + + if ( groupLen < 2 && !firstRun && !!self.isSliding ) { + return; + } + + previous = self.current; + + self.prevIndex = self.currIndex; + self.prevPos = self.currPos; + + // Create slides + current = self.createSlide( pos ); + + if ( groupLen > 1 ) { + if ( loop || current.index > 0 ) { + self.createSlide( pos - 1 ); + } + + if ( loop || current.index < groupLen - 1 ) { + self.createSlide( pos + 1 ); + } + } + + self.current = current; + self.currIndex = current.index; + self.currPos = current.pos; + + self.trigger( 'beforeShow', firstRun ); + + self.updateControls(); + + currentPos = $.fancybox.getTranslate( current.$slide ); + + current.isMoved = ( currentPos.left !== 0 || currentPos.top !== 0 ) && !current.$slide.hasClass( 'fancybox-animated' ); + current.forcedDuration = undefined; + + if ( $.isNumeric( duration ) ) { + current.forcedDuration = duration; + } else { + duration = current.opts[ firstRun ? 'animationDuration' : 'transitionDuration' ]; + } + + duration = parseInt( duration, 10 ); + + // Fresh start - reveal container, current slide and start loading content + if ( firstRun ) { + + if ( current.opts.animationEffect && duration ) { + self.$refs.container.css( 'transition-duration', duration + 'ms' ); + } + + self.$refs.container.removeClass( 'fancybox-is-hidden' ); + + forceRedraw( self.$refs.container ); + + self.$refs.container.addClass( 'fancybox-is-open' ); + + // Make first slide visible (to display loading icon, if needed) + current.$slide.addClass( 'fancybox-slide--current' ); + + self.loadSlide( current ); + + self.preload(); + + return; + } + + // Clean up + $.each(self.slides, function( index, slide ) { + $.fancybox.stop( slide.$slide ); + }); + + // Make current that slide is visible even if content is still loading + current.$slide.removeClass( 'fancybox-slide--next fancybox-slide--previous' ).addClass( 'fancybox-slide--current' ); + + // If slides have been dragged, animate them to correct position + if ( current.isMoved ) { + canvasWidth = Math.round( current.$slide.width() ); + + $.each(self.slides, function( index, slide ) { + var pos = slide.pos - current.pos; + + $.fancybox.animate( slide.$slide, { + top : 0, + left : ( pos * canvasWidth ) + ( pos * slide.opts.gutter ) + }, duration, function() { + + slide.$slide.removeAttr('style').removeClass( 'fancybox-slide--next fancybox-slide--previous' ); + + if ( slide.pos === self.currPos ) { + current.isMoved = false; + + self.complete(); + } + }); + }); + + } else { + self.$refs.stage.children().removeAttr( 'style' ); + } + + // Start transition that reveals current content + // or wait when it will be loaded + + if ( current.isLoaded ) { + self.revealContent( current ); + + } else { + self.loadSlide( current ); + } + + self.preload(); + + if ( previous.pos === current.pos ) { + return; + } + + // Handle previous slide + // ===================== + + transitionProps = 'fancybox-slide--' + ( previous.pos > current.pos ? 'next' : 'previous' ); + + previous.$slide.removeClass( 'fancybox-slide--complete fancybox-slide--current fancybox-slide--next fancybox-slide--previous' ); + + previous.isComplete = false; + + if ( !duration || ( !current.isMoved && !current.opts.transitionEffect ) ) { + return; + } + + if ( current.isMoved ) { + previous.$slide.addClass( transitionProps ); + + } else { + + transitionProps = 'fancybox-animated ' + transitionProps + ' fancybox-fx-' + current.opts.transitionEffect; + + $.fancybox.animate( previous.$slide, transitionProps, duration, function() { + previous.$slide.removeClass( transitionProps ).removeAttr( 'style' ); + }); + + } + + }, + + + // Create new "slide" element + // These are gallery items that are actually added to DOM + // ======================================================= + + createSlide : function( pos ) { + + var self = this; + var $slide; + var index; + + index = pos % self.group.length; + index = index < 0 ? self.group.length + index : index; + + if ( !self.slides[ pos ] && self.group[ index ] ) { + $slide = $('
').appendTo( self.$refs.stage ); + + self.slides[ pos ] = $.extend( true, {}, self.group[ index ], { + pos : pos, + $slide : $slide, + isLoaded : false, + }); + + self.updateSlide( self.slides[ pos ] ); + } + + return self.slides[ pos ]; + }, + + + // Scale image to the actual size of the image + // =========================================== + + scaleToActual : function( x, y, duration ) { + + var self = this; + + var current = self.current; + var $what = current.$content; + + var imgPos, posX, posY, scaleX, scaleY; + + var canvasWidth = parseInt( current.$slide.width(), 10 ); + var canvasHeight = parseInt( current.$slide.height(), 10 ); + + var newImgWidth = current.width; + var newImgHeight = current.height; + + if ( !( current.type == 'image' && !current.hasError) || !$what || self.isAnimating) { + return; + } + + $.fancybox.stop( $what ); + + self.isAnimating = true; + + x = x === undefined ? canvasWidth * 0.5 : x; + y = y === undefined ? canvasHeight * 0.5 : y; + + imgPos = $.fancybox.getTranslate( $what ); + + scaleX = newImgWidth / imgPos.width; + scaleY = newImgHeight / imgPos.height; + + // Get center position for original image + posX = ( canvasWidth * 0.5 - newImgWidth * 0.5 ); + posY = ( canvasHeight * 0.5 - newImgHeight * 0.5 ); + + // Make sure image does not move away from edges + if ( newImgWidth > canvasWidth ) { + posX = imgPos.left * scaleX - ( ( x * scaleX ) - x ); + + if ( posX > 0 ) { + posX = 0; + } + + if ( posX < canvasWidth - newImgWidth ) { + posX = canvasWidth - newImgWidth; + } + } + + if ( newImgHeight > canvasHeight) { + posY = imgPos.top * scaleY - ( ( y * scaleY ) - y ); + + if ( posY > 0 ) { + posY = 0; + } + + if ( posY < canvasHeight - newImgHeight ) { + posY = canvasHeight - newImgHeight; + } + } + + self.updateCursor( newImgWidth, newImgHeight ); + + $.fancybox.animate( $what, { + top : posY, + left : posX, + scaleX : scaleX, + scaleY : scaleY + }, duration || 330, function() { + self.isAnimating = false; + }); + + // Stop slideshow + if ( self.SlideShow && self.SlideShow.isActive ) { + self.SlideShow.stop(); + } + }, + + + // Scale image to fit inside parent element + // ======================================== + + scaleToFit : function( duration ) { + + var self = this; + + var current = self.current; + var $what = current.$content; + var end; + + if ( !( current.type == 'image' && !current.hasError) || !$what || self.isAnimating ) { + return; + } + + $.fancybox.stop( $what ); + + self.isAnimating = true; + + end = self.getFitPos( current ); + + self.updateCursor( end.width, end.height ); + + $.fancybox.animate( $what, { + top : end.top, + left : end.left, + scaleX : end.width / $what.width(), + scaleY : end.height / $what.height() + }, duration || 330, function() { + self.isAnimating = false; + }); + + }, + + // Calculate image size to fit inside viewport + // =========================================== + + getFitPos : function( slide ) { + var self = this; + var $what = slide.$content; + + var imgWidth = slide.width; + var imgHeight = slide.height; + + var margin = slide.opts.margin; + + var canvasWidth, canvasHeight, minRatio, width, height; + + if ( !$what || !$what.length || ( !imgWidth && !imgHeight) ) { + return false; + } + + // Convert "margin to CSS style: [ top, right, bottom, left ] + if ( $.type( margin ) === "number" ) { + margin = [ margin, margin ]; + } + + if ( margin.length == 2 ) { + margin = [ margin[0], margin[1], margin[0], margin[1] ]; + } + + if ( $W.width() < 800 ) { + margin = [ 0, 0, 0, 0 ]; + } + + // We can not use $slide width here, because it can have different diemensions while in transiton + canvasWidth = parseInt( self.$refs.stage.width(), 10 ) - ( margin[ 1 ] + margin[ 3 ] ); + canvasHeight = parseInt( self.$refs.stage.height(), 10 ) - ( margin[ 0 ] + margin[ 2 ] ); + + minRatio = Math.min(1, canvasWidth / imgWidth, canvasHeight / imgHeight ); + + width = Math.floor( minRatio * imgWidth ); + height = Math.floor( minRatio * imgHeight ); + + // Use floor rounding to make sure it really fits + return { + top : Math.floor( ( canvasHeight - height ) * 0.5 ) + margin[ 0 ], + left : Math.floor( ( canvasWidth - width ) * 0.5 ) + margin[ 3 ], + width : width, + height : height + }; + + }, + + + // Update position and content of all slides + // ========================================= + + update : function() { + + var self = this; + + $.each( self.slides, function( key, slide ) { + self.updateSlide( slide ); + }); + + }, + + + // Update slide position and scale content to fit + // ============================================== + + updateSlide : function( slide ) { + + var self = this; + var $what = slide.$content; + + if ( $what && ( slide.width || slide.height ) ) { + $.fancybox.stop( $what ); + + $.fancybox.setTranslate( $what, self.getFitPos( slide ) ); + + if ( slide.pos === self.currPos ) { + self.updateCursor(); + } + } + + slide.$slide.trigger( 'refresh' ); + + self.trigger( 'onUpdate', slide ); + + }, + + // Update cursor style depending if content can be zoomed + // ====================================================== + + updateCursor : function( nextWidth, nextHeight ) { + + var self = this; + var isScaledDown; + + var $container = self.$refs.container.removeClass('fancybox-is-zoomable fancybox-can-zoomIn fancybox-can-drag fancybox-can-zoomOut'); + + if ( !self.current || self.isClosing ) { + return; + } + + if ( self.isZoomable() ) { + + $container.addClass( 'fancybox-is-zoomable' ); + + if ( nextWidth !== undefined && nextHeight !== undefined ) { + isScaledDown = nextWidth < self.current.width && nextHeight < self.current.height; + + } else { + isScaledDown = self.isScaledDown(); + } + + if ( isScaledDown ) { + + // If image is scaled down, then, obviously, it can be zoomed to full size + $container.addClass('fancybox-can-zoomIn'); + + } else { + + if ( self.current.opts.touch ) { + + // If image size ir largen than available available and touch module is not disable, + // then user can do panning + $container.addClass('fancybox-can-drag'); + + } else { + $container.addClass('fancybox-can-zoomOut'); + } + + } + + } else if ( self.current.opts.touch ) { + $container.addClass('fancybox-can-drag'); + } + + }, + + + // Check if current slide is zoomable + // ================================== + + isZoomable : function() { + + var self = this; + + var current = self.current; + var fitPos; + + if ( !current || self.isClosing ) { + return; + } + + // Assume that slide is zoomable if + // - image is loaded successfuly + // - click action is "zoom" + // - actual size of the image is smaller than available area + if ( current.type === 'image' && current.isLoaded && !current.hasError && + ( current.opts.clickContent === 'zoom' || ( $.isFunction( current.opts.clickContent ) && current.opts.clickContent( current ) === "zoom" ) ) + ) { + + fitPos = self.getFitPos( current ); + + if ( current.width > fitPos.width || current.height > fitPos.height ) { + return true; + } + + } + + return false; + + }, + + + // Check if current image dimensions are smaller than actual + // ========================================================= + + isScaledDown : function() { + + var self = this; + + var current = self.current; + var $what = current.$content; + + var rez = false; + + if ( $what ) { + rez = $.fancybox.getTranslate( $what ); + rez = rez.width < current.width || rez.height < current.height; + } + + return rez; + + }, + + + // Check if image dimensions exceed parent element + // =============================================== + + canPan : function() { + + var self = this; + + var current = self.current; + var $what = current.$content; + + var rez = false; + + if ( $what ) { + rez = self.getFitPos( current ); + rez = Math.abs( $what.width() - rez.width ) > 1 || Math.abs( $what.height() - rez.height ) > 1; + + } + + return rez; + + }, + + + // Load content into the slide + // =========================== + + loadSlide : function( slide ) { + + var self = this, type, $slide; + var ajaxLoad; + + if ( slide.isLoading ) { + return; + } + + if ( slide.isLoaded ) { + return; + } + + slide.isLoading = true; + + self.trigger( 'beforeLoad', slide ); + + type = slide.type; + $slide = slide.$slide; + + $slide + .off( 'refresh' ) + .trigger( 'onReset' ) + .addClass( 'fancybox-slide--' + ( type || 'unknown' ) ) + .addClass( slide.opts.slideClass ); + + // Create content depending on the type + + switch ( type ) { + + case 'image': + + self.setImage( slide ); + + break; + + case 'iframe': + + self.setIframe( slide ); + + break; + + case 'html': + + self.setContent( slide, slide.src || slide.content ); + + break; + + case 'inline': + + if ( $( slide.src ).length ) { + self.setContent( slide, $( slide.src ) ); + + } else { + self.setError( slide ); + } + + break; + + case 'ajax': + + self.showLoading( slide ); + + ajaxLoad = $.ajax( $.extend( {}, slide.opts.ajax.settings, { + url : slide.src, + success : function ( data, textStatus ) { + + if ( textStatus === 'success' ) { + self.setContent( slide, data ); + } + + }, + error : function ( jqXHR, textStatus ) { + + if ( jqXHR && textStatus !== 'abort' ) { + self.setError( slide ); + } + + } + })); + + $slide.one( 'onReset', function () { + ajaxLoad.abort(); + }); + + break; + + default: + + self.setError( slide ); + + break; + + } + + return true; + + }, + + + // Use thumbnail image, if possible + // ================================ + + setImage : function( slide ) { + + var self = this; + var srcset = slide.opts.image.srcset; + + var found, temp, pxRatio, windowWidth; + + // If we have "srcset", then we need to find matching "src" value. + // This is necessary, because when you set an src attribute, the browser will preload the image + // before any javascript or even CSS is applied. + if ( srcset ) { + pxRatio = window.devicePixelRatio || 1; + windowWidth = window.innerWidth * pxRatio; + + temp = srcset.split(',').map(function ( el ) { + var ret = {}; + + el.trim().split(/\s+/).forEach(function ( el, i ) { + var value = parseInt( el.substring(0, el.length - 1), 10 ); + + if ( i === 0 ) { + return ( ret.url = el ); + } + + if ( value ) { + ret.value = value; + ret.postfix = el[ el.length - 1 ]; + } + + }); + + return ret; + }); + + // Sort by value + temp.sort(function (a, b) { + return a.value - b.value; + }); + + // Ok, now we have an array of all srcset values + for ( var j = 0; j < temp.length; j++ ) { + var el = temp[ j ]; + + if ( ( el.postfix === 'w' && el.value >= windowWidth ) || ( el.postfix === 'x' && el.value >= pxRatio ) ) { + found = el; + break; + } + } + + // If not found, take the last one + if ( !found && temp.length ) { + found = temp[ temp.length - 1 ]; + } + + if ( found ) { + slide.src = found.url; + + // If we have default width/height values, we can calculate height for matching source + if ( slide.width && slide.height && found.postfix == 'w' ) { + slide.height = ( slide.width / slide.height ) * found.value; + slide.width = found.value; + } + } + } + + // This will be wrapper containing both ghost and actual image + slide.$content = $('
') + .addClass( 'fancybox-is-hidden' ) + .appendTo( slide.$slide ); + + + // If we have a thumbnail, we can display it while actual image is loading + // Users will not stare at black screen and actual image will appear gradually + if ( slide.opts.preload !== false && slide.opts.width && slide.opts.height && ( slide.opts.thumb || slide.opts.$thumb ) ) { + + slide.width = slide.opts.width; + slide.height = slide.opts.height; + + slide.$ghost = $('') + .one('error', function() { + + $(this).remove(); + + slide.$ghost = null; + + self.setBigImage( slide ); + + }) + .one('load', function() { + + self.afterLoad( slide ); + + self.setBigImage( slide ); + + }) + .addClass( 'fancybox-image' ) + .appendTo( slide.$content ) + .attr( 'src', slide.opts.thumb || slide.opts.$thumb.attr( 'src' ) ); + + } else { + + self.setBigImage( slide ); + + } + + }, + + + // Create full-size image + // ====================== + + setBigImage : function ( slide ) { + var self = this; + var $img = $(''); + + slide.$image = $img + .one('error', function() { + + self.setError( slide ); + + }) + .one('load', function() { + + // Clear timeout that checks if loading icon needs to be displayed + clearTimeout( slide.timouts ); + + slide.timouts = null; + + if ( self.isClosing ) { + return; + } + + slide.width = this.naturalWidth; + slide.height = this.naturalHeight; + + if ( slide.opts.image.srcset ) { + $img.attr( 'sizes', '100vw' ).attr( 'srcset', slide.opts.image.srcset ); + } + + self.hideLoading( slide ); + + if ( slide.$ghost ) { + + slide.timouts = setTimeout(function() { + slide.timouts = null; + + slide.$ghost.hide(); + + }, Math.min( 300, Math.max( 1000, slide.height / 1600 ) ) ); + + } else { + self.afterLoad( slide ); + } + + }) + .addClass( 'fancybox-image' ) + .attr('src', slide.src) + .appendTo( slide.$content ); + + if ( $img[0].complete ) { + $img.trigger( 'load' ); + + } else if( $img[0].error ) { + $img.trigger( 'error' ); + + } else { + + slide.timouts = setTimeout(function() { + if ( !$img[0].complete && !slide.hasError ) { + self.showLoading( slide ); + } + + }, 100); + + } + + }, + + + // Create iframe wrapper, iframe and bindings + // ========================================== + + setIframe : function( slide ) { + var self = this, + opts = slide.opts.iframe, + $slide = slide.$slide, + $iframe; + + slide.$content = $('
') + .css( opts.css ) + .appendTo( $slide ); + + $iframe = $( opts.tpl.replace(/\{rnd\}/g, new Date().getTime()) ) + .attr( opts.attr ) + .appendTo( slide.$content ); + + if ( opts.preload ) { + + self.showLoading( slide ); + + // Unfortunately, it is not always possible to determine if iframe is successfully loaded + // (due to browser security policy) + + $iframe.on('load.fb error.fb', function(e) { + this.isReady = 1; + + slide.$slide.trigger( 'refresh' ); + + self.afterLoad( slide ); + }); + + // Recalculate iframe content size + // =============================== + + $slide.on('refresh.fb', function() { + var $wrap = slide.$content, + $contents, + $body, + scrollWidth, + frameWidth, + frameHeight; + + if ( $iframe[0].isReady !== 1 ) { + return; + } + + // Check if content is accessible, + // it will fail if frame is not with the same origin + + try { + $contents = $iframe.contents(); + $body = $contents.find('body'); + + } catch (ignore) {} + + // Calculate dimensions for the wrapper + if ( $body && $body.length && !( opts.css.width !== undefined && opts.css.height !== undefined ) ) { + + scrollWidth = $iframe[0].contentWindow.document.documentElement.scrollWidth; + + frameWidth = Math.ceil( $body.outerWidth(true) + ( $wrap.width() - scrollWidth ) ); + frameHeight = Math.ceil( $body.outerHeight(true) ); + + // Resize wrapper to fit iframe content + $wrap.css({ + 'width' : opts.css.width === undefined ? frameWidth + ( $wrap.outerWidth() - $wrap.innerWidth() ) : opts.css.width, + 'height' : opts.css.height === undefined ? frameHeight + ( $wrap.outerHeight() - $wrap.innerHeight() ) : opts.css.height + }); + + } + + $wrap.removeClass( 'fancybox-is-hidden' ); + + }); + + } else { + + this.afterLoad( slide ); + + } + + $iframe.attr( 'src', slide.src ); + + if ( slide.opts.smallBtn === true ) { + slide.$content.prepend( self.translate( slide, slide.opts.btnTpl.smallBtn ) ); + } + + // Remove iframe if closing or changing gallery item + $slide.one( 'onReset', function () { + + // This helps IE not to throw errors when closing + try { + + $( this ).find( 'iframe' ).hide().attr( 'src', '//about:blank' ); + + } catch ( ignore ) {} + + $( this ).empty(); + + slide.isLoaded = false; + + }); + + }, + + + // Wrap and append content to the slide + // ====================================== + + setContent : function ( slide, content ) { + + var self = this; + + if ( self.isClosing ) { + return; + } + + self.hideLoading( slide ); + + slide.$slide.empty(); + + if ( isQuery( content ) && content.parent().length ) { + + // If content is a jQuery object, then it will be moved to the slide. + // The placeholder is created so we will know where to put it back. + // If user is navigating gallery fast, then the content might be already inside fancyBox + // ===================================================================================== + + // Make sure content is not already moved to fancyBox + content.parent( '.fancybox-slide--inline' ).trigger( 'onReset' ); + + // Create temporary element marking original place of the content + slide.$placeholder = $( '
' ).hide().insertAfter( content ); + + // Make sure content is visible + content.css('display', 'inline-block'); + + } else if ( !slide.hasError ) { + + // If content is just a plain text, try to convert it to html + if ( $.type( content ) === 'string' ) { + content = $('
').append( $.trim( content ) ).contents(); + + // If we have text node, then add wrapping element to make vertical alignment work + if ( content[0].nodeType === 3 ) { + content = $('
').html( content ); + } + } + + // If "filter" option is provided, then filter content + if ( slide.opts.filter ) { + content = $('
').html( content ).find( slide.opts.filter ); + } + + } + + slide.$slide.one('onReset', function () { + + // Put content back + if ( slide.$placeholder ) { + slide.$placeholder.after( content.hide() ).remove(); + + slide.$placeholder = null; + } + + // Remove custom close button + if ( slide.$smallBtn ) { + slide.$smallBtn.remove(); + + slide.$smallBtn = null; + } + + // Remove content and mark slide as not loaded + if ( !slide.hasError ) { + $(this).empty(); + + slide.isLoaded = false; + } + + }); + + slide.$content = $( content ).appendTo( slide.$slide ); + + if ( slide.opts.smallBtn && !slide.$smallBtn ) { + slide.$smallBtn = $( self.translate( slide, slide.opts.btnTpl.smallBtn ) ).appendTo( slide.$content ); + } + + this.afterLoad( slide ); + }, + + // Display error message + // ===================== + + setError : function ( slide ) { + + slide.hasError = true; + + slide.$slide.removeClass( 'fancybox-slide--' + slide.type ); + + this.setContent( slide, this.translate( slide, slide.opts.errorTpl ) ); + + }, + + + // Show loading icon inside the slide + // ================================== + + showLoading : function( slide ) { + + var self = this; + + slide = slide || self.current; + + if ( slide && !slide.$spinner ) { + slide.$spinner = $( self.opts.spinnerTpl ).appendTo( slide.$slide ); + } + + }, + + // Remove loading icon from the slide + // ================================== + + hideLoading : function( slide ) { + + var self = this; + + slide = slide || self.current; + + if ( slide && slide.$spinner ) { + slide.$spinner.remove(); + + delete slide.$spinner; + } + + }, + + + // Adjustments after slide content has been loaded + // =============================================== + + afterLoad : function( slide ) { + + var self = this; + + if ( self.isClosing ) { + return; + } + + slide.isLoading = false; + slide.isLoaded = true; + + self.trigger( 'afterLoad', slide ); + + self.hideLoading( slide ); + + if ( slide.opts.protect && slide.$content && !slide.hasError ) { + + // Disable right click + slide.$content.on( 'contextmenu.fb', function( e ) { + if ( e.button == 2 ) { + e.preventDefault(); + } + + return true; + }); + + // Add fake element on top of the image + // This makes a bit harder for user to select image + if ( slide.type === 'image' ) { + $( '
' ).appendTo( slide.$content ); + } + + } + + self.revealContent( slide ); + + }, + + + // Make content visible + // This method is called right after content has been loaded or + // user navigates gallery and transition should start + // ============================================================ + + revealContent : function( slide ) { + + var self = this; + var $slide = slide.$slide; + + var effect, effectClassName, duration, opacity, end, start = false; + + effect = slide.opts[ self.firstRun ? 'animationEffect' : 'transitionEffect' ]; + duration = slide.opts[ self.firstRun ? 'animationDuration' : 'transitionDuration' ]; + + duration = parseInt( slide.forcedDuration === undefined ? duration : slide.forcedDuration, 10 ); + + if ( slide.isMoved || slide.pos !== self.currPos || !duration ) { + effect = false; + } + + // Check if can zoom + if ( effect === 'zoom' && !( slide.pos === self.currPos && duration && slide.type === 'image' && !slide.hasError && ( start = self.getThumbPos( slide ) ) ) ) { + effect = 'fade'; + } + + + // Zoom animation + // ============== + + if ( effect === 'zoom' ) { + end = self.getFitPos( slide ); + + end.scaleX = end.width / start.width; + end.scaleY = end.height / start.height; + + delete end.width; + delete end.height; + + // Check if we need to animate opacity + opacity = slide.opts.zoomOpacity; + + if ( opacity == 'auto' ) { + opacity = Math.abs( slide.width / slide.height - start.width / start.height ) > 0.1; + } + + if ( opacity ) { + start.opacity = 0.1; + end.opacity = 1; + } + + // Draw image at start position + $.fancybox.setTranslate( slide.$content.removeClass( 'fancybox-is-hidden' ), start ); + + forceRedraw( slide.$content ); + + // Start animation + $.fancybox.animate( slide.$content, end, duration, function() { + self.complete(); + }); + + return; + } + + + self.updateSlide( slide ); + + + // Simply show content + // =================== + + if ( !effect ) { + forceRedraw( $slide ); + + slide.$content.removeClass( 'fancybox-is-hidden' ); + + if ( slide.pos === self.currPos ) { + self.complete(); + } + + return; + } + + $.fancybox.stop( $slide ); + + effectClassName = 'fancybox-animated fancybox-slide--' + ( slide.pos > self.prevPos ? 'next' : 'previous' ) + ' fancybox-fx-' + effect; + + $slide.removeAttr( 'style' ).removeClass( 'fancybox-slide--current fancybox-slide--next fancybox-slide--previous' ).addClass( effectClassName ); + + slide.$content.removeClass( 'fancybox-is-hidden' ); + + //Force reflow for CSS3 transitions + forceRedraw( $slide ); + + $.fancybox.animate( $slide, 'fancybox-slide--current', duration, function(e) { + $slide.removeClass( effectClassName ).removeAttr( 'style' ); + + if ( slide.pos === self.currPos ) { + self.complete(); + } + + }, true); + + }, + + + // Check if we can and have to zoom from thumbnail + //================================================ + + getThumbPos : function( slide ) { + + var self = this; + var rez = false; + + // Check if element is inside the viewport by at least 1 pixel + var isElementVisible = function( $el ) { + var element = $el[0]; + + var elementRect = element.getBoundingClientRect(); + var parentRects = []; + + var visibleInAllParents; + + while ( element.parentElement !== null ) { + if ( $(element.parentElement).css('overflow') === 'hidden' || $(element.parentElement).css('overflow') === 'auto' ) { + parentRects.push(element.parentElement.getBoundingClientRect()); + } + + element = element.parentElement; + } + + visibleInAllParents = parentRects.every(function(parentRect){ + var visiblePixelX = Math.min(elementRect.right, parentRect.right) - Math.max(elementRect.left, parentRect.left); + var visiblePixelY = Math.min(elementRect.bottom, parentRect.bottom) - Math.max(elementRect.top, parentRect.top); + + return visiblePixelX > 0 && visiblePixelY > 0; + }); + + return visibleInAllParents && + elementRect.bottom > 0 && elementRect.right > 0 && + elementRect.left < $(window).width() && elementRect.top < $(window).height(); + }; + + var $thumb = slide.opts.$thumb; + var thumbPos = $thumb ? $thumb.offset() : 0; + var slidePos; + + if ( thumbPos && $thumb[0].ownerDocument === document && isElementVisible( $thumb ) ) { + slidePos = self.$refs.stage.offset(); + + rez = { + top : thumbPos.top - slidePos.top + parseFloat( $thumb.css( "border-top-width" ) || 0 ), + left : thumbPos.left - slidePos.left + parseFloat( $thumb.css( "border-left-width" ) || 0 ), + width : $thumb.width(), + height : $thumb.height(), + scaleX : 1, + scaleY : 1 + }; + } + + return rez; + }, + + + // Final adjustments after current gallery item is moved to position + // and it`s content is loaded + // ================================================================== + + complete : function() { + + var self = this; + + var current = self.current; + var slides = {}; + + if ( current.isMoved || !current.isLoaded || current.isComplete ) { + return; + } + + current.isComplete = true; + + current.$slide.siblings().trigger( 'onReset' ); + + // Trigger any CSS3 transiton inside the slide + forceRedraw( current.$slide ); + + current.$slide.addClass( 'fancybox-slide--complete' ); + + // Remove unnecessary slides + $.each( self.slides, function( key, slide ) { + if ( slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1 ) { + slides[ slide.pos ] = slide; + + } else if ( slide ) { + + $.fancybox.stop( slide.$slide ); + + slide.$slide.unbind().remove(); + } + }); + + self.slides = slides; + + self.updateCursor(); + + self.trigger( 'afterShow' ); + + // Try to focus on the first focusable element + if ( $( document.activeElement ).is( '[disabled]' ) || ( current.opts.autoFocus && !( current.type == 'image' || current.type === 'iframe' ) ) ) { + self.focus(); + } + + }, + + + // Preload next and previous slides + // ================================ + + preload : function() { + var self = this; + var next, prev; + + if ( self.group.length < 2 ) { + return; + } + + next = self.slides[ self.currPos + 1 ]; + prev = self.slides[ self.currPos - 1 ]; + + if ( next && next.type === 'image' ) { + self.loadSlide( next ); + } + + if ( prev && prev.type === 'image' ) { + self.loadSlide( prev ); + } + + }, + + + // Try to find and focus on the first focusable element + // ==================================================== + + focus : function() { + var current = this.current; + var $el; + + if ( this.isClosing ) { + return; + } + + // Skip for images and iframes + $el = current && current.isComplete ? current.$slide.find('button,:input,[tabindex],a').filter(':not([disabled]):visible:first') : null; + $el = $el && $el.length ? $el : this.$refs.container; + + $el.focus(); + }, + + + // Activates current instance - brings container to the front and enables keyboard, + // notifies other instances about deactivating + // ================================================================================= + + activate : function () { + var self = this; + + // Deactivate all instances + $( '.fancybox-container' ).each(function () { + var instance = $(this).data( 'FancyBox' ); + + // Skip self and closing instances + if (instance && instance.uid !== self.uid && !instance.isClosing) { + instance.trigger( 'onDeactivate' ); + } + + }); + + if ( self.current ) { + if ( self.$refs.container.index() > 0 ) { + self.$refs.container.prependTo( document.body ); + } + + self.updateControls(); + } + + self.trigger( 'onActivate' ); + + self.addEvents(); + + }, + + + // Start closing procedure + // This will start "zoom-out" animation if needed and clean everything up afterwards + // ================================================================================= + + close : function( e, d ) { + + var self = this; + var current = self.current; + + var effect, duration; + var $what, opacity, start, end; + + var done = function() { + self.cleanUp( e ); + }; + + if ( self.isClosing ) { + return false; + } + + self.isClosing = true; + + // If beforeClose callback prevents closing, make sure content is centered + if ( self.trigger( 'beforeClose', e ) === false ) { + self.isClosing = false; + + requestAFrame(function() { + self.update(); + }); + + return false; + } + + // Remove all events + // If there are multiple instances, they will be set again by "activate" method + self.removeEvents(); + + if ( current.timouts ) { + clearTimeout( current.timouts ); + } + + $what = current.$content; + effect = current.opts.animationEffect; + duration = $.isNumeric( d ) ? d : ( effect ? current.opts.animationDuration : 0 ); + + // Remove other slides + current.$slide.off( transitionEnd ).removeClass( 'fancybox-slide--complete fancybox-slide--next fancybox-slide--previous fancybox-animated' ); + + current.$slide.siblings().trigger( 'onReset' ).remove(); + + // Trigger animations + if ( duration ) { + self.$refs.container.removeClass( 'fancybox-is-open' ).addClass( 'fancybox-is-closing' ); + } + + // Clean up + self.hideLoading( current ); + + self.hideControls(); + + self.updateCursor(); + + // Check if possible to zoom-out + if ( effect === 'zoom' && !( e !== true && $what && duration && current.type === 'image' && !current.hasError && ( end = self.getThumbPos( current ) ) ) ) { + effect = 'fade'; + } + + if ( effect === 'zoom' ) { + $.fancybox.stop( $what ); + + start = $.fancybox.getTranslate( $what ); + + start.width = start.width * start.scaleX; + start.height = start.height * start.scaleY; + + // Check if we need to animate opacity + opacity = current.opts.zoomOpacity; + + if ( opacity == 'auto' ) { + opacity = Math.abs( current.width / current.height - end.width / end.height ) > 0.1; + } + + if ( opacity ) { + end.opacity = 0; + } + + start.scaleX = start.width / end.width; + start.scaleY = start.height / end.height; + + start.width = end.width; + start.height = end.height; + + $.fancybox.setTranslate( current.$content, start ); + + $.fancybox.animate( current.$content, end, duration, done ); + + return true; + } + + if ( effect && duration ) { + + // If skip animation + if ( e === true ) { + setTimeout( done, duration ); + + } else { + $.fancybox.animate( current.$slide.removeClass( 'fancybox-slide--current' ), 'fancybox-animated fancybox-slide--previous fancybox-fx-' + effect, duration, done ); + } + + } else { + done(); + } + + return true; + }, + + + // Final adjustments after removing the instance + // ============================================= + + cleanUp : function( e ) { + var self = this, + instance; + + self.current.$slide.trigger( 'onReset' ); + + self.$refs.container.empty().remove(); + + self.trigger( 'afterClose', e ); + + // Place back focus + if ( self.$lastFocus && !!self.current.opts.backFocus ) { + self.$lastFocus.focus(); + } + + self.current = null; + + // Check if there are other instances + instance = $.fancybox.getInstance(); + + if ( instance ) { + instance.activate(); + + } else { + + $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft ); + + $( 'html' ).removeClass( 'fancybox-enabled' ); + + $( '#fancybox-style-noscroll' ).remove(); + } + + }, + + + // Call callback and trigger an event + // ================================== + + trigger : function( name, slide ) { + var args = Array.prototype.slice.call(arguments, 1), + self = this, + obj = slide && slide.opts ? slide : self.current, + rez; + + if ( obj ) { + args.unshift( obj ); + + } else { + obj = self; + } + + args.unshift( self ); + + if ( $.isFunction( obj.opts[ name ] ) ) { + rez = obj.opts[ name ].apply( obj, args ); + } + + if ( rez === false ) { + return rez; + } + + if ( name === 'afterClose' ) { + $D.trigger( name + '.fb', args ); + + } else { + self.$refs.container.trigger( name + '.fb', args ); + } + + }, + + + // Update infobar values, navigation button states and reveal caption + // ================================================================== + + updateControls : function ( force ) { + + var self = this; + + var current = self.current; + var index = current.index; + var opts = current.opts; + var caption = opts.caption; + var $caption = self.$refs.caption; + + // Recalculate content dimensions + current.$slide.trigger( 'refresh' ); + + self.$caption = caption && caption.length ? $caption.html( caption ) : null; + + if ( !self.isHiddenControls ) { + self.showControls(); + } + + // Update info and navigation elements + $('[data-fancybox-count]').html( self.group.length ); + $('[data-fancybox-index]').html( index + 1 ); + + $('[data-fancybox-prev]').prop('disabled', ( !opts.loop && index <= 0 ) ); + $('[data-fancybox-next]').prop('disabled', ( !opts.loop && index >= self.group.length - 1 ) ); + + }, + + // Hide toolbar and caption + // ======================== + + hideControls : function () { + + this.isHiddenControls = true; + + this.$refs.container.removeClass('fancybox-show-infobar fancybox-show-toolbar fancybox-show-caption fancybox-show-nav'); + + }, + + showControls : function() { + + var self = this; + var opts = self.current ? self.current.opts : self.opts; + var $container = self.$refs.container; + + self.isHiddenControls = false; + self.idleSecondsCounter = 0; + + $container + .toggleClass('fancybox-show-toolbar', !!( opts.toolbar && opts.buttons ) ) + .toggleClass('fancybox-show-infobar', !!( opts.infobar && self.group.length > 1 ) ) + .toggleClass('fancybox-show-nav', !!( opts.arrows && self.group.length > 1 ) ) + .toggleClass('fancybox-is-modal', !!opts.modal ); + + if ( self.$caption ) { + $container.addClass( 'fancybox-show-caption '); + + } else { + $container.removeClass( 'fancybox-show-caption' ); + } + + }, + + + // Toggle toolbar and caption + // ========================== + + toggleControls : function() { + + if ( this.isHiddenControls ) { + this.showControls(); + + } else { + this.hideControls(); + } + + }, + + + }); + + + $.fancybox = { + + version : "{fancybox-version}", + defaults : defaults, + + + // Get current instance and execute a command. + // + // Examples of usage: + // + // $instance = $.fancybox.getInstance(); + // $.fancybox.getInstance().jumpTo( 1 ); + // $.fancybox.getInstance( 'jumpTo', 1 ); + // $.fancybox.getInstance( function() { + // console.info( this.currIndex ); + // }); + // ====================================================== + + getInstance : function ( command ) { + var instance = $('.fancybox-container:not(".fancybox-is-closing"):first').data( 'FancyBox' ); + var args = Array.prototype.slice.call(arguments, 1); + + if ( instance instanceof FancyBox ) { + + if ( $.type( command ) === 'string' ) { + instance[ command ].apply( instance, args ); + + } else if ( $.type( command ) === 'function' ) { + command.apply( instance, args ); + + } + + return instance; + } + + return false; + + }, + + + // Create new instance + // =================== + + open : function ( items, opts, index ) { + return new FancyBox( items, opts, index ); + }, + + + // Close current or all instances + // ============================== + + close : function ( all ) { + var instance = this.getInstance(); + + if ( instance ) { + instance.close(); + + // Try to find and close next instance + + if ( all === true ) { + this.close(); + } + } + + }, + + // Close instances and unbind all events + // ============================== + + destroy : function() { + + this.close( true ); + + $D.off( 'click.fb-start' ); + + }, + + + // Try to detect mobile devices + // ============================ + + isMobile : document.createTouch !== undefined && /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent), + + + // Detect if 'translate3d' support is available + // ============================================ + + use3d : (function() { + var div = document.createElement('div'); + + return window.getComputedStyle && window.getComputedStyle( div ).getPropertyValue('transform') && !(document.documentMode && document.documentMode < 11); + }()), + + + // Helper function to get current visual state of an element + // returns array[ top, left, horizontal-scale, vertical-scale, opacity ] + // ===================================================================== + + getTranslate : function( $el ) { + var matrix; + + if ( !$el || !$el.length ) { + return false; + } + + matrix = $el.eq( 0 ).css('transform'); + + if ( matrix && matrix.indexOf( 'matrix' ) !== -1 ) { + matrix = matrix.split('(')[1]; + matrix = matrix.split(')')[0]; + matrix = matrix.split(','); + } else { + matrix = []; + } + + if ( matrix.length ) { + + // If IE + if ( matrix.length > 10 ) { + matrix = [ matrix[13], matrix[12], matrix[0], matrix[5] ]; + + } else { + matrix = [ matrix[5], matrix[4], matrix[0], matrix[3]]; + } + + matrix = matrix.map(parseFloat); + + } else { + matrix = [ 0, 0, 1, 1 ]; + + var transRegex = /\.*translate\((.*)px,(.*)px\)/i; + var transRez = transRegex.exec( $el.eq( 0 ).attr('style') ); + + if ( transRez ) { + matrix[ 0 ] = parseFloat( transRez[2] ); + matrix[ 1 ] = parseFloat( transRez[1] ); + } + } + + return { + top : matrix[ 0 ], + left : matrix[ 1 ], + scaleX : matrix[ 2 ], + scaleY : matrix[ 3 ], + opacity : parseFloat( $el.css('opacity') ), + width : $el.width(), + height : $el.height() + }; + + }, + + + // Shortcut for setting "translate3d" properties for element + // Can set be used to set opacity, too + // ======================================================== + + setTranslate : function( $el, props ) { + var str = ''; + var css = {}; + + if ( !$el || !props ) { + return; + } + + if ( props.left !== undefined || props.top !== undefined ) { + str = ( props.left === undefined ? $el.position().left : props.left ) + 'px, ' + ( props.top === undefined ? $el.position().top : props.top ) + 'px'; + + if ( this.use3d ) { + str = 'translate3d(' + str + ', 0px)'; + + } else { + str = 'translate(' + str + ')'; + } + } + + if ( props.scaleX !== undefined && props.scaleY !== undefined ) { + str = (str.length ? str + ' ' : '') + 'scale(' + props.scaleX + ', ' + props.scaleY + ')'; + } + + if ( str.length ) { + css.transform = str; + } + + if ( props.opacity !== undefined ) { + css.opacity = props.opacity; + } + + if ( props.width !== undefined ) { + css.width = props.width; + } + + if ( props.height !== undefined ) { + css.height = props.height; + } + + return $el.css( css ); + }, + + + // Simple CSS transition handler + // ============================= + + animate : function ( $el, to, duration, callback, leaveAnimationName ) { + var event = transitionEnd || 'transitionend'; + + if ( $.isFunction( duration ) ) { + callback = duration; + duration = null; + } + + if ( !$.isPlainObject( to ) ) { + $el.removeAttr('style'); + } + + $el.on( event, function(e) { + + // Skip events from child elements and z-index change + if ( e && e.originalEvent && ( !$el.is( e.originalEvent.target ) || e.originalEvent.propertyName == 'z-index' ) ) { + return; + } + + $el.off( event ); + + if ( $.isPlainObject( to ) ) { + + if ( to.scaleX !== undefined && to.scaleY !== undefined ) { + $el.css( 'transition-duration', '0ms' ); + + to.width = Math.round( $el.width() * to.scaleX ); + to.height = Math.round( $el.height() * to.scaleY ); + + to.scaleX = 1; + to.scaleY = 1; + + $.fancybox.setTranslate( $el, to ); + } + + } else if ( leaveAnimationName !== true ) { + $el.removeClass( to ); + } + + if ( $.isFunction( callback ) ) { + callback( e ); + } + + }); + + if ( $.isNumeric( duration ) ) { + $el.css( 'transition-duration', duration + 'ms' ); + } + + if ( $.isPlainObject( to ) ) { + $.fancybox.setTranslate( $el, to ); + + } else { + $el.addClass( to ); + } + + $el.data("timer", setTimeout(function() { + $el.trigger( 'transitionend' ); + }, duration + 16)); + + }, + + stop : function( $el ) { + clearTimeout( $el.data("timer") ); + + $el.off( transitionEnd ); + } + + }; + + + // Default click handler for "fancyboxed" links + // ============================================ + + function _run( e ) { + var target = e.currentTarget, + opts = e.data ? e.data.options : {}, + items = opts.selector ? $( opts.selector ) : ( e.data ? e.data.items : [] ), + value = $(target).attr( 'data-fancybox' ) || '', + index = 0, + active = $.fancybox.getInstance(); + + e.preventDefault(); + e.stopPropagation(); + + // Avoid opening multiple times + if ( active && active.current.opts.$orig.is( target ) ) { + return; + } + + // Get all related items and find index for clicked one + if ( value ) { + items = items.length ? items.filter( '[data-fancybox="' + value + '"]' ) : $( '[data-fancybox="' + value + '"]' ); + index = items.index( target ); + + // Sometimes current item can not be found + // (for example, when slider clones items) + if ( index < 0 ) { + index = 0; + } + + } else { + items = [ target ]; + } + + $.fancybox.open( items, opts, index ); + } + + + // Create a jQuery plugin + // ====================== + + $.fn.fancybox = function (options) { + var selector; + + options = options || {}; + selector = options.selector || false; + + if ( selector ) { + + $( 'body' ).off( 'click.fb-start', selector ).on( 'click.fb-start', selector, { + options : options + }, _run ); + + } else { + + this.off( 'click.fb-start' ).on( 'click.fb-start', { + items : this, + options : options + }, _run); + + } + + return this; + }; + + + // Self initializing plugin + // ======================== + + $D.on( 'click.fb-start', '[data-fancybox]', _run ); + +}( window, document, window.jQuery )); diff --git a/fancybox/src/js/fullscreen.js b/fancybox/src/js/fullscreen.js new file mode 100644 index 0000000..360595c --- /dev/null +++ b/fancybox/src/js/fullscreen.js @@ -0,0 +1,186 @@ +// ========================================================================== +// +// FullScreen +// Adds fullscreen functionality +// +// ========================================================================== +;(function (document, $) { + 'use strict'; + + // Collection of methods supported by user browser + var fn = (function () { + + var fnMap = [ + [ + 'requestFullscreen', + 'exitFullscreen', + 'fullscreenElement', + 'fullscreenEnabled', + 'fullscreenchange', + 'fullscreenerror' + ], + // new WebKit + [ + 'webkitRequestFullscreen', + 'webkitExitFullscreen', + 'webkitFullscreenElement', + 'webkitFullscreenEnabled', + 'webkitfullscreenchange', + 'webkitfullscreenerror' + + ], + // old WebKit (Safari 5.1) + [ + 'webkitRequestFullScreen', + 'webkitCancelFullScreen', + 'webkitCurrentFullScreenElement', + 'webkitCancelFullScreen', + 'webkitfullscreenchange', + 'webkitfullscreenerror' + + ], + [ + 'mozRequestFullScreen', + 'mozCancelFullScreen', + 'mozFullScreenElement', + 'mozFullScreenEnabled', + 'mozfullscreenchange', + 'mozfullscreenerror' + ], + [ + 'msRequestFullscreen', + 'msExitFullscreen', + 'msFullscreenElement', + 'msFullscreenEnabled', + 'MSFullscreenChange', + 'MSFullscreenError' + ] + ]; + + var val; + var ret = {}; + var i, j; + + for ( i = 0; i < fnMap.length; i++ ) { + val = fnMap[ i ]; + + if ( val && val[ 1 ] in document ) { + for ( j = 0; j < val.length; j++ ) { + ret[ fnMap[ 0 ][ j ] ] = val[ j ]; + } + + return ret; + } + } + + return false; + })(); + + // If browser does not have Full Screen API, then simply unset default button template and stop + if ( !fn ) { + $.fancybox.defaults.btnTpl.fullScreen = false; + + return; + } + + var FullScreen = { + request : function ( elem ) { + + elem = elem || document.documentElement; + + elem[ fn.requestFullscreen ]( elem.ALLOW_KEYBOARD_INPUT ); + + }, + exit : function () { + + document[ fn.exitFullscreen ](); + + }, + toggle : function ( elem ) { + + elem = elem || document.documentElement; + + if ( this.isFullscreen() ) { + this.exit(); + + } else { + this.request( elem ); + } + + }, + isFullscreen : function() { + + return Boolean( document[ fn.fullscreenElement ] ); + + }, + enabled : function() { + + return Boolean( document[ fn.fullscreenEnabled ] ); + + } + }; + + $(document).on({ + 'onInit.fb' : function(e, instance) { + var $container; + + var $button = instance.$refs.toolbar.find('[data-fancybox-fullscreen]'); + + if ( instance && !instance.FullScreen && instance.group[ instance.currIndex ].opts.fullScreen ) { + $container = instance.$refs.container; + + $container.on('click.fb-fullscreen', '[data-fancybox-fullscreen]', function(e) { + + e.stopPropagation(); + e.preventDefault(); + + FullScreen.toggle( $container[ 0 ] ); + + }); + + if ( instance.opts.fullScreen && instance.opts.fullScreen.autoStart === true ) { + FullScreen.request( $container[ 0 ] ); + } + + // Expose API + instance.FullScreen = FullScreen; + + } else { + $button.hide(); + } + + }, + + 'afterKeydown.fb' : function(e, instance, current, keypress, keycode) { + + // "P" or Spacebar + if ( instance && instance.FullScreen && keycode === 70 ) { + keypress.preventDefault(); + + instance.FullScreen.toggle( instance.$refs.container[ 0 ] ); + } + + }, + + 'beforeClose.fb' : function( instance ) { + if ( instance && instance.FullScreen ) { + FullScreen.exit(); + } + } + }); + + $(document).on(fn.fullscreenchange, function() { + var instance = $.fancybox.getInstance(); + + // If image is zooming, then force to stop and reposition properly + if ( instance.current && instance.current.type === 'image' && instance.isAnimating ) { + instance.current.$content.css( 'transition', 'none' ); + + instance.isAnimating = false; + + instance.update( true, true, 0 ); + } + + }); + +}(document, window.jQuery)); diff --git a/fancybox/src/js/guestures.js b/fancybox/src/js/guestures.js new file mode 100644 index 0000000..d31ca16 --- /dev/null +++ b/fancybox/src/js/guestures.js @@ -0,0 +1,920 @@ +// ========================================================================== +// +// Guestures +// Adds touch guestures, handles click and tap events +// +// ========================================================================== +;(function (window, document, $) { + 'use strict'; + + var requestAFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + // if all else fails, use setTimeout + function (callback) { + return window.setTimeout(callback, 1000 / 60); + }; + })(); + + + var cancelAFrame = (function () { + return window.cancelAnimationFrame || + window.webkitCancelAnimationFrame || + window.mozCancelAnimationFrame || + window.oCancelAnimationFrame || + function (id) { + window.clearTimeout(id); + }; + })(); + + + var pointers = function( e ) { + var result = []; + + e = e.originalEvent || e || window.e; + e = e.touches && e.touches.length ? e.touches : ( e.changedTouches && e.changedTouches.length ? e.changedTouches : [ e ] ); + + for ( var key in e ) { + + if ( e[ key ].pageX ) { + result.push( { x : e[ key ].pageX, y : e[ key ].pageY } ); + + } else if ( e[ key ].clientX ) { + result.push( { x : e[ key ].clientX, y : e[ key ].clientY } ); + } + } + + return result; + }; + + var distance = function( point2, point1, what ) { + if ( !point1 || !point2 ) { + return 0; + } + + if ( what === 'x' ) { + return point2.x - point1.x; + + } else if ( what === 'y' ) { + return point2.y - point1.y; + } + + return Math.sqrt( Math.pow( point2.x - point1.x, 2 ) + Math.pow( point2.y - point1.y, 2 ) ); + }; + + var isClickable = function( $el ) { + if ( $el.is('a,button,input,select,textarea') || $.isFunction( $el.get(0).onclick ) ) { + return true; + } + + // Check for attributes like data-fancybox-next or data-fancybox-close + for ( var i = 0, atts = $el[0].attributes, n = atts.length; i < n; i++ ) { + if ( atts[i].nodeName.substr(0, 14) === 'data-fancybox-' ) { + return true; + } + } + + return false; + }; + + var hasScrollbars = function( el ) { + var overflowY = window.getComputedStyle( el )['overflow-y']; + var overflowX = window.getComputedStyle( el )['overflow-x']; + + var vertical = (overflowY === 'scroll' || overflowY === 'auto') && el.scrollHeight > el.clientHeight; + var horizontal = (overflowX === 'scroll' || overflowX === 'auto') && el.scrollWidth > el.clientWidth; + + return vertical || horizontal; + }; + + var isScrollable = function ( $el ) { + var rez = false; + + while ( true ) { + rez = hasScrollbars( $el.get(0) ); + + if ( rez ) { + break; + } + + $el = $el.parent(); + + if ( !$el.length || $el.hasClass( 'fancybox-stage' ) || $el.is( 'body' ) ) { + break; + } + } + + return rez; + }; + + + var Guestures = function ( instance ) { + var self = this; + + self.instance = instance; + + self.$bg = instance.$refs.bg; + self.$stage = instance.$refs.stage; + self.$container = instance.$refs.container; + + self.destroy(); + + self.$container.on( 'touchstart.fb.touch mousedown.fb.touch', $.proxy(self, 'ontouchstart') ); + }; + + Guestures.prototype.destroy = function() { + this.$container.off( '.fb.touch' ); + }; + + Guestures.prototype.ontouchstart = function( e ) { + var self = this; + + var $target = $( e.target ); + var instance = self.instance; + var current = instance.current; + var $content = current.$content; + + var isTouchDevice = ( e.type == 'touchstart' ); + + // Do not respond to both events + if ( isTouchDevice ) { + self.$container.off( 'mousedown.fb.touch' ); + } + + // Ignore clicks while zooming or closing + if ( !current || self.instance.isAnimating || self.instance.isClosing ) { + e.stopPropagation(); + e.preventDefault(); + + return; + } + + // Ignore right click + if ( e.originalEvent && e.originalEvent.button == 2 ) { + return; + } + + // Ignore taping on links, buttons, input elements + if ( !$target.length || isClickable( $target ) || isClickable( $target.parent() ) ) { + return; + } + + // Ignore clicks on the scrollbar + if ( e.originalEvent.clientX > $target[0].clientWidth + $target.offset().left ) { + return; + } + + self.startPoints = pointers( e ); + + // Prevent zooming if already swiping + if ( !self.startPoints || ( self.startPoints.length > 1 && instance.isSliding ) ) { + return; + } + + self.$target = $target; + self.$content = $content; + self.canTap = true; + + $(document).off( '.fb.touch' ); + + $(document).on( isTouchDevice ? 'touchend.fb.touch touchcancel.fb.touch' : 'mouseup.fb.touch mouseleave.fb.touch', $.proxy(self, "ontouchend")); + $(document).on( isTouchDevice ? 'touchmove.fb.touch' : 'mousemove.fb.touch', $.proxy(self, "ontouchmove")); + + e.stopPropagation(); + + if ( !(instance.current.opts.touch || instance.canPan() ) || !( $target.is( self.$stage ) || self.$stage.find( $target ).length ) ) { + + // Prevent ghosting + if ( $target.is('img') ) { + e.preventDefault(); + } + + return; + } + + if ( !( $.fancybox.isMobile && ( isScrollable( self.$target ) || isScrollable( self.$target.parent() ) ) ) ) { + e.preventDefault(); + } + + self.canvasWidth = Math.round( current.$slide[0].clientWidth ); + self.canvasHeight = Math.round( current.$slide[0].clientHeight ); + + self.startTime = new Date().getTime(); + self.distanceX = self.distanceY = self.distance = 0; + + self.isPanning = false; + self.isSwiping = false; + self.isZooming = false; + + self.sliderStartPos = self.sliderLastPos || { top: 0, left: 0 }; + self.contentStartPos = $.fancybox.getTranslate( self.$content ); + self.contentLastPos = null; + + if ( self.startPoints.length === 1 && !self.isZooming ) { + self.canTap = !instance.isSliding; + + if ( current.type === 'image' && ( self.contentStartPos.width > self.canvasWidth + 1 || self.contentStartPos.height > self.canvasHeight + 1 ) ) { + + $.fancybox.stop( self.$content ); + + self.$content.css( 'transition-duration', '0ms' ); + + self.isPanning = true; + + } else { + + self.isSwiping = true; + } + + self.$container.addClass('fancybox-controls--isGrabbing'); + } + + if ( self.startPoints.length === 2 && !instance.isAnimating && !current.hasError && current.type === 'image' && ( current.isLoaded || current.$ghost ) ) { + self.isZooming = true; + + self.isSwiping = false; + self.isPanning = false; + + $.fancybox.stop( self.$content ); + + self.$content.css( 'transition-duration', '0ms' ); + + self.centerPointStartX = ( ( self.startPoints[0].x + self.startPoints[1].x ) * 0.5 ) - $(window).scrollLeft(); + self.centerPointStartY = ( ( self.startPoints[0].y + self.startPoints[1].y ) * 0.5 ) - $(window).scrollTop(); + + self.percentageOfImageAtPinchPointX = ( self.centerPointStartX - self.contentStartPos.left ) / self.contentStartPos.width; + self.percentageOfImageAtPinchPointY = ( self.centerPointStartY - self.contentStartPos.top ) / self.contentStartPos.height; + + self.startDistanceBetweenFingers = distance( self.startPoints[0], self.startPoints[1] ); + } + + }; + + Guestures.prototype.ontouchmove = function( e ) { + + var self = this; + + self.newPoints = pointers( e ); + + if ( $.fancybox.isMobile && ( isScrollable( self.$target ) || isScrollable( self.$target.parent() ) ) ) { + e.stopPropagation(); + + self.canTap = false; + + return; + } + + if ( !( self.instance.current.opts.touch || self.instance.canPan() ) || !self.newPoints || !self.newPoints.length ) { + return; + } + + self.distanceX = distance( self.newPoints[0], self.startPoints[0], 'x' ); + self.distanceY = distance( self.newPoints[0], self.startPoints[0], 'y' ); + + self.distance = distance( self.newPoints[0], self.startPoints[0] ); + + // Skip false ontouchmove events (Chrome) + if ( self.distance > 0 ) { + + if ( !( self.$target.is( self.$stage ) || self.$stage.find( self.$target ).length ) ) { + return; + } + + e.stopPropagation(); + e.preventDefault(); + + if ( self.isSwiping ) { + self.onSwipe(); + + } else if ( self.isPanning ) { + self.onPan(); + + } else if ( self.isZooming ) { + self.onZoom(); + } + + } + + }; + + Guestures.prototype.onSwipe = function() { + + var self = this; + + var swiping = self.isSwiping; + var left = self.sliderStartPos.left || 0; + var angle; + + if ( swiping === true ) { + + if ( Math.abs( self.distance ) > 10 ) { + + self.canTap = false; + + if ( self.instance.group.length < 2 && self.instance.opts.touch.vertical ) { + self.isSwiping = 'y'; + + } else if ( self.instance.isSliding || self.instance.opts.touch.vertical === false || ( self.instance.opts.touch.vertical === 'auto' && $( window ).width() > 800 ) ) { + self.isSwiping = 'x'; + + } else { + angle = Math.abs( Math.atan2( self.distanceY, self.distanceX ) * 180 / Math.PI ); + + self.isSwiping = ( angle > 45 && angle < 135 ) ? 'y' : 'x'; + } + + self.instance.isSliding = self.isSwiping; + + // Reset points to avoid jumping, because we dropped first swipes to calculate the angle + self.startPoints = self.newPoints; + + $.each(self.instance.slides, function( index, slide ) { + $.fancybox.stop( slide.$slide ); + + slide.$slide.css( 'transition-duration', '0ms' ); + + slide.inTransition = false; + + if ( slide.pos === self.instance.current.pos ) { + self.sliderStartPos.left = $.fancybox.getTranslate( slide.$slide ).left; + } + }); + + //self.instance.current.isMoved = true; + + // Stop slideshow + if ( self.instance.SlideShow && self.instance.SlideShow.isActive ) { + self.instance.SlideShow.stop(); + } + } + + } else { + + if ( swiping == 'x' ) { + + // Sticky edges + if ( self.distanceX > 0 && ( self.instance.group.length < 2 || ( self.instance.current.index === 0 && !self.instance.current.opts.loop ) ) ) { + left = left + Math.pow( self.distanceX, 0.8 ); + + } else if ( self.distanceX < 0 && ( self.instance.group.length < 2 || ( self.instance.current.index === self.instance.group.length - 1 && !self.instance.current.opts.loop ) ) ) { + left = left - Math.pow( -self.distanceX, 0.8 ); + + } else { + left = left + self.distanceX; + } + + } + + self.sliderLastPos = { + top : swiping == 'x' ? 0 : self.sliderStartPos.top + self.distanceY, + left : left + }; + + if ( self.requestId ) { + cancelAFrame( self.requestId ); + + self.requestId = null; + } + + self.requestId = requestAFrame(function() { + + if ( self.sliderLastPos ) { + $.each(self.instance.slides, function( index, slide ) { + var pos = slide.pos - self.instance.currPos; + + $.fancybox.setTranslate( slide.$slide, { + top : self.sliderLastPos.top, + left : self.sliderLastPos.left + ( pos * self.canvasWidth ) + ( pos * slide.opts.gutter ) + }); + }); + + self.$container.addClass( 'fancybox-is-sliding' ); + } + + }); + + } + + }; + + Guestures.prototype.onPan = function() { + + var self = this; + + var newOffsetX, newOffsetY, newPos; + + self.canTap = false; + + if ( self.contentStartPos.width > self.canvasWidth ) { + newOffsetX = self.contentStartPos.left + self.distanceX; + + } else { + newOffsetX = self.contentStartPos.left; + } + + newOffsetY = self.contentStartPos.top + self.distanceY; + + newPos = self.limitMovement( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height ); + + newPos.scaleX = self.contentStartPos.scaleX; + newPos.scaleY = self.contentStartPos.scaleY; + + self.contentLastPos = newPos; + + if ( self.requestId ) { + cancelAFrame( self.requestId ); + + self.requestId = null; + } + + self.requestId = requestAFrame(function() { + $.fancybox.setTranslate( self.$content, self.contentLastPos ); + }); + }; + + // Make panning sticky to the edges + Guestures.prototype.limitMovement = function( newOffsetX, newOffsetY, newWidth, newHeight ) { + + var self = this; + + var minTranslateX, minTranslateY, maxTranslateX, maxTranslateY; + + var canvasWidth = self.canvasWidth; + var canvasHeight = self.canvasHeight; + + var currentOffsetX = self.contentStartPos.left; + var currentOffsetY = self.contentStartPos.top; + + var distanceX = self.distanceX; + var distanceY = self.distanceY; + + // Slow down proportionally to traveled distance + + minTranslateX = Math.max(0, canvasWidth * 0.5 - newWidth * 0.5 ); + minTranslateY = Math.max(0, canvasHeight * 0.5 - newHeight * 0.5 ); + + maxTranslateX = Math.min( canvasWidth - newWidth, canvasWidth * 0.5 - newWidth * 0.5 ); + maxTranslateY = Math.min( canvasHeight - newHeight, canvasHeight * 0.5 - newHeight * 0.5 ); + + if ( newWidth > canvasWidth ) { + + // -> + if ( distanceX > 0 && newOffsetX > minTranslateX ) { + newOffsetX = minTranslateX - 1 + Math.pow( -minTranslateX + currentOffsetX + distanceX, 0.8 ) || 0; + } + + // <- + if ( distanceX < 0 && newOffsetX < maxTranslateX ) { + newOffsetX = maxTranslateX + 1 - Math.pow( maxTranslateX - currentOffsetX - distanceX, 0.8 ) || 0; + } + + } + + if ( newHeight > canvasHeight ) { + + // \/ + if ( distanceY > 0 && newOffsetY > minTranslateY ) { + newOffsetY = minTranslateY - 1 + Math.pow(-minTranslateY + currentOffsetY + distanceY, 0.8 ) || 0; + } + + // /\ + if ( distanceY < 0 && newOffsetY < maxTranslateY ) { + newOffsetY = maxTranslateY + 1 - Math.pow ( maxTranslateY - currentOffsetY - distanceY, 0.8 ) || 0; + } + + } + + return { + top : newOffsetY, + left : newOffsetX + }; + + }; + + + Guestures.prototype.limitPosition = function( newOffsetX, newOffsetY, newWidth, newHeight ) { + + var self = this; + + var canvasWidth = self.canvasWidth; + var canvasHeight = self.canvasHeight; + + if ( newWidth > canvasWidth ) { + newOffsetX = newOffsetX > 0 ? 0 : newOffsetX; + newOffsetX = newOffsetX < canvasWidth - newWidth ? canvasWidth - newWidth : newOffsetX; + + } else { + + // Center horizontally + newOffsetX = Math.max( 0, canvasWidth / 2 - newWidth / 2 ); + + } + + if ( newHeight > canvasHeight ) { + newOffsetY = newOffsetY > 0 ? 0 : newOffsetY; + newOffsetY = newOffsetY < canvasHeight - newHeight ? canvasHeight - newHeight : newOffsetY; + + } else { + + // Center vertically + newOffsetY = Math.max( 0, canvasHeight / 2 - newHeight / 2 ); + + } + + return { + top : newOffsetY, + left : newOffsetX + }; + + }; + + Guestures.prototype.onZoom = function() { + + var self = this; + + // Calculate current distance between points to get pinch ratio and new width and height + + var currentWidth = self.contentStartPos.width; + var currentHeight = self.contentStartPos.height; + + var currentOffsetX = self.contentStartPos.left; + var currentOffsetY = self.contentStartPos.top; + + var endDistanceBetweenFingers = distance( self.newPoints[0], self.newPoints[1] ); + + var pinchRatio = endDistanceBetweenFingers / self.startDistanceBetweenFingers; + + var newWidth = Math.floor( currentWidth * pinchRatio ); + var newHeight = Math.floor( currentHeight * pinchRatio ); + + // This is the translation due to pinch-zooming + var translateFromZoomingX = (currentWidth - newWidth) * self.percentageOfImageAtPinchPointX; + var translateFromZoomingY = (currentHeight - newHeight) * self.percentageOfImageAtPinchPointY; + + //Point between the two touches + + var centerPointEndX = ((self.newPoints[0].x + self.newPoints[1].x) / 2) - $(window).scrollLeft(); + var centerPointEndY = ((self.newPoints[0].y + self.newPoints[1].y) / 2) - $(window).scrollTop(); + + // And this is the translation due to translation of the centerpoint + // between the two fingers + + var translateFromTranslatingX = centerPointEndX - self.centerPointStartX; + var translateFromTranslatingY = centerPointEndY - self.centerPointStartY; + + // The new offset is the old/current one plus the total translation + + var newOffsetX = currentOffsetX + ( translateFromZoomingX + translateFromTranslatingX ); + var newOffsetY = currentOffsetY + ( translateFromZoomingY + translateFromTranslatingY ); + + var newPos = { + top : newOffsetY, + left : newOffsetX, + scaleX : self.contentStartPos.scaleX * pinchRatio, + scaleY : self.contentStartPos.scaleY * pinchRatio + }; + + self.canTap = false; + + self.newWidth = newWidth; + self.newHeight = newHeight; + + self.contentLastPos = newPos; + + if ( self.requestId ) { + cancelAFrame( self.requestId ); + + self.requestId = null; + } + + self.requestId = requestAFrame(function() { + $.fancybox.setTranslate( self.$content, self.contentLastPos ); + }); + + }; + + Guestures.prototype.ontouchend = function( e ) { + + var self = this; + var dMs = Math.max( (new Date().getTime() ) - self.startTime, 1); + + var swiping = self.isSwiping; + var panning = self.isPanning; + var zooming = self.isZooming; + + self.endPoints = pointers( e ); + + self.$container.removeClass( 'fancybox-controls--isGrabbing' ); + + $(document).off( '.fb.touch' ); + + if ( self.requestId ) { + cancelAFrame( self.requestId ); + + self.requestId = null; + } + + self.isSwiping = false; + self.isPanning = false; + self.isZooming = false; + + if ( self.canTap ) { + return self.onTap( e ); + } + + self.speed = 366; + + // Speed in px/ms + self.velocityX = self.distanceX / dMs * 0.5; + self.velocityY = self.distanceY / dMs * 0.5; + + self.speedX = Math.max( self.speed * 0.5, Math.min( self.speed * 1.5, ( 1 / Math.abs( self.velocityX ) ) * self.speed ) ); + + if ( panning ) { + self.endPanning(); + + } else if ( zooming ) { + self.endZooming(); + + } else { + self.endSwiping( swiping ); + } + + return; + }; + + Guestures.prototype.endSwiping = function( swiping ) { + + var self = this; + var ret = false; + + self.instance.isSliding = false; + self.sliderLastPos = null; + + // Close if swiped vertically / navigate if horizontally + if ( swiping == 'y' && Math.abs( self.distanceY ) > 50 ) { + + // Continue vertical movement + $.fancybox.animate( self.instance.current.$slide, { + top : self.sliderStartPos.top + self.distanceY + ( self.velocityY * 150 ), + opacity : 0 + }, 150 ); + + ret = self.instance.close( true, 300 ); + + } else if ( swiping == 'x' && self.distanceX > 50 && self.instance.group.length > 1 ) { + ret = self.instance.previous( self.speedX ); + + } else if ( swiping == 'x' && self.distanceX < -50 && self.instance.group.length > 1 ) { + ret = self.instance.next( self.speedX ); + } + + if ( ret === false && ( swiping == 'x' || swiping == 'y' ) ) { + self.instance.jumpTo( self.instance.current.index, 150 ); + } + + self.$container.removeClass( 'fancybox-is-sliding' ); + + }; + + // Limit panning from edges + // ======================== + + Guestures.prototype.endPanning = function() { + + var self = this; + var newOffsetX, newOffsetY, newPos; + + if ( !self.contentLastPos ) { + return; + } + + if ( self.instance.current.opts.touch.momentum === false ) { + newOffsetX = self.contentLastPos.left; + newOffsetY = self.contentLastPos.top; + + } else { + + // Continue movement + newOffsetX = self.contentLastPos.left + ( self.velocityX * self.speed ); + newOffsetY = self.contentLastPos.top + ( self.velocityY * self.speed ); + } + + newPos = self.limitPosition( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height ); + + newPos.width = self.contentStartPos.width; + newPos.height = self.contentStartPos.height; + + $.fancybox.animate( self.$content, newPos, 330 ); + }; + + + Guestures.prototype.endZooming = function() { + + var self = this; + + var current = self.instance.current; + + var newOffsetX, newOffsetY, newPos, reset; + + var newWidth = self.newWidth; + var newHeight = self.newHeight; + + if ( !self.contentLastPos ) { + return; + } + + newOffsetX = self.contentLastPos.left; + newOffsetY = self.contentLastPos.top; + + reset = { + top : newOffsetY, + left : newOffsetX, + width : newWidth, + height : newHeight, + scaleX : 1, + scaleY : 1 + }; + + // Reset scalex/scaleY values; this helps for perfomance and does not break animation + $.fancybox.setTranslate( self.$content, reset ); + + if ( newWidth < self.canvasWidth && newHeight < self.canvasHeight ) { + self.instance.scaleToFit( 150 ); + + } else if ( newWidth > current.width || newHeight > current.height ) { + self.instance.scaleToActual( self.centerPointStartX, self.centerPointStartY, 150 ); + + } else { + + newPos = self.limitPosition( newOffsetX, newOffsetY, newWidth, newHeight ); + + // Switch from scale() to width/height or animation will not work correctly + $.fancybox.setTranslate( self.content, $.fancybox.getTranslate( self.$content ) ); + + $.fancybox.animate( self.$content, newPos, 150 ); + } + + }; + + Guestures.prototype.onTap = function(e) { + var self = this; + var $target = $( e.target ); + + var instance = self.instance; + var current = instance.current; + + var endPoints = ( e && pointers( e ) ) || self.startPoints; + + var tapX = endPoints[0] ? endPoints[0].x - self.$stage.offset().left : 0; + var tapY = endPoints[0] ? endPoints[0].y - self.$stage.offset().top : 0; + + var where; + + var process = function ( prefix ) { + + var action = current.opts[ prefix ]; + + if ( $.isFunction( action ) ) { + action = action.apply( instance, [ current, e ] ); + } + + if ( !action) { + return; + } + + switch ( action ) { + + case "close" : + + instance.close( self.startEvent ); + + break; + + case "toggleControls" : + + instance.toggleControls( true ); + + break; + + case "next" : + + instance.next(); + + break; + + case "nextOrClose" : + + if ( instance.group.length > 1 ) { + instance.next(); + + } else { + instance.close( self.startEvent ); + } + + break; + + case "zoom" : + + if ( current.type == 'image' && ( current.isLoaded || current.$ghost ) ) { + + if ( instance.canPan() ) { + instance.scaleToFit(); + + } else if ( instance.isScaledDown() ) { + instance.scaleToActual( tapX, tapY ); + + } else if ( instance.group.length < 2 ) { + instance.close( self.startEvent ); + } + } + + break; + } + + }; + + // Ignore right click + if ( e.originalEvent && e.originalEvent.button == 2 ) { + return; + } + + // Skip if current slide is not in the center + if ( instance.isSliding ) { + return; + } + + // Skip if clicked on the scrollbar + if ( tapX > $target[0].clientWidth + $target.offset().left ) { + return; + } + + // Check where is clicked + if ( $target.is( '.fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-container' ) ) { + where = 'Outside'; + + } else if ( $target.is( '.fancybox-slide' ) ) { + where = 'Slide'; + + } else if ( instance.current.$content && instance.current.$content.has( e.target ).length ) { + where = 'Content'; + + } else { + return; + } + + // Check if this is a double tap + if ( self.tapped ) { + + // Stop previously created single tap + clearTimeout( self.tapped ); + self.tapped = null; + + // Skip if distance between taps is too big + if ( Math.abs( tapX - self.tapX ) > 50 || Math.abs( tapY - self.tapY ) > 50 || instance.isSliding ) { + return this; + } + + // OK, now we assume that this is a double-tap + process( 'dblclick' + where ); + + } else { + + // Single tap will be processed if user has not clicked second time within 300ms + // or there is no need to wait for double-tap + self.tapX = tapX; + self.tapY = tapY; + + if ( current.opts[ 'dblclick' + where ] && current.opts[ 'dblclick' + where ] !== current.opts[ 'click' + where ] ) { + self.tapped = setTimeout(function() { + self.tapped = null; + + process( 'click' + where ); + + }, 300); + + } else { + process( 'click' + where ); + } + + } + + return this; + }; + + $(document).on('onActivate.fb', function (e, instance) { + if ( instance && !instance.Guestures ) { + instance.Guestures = new Guestures( instance ); + } + }); + + $(document).on('beforeClose.fb', function (e, instance) { + if ( instance && instance.Guestures ) { + instance.Guestures.destroy(); + } + }); + + +}(window, document, window.jQuery)); diff --git a/fancybox/src/js/hash.js b/fancybox/src/js/hash.js new file mode 100644 index 0000000..8cfc5bf --- /dev/null +++ b/fancybox/src/js/hash.js @@ -0,0 +1,223 @@ +// ========================================================================== +// +// Hash +// Enables linking to each modal +// +// ========================================================================== +;(function (document, window, $) { + 'use strict'; + + // Simple $.escapeSelector polyfill (for jQuery prior v3) + if ( !$.escapeSelector ) { + $.escapeSelector = function( sel ) { + var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; + var fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }; + + return ( sel + "" ).replace( rcssescape, fcssescape ); + }; + } + + // Variable containing last hash value set by fancyBox + // It will be used to determine if fancyBox needs to close after hash change is detected + var currentHash = null; + + // Throtlling the history change + var timerID = null; + + // Get info about gallery name and current index from url + function parseUrl() { + var hash = window.location.hash.substr( 1 ); + var rez = hash.split( '-' ); + var index = rez.length > 1 && /^\+?\d+$/.test( rez[ rez.length - 1 ] ) ? parseInt( rez.pop( -1 ), 10 ) || 1 : 1; + var gallery = rez.join( '-' ); + + // Index is starting from 1 + if ( index < 1 ) { + index = 1; + } + + return { + hash : hash, + index : index, + gallery : gallery + }; + } + + // Trigger click evnt on links to open new fancyBox instance + function triggerFromUrl( url ) { + var $el; + + if ( url.gallery !== '' ) { + + // If we can find element matching 'data-fancybox' atribute, then trigger click event for that .. + $el = $( "[data-fancybox='" + $.escapeSelector( url.gallery ) + "']" ).eq( url.index - 1 ); + + if ( $el.length ) { + $el.trigger( 'click' ); + + } else { + + // .. if not, try finding element by ID + $( "#" + $.escapeSelector( url.gallery ) + "" ).trigger( 'click' ); + + } + + } + } + + // Get gallery name from current instance + function getGallery( instance ) { + var opts; + + if ( !instance ) { + return false; + } + + opts = instance.current ? instance.current.opts : instance.opts; + + return opts.$orig ? opts.$orig.data( 'fancybox' ) : ( opts.hash || '' ); + } + + // Star when DOM becomes ready + $(function() { + + // Small delay is used to allow other scripts to process "dom ready" event + setTimeout(function() { + + // Check if this module is not disabled + if ( $.fancybox.defaults.hash === false ) { + return; + } + + // Update hash when opening/closing fancyBox + $(document).on({ + 'onInit.fb' : function( e, instance ) { + var url, gallery; + + if ( instance.group[ instance.currIndex ].opts.hash === false ) { + return; + } + + url = parseUrl(); + gallery = getGallery( instance ); + + // Make sure gallery start index matches index from hash + if ( gallery && url.gallery && gallery == url.gallery ) { + instance.currIndex = url.index - 1; + } + + }, + + 'beforeShow.fb' : function( e, instance, current, firstRun ) { + var gallery; + + if ( current.opts.hash === false ) { + return; + } + + gallery = getGallery( instance ); + + // Update window hash + if ( gallery && gallery !== '' ) { + + if ( window.location.hash.indexOf( gallery ) < 0 ) { + instance.opts.origHash = window.location.hash; + } + + currentHash = gallery + ( instance.group.length > 1 ? '-' + ( current.index + 1 ) : '' ); + + if ( 'replaceState' in window.history ) { + if ( timerID ) { + clearTimeout( timerID ); + } + + timerID = setTimeout(function() { + window.history[ firstRun ? 'pushState' : 'replaceState' ]( {} , document.title, window.location.pathname + window.location.search + '#' + currentHash ); + + timerID = null; + + }, 300); + + } else { + window.location.hash = currentHash; + } + + } + + }, + + 'beforeClose.fb' : function( e, instance, current ) { + var gallery, origHash; + + if ( timerID ) { + clearTimeout( timerID ); + } + + if ( current.opts.hash === false ) { + return; + } + + gallery = getGallery( instance ); + origHash = instance && instance.opts.origHash ? instance.opts.origHash : ''; + + // Remove hash from location bar + if ( gallery && gallery !== '' ) { + + if ( 'replaceState' in history ) { + window.history.replaceState( {} , document.title, window.location.pathname + window.location.search + origHash ); + + } else { + window.location.hash = origHash; + + // Keep original scroll position + $( window ).scrollTop( instance.scrollTop ).scrollLeft( instance.scrollLeft ); + } + } + + currentHash = null; + } + }); + + // Check if need to close after url has changed + $(window).on('hashchange.fb', function() { + var url = parseUrl(); + + if ( $.fancybox.getInstance() ) { + if ( currentHash && currentHash !== url.gallery + '-' + url.index && !( url.index === 1 && currentHash == url.gallery ) ) { + currentHash = null; + + $.fancybox.close(); + } + + } else if ( url.gallery !== '' ) { + triggerFromUrl( url ); + } + }); + + // If navigating away from current page + $(window).one('unload.fb popstate.fb', function() { + $.fancybox.getInstance( 'close', true, 0 ); + }); + + // Check current hash and trigger click event on matching element to start fancyBox, if needed + triggerFromUrl( parseUrl() ); + + }, 50); + + }); + + +}(document, window, window.jQuery)); diff --git a/fancybox/src/js/media.js b/fancybox/src/js/media.js new file mode 100644 index 0000000..fb99347 --- /dev/null +++ b/fancybox/src/js/media.js @@ -0,0 +1,223 @@ +// ========================================================================== +// +// Media +// Adds additional media type support +// +// ========================================================================== +;(function ($) { + + 'use strict'; + + // Formats matching url to final form + + var format = function (url, rez, params) { + if ( !url ) { + return; + } + + params = params || ''; + + if ( $.type(params) === "object" ) { + params = $.param(params, true); + } + + $.each(rez, function (key, value) { + url = url.replace('$' + key, value || ''); + }); + + if (params.length) { + url += (url.indexOf('?') > 0 ? '&' : '?') + params; + } + + return url; + }; + + // Object containing properties for each media type + + var defaults = { + youtube : { + matcher : /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i, + params : { + autoplay : 1, + autohide : 1, + fs : 1, + rel : 0, + hd : 1, + wmode : 'transparent', + enablejsapi : 1, + html5 : 1 + }, + paramPlace : 8, + type : 'iframe', + url : '//www.youtube.com/embed/$4', + thumb : '//img.youtube.com/vi/$4/hqdefault.jpg' + }, + + vimeo : { + matcher : /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/, + params : { + autoplay : 1, + hd : 1, + show_title : 1, + show_byline : 1, + show_portrait : 0, + fullscreen : 1, + api : 1 + }, + paramPlace : 3, + type : 'iframe', + url : '//player.vimeo.com/video/$2' + }, + + metacafe : { + matcher : /metacafe.com\/watch\/(\d+)\/(.*)?/, + type : 'iframe', + url : '//www.metacafe.com/embed/$1/?ap=1' + }, + + dailymotion : { + matcher : /dailymotion.com\/video\/(.*)\/?(.*)/, + params : { + additionalInfos : 0, + autoStart : 1 + }, + type : 'iframe', + url : '//www.dailymotion.com/embed/video/$1' + }, + + vine : { + matcher : /vine.co\/v\/([a-zA-Z0-9\?\=\-]+)/, + type : 'iframe', + url : '//vine.co/v/$1/embed/simple' + }, + + instagram : { + matcher : /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i, + type : 'image', + url : '//$1/p/$2/media/?size=l' + }, + + // Examples: + // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16 + // https://www.google.com/maps/@37.7852006,-122.4146355,14.65z + // https://www.google.com/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572 + gmap_place : { + matcher : /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i, + type : 'iframe', + url : function (rez) { + return '//maps.google.' + rez[2] + '/?ll=' + ( rez[9] ? rez[9] + '&z=' + Math.floor( rez[10] ) + ( rez[12] ? rez[12].replace(/^\//, "&") : '' ) : rez[12] ) + '&output=' + ( rez[12] && rez[12].indexOf('layer=c') > 0 ? 'svembed' : 'embed' ); + } + }, + + // Examples: + // https://www.google.com/maps/search/Empire+State+Building/ + // https://www.google.com/maps/search/?api=1&query=centurylink+field + // https://www.google.com/maps/search/?api=1&query=47.5951518,-122.3316393 + gmap_search : { + matcher : /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(maps\/search\/)(.*)/i, + type : 'iframe', + url : function (rez) { + return '//maps.google.' + rez[2] + '/maps?q=' + rez[5].replace('query=', 'q=').replace('api=1', '') + '&output=embed'; + } + } + }; + + $(document).on('onInit.fb', function (e, instance) { + + $.each(instance.group, function( i, item ) { + + var url = item.src || '', + type = false, + media, + thumb, + rez, + params, + urlParams, + o, + provider; + + // Skip items that already have content type + if ( item.type ) { + return; + } + + media = $.extend( true, {}, defaults, item.opts.media ); + + // Look for any matching media type + $.each(media, function ( n, el ) { + rez = url.match(el.matcher); + o = {}; + provider = n; + + if (!rez) { + return; + } + + type = el.type; + + if ( el.paramPlace && rez[ el.paramPlace ] ) { + urlParams = rez[ el.paramPlace ]; + + if ( urlParams[ 0 ] == '?' ) { + urlParams = urlParams.substring(1); + } + + urlParams = urlParams.split('&'); + + for ( var m = 0; m < urlParams.length; ++m ) { + var p = urlParams[ m ].split('=', 2); + + if ( p.length == 2 ) { + o[ p[0] ] = decodeURIComponent( p[1].replace(/\+/g, " ") ); + } + } + } + + params = $.extend( true, {}, el.params, item.opts[ n ], o ); + + url = $.type(el.url) === "function" ? el.url.call(this, rez, params, item) : format(el.url, rez, params); + thumb = $.type(el.thumb) === "function" ? el.thumb.call(this, rez, params, item) : format(el.thumb, rez); + + if ( provider === 'vimeo' ) { + url = url.replace('&%23', '#'); + } + + return false; + }); + + // If it is found, then change content type and update the url + + if ( type ) { + item.src = url; + item.type = type; + + if ( !item.opts.thumb && !( item.opts.$thumb && item.opts.$thumb.length ) ) { + item.opts.thumb = thumb; + } + + if ( type === 'iframe' ) { + $.extend(true, item.opts, { + iframe : { + preload : false, + attr : { + scrolling : "no" + } + } + }); + + item.contentProvider = provider; + + item.opts.slideClass += ' fancybox-slide--' + ( provider == 'gmap_place' || provider == 'gmap_search' ? 'map' : 'video' ); + } + + } else { + + // If no content type is found, then set it to `image` as fallback + item.type = 'image'; + } + + }); + + }); + +}(window.jQuery)); diff --git a/fancybox/src/js/slideshow.js b/fancybox/src/js/slideshow.js new file mode 100644 index 0000000..b839cf4 --- /dev/null +++ b/fancybox/src/js/slideshow.js @@ -0,0 +1,170 @@ +// ========================================================================== +// +// SlideShow +// Enables slideshow functionality +// +// Example of usage: +// $.fancybox.getInstance().SlideShow.start() +// +// ========================================================================== +;(function (document, $) { + 'use strict'; + + var SlideShow = function( instance ) { + this.instance = instance; + this.init(); + }; + + $.extend( SlideShow.prototype, { + timer : null, + isActive : false, + $button : null, + speed : 3000, + + init : function() { + var self = this; + + self.$button = self.instance.$refs.toolbar.find('[data-fancybox-play]').on('click', function() { + self.toggle(); + }); + + if ( self.instance.group.length < 2 || !self.instance.group[ self.instance.currIndex ].opts.slideShow ) { + self.$button.hide(); + } + }, + + set : function() { + var self = this; + + // Check if reached last element + if ( self.instance && self.instance.current && (self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1 )) { + self.timer = setTimeout(function() { + self.instance.next(); + + }, self.instance.current.opts.slideShow.speed || self.speed); + + } else { + self.stop(); + self.instance.idleSecondsCounter = 0; + self.instance.showControls(); + } + + }, + + clear : function() { + var self = this; + + clearTimeout( self.timer ); + + self.timer = null; + }, + + start : function() { + var self = this; + var current = self.instance.current; + + if ( self.instance && current && ( current.opts.loop || current.index < self.instance.group.length - 1 )) { + + self.isActive = true; + + self.$button + .attr( 'title', current.opts.i18n[ current.opts.lang ].PLAY_STOP ) + .addClass( 'fancybox-button--pause' ); + + if ( current.isComplete ) { + self.set(); + } + } + }, + + stop : function() { + var self = this; + var current = self.instance.current; + + self.clear(); + + self.$button + .attr( 'title', current.opts.i18n[ current.opts.lang ].PLAY_START ) + .removeClass( 'fancybox-button--pause' ); + + self.isActive = false; + }, + + toggle : function() { + var self = this; + + if ( self.isActive ) { + self.stop(); + + } else { + self.start(); + } + } + + }); + + $(document).on({ + 'onInit.fb' : function(e, instance) { + if ( instance && !instance.SlideShow ) { + instance.SlideShow = new SlideShow( instance ); + } + }, + + 'beforeShow.fb' : function(e, instance, current, firstRun) { + var SlideShow = instance && instance.SlideShow; + + if ( firstRun ) { + + if ( SlideShow && current.opts.slideShow.autoStart ) { + SlideShow.start(); + } + + } else if ( SlideShow && SlideShow.isActive ) { + SlideShow.clear(); + } + }, + + 'afterShow.fb' : function(e, instance, current) { + var SlideShow = instance && instance.SlideShow; + + if ( SlideShow && SlideShow.isActive ) { + SlideShow.set(); + } + }, + + 'afterKeydown.fb' : function(e, instance, current, keypress, keycode) { + var SlideShow = instance && instance.SlideShow; + + // "P" or Spacebar + if ( SlideShow && current.opts.slideShow && ( keycode === 80 || keycode === 32 ) && !$(document.activeElement).is( 'button,a,input' ) ) { + keypress.preventDefault(); + + SlideShow.toggle(); + } + }, + + 'beforeClose.fb onDeactivate.fb' : function(e, instance) { + var SlideShow = instance && instance.SlideShow; + + if ( SlideShow ) { + SlideShow.stop(); + } + } + }); + + // Page Visibility API to pause slideshow when window is not active + $(document).on("visibilitychange", function() { + var instance = $.fancybox.getInstance(); + var SlideShow = instance && instance.SlideShow; + + if ( SlideShow && SlideShow.isActive ) { + if ( document.hidden ) { + SlideShow.clear(); + + } else { + SlideShow.set(); + } + } + }); + +}(document, window.jQuery)); diff --git a/fancybox/src/js/thumbs.js b/fancybox/src/js/thumbs.js new file mode 100644 index 0000000..abbd2f4 --- /dev/null +++ b/fancybox/src/js/thumbs.js @@ -0,0 +1,231 @@ +// ========================================================================== +// +// Thumbs +// Displays thumbnails in a grid +// +// ========================================================================== +;(function (document, $) { + 'use strict'; + + var FancyThumbs = function( instance ) { + this.instance = instance; + this.init(); + }; + + $.extend( FancyThumbs.prototype, { + + $button : null, + $grid : null, + $list : null, + isVisible : false, + + init : function() { + var self = this; + + var first = self.instance.group[0], + second = self.instance.group[1]; + + self.$button = self.instance.$refs.toolbar.find( '[data-fancybox-thumbs]' ); + + if ( self.instance.group.length > 1 && self.instance.group[ self.instance.currIndex ].opts.thumbs && ( + ( first.type == 'image' || first.opts.thumb || first.opts.$thumb ) && + ( second.type == 'image' || second.opts.thumb || second.opts.$thumb ) + )) { + + self.$button.on('click', function() { + self.toggle(); + }); + + self.isActive = true; + + } else { + self.$button.hide(); + + self.isActive = false; + } + + }, + + create : function() { + var instance = this.instance, + list, + src; + + this.$grid = $('
').appendTo( instance.$refs.container ); + + list = '
    '; + + $.each(instance.group, function( i, item ) { + + src = item.opts.thumb || ( item.opts.$thumb ? item.opts.$thumb.attr('src') : null ); + + if ( !src && item.type === 'image' ) { + src = item.src; + } + + if ( src && src.length ) { + list += '
  • '; + } + + }); + + list += '
'; + + this.$list = $( list ).appendTo( this.$grid ).on('click', 'li', function() { + instance.jumpTo( $(this).data('index') ); + }); + + this.$list.find('img').hide().one('load', function() { + + var $parent = $(this).parent().removeClass('fancybox-thumbs-loading'), + thumbWidth = $parent.outerWidth(), + thumbHeight = $parent.outerHeight(), + width, + height, + widthRatio, + heightRatio; + + width = this.naturalWidth || this.width; + height = this.naturalHeight || this.height; + + //Calculate thumbnail width/height and center it + + widthRatio = width / thumbWidth; + heightRatio = height / thumbHeight; + + if (widthRatio >= 1 && heightRatio >= 1) { + if (widthRatio > heightRatio) { + width = width / heightRatio; + height = thumbHeight; + + } else { + width = thumbWidth; + height = height / widthRatio; + } + } + + $(this).css({ + width : Math.floor(width), + height : Math.floor(height), + 'margin-top' : Math.min( 0, Math.floor(thumbHeight * 0.3 - height * 0.3 ) ), + 'margin-left' : Math.min( 0, Math.floor(thumbWidth * 0.5 - width * 0.5 ) ) + }).show(); + + }) + .each(function() { + this.src = $( this ).data( 'src' ); + }); + + }, + + focus : function() { + + if ( this.instance.current ) { + this.$list + .children() + .removeClass('fancybox-thumbs-active') + .filter('[data-index="' + this.instance.current.index + '"]') + .addClass('fancybox-thumbs-active') + .focus(); + } + + }, + + close : function() { + this.$grid.hide(); + }, + + update : function() { + + this.instance.$refs.container.toggleClass( 'fancybox-show-thumbs', this.isVisible ); + + if ( this.isVisible ) { + + if ( !this.$grid ) { + this.create(); + } + + this.instance.trigger( 'onThumbsShow' ); + + this.focus(); + + } else if ( this.$grid ) { + this.instance.trigger( 'onThumbsHide' ); + } + + // Update content position + this.instance.update(); + + }, + + hide : function() { + this.isVisible = false; + this.update(); + }, + + show : function() { + this.isVisible = true; + this.update(); + }, + + toggle : function() { + this.isVisible = !this.isVisible; + this.update(); + } + + }); + + $(document).on({ + + 'onInit.fb' : function(e, instance) { + if ( instance && !instance.Thumbs ) { + instance.Thumbs = new FancyThumbs( instance ); + } + }, + + 'beforeShow.fb' : function(e, instance, item, firstRun) { + var Thumbs = instance && instance.Thumbs; + + if ( !Thumbs || !Thumbs.isActive ) { + return; + } + + if ( item.modal ) { + Thumbs.$button.hide(); + + Thumbs.hide(); + + return; + } + + if ( firstRun && instance.opts.thumbs.autoStart === true ) { + Thumbs.show(); + } + + if ( Thumbs.isVisible ) { + Thumbs.focus(); + } + }, + + 'afterKeydown.fb' : function(e, instance, current, keypress, keycode) { + var Thumbs = instance && instance.Thumbs; + + // "G" + if ( Thumbs && Thumbs.isActive && keycode === 71 ) { + keypress.preventDefault(); + + Thumbs.toggle(); + } + }, + + 'beforeClose.fb' : function( e, instance ) { + var Thumbs = instance && instance.Thumbs; + + if ( Thumbs && Thumbs.isVisible && instance.opts.thumbs.hideOnClose !== false ) { + Thumbs.close(); + } + } + + }); + +}(document, window.jQuery)); diff --git a/models/front/forSale/detail.php b/models/front/forSale/detail.php index d1eff1a..d2b81bf 100644 --- a/models/front/forSale/detail.php +++ b/models/front/forSale/detail.php @@ -104,7 +104,10 @@ class GlmMembersFront_forSale_detail extends GlmDataForSale public function modelAction($actionData = false) { - echo "ASF"; + + wp_enqueue_style('fancyStyle', GLM_MEMBERS_FOR_SALE_PLUGIN_URL . '/fancybox/dist/jquery.fancybox.min.css'); + wp_enqueue_script('fancyScript', GLM_MEMBERS_FOR_SALE_PLUGIN_URL . '/fancybox/dist/jquery.fancybox.min.js', array('jquery'), '', true ); + $success_message = ""; $option = false; if (isset($_REQUEST['option']) && trim($_REQUEST['option']) != '') { diff --git a/scss/admin.scss b/scss/admin.scss index f57cb99..9acf9f5 100644 --- a/scss/admin.scss +++ b/scss/admin.scss @@ -43,6 +43,7 @@ } .item-image{ display: block; + max-width: 200px; } .item-image-row{ border-bottom: 1px solid lightgray; diff --git a/scss/front.scss b/scss/front.scss index b0898c1..8a51dd6 100644 --- a/scss/front.scss +++ b/scss/front.scss @@ -11,7 +11,7 @@ } div{ line-height: 1.1; - padding: 5px; + padding: 3px 0 3px 0; } .front-list-topic{ padding: 5px; @@ -19,7 +19,7 @@ text-align: center; color: white; border-radius: 5px; - margin-bottom: 10px; + margin: 0 auto 10px auto; } .item-wanted{ background: green; @@ -27,4 +27,17 @@ .item-sale{ background: red; } + .item-detail-link{ + display: inline-block; + padding: 5px; + background: blue; + color: white; + text-decoration: none; + margin-top: 5px; + } +} +.front-item-detail-wrapper{ + .item-detail-images{ + padding-top: 20px; + } } \ No newline at end of file diff --git a/views/front/forSale/detail.html b/views/front/forSale/detail.html index e69de29..bc7774a 100644 --- a/views/front/forSale/detail.html +++ b/views/front/forSale/detail.html @@ -0,0 +1,47 @@ +
+
+ {$itemData.title} +
+
+ {$itemData.company} +
+
+ {$itemData.descr} +
+
+ {$itemData.terms} +
+
+ {$itemData.contact_name} +
+
+ {$itemData.contact_email} +
+
+ {$itemData.contact_phone} +
+
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+ diff --git a/views/front/forSale/list.html b/views/front/forSale/list.html index 2bd0717..81ffbbd 100644 --- a/views/front/forSale/list.html +++ b/views/front/forSale/list.html @@ -28,6 +28,7 @@ + Item Details
{if $data.topic.value == 1} @@ -38,7 +39,7 @@ {if $data.image_1} {/if} - Item Details +
{/foreach}