From 530f2ea3097a0fd195fbf2e3acc22b3cfa2c625b Mon Sep 17 00:00:00 2001 From: Nick Stakenburg Date: Sat, 26 Oct 2019 13:19:06 +0200 Subject: [PATCH] Fresco is now open-source! Licensed under the Creative Commons Attribution 4.0 License. --- .gitattributes | 17 + .gitignore | 2 + Gruntfile.js | 107 + LICENSE | 21 - LICENSE.md | 159 + README.md | 8 +- dist/css/fresco.css | 1616 ++++++ dist/css/fresco/skins/fresco/sprite.png | Bin 0 -> 38463 bytes dist/css/fresco/skins/fresco/sprite.svg | 2111 ++++++++ dist/js/fresco.js | 5915 ++++++++++++++++++++++ dist/js/fresco.min.js | 9 + example/css/style.css | 146 + example/images/frans_hals.jpg | Bin 0 -> 218127 bytes example/images/reynolds.jpg | Bin 0 -> 166734 bytes example/images/thumbnails/frans_hals.jpg | Bin 0 -> 5518 bytes example/images/thumbnails/reynolds.jpg | Bin 0 -> 4513 bytes example/index.html | 58 + package.json | 26 + src/css/fresco.css | 1616 ++++++ src/css/fresco/skins/fresco/sprite.png | Bin 0 -> 38463 bytes src/css/fresco/skins/fresco/sprite.svg | 2111 ++++++++ src/js/api.js | 248 + src/js/helpers/bounds.js | 19 + src/js/helpers/browser.js | 26 + src/js/helpers/helpers.js | 123 + src/js/helpers/imageready.js | 170 + src/js/helpers/support.js | 72 + src/js/helpers/timers.js | 34 + src/js/helpers/url.js | 82 + src/js/helpers/vimeoready.js | 87 + src/js/helpers/vimeothumbnail.js | 87 + src/js/keyboard.js | 77 + src/js/options.js | 192 + src/js/overlay.js | 134 + src/js/page.js | 1298 +++++ src/js/pages.js | 265 + src/js/setup.js | 5 + src/js/skins.js | 4 + src/js/spinner.js | 132 + src/js/thumbnail.js | 334 ++ src/js/thumbnails.js | 533 ++ src/js/types_.js | 75 + src/js/ui-fullclick.js | 444 ++ src/js/ui-inside.js | 438 ++ src/js/ui-outside.js | 376 ++ src/js/ui.js | 116 + src/js/umd-head.js | 23 + src/js/umd-tail.js | 7 + src/js/view.js | 67 + src/js/window.js | 495 ++ 50 files changed, 19863 insertions(+), 22 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Gruntfile.js delete mode 100644 LICENSE create mode 100644 LICENSE.md create mode 100644 dist/css/fresco.css create mode 100644 dist/css/fresco/skins/fresco/sprite.png create mode 100644 dist/css/fresco/skins/fresco/sprite.svg create mode 100644 dist/js/fresco.js create mode 100644 dist/js/fresco.min.js create mode 100644 example/css/style.css create mode 100644 example/images/frans_hals.jpg create mode 100644 example/images/reynolds.jpg create mode 100644 example/images/thumbnails/frans_hals.jpg create mode 100644 example/images/thumbnails/reynolds.jpg create mode 100644 example/index.html create mode 100644 package.json create mode 100644 src/css/fresco.css create mode 100644 src/css/fresco/skins/fresco/sprite.png create mode 100644 src/css/fresco/skins/fresco/sprite.svg create mode 100644 src/js/api.js create mode 100644 src/js/helpers/bounds.js create mode 100644 src/js/helpers/browser.js create mode 100644 src/js/helpers/helpers.js create mode 100644 src/js/helpers/imageready.js create mode 100644 src/js/helpers/support.js create mode 100644 src/js/helpers/timers.js create mode 100644 src/js/helpers/url.js create mode 100644 src/js/helpers/vimeoready.js create mode 100644 src/js/helpers/vimeothumbnail.js create mode 100644 src/js/keyboard.js create mode 100644 src/js/options.js create mode 100644 src/js/overlay.js create mode 100644 src/js/page.js create mode 100644 src/js/pages.js create mode 100644 src/js/setup.js create mode 100644 src/js/skins.js create mode 100644 src/js/spinner.js create mode 100644 src/js/thumbnail.js create mode 100644 src/js/thumbnails.js create mode 100644 src/js/types_.js create mode 100644 src/js/ui-fullclick.js create mode 100644 src/js/ui-inside.js create mode 100644 src/js/ui-outside.js create mode 100644 src/js/ui.js create mode 100644 src/js/umd-head.js create mode 100644 src/js/umd-tail.js create mode 100644 src/js/view.js create mode 100644 src/js/window.js diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..bdb0cab --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b04c74 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +node_modules/ diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..7725173 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,107 @@ +module.exports = function(grunt) { + grunt.initConfig({ + pkg: grunt.file.readJSON("package.json"), + dirs: { + dest: "dist" + }, + + vars: {}, + + concat: { + options: { process: true }, + dist: { + src: [ + "src/js/umd-head.js", + "src/js/setup.js", + "src/js/skins.js", + + // helpers + "src/js/helpers/bounds.js", + "src/js/helpers/browser.js", + "src/js/helpers/helpers.js", + "src/js/helpers/support.js", + "src/js/helpers/imageready.js", + "src/js/helpers/timers.js", + "src/js/helpers/url.js", + "src/js/helpers/vimeothumbnail.js", + "src/js/helpers/vimeoready.js", + + "src/js/options.js", + "src/js/loading.js", + "src/js/overlay.js", + "src/js/window.js", + "src/js/keyboard.js", + "src/js/page.js", + "src/js/pages.js", + "src/js/view.js", + "src/js/spinner.js", + + "src/js/api.js", + + "src/js/thumbnails.js", + "src/js/thumbnail.js", + "src/js/ui.js", + "src/js/ui-fullclick.js", + "src/js/ui-inside.js", + "src/js/ui-outside.js", + + "src/js/umd-tail.js" + ], + dest: "<%= dirs.dest %>/js/fresco.js" + } + }, + + copy: { + dist: { + files: [ + { + expand: true, + cwd: "src/css/", + src: ["**"], + dest: "<%= dirs.dest %>/css/" + } + ] + } + }, + + uglify: { + dist: { + options: { + output: { + comments: "some" + } + }, + src: ["<%= dirs.dest %>/js/fresco.js"], + dest: "<%= dirs.dest %>/js/fresco.min.js" + } + }, + + clean: { + dist: "dist/" + }, + + watch: { + scripts: { + files: ["src/**/*.js", "src/**/*.css"], + tasks: ["default"], + options: { + spawn: false + } + } + } + }); + + // Load plugins + grunt.loadNpmTasks("grunt-contrib-concat"); + grunt.loadNpmTasks("grunt-contrib-copy"); + grunt.loadNpmTasks("grunt-contrib-uglify"); + grunt.loadNpmTasks("grunt-contrib-clean"); + grunt.loadNpmTasks("grunt-contrib-watch"); + + grunt.registerTask("default", [ + "clean:dist", + "concat:dist", + "copy:dist", + "uglify:dist" + ]); +}; diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 1e93ada..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Nick Stakenburg - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..d7244c0 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,159 @@ +## creative commons + +# Attribution 4.0 International + +Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. + +### Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. + +* __Considerations for licensors:__ Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. [More considerations for licensors](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensors). + +* __Considerations for the public:__ By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. [More considerations for the public](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensees). + +## Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. + +### Section 1 – Definitions. + +a. __Adapted Material__ means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. + +b. __Adapter's License__ means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. + +c. __Copyright and Similar Rights__ means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. + +d. __Effective Technological Measures__ means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. + +e. __Exceptions and Limitations__ means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. + +f. __Licensed Material__ means the artistic or literary work, database, or other material to which the Licensor applied this Public License. + +g. __Licensed Rights__ means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. + +h. __Licensor__ means the individual(s) or entity(ies) granting rights under this Public License. + +i. __Share__ means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. + +j. __Sui Generis Database Rights__ means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. + +k. __You__ means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. + +### Section 2 – Scope. + +a. ___License grant.___ + + 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: + + A. reproduce and Share the Licensed Material, in whole or in part; and + + B. produce, reproduce, and Share Adapted Material. + + 2. __Exceptions and Limitations.__ For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. + + 3. __Term.__ The term of this Public License is specified in Section 6(a). + + 4. __Media and formats; technical modifications allowed.__ The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. + + 5. __Downstream recipients.__ + + A. __Offer from the Licensor – Licensed Material.__ Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. + + B. __No downstream restrictions.__ You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. + + 6. __No endorsement.__ Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). + +b. ___Other rights.___ + + 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this Public License. + + 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. + +### Section 3 – License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the following conditions. + +a. ___Attribution.___ + + 1. If You Share the Licensed Material (including in modified form), You must: + + A. retain the following if it is supplied by the Licensor with the Licensed Material: + + i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of warranties; + + v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; + + B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and + + C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. + + 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. + +### Section 4 – Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: + +a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; + +b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and + +c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. + +### Section 5 – Disclaimer of Warranties and Limitation of Liability. + +a. __Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.__ + +b. __To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.__ + +c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. + +### Section 6 – Term and Termination. + +a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. + +b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. + +c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. + +d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. + +### Section 7 – Other Terms and Conditions. + +a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. + +b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. + +### Section 8 – Interpretation. + +a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. + +b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. + +c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. + +d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. + +> Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at [creativecommons.org/policies](http://creativecommons.org/policies), Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. +> +> Creative Commons may be contacted at creativecommons.org diff --git a/README.md b/README.md index de7e340..5337f1a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,8 @@ -# fresco +# Fresco A Beautiful Responsive Lightbox + +https://www.frescojs.com + +--- + +Fresco has been open-sourced under the [Creative Commons BY 4.0 license](https://creativecommons.org/licenses/by/4.0) as of oct. 26 2019. diff --git a/dist/css/fresco.css b/dist/css/fresco.css new file mode 100644 index 0000000..e970cf1 --- /dev/null +++ b/dist/css/fresco.css @@ -0,0 +1,1616 @@ +/* box-sizing */ +.fr-window, +.fr-window [class^="fr-"], +.fr-overlay, +.fr-overlay [class^="fr-"], +.fr-spinner, +.fr-spinner [class^="fr-"] { + box-sizing: border-box; +} + +.fr-window { + position: fixed; + width: 100%; + height: 100%; + top: 0; + left: 0; + font: 13px/20px "Lucida Sans", "Lucida Sans Unicode", "Lucida Grande", Verdana, + Arial, sans-serif; + /* Chrome hack, this fixes a visual glitch when quickly toggling a video */ + transform: translateZ(0px); +} + +/* z-index */ +.fr-overlay { + z-index: 99998; +} +.fr-window { + z-index: 99999; +} +.fr-spinner { + z-index: 100000; +} + +/* overlay */ +.fr-overlay { + position: fixed; + top: 0; + left: 0; + height: 100%; + width: 100%; +} +.fr-overlay-background { + float: left; + width: 100%; + height: 100%; + background: #000; + opacity: 0.9; + filter: alpha(opacity=90); +} + +.fr-overlay-ui-fullclick .fr-overlay-background { + background: #292929; + opacity: 1; + filter: alpha(opacity=100); +} + +/* mobile touch has position:absolute to allow zooming */ +.fr-window.fr-mobile-touch, +.fr-overlay.fr-mobile-touch { + position: absolute; + overflow: visible; +} + +/* some properties on the window are used to toggle things + * like margin and the fullclick mode, + * we reset those properties after measuring them + */ +.fr-measured { + margin: 0 !important; + min-width: 0 !important; + min-height: 0 !important; +} + +.fr-box { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.fr-pages { + position: absolute; + width: 100%; + height: 100%; + overflow: hidden; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.fr-page { + position: absolute; + width: 100%; + height: 100%; +} + +.fr-container { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: 0; + padding: 0; + text-align: center; +} + +.fr-hovering-clickable .fr-container { + cursor: pointer; +} + +/* + padding: + youtube & vimeo always have buttons on the outside + side button = 72 = 54 + (2 * 9 margin) + close button = 48 +*/ +/* padding ui:inside */ +.fr-ui-inside .fr-container { + padding: 20px 20px; +} +.fr-ui-inside.fr-no-sides .fr-container { + padding: 20px; +} + +/* padding ui:outside */ +.fr-ui-outside .fr-container { + padding: 20px 82px; +} +.fr-ui-outside.fr-no-sides .fr-container { + padding: 20px 48px; +} + +/* reduce padding on smaller screens */ +@media all and (max-width: 700px) and (orientation: portrait), + all and (orientation: landscape) and (max-height: 700px) { + /* padding ui:inside */ + .fr-ui-inside .fr-container { + padding: 12px 12px; + } + .fr-ui-inside.fr-no-sides .fr-container { + padding: 12px; + } + + /* padding ui:outside */ + .fr-ui-outside .fr-container { + padding: 12px 72px; + } + .fr-ui-outside.fr-no-sides .fr-container { + padding: 12px 48px; + } +} + +@media all and (max-width: 500px) and (orientation: portrait), + all and (orientation: landscape) and (max-height: 500px) { + /* padding ui:inside */ + .fr-ui-inside .fr-container { + padding: 0px 0px; + } + .fr-ui-inside.fr-no-sides .fr-container { + padding: 0px; + } + + /* padding ui:outside */ + .fr-ui-outside .fr-container { + padding: 0px 72px; + } + .fr-ui-outside.fr-no-sides .fr-container { + padding: 0px 48px; + } +} + +/* padding ui:fullclick */ +.fr-ui-fullclick .fr-container { + padding: 0; +} +.fr-ui-fullclick.fr-no-sides .fr-container { + padding: 0; +} + +/* video fullclick */ +.fr-ui-fullclick.fr-type-video .fr-container { + padding: 0px 62px; +} +.fr-ui-fullclick.fr-no-sides.fr-type-video .fr-container { + padding: 48px 0px; +} + +/* overflow-y should always have 0 padding top and bottom */ +.fr-overflow-y .fr-container { + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +.fr-content, +.fr-content-background { + position: absolute; + top: 50%; + left: 50%; + /* IE11 has a 1px blur bug on the edges of the caption (ui:inside) + during animation with overflow:hidden */ + overflow: visible; +} + +/* img/iframe/error */ +.fr-content-element { + float: left; + width: 100%; + height: 100%; +} + +.fr-content-background { + background: #101010; +} + +.fr-info { + position: absolute; + top: 0; + left: 0; + width: 100%; + color: #efefef; + font-size: 13px; + line-height: 20px; + text-align: left; + -webkit-text-size-adjust: none; + text-size-adjust: none; + -webkit-user-select: text; + -khtml-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} +.fr-info-background { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + background: #000; + line-height: 1%; + filter: none; + opacity: 1; +} +.fr-ui-inside .fr-info-background { + filter: alpha(opacity=80); + opacity: 0.8; + zoom: 1; +} +/* ui:outside has a slightly lighter info background to break up from black page backgrounds */ +.fr-ui-outside .fr-info-background { + background: #0d0d0d; +} + +/* info at the bottom for ui:inside */ +.fr-content .fr-info { + top: auto; + bottom: 0; +} + +.fr-info-padder { + display: block; + overflow: hidden; + padding: 12px; + position: relative; + width: auto; +} +.fr-caption { + width: auto; + display: inline; +} + +.fr-position { + color: #b3b3b3; + float: right; + line-height: 21px; + opacity: 0.99; + position: relative; + text-align: right; + margin-left: 15px; + white-space: nowrap; +} + +/* positions not within the info bar + for ui:outside/inside */ +.fr-position-outside, +.fr-position-inside { + position: absolute; + bottom: 0; + right: 0; + margin: 12px; + width: auto; + padding: 2px 8px; + border-radius: 10px; + font-size: 11px; + line-height: 20px; + text-shadow: 0 1px 0 rgba(0, 0, 0, 0.4); + display: none; + overflow: hidden; + white-space: nowrap; + -webkit-text-size-adjust: none; + text-size-adjust: none; + -webkit-user-select: text; + -khtml-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} +.fr-position-inside { + border: 0; +} + +.fr-position-background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #0d0d0d; + filter: alpha(opacity=80); + opacity: 0.8; + zoom: 1; /* oldIE */ +} +.fr-position-text { + color: #b3b3b3; +} +.fr-position-outside .fr-position-text, +.fr-position-inside .fr-position-text { + float: left; + position: relative; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3); + opacity: 1; +} + +/* display it for the outside ui, hide the caption inside */ +.fr-ui-outside .fr-position-outside { + display: block; +} +.fr-ui-outside .fr-info .fr-position { + display: none; +} + +.fr-ui-inside.fr-no-caption .fr-position-inside { + display: block; +} + +/* links */ +.fr-info a, +.fr-info a:hover { + color: #ccc; + border: 0; + background: none; + text-decoration: underline; +} +.fr-info a:hover { + color: #eee; +} + +/* no-caption */ +.fr-ui-outside.fr-no-caption .fr-info { + display: none; +} +.fr-ui-inside.fr-no-caption .fr-caption { + display: none; +} + +/* --- stroke */ +.fr-stroke { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 1px; +} +.fr-stroke-vertical { + width: 1px; + height: 100%; +} +.fr-stroke-horizontal { + padding: 0 1px; +} + +.fr-stroke-right { + left: auto; + right: 0; +} +.fr-stroke-bottom { + top: auto; + bottom: 0; +} + +.fr-stroke-color { + float: left; + width: 100%; + height: 100%; + background: rgba(255, 255, 255, 0.08); +} + +/* the stroke is brighter inside the info box */ +.fr-info .fr-stroke-color { + background: rgba(80, 80, 80, 0.3); +} + +/* remove the stroke when outside ui has a caption */ +.fr-ui-outside.fr-has-caption .fr-content .fr-stroke-bottom { + display: none; +} + +/* remove stroke and shadow when ui:fullclick */ +.fr-ui-fullclick .fr-stroke { + display: none; +} +.fr-ui-fullclick .fr-content-background { + box-shadow: none; +} + +/* the info box never bas a top stroke */ +.fr-info .fr-stroke-top { + display: none; +} + +/* < > */ +.fr-side { + position: absolute; + top: 50%; + width: 54px; + height: 72px; + margin: 0 9px; + margin-top: -36px; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + zoom: 1; +} +.fr-side-previous { + left: 0; +} +.fr-side-next { + right: 0; + left: auto; +} + +.fr-side-disabled { + cursor: default; +} +.fr-side-hidden { + display: none !important; +} + +.fr-side-button { + float: left; + width: 100%; + height: 100%; + margin: 0; + padding: 0; +} +.fr-side-button-background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #101010; +} +.fr-side-button-icon { + float: left; + position: relative; + height: 100%; + width: 100%; + zoom: 1; + background-position: 50% 50%; + background-repeat: no-repeat; +} +/* smaller side buttons */ +@media all and (max-width: 500px) and (orientation: portrait), + all and (orientation: landscape) and (max-height: 414px) { + .fr-side { + width: 54px; + height: 60px; + margin: 0; + margin-top: -30px; + } + .fr-side-button { + width: 48px; + height: 60px; + margin: 0 3px; + } +} + +/* X */ +.fr-close { + position: absolute; + width: 48px; + height: 48px; + top: 0; + right: 0; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.fr-close-background, +.fr-close-icon { + position: absolute; + top: 12px; + left: 12px; + height: 26px; + width: 26px; + background-position: 50% 50%; + background-repeat: no-repeat; +} +.fr-close-background { + background-color: #000; +} + +/* Thumbnails */ +.fr-thumbnails { + position: absolute; + overflow: hidden; +} +.fr-thumbnails-disabled .fr-thumbnails { + display: none !important; +} +.fr-thumbnails-horizontal .fr-thumbnails { + width: 100%; + height: 12%; + min-height: 74px; + max-height: 160px; + bottom: 0; +} + +.fr-thumbnails-vertical .fr-thumbnails { + height: 100%; + width: 10%; + min-width: 74px; + max-width: 160px; + left: 0; +} + +.fr-thumbnails, +.fr-thumbnails * { + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.fr-thumbnails-wrapper { + position: absolute; + top: 0; + left: 50%; + height: 100%; +} +.fr-thumbnails-vertical .fr-thumbnails-wrapper { + top: 50%; + left: 0; +} + +.fr-thumbnails-slider { + position: relative; + width: 100%; + height: 100%; + float: left; + zoom: 1; +} + +.fr-thumbnails-slider-slide { + position: absolute; + top: 0; + left: 0; + height: 100%; +} + +.fr-thumbnails-thumbs { + float: left; + height: 100%; + overflow: hidden; + position: relative; + top: 0; + left: 0; +} + +.fr-thumbnails-slide { + position: absolute; + top: 0; + height: 100%; + width: 100%; +} + +.fr-thumbnail-frame { + position: absolute; + zoom: 1; + overflow: hidden; +} + +.fr-thumbnail { + position: absolute; + width: 30px; + height: 100%; + left: 50%; + top: 50%; + zoom: 1; + cursor: pointer; + margin: 0 10px; +} +.fr-ltIE9 .fr-thumbnail * { + overflow: hidden; /* IE6(7) */ + z-index: 1; + zoom: 1; +} + +.fr-thumbnail-wrapper { + position: relative; + background: #161616; + width: 100%; + height: 100%; + float: left; + overflow: hidden; + display: inline; /* IE6(7) */ + z-index: 0; /* IE8 */ +} + +.fr-thumbnail-overlay { + cursor: pointer; +} +.fr-thumbnail-active .fr-thumbnail-overlay { + cursor: default; +} +.fr-thumbnail-overlay, +.fr-thumbnail-overlay-background, +.fr-thumbnail-overlay-border { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-width: 0; + overflow: hidden; + border-style: solid; + border-color: transparent; +} +.fr-ltIE9 .fr-thumbnail-overlay-border { + border-width: 0 !important; +} +.fr-thumbnail .fr-thumbnail-image { + position: absolute; + filter: alpha(opacity=85); + opacity: 0.85; + max-width: none; +} +.fr-thumbnail:hover .fr-thumbnail-image, +.fr-thumbnail-active:hover .fr-thumbnail-image { + filter: alpha(opacity=99); + opacity: 0.99; +} +.fr-thumbnail-active .fr-thumbnail-image, +.fr-thumbnail-active:hover .fr-thumbnail-image { + filter: alpha(opacity=35); + opacity: 0.35; +} +.fr-thumbnail-active { + cursor: default; +} + +/* Thumbnails loading */ +.fr-thumbnail-loading, +.fr-thumbnail-loading-background, +.fr-thumbnail-loading-icon { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.fr-thumbnail-loading-background { + background-color: #161616; + background-position: 50% 50%; + background-repeat: no-repeat; + opacity: 0.8; + position: relative; + float: left; +} +/* this element is there as an alternative to putting the loading image on the background */ +.fr-thumbnail-loading-icon { + display: none; +} + +/* error on thumbnail */ +.fr-thumbnail-error .fr-thumbnail-image { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #202020; +} + +/* Thumbnail < > */ +.fr-thumbnails-side { + float: left; + height: 100%; + width: 28px; + margin: 0 5px; + position: relative; + overflow: hidden; +} +.fr-thumbnails-side-previous { + margin-left: 12px; +} +.fr-thumbnails-side-next { + margin-right: 12px; +} + +.fr-thumbnails-vertical .fr-thumbnails-side { + height: 28px; + width: 100%; + margin: 10px 0; +} +.fr-thumbnails-vertical .fr-thumbnails-side-previous { + margin-top: 20px; +} +.fr-thumbnails-vertical .fr-thumbnails-side-next { + margin-bottom: 20px; +} + +.fr-thumbnails-side-button { + position: absolute; + top: 50%; + left: 50%; + margin-top: -14px; + margin-left: -14px; + width: 28px; + height: 28px; + cursor: pointer; +} + +.fr-thumbnails-side-button-background { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + filter: alpha(opacity=80); + opacity: 0.8; + -moz-transition: background-color 0.2s ease-in; + -webkit-transition: background-color 0.2s ease-in; + transition: background-color 0.2s ease-in; + background-color: #333; + cursor: pointer; + border-radius: 4px; +} +.fr-thumbnails-side-button:hover .fr-thumbnails-side-button-background { + background-color: #3b3b3b; +} + +.fr-thumbnails-side-button-disabled * { + cursor: default; +} +.fr-thumbnails-side-button-disabled:hover + .fr-thumbnails-side-button-background { + background-color: #333; +} + +.fr-thumbnails-side-button-icon { + position: absolute; + /*top: -7px; + left: -7px;*/ + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +/* vertical thumbnails */ +.fr-thumbnails-vertical .fr-thumbnails-side, +.fr-thumbnails-vertical .fr-thumbnails-thumbs, +.fr-thumbnails-vertical .fr-thumbnail-frame { + clear: both; +} + +/* ui:fullclick has a dark thumbnail background */ +.fr-window-ui-fullclick .fr-thumbnails { + background: #090909; +} + +/* put a stroke on the connecting caption*/ +.fr-window-ui-fullclick.fr-thumbnails-enabled.fr-thumbnails-horizontal + .fr-info + .fr-stroke-bottom { + display: block !important; +} /*({ box-shadow: inset 0 -1px 0 rgba(255,255,255,.1); } */ +.fr-window-ui-fullclick.fr-thumbnails-enabled.fr-thumbnails-vertical + .fr-info + .fr-stroke-left { + display: block !important; +} + +/* thumbnail spacing */ +.fr-thumbnails-horizontal .fr-thumbnails-thumbs { + padding: 12px 5px; +} +.fr-thumbnails-vertical .fr-thumbnails-thumbs { + padding: 5px 12px; +} +.fr-thumbnails-measured .fr-thumbnails-thumbs { + padding: 0 !important; +} +/* horizontal */ +@media all and (min-height: 700px) { + .fr-thumbnails-horizontal .fr-thumbnails-thumbs { + padding: 16px 8px; + } + .fr-thumbnails-horizontal .fr-thumbnails-side { + margin: 0 8px; + } + .fr-thumbnails-horizontal .fr-thumbnails-side-previous { + margin-left: 16px; + } + .fr-thumbnails-horizontal .fr-thumbnails-side-next { + margin-right: 16px; + } +} +@media all and (min-height: 980px) { + .fr-thumbnails-horizontal .fr-thumbnails-thumbs { + padding: 20px 10px; + } + .fr-thumbnails-horizontal .fr-thumbnails-side { + margin: 0 10px; + } + .fr-thumbnails-horizontal .fr-thumbnails-side-previous { + margin-left: 20px; + } + .fr-thumbnails-horizontal .fr-thumbnails-side-next { + margin-right: 20px; + } +} + +/* vertical */ +@media all and (min-width: 1200px) { + .fr-thumbnails-vertical .fr-thumbnails-thumbs { + padding: 8px 16px; + } + .fr-thumbnails-vertical .fr-thumbnails-side { + margin: 0 8px; + } + .fr-thumbnails-vertical .fr-thumbnails-side-previous { + margin-top: 16px; + } + .fr-thumbnails-vertical .fr-thumbnails-side-next { + margin-bottom: 16px; + } +} +@media all and (min-width: 1800px) { + .fr-thumbnails-vertical .fr-thumbnails-thumbs { + padding: 10px 20px; + } + .fr-thumbnails-vertical .fr-thumbnails-side { + margin: 10px 0; + } + .fr-thumbnails-vertical .fr-thumbnails-side-previous { + margin-top: 20px; + } + .fr-thumbnails-vertical .fr-thumbnails-side-next { + margin-bottom: 20px; + } +} + +/* hide thumbnails on smaller screens + the js always hides them on touch based devices +*/ +@media all and (max-width: 500px) and (orientation: portrait), + all and (orientation: landscape) and (max-height: 500px) { + .fr-thumbnails-horizontal .fr-thumbnails { + display: none !important; + } +} +@media all and (max-width: 700px) and (orientation: portrait), + all and (orientation: landscape) and (max-height: 414px) { + .fr-thumbnails-vertical .fr-thumbnails { + display: none !important; + } +} + +/* force fullClick: true + and force overflow: false */ +@media all and (max-width: 500px) and (orientation: portrait), + all and (orientation: landscape) and (max-height: 414px) { + .fr-page { + min-width: 100%; + } /* fullclick */ + .fr-page { + min-height: 100%; + } /* overflow: false */ +} + +/* switch between ui modes + we force everything not in the current UI mode to hide +*/ +.fr-window-ui-fullclick .fr-side-next-outside, +.fr-window-ui-fullclick .fr-side-previous-outside, +.fr-window-ui-fullclick .fr-close-outside, +.fr-window-ui-fullclick .fr-content .fr-side-next, +.fr-window-ui-fullclick .fr-content .fr-side-previous, +.fr-window-ui-fullclick .fr-content .fr-close, +.fr-window-ui-fullclick .fr-content .fr-info, +.fr-window-ui-outside .fr-side-next-fullclick, +.fr-window-ui-outside .fr-side-previous-fullclick, +.fr-window-ui-outside .fr-close-fullclick, +.fr-window-ui-outside .fr-content .fr-side-next, +.fr-window-ui-outside .fr-content .fr-side-previous, +.fr-window-ui-outside .fr-content .fr-close, +.fr-window-ui-outside .fr-content .fr-info, +.fr-window-ui-inside .fr-page > .fr-info, +.fr-window-ui-inside .fr-side-next-fullclick, +.fr-window-ui-inside .fr-side-previous-fullclick, +.fr-window-ui-inside .fr-close-fullclick, +.fr-window-ui-inside .fr-side-next-outside, +.fr-window-ui-inside .fr-side-previous-outside, +.fr-window-ui-inside .fr-close-outside { + display: none !important; +} + +/* some ui elements can be toggled on mousemove. + filter:alpha(opacity) is bugged so we use display in oldIE +*/ +.fr-toggle-ui { + opacity: 0; + -webkit-transition: opacity 0.3s; + transition: opacity 0.3s; +} +.fr-visible-fullclick-ui .fr-box > .fr-toggle-ui, +.fr-visible-inside-ui .fr-ui-inside .fr-toggle-ui { + opacity: 1; +} + +.fr-hidden-fullclick-ui .fr-box > .fr-toggle-ui, +.fr-hidden-inside-ui .fr-ui-inside .fr-toggle-ui { + -webkit-transition: opacity 0.3s; + transition: opacity 0.3s; +} +.fr-ltIE9.fr-hidden-fullclick-ui .fr-box > .fr-toggle-ui, +.fr-ltIE9.fr-hidden-inside-ui .fr-ui-inside .fr-toggle-ui { + display: none; +} + +/* Spinner */ +.fr-spinner { + position: fixed; + width: 52px; + height: 52px; + background: #101010; + background: rgba(16, 16, 16, 0.85); + border-radius: 5px; +} +.fr-spinner div { + position: absolute; + top: 0; + left: 0; + height: 64%; + width: 64%; + margin-left: 18%; + margin-top: 18%; + opacity: 1; + -webkit-animation: fresco-12 1.2s infinite ease-in-out; + animation: fresco-12 1.2s infinite ease-in-out; +} + +.fr-spinner div:after { + content: ""; + position: absolute; + top: 0; + left: 50%; + width: 2px; + height: 8px; + margin-left: -1px; + background: #fff; + box-shadow: 0 0 1px rgba(0, 0, 0, 0); /* fixes rendering in Firefox */ +} + +.fr-spinner div.fr-spin-1 { + -ms-transform: rotate(30deg); + -webkit-transform: rotate(30deg); + transform: rotate(30deg); + -webkit-animation-delay: -1.1s; + animation-delay: -1.1s; +} +.fr-spinner div.fr-spin-2 { + -ms-transform: rotate(60deg); + -webkit-transform: rotate(60deg); + transform: rotate(60deg); + -webkit-animation-delay: -1s; + animation-delay: -1s; +} +.fr-spinner div.fr-spin-3 { + -ms-transform: rotate(90deg); + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + -webkit-animation-delay: -0.9s; + animation-delay: -0.9s; +} +.fr-spinner div.fr-spin-4 { + -ms-transform: rotate(120deg); + -webkit-transform: rotate(120deg); + transform: rotate(120deg); + -webkit-animation-delay: -0.8s; + animation-delay: -0.8s; +} +.fr-spinner div.fr-spin-5 { + -ms-transform: rotate(150deg); + -webkit-transform: rotate(150deg); + transform: rotate(150deg); + -webkit-animation-delay: -0.7s; + animation-delay: -0.7s; +} +.fr-spinner div.fr-spin-6 { + -ms-transform: rotate(180deg); + -webkit-transform: rotate(180deg); + transform: rotate(180deg); + -webkit-animation-delay: -0.6s; + animation-delay: -0.6s; +} +.fr-spinner div.fr-spin-6 { + -ms-transform: rotate(180deg); + -webkit-transform: rotate(180deg); + transform: rotate(180deg); + -webkit-animation-delay: -0.6s; + animation-delay: -0.6s; +} +.fr-spinner div.fr-spin-7 { + -ms-transform: rotate(210deg); + -webkit-transform: rotate(210deg); + transform: rotate(210deg); + -webkit-animation-delay: -0.5s; + animation-delay: -0.5s; +} +.fr-spinner div.fr-spin-8 { + -ms-transform: rotate(240deg); + -webkit-transform: rotate(240deg); + transform: rotate(240deg); + -webkit-animation-delay: -0.4s; + animation-delay: -0.4s; +} +.fr-spinner div.fr-spin-9 { + -ms-transform: rotate(270deg); + -webkit-transform: rotate(270deg); + transform: rotate(270deg); + -webkit-animation-delay: -0.3s; + animation-delay: -0.3s; +} +.fr-spinner div.fr-spin-10 { + -ms-transform: rotate(300deg); + -webkit-transform: rotate(300deg); + transform: rotate(300deg); + -webkit-animation-delay: -0.2s; + animation-delay: -0.2s; +} +.fr-spinner div.fr-spin-11 { + -ms-transform: rotate(330deg); + -webkit-transform: rotate(330deg); + transform: rotate(330deg); + -webkit-animation-delay: -0.1s; + animation-delay: -0.1s; +} +.fr-spinner div.fr-spin-12 { + -ms-transform: rotate(360deg); + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + -webkit-animation-delay: 0s; + animation-delay: 0s; +} + +@-webkit-keyframes fresco-12 { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } +} +@keyframes fresco-12 { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } +} + +/* Thumbnail spinner */ +.fr-thumbnail-spinner { + position: absolute; + top: 50%; + left: 50%; + margin-top: -20px; + margin-left: -20px; + width: 40px; + height: 40px; +} +.fr-thumbnail-spinner-spin { + position: relative; + float: left; + margin: 8px 0 0 8px; + text-indent: -9999em; + border-top: 2px solid rgba(255, 255, 255, 0.2); + border-right: 2px solid rgba(255, 255, 255, 0.2); + border-bottom: 2px solid rgba(255, 255, 255, 0.2); + border-left: 2px solid #fff; + -webkit-animation: fr-thumbnail-spin 1.1s infinite linear; + animation: fr-thumbnail-spin 1.1s infinite linear; +} +.fr-thumbnail-spinner-spin, +.fr-thumbnail-spinner-spin:after { + border-radius: 50%; + width: 24px; + height: 24px; +} +@-webkit-keyframes fr-thumbnail-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes fr-thumbnail-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +/* Error */ +.fr-error { + float: left; + position: relative; + background-color: #ca3434; + width: 160px; + height: 160px; +} + +.fr-error-icon { + position: absolute; + width: 42px; + height: 42px; + top: 50%; + left: 50%; + margin-left: -21px; + margin-top: -21px; +} + +/* skin: 'fresco' */ + +/* Sprite */ +.fr-window-skin-fresco .fr-side-button-icon, +.fr-window-skin-fresco .fr-close-icon, +.fr-window-skin-fresco .fr-thumbnails-side-button-icon, +.fr-window-skin-fresco .fr-error-icon { + background-image: url("fresco/skins/fresco/sprite.svg"); +} +/* fallback png sprite */ +.fr-window-skin-fresco.fr-no-svg .fr-side-button-icon, +.fr-window-skin-fresco.fr-no-svg .fr-close-icon, +.fr-window-skin-fresco.fr-no-svg .fr-thumbnails-side-button-icon, +.fr-window-skin-fresco .fr-error-icon { + background-image: url("fresco/skins/fresco/sprite.png"); +} + +.fr-window-skin-fresco .fr-error-icon { + background-position: -160px -126px; +} + +.fr-window-skin-fresco .fr-content-background { + background: #101010; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.4); +} +.fr-window-skin-fresco.fr-window-ui-fullclick .fr-content-background { + box-shadow: none; +} + +/* thumbnail shadow */ +.fr-window-skin-fresco .fr-thumbnail-wrapper { + box-shadow: 0 0 3px rgba(0, 0, 0, 0.3); +} +.fr-window-skin-fresco .fr-thumbnail-active .fr-thumbnail-wrapper { + box-shadow: 0 0 1px rgba(0, 0, 0, 0.1); +} + +/* < > */ +.fr-window-skin-fresco .fr-side-button-background { + background-color: transparent; +} +.fr-window-skin-fresco .fr-side-previous .fr-side-button-icon { + background-position: -13px -14px; +} +.fr-window-skin-fresco .fr-side-next .fr-side-button-icon { + background-position: -93px -14px; +} + +.fr-window-skin-fresco .fr-side-previous:hover .fr-side-button-icon { + background-position: -13px -114px; +} +.fr-window-skin-fresco .fr-side-next:hover .fr-side-button-icon { + background-position: -93px -114px; +} + +/* transition (png) */ +.fr-window-skin-fresco.fr-no-svg + .fr-hovering-previous + .fr-side-previous + .fr-side-button-icon, +.fr-window-skin-fresco.fr-no-svg.fr-mobile-touch + .fr-side-previous + .fr-side-button-icon { + background-position: -13px -114px; +} +.fr-window-skin-fresco.fr-no-svg + .fr-hovering-next + .fr-side-next + .fr-side-button-icon, +.fr-window-skin-fresco.fr-no-svg.fr-mobile-touch + .fr-side-next + .fr-side-button-icon { + background-position: -93px -114px; +} + +/* disabled state (png) */ +.fr-window-skin-fresco.fr-no-svg + .fr-side-previous.fr-side-disabled + .fr-side-button-icon, +.fr-window-skin-fresco.fr-no-svg + .fr-hovering-previous + .fr-side-previous.fr-side-disabled + .fr-side-button-icon, +.fr-window-skin-fresco.fr-no-svg + .fr-side-previous.fr-side-disabled:hover + .fr-side-button-icon { + background-position: -13px -214px; +} +.fr-window-skin-fresco.fr-no-svg + .fr-side-next.fr-side-disabled + .fr-side-button-icon, +.fr-window-skin-fresco.fr-no-svg + .fr-hovering-next + .fr-side-next.fr-side-disabled + .fr-side-button-icon, +.fr-window-skin-fresco.fr-no-svg + .fr-side-next.fr-side-disabled:hover + .fr-side-button-icon { + background-position: -93px -214px; +} + +/* transition (svg) */ +.fr-window-skin-fresco.fr-svg .fr-side-previous .fr-side-button-icon { + background-position: -13px -114px; +} +.fr-window-skin-fresco.fr-svg .fr-side-next .fr-side-button-icon { + background-position: -93px -114px; +} +.fr-window-skin-fresco.fr-svg .fr-side-button-icon { + opacity: 0.5; +} + +.fr-window-skin-fresco.fr-svg .fr-side:hover .fr-side-button-icon, +.fr-window-skin-fresco.fr-svg + .fr-hovering-previous + .fr-side-previous + .fr-side-button-icon, +.fr-window-skin-fresco.fr-svg + .fr-hovering-next + .fr-side-next + .fr-side-button-icon { + opacity: 1; +} + +.fr-window-skin-fresco.fr-svg.fr-mobile-touch .fr-side .fr-side-button-icon { + opacity: 0.8; +} /* touch always active but at lower opacity */ + +/* disabled (svg) */ +.fr-window-skin-fresco.fr-svg .fr-side-disabled .fr-side-button-icon, +.fr-window-skin-fresco.fr-svg + .fr-hovering-previous + .fr-side-disabled + .fr-side-button-icon, +.fr-window-skin-fresco.fr-svg + .fr-hovering-next + .fr-side-disabled + .fr-side-button-icon, +.fr-window-skin-fresco.fr-svg .fr-side-disabled:hover .fr-side-button-icon, +.fr-window-skin-fresco.fr-svg.fr-mobile-touch + .fr-side-disabled + .fr-side-button-icon { + opacity: 0.2; +} + +/* hide for ui:inside/fullclick, only for the image type */ +.fr-window-skin-fresco.fr-window-ui-inside + .fr-type-image + .fr-side-disabled + .fr-side-button-icon, +.fr-window-skin-fresco.fr-window-ui-fullclick.fr-showing-type-image + .fr-side-disabled + .fr-side-button-icon { + background-image: none; +} + +/* < > responsive */ +@media all and (max-width: 500px) and (orientation: portrait), + all and (orientation: landscape) and (max-height: 414px) { + .fr-window-skin-fresco .fr-side-previous .fr-side-button-icon { + background-position: 0px -300px; + } + .fr-window-skin-fresco .fr-side-next .fr-side-button-icon { + background-position: -48px -300px; + } + + .fr-window-skin-fresco .fr-side-previous:hover .fr-side-button-icon { + background-position: 0px -360px; + } + .fr-window-skin-fresco .fr-side-next:hover .fr-side-button-icon { + background-position: -48px -360px; + } + + /* transition (png) */ + .fr-window-skin-fresco.fr-no-svg + .fr-hovering-previous + .fr-side-previous + .fr-side-button-icon, + .fr-window-skin-fresco.fr-no-svg.fr-mobile-touch + .fr-side-previous + .fr-side-button-icon { + background-position: 0px -360px; + } + .fr-window-skin-fresco.fr-no-svg + .fr-hovering-next + .fr-side-next + .fr-side-button-icon, + .fr-window-skin-fresco.fr-no-svg.fr-mobile-touch + .fr-side-next + .fr-side-button-icon { + background-position: -48px -360px; + } + + /* transition (svg) */ + .fr-window-skin-fresco.fr-svg .fr-side-previous .fr-side-button-icon { + background-position: 0px -360px; + } + .fr-window-skin-fresco.fr-svg .fr-side-next .fr-side-button-icon { + background-position: -48px -360px; + } + + /* disabled state (png) */ + .fr-window-skin-fresco.fr-no-svg + .fr-side-previous.fr-side-disabled + .fr-side-button-icon, + .fr-window-skin-fresco.fr-no-svg + .fr-hovering-previous + .fr-side-previous.fr-side-disabled + .fr-side-button-icon, + .fr-window-skin-fresco.fr-no-svg + .fr-side-previous.fr-side-disabled:hover + .fr-side-button-icon { + background-position: 0px -420px; + } + + .fr-window-skin-fresco.fr-no-svg + .fr-side-next.fr-side-disabled + .fr-side-button-icon, + .fr-window-skin-fresco.fr-no-svg + .fr-hovering-next + .fr-side-next.fr-side-disabled + .fr-side-button-icon, + .fr-window-skin-fresco.fr-no-svg + .fr-side-next.fr-side-disabled:hover + .fr-side-button-icon { + background-position: -48px -420px; + } +} + +/* X */ +/* colors */ +.fr-window-skin-fresco.fr-window-ui-outside .fr-close-background { + background-color: #363636; +} +.fr-window-skin-fresco.fr-window-ui-outside + .fr-close:hover + .fr-close-background { + background-color: #434343; +} + +.fr-window-skin-fresco.fr-window-ui-inside .fr-close-background, +.fr-window-skin-fresco.fr-window-ui-fullclick .fr-close-background { + background-color: #131313; + filter: alpha(opacity=80); + opacity: 0.8; +} +.fr-window-skin-fresco.fr-window-ui-inside .fr-close:hover .fr-close-background, +.fr-window-skin-fresco.fr-window-ui-fullclick + .fr-close:hover + .fr-close-background { + background-color: #191919; +} + +/* - image */ +.fr-window-skin-fresco .fr-close .fr-close-icon { + background-position: -168px -8px; +} +.fr-window-skin-fresco .fr-close:hover .fr-close-icon { + background-position: -210px -8px; +} + +/* - transition */ +.fr-window-skin-fresco.fr-svg .fr-close .fr-close-icon { + background-position: -210px -8px; + opacity: 0.8; +} +.fr-window-skin-fresco .fr-close:hover .fr-close-icon { + opacity: 1; +} +/* iOS 8.4.1 bug: when opacity changes it'll require 2 taps + force a single opacity to fix this +*/ +.fr-window-skin-fresco.fr-svg.fr-mobile-touch .fr-close .fr-close-icon, +.fr-window-skin-fresco.fr-mobile-touch .fr-close:hover .fr-close-icon { + opacity: 1; +} + +/* Thumbnails */ +.fr-window-skin-fresco .fr-thumbnail-wrapper { + border-color: transparent; + border-style: solid; + border-width: 0; +} +.fr-window-skin-fresco .fr-thumbnail-wrapper { + box-shadow: 0 0 3px rgba(0, 0, 0, 0.3); +} +.fr-window-skin-fresco .fr-thumbnail-active .fr-thumbnail-wrapper { + box-shadow: 0 0 1px rgba(0, 0, 0, 0.1); +} +.fr-window-skin-fresco .fr-thumbnail-wrapper { + box-shadow: 0 -1px 4px rgba(0, 0, 0, 0.3); +} +.fr-window-skin-fresco .fr-thumbnail-overlay-border { + border-width: 1px; + border-color: rgba(255, 255, 255, 0.08); /* should remain rgba */ +} +/* no inner border on active thumbnail */ +.fr-window-skin-fresco .fr-thumbnail-active .fr-thumbnail-overlay-border, +.fr-window-skin-fresco .fr-thumbnail-active:hover .fr-thumbnail-overlay-border { + border: 0; +} + +/* Thumbnails < > */ +.fr-window-skin-fresco + .fr-thumbnails-side-previous + .fr-thumbnails-side-button-icon { + background-position: -167px -49px; +} +.fr-window-skin-fresco + .fr-thumbnails-side-previous:hover + .fr-thumbnails-side-button-icon { + background-position: -209px -49px; +} +.fr-window-skin-fresco + .fr-thumbnails-side-next + .fr-thumbnails-side-button-icon { + background-position: -167px -91px; +} +.fr-window-skin-fresco + .fr-thumbnails-side-next:hover + .fr-thumbnails-side-button-icon { + background-position: -209px -91px; +} +/* vertical ^ (up/down) adjustments */ +.fr-window-skin-fresco.fr-thumbnails-vertical + .fr-thumbnails-side-previous + .fr-thumbnails-side-button-icon { + background-position: -293px -49px; +} +.fr-window-skin-fresco.fr-thumbnails-vertical + .fr-thumbnails-side-previous:hover + .fr-thumbnails-side-button-icon { + background-position: -335px -49px; +} +.fr-window-skin-fresco.fr-thumbnails-vertical + .fr-thumbnails-side-next + .fr-thumbnails-side-button-icon { + background-position: -293px -91px; +} +.fr-window-skin-fresco.fr-thumbnails-vertical + .fr-thumbnails-side-next:hover + .fr-thumbnails-side-button-icon { + background-position: -335px -91px; +} + +/* Thumbnails < > transition */ +.fr-window-skin-fresco.fr-svg + .fr-thumbnails-side + .fr-thumbnails-side-button-icon { + -moz-transition: opacity 0.2s ease-in; + -webkit-transition: opacity 0.2s ease-in; + transition: opacity 0.2s ease-in; + opacity: 0.8; +} +.fr-window-skin-fresco.fr-svg + .fr-thumbnails-side-previous + .fr-thumbnails-side-button-icon, +.fr-window-skin-fresco.fr-svg + .fr-thumbnails-side-previous + .fr-thumbnails-side-button-disabled { + background-position: -167px -49px; +} +.fr-window-skin-fresco.fr-svg + .fr-thumbnails-side-next + .fr-thumbnails-side-button-icon, +.fr-window-skin-fresco.fr-svg + .fr-thumbnails-side-next + .fr-thumbnails-side-button-disabled { + background-position: -209px -91px; +} +.fr-window-skin-fresco.fr-svg + .fr-thumbnails-side:hover + .fr-thumbnails-side-button-icon { + opacity: 1; +} +/* vertical ^ (up/down) adjustments */ +.fr-window-skin-fresco.fr-svg.fr-thumbnails-vertical + .fr-thumbnails-side-previous + .fr-thumbnails-side-button-icon, +.fr-window-skin-fresco.fr-svg.fr-thumbnails-vertical + .fr-thumbnails-side-previous + .fr-thumbnails-side-button-disabled { + background-position: -293px -49px; +} +.fr-window-skin-fresco.fr-svg.fr-thumbnails-vertical + .fr-thumbnails-side-next + .fr-thumbnails-side-button-icon, +.fr-window-skin-fresco.fr-svg.fr-thumbnails-vertical + .fr-thumbnails-side-next + .fr-thumbnails-side-button-disabled { + background-position: -335px -91px; +} + +/* lower opacity on disabled states */ +.fr-window-skin-fresco.fr-svg + .fr-thumbnails-side + .fr-thumbnails-side-button-disabled, +.fr-window-skin-fresco.fr-svg + .fr-thumbnails-side:hover + .fr-thumbnails-side-button-disabled { + opacity: 0.5; +} + +/* lower opacity IE < 9 using images */ +.fr-window-skin-fresco.fr-no-svg + .fr-thumbnails-side-previous + .fr-thumbnails-side-button-disabled + .fr-thumbnails-side-button-icon, +.fr-window-skin-fresco.fr-no-svg + .fr-thumbnails-side-previous:hover + .fr-thumbnails-side-button-disabled + .fr-thumbnails-side-button-icon { + background-position: -251px -49px; +} +.fr-window-skin-fresco.fr-no-svg + .fr-thumbnails-side-next + .fr-thumbnails-side-button-disabled + .fr-thumbnails-side-button-icon, +.fr-window-skin-fresco.fr-no-svg + .fr-thumbnails-side-next:hover + .fr-thumbnails-side-button-disabled + .fr-thumbnails-side-button-icon { + background-position: -251px -91px; +} +.fr-window-skin-fresco.fr-no-svg + .fr-thumbnails-side + .fr-thumbnails-side-button-disabled + .fr-thumbnails-side-button-background, +.fr-window-skin-fresco.fr-no-svg + .fr-thumbnails-side:hover + .fr-thumbnails-side-button-disabled + .fr-thumbnails-side-button-background { + filter: alpha(opacity=50); +} +/* vertical ^ (up/down) adjustments */ +.fr-window-skin-fresco.fr-no-svg.fr-thumbnails-vertical + .fr-thumbnails-side-previous + .fr-thumbnails-side-button-disabled + .fr-thumbnails-side-button-icon, +.fr-window-skin-fresco.fr-no-svg.fr-thumbnails-vertical + .fr-thumbnails-side-previous:hover + .fr-thumbnails-side-button-disabled + .fr-thumbnails-side-button-icon { + background-position: -377px -49px; +} +.fr-window-skin-fresco.fr-no-svg.fr-thumbnails-vertical + .fr-thumbnails-side-next + .fr-thumbnails-side-button-disabled + .fr-thumbnails-side-button-icon, +.fr-window-skin-fresco.fr-no-svg.fr-thumbnails-vertical + .fr-thumbnails-side-next:hover + .fr-thumbnails-side-button-disabled + .fr-thumbnails-side-button-icon { + background-position: -377px -91px; +} diff --git a/dist/css/fresco/skins/fresco/sprite.png b/dist/css/fresco/skins/fresco/sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..1f158d4358125e0c63465ae0d2114e842242816c GIT binary patch literal 38463 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dist/js/fresco.js b/dist/js/fresco.js new file mode 100644 index 0000000..981165b --- /dev/null +++ b/dist/js/fresco.js @@ -0,0 +1,5915 @@ +/** + * Fresco - A Beautiful Responsive Lightbox - v2.3.0 + * (c) 2012-2019 Nick Stakenburg + * + * https://www.frescojs.com + * + * @license: https://creativecommons.org/licenses/by/4.0 + */ + +// UMD wrapper +(function(root, factory) { + if (typeof define === "function" && define.amd) { + // AMD + define(["jquery"], factory); + } else if (typeof module === "object" && module.exports) { + // Node/CommonJS + module.exports = factory(require("jquery")); + } else { + // Browser global + root.Fresco = factory(jQuery); + } +})(this, function($) { + + +var Fresco = {}; + +$.extend(Fresco, { + version: "2.3.0" +}); + +Fresco.Skins = { + // the default skin + fresco: {} +}; + +var Bounds = { + viewport: function() { + var dimensions = { + width: $(window).width() + }; + + // Mobile Safari has a bugged viewport height after scrolling + // Firefox on Android also has problems with height + if (Browser.MobileSafari || (Browser.Android && Browser.Gecko)) { + var zoom = document.documentElement.clientWidth / window.innerWidth; + dimensions.height = window.innerHeight * zoom; + } else { + // default + dimensions.height = $(window).height(); + } + + return dimensions; + } +}; + +var Browser = (function(uA) { + function getVersion(identifier) { + var version = new RegExp(identifier + "([\\d.]+)").exec(uA); + return version ? parseFloat(version[1]) : true; + } + + return { + IE: + !!(window.attachEvent && uA.indexOf("Opera") === -1) && + getVersion("MSIE "), + Opera: + uA.indexOf("Opera") > -1 && + ((!!window.opera && opera.version && parseFloat(opera.version())) || + 7.55), + WebKit: uA.indexOf("AppleWebKit/") > -1 && getVersion("AppleWebKit/"), + Gecko: + uA.indexOf("Gecko") > -1 && + uA.indexOf("KHTML") === -1 && + getVersion("rv:"), + MobileSafari: !!uA.match(/Apple.*Mobile.*Safari/), + Chrome: uA.indexOf("Chrome") > -1 && getVersion("Chrome/"), + ChromeMobile: uA.indexOf("CrMo") > -1 && getVersion("CrMo/"), + Android: uA.indexOf("Android") > -1 && getVersion("Android "), + IEMobile: uA.indexOf("IEMobile") > -1 && getVersion("IEMobile/") + }; +})(navigator.userAgent); + +var _slice = Array.prototype.slice; + +function baseToString(value) { + if (typeof value === "string") { + return value; + } + return value == null ? "" : value + ""; +} + +var _ = { + isElement: function(object) { + return object && object.nodeType === 1; + }, + + String: { + capitalize: function(string) { + string = baseToString(string); + return string && string.charAt(0).toUpperCase() + string.slice(1); + } + } +}; + +//mousewheel +(function() { + function wheel(event) { + var realDelta; + + // normalize the delta + if (event.originalEvent.wheelDelta) + // IE & Opera + realDelta = event.originalEvent.wheelDelta / 120; + else if (event.originalEvent.detail) + // W3C + realDelta = -event.originalEvent.detail / 3; + + if (!realDelta) return; + + var customEvent = $.Event("fresco:mousewheel"); + $(event.target).trigger(customEvent, realDelta); + + if (customEvent.isPropagationStopped()) { + event.stopPropagation(); + } + if (customEvent.isDefaultPrevented()) { + event.preventDefault(); + } + } + + $(document.documentElement).on("mousewheel DOMMouseScroll", wheel); +})(); + +// Fit +var Fit = { + within: function(bounds, dimensions) { + var options = $.extend( + { + height: true, + width: true + }, + arguments[2] || {} + ); + + var size = $.extend({}, dimensions), + scale = 1, + attempts = 5; + + var fit = { width: options.width, height: options.height }; + + // adjust the bounds depending on what to fit (width/height) + // start + while ( + attempts > 0 && + ((fit.width && size.width > bounds.width) || + (fit.height && size.height > bounds.height)) + ) { + // if both dimensions fall underneath a minimum, then don't event continue + //if (size.width < 100 && size.height < 100) { + var scaleX = 1, + scaleY = 1; + + if (fit.width && size.width > bounds.width) { + scaleX = bounds.width / size.width; + } + if (fit.height && size.height > bounds.height) { + scaleY = bounds.height / size.height; + } + + // we'll end up using the largest scaled down factor + scale = Math.min(scaleX, scaleY); + + // adjust current size, based on original dimensions + size = { + width: dimensions.width * scale, + height: dimensions.height * scale + }; + //} + + attempts--; + } + + // make sure size is never pressed into negative + size.width = Math.max(size.width, 0); + size.height = Math.max(size.height, 0); + + return size; + } +}; + +// we only uses some of the jQueryUI easing functions +// add those with a prefix to prevent conflicts +$.extend($.easing, { + frescoEaseInCubic: function(x, t, b, c, d) { + return c * (t /= d) * t * t + b; + }, + + frescoEaseInSine: function(x, t, b, c, d) { + return -c * Math.cos((t / d) * (Math.PI / 2)) + c + b; + }, + + frescoEaseOutSine: function(x, t, b, c, d) { + return c * Math.sin((t / d) * (Math.PI / 2)) + b; + } +}); + +var Support = (function() { + var testElement = document.createElement("div"), + domPrefixes = "Webkit Moz O ms Khtml".split(" "); + + function prefixed(property) { + return testAllProperties(property, "prefix"); + } + + function testProperties(properties, prefixed) { + for (var i in properties) { + if (testElement.style[properties[i]] !== undefined) { + return prefixed === "prefix" ? properties[i] : true; + } + } + return false; + } + + function testAllProperties(property, prefixed) { + var ucProperty = property.charAt(0).toUpperCase() + property.substr(1), + properties = ( + property + + " " + + domPrefixes.join(ucProperty + " ") + + ucProperty + ).split(" "); + + return testProperties(properties, prefixed); + } + + // feature detect + return { + canvas: (function() { + var canvas = document.createElement("canvas"); + return !!(canvas.getContext && canvas.getContext("2d")); + })(), + + css: { + animation: testAllProperties("animation"), + transform: testAllProperties("transform"), + prefixed: prefixed + }, + + svg: + !!document.createElementNS && + !!document.createElementNS("http://www.w3.org/2000/svg", "svg") + .createSVGRect, + + touch: (function() { + try { + return !!( + "ontouchstart" in window || + (window.DocumentTouch && document instanceof DocumentTouch) + ); // firefox on Android + } catch (e) { + return false; + } + })() + }; +})(); + +// add mobile touch to support +Support.detectMobileTouch = function() { + Support.mobileTouch = + Support.touch && + (Browser.MobileSafari || + Browser.Android || + Browser.IEMobile || + Browser.ChromeMobile || + !/^(Win|Mac|Linux)/.test(navigator.platform)); // otherwise, assume anything not on Windows, Mac or Linux is a mobile device + //Support.mobileTouch = true; +}; +Support.detectMobileTouch(); + +/* ImageReady (standalone) - part of Voilà + * http://voila.nickstakenburg.com + * MIT License + */ +var ImageReady = function() { + return this.initialize.apply(this, Array.prototype.slice.call(arguments)); +}; + +$.extend(ImageReady.prototype, { + supports: { + naturalWidth: (function() { + return "naturalWidth" in new Image(); + })() + }, + + // NOTE: setTimeouts allow callbacks to be attached + initialize: function(img, successCallback, errorCallback) { + this.img = $(img)[0]; + this.successCallback = successCallback; + this.errorCallback = errorCallback; + this.isLoaded = false; + + this.options = $.extend( + { + method: "naturalWidth", + pollFallbackAfter: 1000 + }, + arguments[3] || {} + ); + + // a fallback is used when we're not polling for naturalWidth/Height + // IE6-7 also use this to add support for naturalWidth/Height + if (!this.supports.naturalWidth || this.options.method === "onload") { + setTimeout($.proxy(this.fallback, this)); + return; + } + + // can exit out right away if we have a naturalWidth + if (this.img.complete && $.type(this.img.naturalWidth) !== "undefined") { + setTimeout( + $.proxy(function() { + if (this.img.naturalWidth > 0) { + this.success(); + } else { + this.error(); + } + }, this) + ); + return; + } + + // we instantly bind to onerror so we catch right away + $(this.img).bind( + "error", + $.proxy(function() { + setTimeout( + $.proxy(function() { + this.error(); + }, this) + ); + }, this) + ); + + this.intervals = [ + [1000, 10], + [2 * 1000, 50], + [4 * 1000, 100], + [20 * 1000, 500] + ]; + + // for testing, 2sec delay + // this.intervals = [[20 * 1000, 2000]]; + + this._ipos = 0; + this._time = 0; + this._delay = this.intervals[this._ipos][1]; + + // start polling + this.poll(); + }, + + poll: function() { + this._polling = setTimeout( + $.proxy(function() { + if (this.img.naturalWidth > 0) { + this.success(); + return; + } + + // update time spend + this._time += this._delay; + + // use a fallback after waiting + if ( + this.options.pollFallbackAfter && + this._time >= this.options.pollFallbackAfter && + !this._usedPollFallback + ) { + this._usedPollFallback = true; + this.fallback(); + } + + // next i within the interval + if (this._time > this.intervals[this._ipos][0]) { + // if there's no next interval, we asume + // the image image errored out + if (!this.intervals[this._ipos + 1]) { + this.error(); + return; + } + + this._ipos++; + + // update to the new bracket + this._delay = this.intervals[this._ipos][1]; + } + + this.poll(); + }, this), + this._delay + ); + }, + + fallback: function() { + var img = new Image(); + this._fallbackImg = img; + + img.onload = $.proxy(function() { + img.onload = function() {}; + + if (!this.supports.naturalWidth) { + this.img.naturalWidth = img.width; + this.img.naturalHeight = img.height; + } + + this.success(); + }, this); + + img.onerror = $.proxy(this.error, this); + + img.src = this.img.src; + }, + + abort: function() { + if (this._fallbackImg) { + this._fallbackImg.onload = function() {}; + } + + if (this._polling) { + clearTimeout(this._polling); + this._polling = null; + } + }, + + success: function() { + if (this._calledSuccess) return; + this._calledSuccess = true; + + this.isLoaded = true; + this.successCallback(this); + }, + + error: function() { + if (this._calledError) return; + this._calledError = true; + + this.abort(); + if (this.errorCallback) this.errorCallback(this); + } +}); + +function Timers() { + return this.initialize.apply(this, _slice.call(arguments)); +} +$.extend(Timers.prototype, { + initialize: function() { + this._timers = {}; + }, + + set: function(name, handler, ms) { + this._timers[name] = setTimeout(handler, ms); + }, + + get: function(name) { + return this._timers[name]; + }, + + clear: function(name) { + if (name) { + if (this._timers[name]) { + clearTimeout(this._timers[name]); + delete this._timers[name]; + } + } else { + this.clearAll(); + } + }, + + clearAll: function() { + $.each(this._timers, function(i, timer) { + clearTimeout(timer); + }); + this._timers = {}; + } +}); + +// uses Types to scan a URI for info +function getURIData(url) { + var result = { type: "image" }; + $.each(Types, function(i, type) { + var data = type.data(url); + if (data) { + result = data; + result.type = i; + result.url = url; + } + }); + + return result; +} + +function detectExtension(url) { + var ext = (url || "").replace(/\?.*/g, "").match(/\.([^.]{3,4})$/); + return ext ? ext[1].toLowerCase() : null; +} + +var Type = { + isVideo: function(type) { + return /^(youtube|vimeo)$/.test(type); + } +}; + +var Types = { + image: { + extensions: "bmp gif jpeg jpg png webp", + detect: function(url) { + return $.inArray(detectExtension(url), this.extensions.split(" ")) > -1; + }, + data: function(url) { + if (!this.detect()) return false; + + return { + extension: detectExtension(url) + }; + } + }, + + vimeo: { + detect: function(url) { + var res = /(vimeo\.com)\/([a-zA-Z0-9-_]+)(?:\S+)?$/i.exec(url); + if (res && res[2]) return res[2]; + + return false; + }, + data: function(url) { + var id = this.detect(url); + if (!id) return false; + + return { + id: id + }; + } + }, + + youtube: { + detect: function(url) { + var res = /(youtube\.com|youtu\.be)\/watch\?(?=.*vi?=([a-zA-Z0-9-_]+))(?:\S+)?$/.exec( + url + ); + if (res && res[2]) return res[2]; + + res = /(youtube\.com|youtu\.be)\/(vi?\/|u\/|embed\/)?([a-zA-Z0-9-_]+)(?:\S+)?$/i.exec( + url + ); + if (res && res[3]) return res[3]; + + return false; + }, + data: function(url) { + var id = this.detect(url); + if (!id) return false; + + return { + id: id + }; + } + } +}; + +var VimeoThumbnail = (function() { + var VimeoThumbnail = function() { + return this.initialize.apply(this, _slice.call(arguments)); + }; + $.extend(VimeoThumbnail.prototype, { + initialize: function(url, successCallback, errorCallback) { + this.url = url; + this.successCallback = successCallback; + this.errorCallback = errorCallback; + + this.load(); + }, + + load: function() { + // first try the cache + var cache = Cache.get(this.url); + + if (cache) { + return this.successCallback(cache.data.url); + } + + var protocol = + "http" + + (window.location && window.location.protocol === "https:" + ? "s" + : "") + + ":", + video_id = getURIData(this.url).id; + + this._xhr = $.getJSON( + protocol + + "//vimeo.com/api/oembed.json?url=" + + protocol + + "//vimeo.com/" + + video_id + + "&callback=?", + $.proxy(function(_data) { + if (_data && _data.thumbnail_url) { + var data = { + url: _data.thumbnail_url + }; + + Cache.set(this.url, data); + + this.successCallback(data.url); + } else { + this.errorCallback(); + } + }, this) + ); + }, + + abort: function() { + if (this._xhr) { + this._xhr.abort(); + this._xhr = null; + } + } + }); + + var Cache = { + cache: [], + + get: function(url) { + var entry = null; + for (var i = 0; i < this.cache.length; i++) { + if (this.cache[i] && this.cache[i].url === url) entry = this.cache[i]; + } + return entry; + }, + + set: function(url, data) { + this.remove(url); + this.cache.push({ url: url, data: data }); + }, + + remove: function(url) { + for (var i = 0; i < this.cache.length; i++) { + if (this.cache[i] && this.cache[i].url === url) { + delete this.cache[i]; + } + } + } + }; + + return VimeoThumbnail; +})(); + +var VimeoReady = (function() { + var VimeoReady = function() { + return this.initialize.apply(this, _slice.call(arguments)); + }; + $.extend(VimeoReady.prototype, { + initialize: function(url, callback) { + this.url = url; + this.callback = callback; + + this.load(); + }, + + load: function() { + // first try the cache + var cache = Cache.get(this.url); + + if (cache) { + return this.callback(cache.data); + } + + var protocol = + "http" + + (window.location && window.location.protocol === "https:" + ? "s" + : "") + + ":", + video_id = getURIData(this.url).id; + + // NOTE: We're using a maxwidth/maxheight hack because of a regression in the oEmbed API + // see: https://vimeo.com/forums/api/topic:283559 + this._xhr = $.getJSON( + protocol + + "//vimeo.com/api/oembed.json?url=" + + protocol + + "//vimeo.com/" + + video_id + + "&maxwidth=9999999&maxheight=9999999&callback=?", + $.proxy(function(_data) { + var data = { + dimensions: { + width: _data.width, + height: _data.height + } + }; + + Cache.set(this.url, data); + + if (this.callback) this.callback(data); + }, this) + ); + }, + + abort: function() { + if (this._xhr) { + this._xhr.abort(); + this._xhr = null; + } + } + }); + + var Cache = { + cache: [], + + get: function(url) { + var entry = null; + for (var i = 0; i < this.cache.length; i++) { + if (this.cache[i] && this.cache[i].url === url) entry = this.cache[i]; + } + return entry; + }, + + set: function(url, data) { + this.remove(url); + this.cache.push({ url: url, data: data }); + }, + + remove: function(url) { + for (var i = 0; i < this.cache.length; i++) { + if (this.cache[i] && this.cache[i].url === url) { + delete this.cache[i]; + } + } + } + }; + + return VimeoReady; +})(); + +var Options = { + defaults: { + effects: { + content: { show: 0, hide: 0 }, + spinner: { show: 150, hide: 150 }, + window: { show: 440, hide: 300 }, + thumbnail: { show: 300, delay: 150 }, + thumbnails: { slide: 0 } + }, + keyboard: { + left: true, + right: true, + esc: true + }, + loadedMethod: "naturalWidth", + loop: false, + onClick: "previous-next", + overflow: false, + overlay: { + close: true + }, + preload: [1, 2], + position: true, + skin: "fresco", + spinner: true, + spinnerDelay: 300, + sync: true, + thumbnails: "horizontal", + ui: "outside", + uiDelay: 3000, + vimeo: { + autoplay: 1, + api: 1, + title: 1, + byline: 1, + portrait: 0, + loop: 0 + }, + youtube: { + autoplay: 1, + controls: 1, + //cc_load_policy: 0, + enablejsapi: 1, + hd: 1, + iv_load_policy: 3, + loop: 0, + modestbranding: 1, + rel: 0, + vq: "hd1080" // force hd: http://stackoverflow.com/a/12467865 + }, + + initialTypeOptions: { + image: {}, + vimeo: { + width: 1280 + }, + // Youtube needs both dimensions, it doesn't support fetching video dimensions like Vimeo yet. + // Star this ticket if you'd like to get support for it at some point: + // https://code.google.com/p/gdata-issues/issues/detail?id=4329 + youtube: { + width: 1280, + height: 720 + } + } + }, + + create: function(opts, type, data) { + opts = opts || {}; + data = data || {}; + + opts.skin = opts.skin || this.defaults.skin; + + var selected = opts.skin + ? $.extend( + {}, + Fresco.Skins[opts.skin] || Fresco.Skins[this.defaults.skin] + ) + : {}, + merged = $.extend(true, {}, this.defaults, selected); + + // merge initial type options + if (merged.initialTypeOptions) { + if (type && merged.initialTypeOptions[type]) { + merged = $.extend(true, {}, merged.initialTypeOptions[type], merged); + } + // these aren't used further, so remove them + delete merged.initialTypeOptions; + } + + // safe options to work with + var options = $.extend(true, {}, merged, opts); + + // touch should never use ui:inside + if (Support.mobileTouch && options.ui === "inside") { + options.ui = "outside"; + } + + // set all effect duration to 0 for effects: false + // IE8 and below never use effects + if (!options.effects || (Browser.IE && Browser.IE < 9)) { + options.effects = {}; + $.each(this.defaults.effects, function(name, effect) { + $.each((options.effects[name] = $.extend({}, effect)), function( + option + ) { + options.effects[name][option] = 0; + }); + }); + + // disable the spinner when effects are disabled + options.spinner = false; + } + + // keyboard + if (options.keyboard) { + // when keyboard is true, enable all keys + if ($.type(options.keyboard) === "boolean") { + options.keyboard = {}; + $.each(this.defaults.keyboard, function(key, bool) { + options.keyboard[key] = true; + }); + } + + // disable left and right keys for video, because players like + // youtube use these keys + if (type === "vimeo" || type === "youtube") { + $.extend(options.keyboard, { left: false, right: false }); + } + } + + // overflow + if (!options.overflow || Support.mobileTouch) { + // false + options.overflow = { x: false, y: false }; + } else { + if ($.type(options.overflow) === "boolean") { + // true + options.overflow = { x: false, y: true }; + } + } + + // vimeo & youtube always have no overlap + if (type === "vimeo" || type === "youtube") { + options.overlap = false; + } + + // disabled thumbnails IE < 9 & touch based devices + if ((Browser.IE && Browser.IE < 9) || Support.mobileTouch) { + options.thumbnail = false; + options.thumbnails = false; + } + + // width/height are only used for youtube + // convert it to maxWidth/Height for the other content + // when no max values have been set + if (type !== "youtube") { + if (options.width && !options.maxWidth) { + options.maxWidth = options.width; + } + if (options.height && !options.maxHeight) { + options.maxHeight = options.height; + } + } + + // youtube thumbnails + if (!options.thumbnail && $.type(options.thumbnail) !== "boolean") { + // only continue if undefined, forced false stays false + var thumbnail = false; + + switch (type) { + case "youtube": + var protocol = + "http" + + (window.location && window.location.protocol === "https:" + ? "s" + : "") + + ":"; + + thumbnail = protocol + "//img.youtube.com/vi/" + data.id + "/0.jpg"; + break; + case "image": + case "vimeo": + thumbnail = true; + break; + } + + options.thumbnail = thumbnail; + } + + return options; + } +}; + +var Overlay = { + initialize: function() { + this.build(); + this.visible = false; + }, + + build: function() { + this.element = $("
") + .addClass("fr-overlay") + .hide() + .append($("
").addClass("fr-overlay-background")); + + this.element.on( + "click", + $.proxy(function() { + var page = Pages.page; + if ( + page && + page.view && + page.view.options.overlay && + !page.view.options.overlay.close + ) { + return; + } + Window.hide(); + }, this) + ); + + if (Support.mobileTouch) { + this.element.addClass("fr-mobile-touch"); + } + + // prevent mousewheel scroll + this.element.on("fresco:mousewheel", function(event) { + event.preventDefault(); + }); + }, + + setSkin: function(skin) { + if (this.skin) { + this.element.removeClass("fr-overlay-skin-" + this.skin); + } + + this.element.addClass("fr-overlay-skin-" + skin); + this.skin = skin; + }, + + attach: function() { + $(document.body).append(this.element); + }, + + detach: function() { + this.element.detach(); + }, + + show: function(callback, alternateDuration) { + if (this.visible) { + if (callback) callback(); + return; + } + + this.visible = true; + + this.attach(); + this.max(); + + var pDuration = + (Pages.page && Pages.page.view.options.effects.window.show) || 0, + duration = + ($.type(alternateDuration) === "number" + ? alternateDuration + : pDuration) || 0; + + this.element.stop(true).fadeTo(duration, 1, callback); + }, + + hide: function(callback, alternateDuration) { + if (!this.visible) { + if (callback) callback(); + return; + } + + var pDuration = + (Pages.page && Pages.page.view.options.effects.window.hide) || 0, + duration = + ($.type(alternateDuration) === "number" + ? alternateDuration + : pDuration) || 0; + + this.element.stop(true).fadeOut( + duration || 0, + $.proxy(function() { + this.detach(); + this.visible = false; + if (callback) callback(); + }, this) + ); + }, + + getScrollDimensions: function() { + var dimensions = {}; + $.each(["width", "height"], function(i, d) { + var D = d.substr(0, 1).toUpperCase() + d.substr(1), + ddE = document.documentElement; + + dimensions[d] = + (Browser.IE + ? Math.max(ddE["offset" + D], ddE["scroll" + D]) + : Browser.WebKit + ? document.body["scroll" + D] + : ddE["scroll" + D]) || 0; + }); + return dimensions; + }, + + max: function() { + var scrollDimensions; + if (Browser.MobileSafari && (Browser.WebKit && Browser.WebKit < 533.18)) { + scrollDimensions = this.getScrollDimensions(); + this.element.css(scrollDimensions); + } + + if (Browser.IE && Browser.IE < 9) { + var viewport = Bounds.viewport(); + this.element.css({ height: viewport.height, width: viewport.width }); + } + + if (Support.mobileTouch && !scrollDimensions) { + this.element.css({ + height: this.getScrollDimensions().height + }); + } + } +}; + +var Window = { + initialize: function() { + this.queues = []; + this.queues.hide = $({}); + + this.pages = []; + this._tracking = []; + this._first = true; + + this.timers = new Timers(); + + this.build(); + this.setSkin(Options.defaults.skin); + }, + + build: function() { + // window + this.element = $("
") + .addClass("fr-window fr-measured") + .hide() // start hidden + + .append( + (this._box = $("
") + .addClass("fr-box") + .append((this._pages = $("
").addClass("fr-pages")))) + ) + + .append((this._thumbnails = $("
").addClass("fr-thumbnails"))); + + Overlay.initialize(); + Pages.initialize(this._pages); + Thumbnails.initialize(this._thumbnails); + Spinner.initialize(); + UI.initialize(); + + // support classes + this.element.addClass( + "fr" + (!Support.mobileTouch ? "-no" : "") + "-mobile-touch" + ); + this.element.addClass("fr" + (!Support.svg ? "-no" : "") + "-svg"); + + if (Browser.IE) { + for (var i = 7; i <= 9; i++) { + if (Browser.IE < i) { + this.element.addClass("fr-ltIE" + i); + } + } + } + + // prevent mousewheel scroll + this.element.on("fresco:mousewheel", function(event) { + event.preventDefault(); + }); + }, + + attach: function() { + if (this._attached) return; + $(document.body).append(this.element); + this._attached = true; + }, + + detach: function() { + if (!this._attached) return; + this.element.detach(); + this._attached = false; + }, + + setSkin: function(skin) { + if (this._skin) { + this.element.removeClass("fr-window-skin-" + this._skin); + } + this.element.addClass("fr-window-skin-" + skin); + + Overlay.setSkin(skin); + + this._skin = skin; + }, + + setShowingType: function(type) { + if (this._showingType === type) return; + + if (this._showingType) { + this.element.removeClass("fr-showing-type-" + this._showingType); + if (Type.isVideo(this._showingType)) { + this.element.removeClass("fr-showing-type-video"); + } + } + + this.element.addClass("fr-showing-type-" + type); + if (Type.isVideo(type)) { + this.element.addClass("fr-showing-type-video"); + } + + this._showingType = type; + }, + + // Resize + startObservingResize: function() { + if (this._onWindowResizeHandler) return; + $(window).on( + "resize orientationchange", + (this._onWindowResizeHandler = $.proxy(this._onWindowResize, this)) + ); + }, + + stopObservingResize: function() { + if (this._onWindowResizeHandler) { + $(window).off("resize orientationchange", this._onWindowResizeHandler); + this._onWindowResizeHandler = null; + } + }, + + /*startObservingScroll: function() { + if (this._onScrollHandler) return; + $(window).on('scroll', this._onScrollHandler = $.proxy(this._onScroll, this)); + }, + + stopObservingScroll: function() { + if (this._onScrollHandler) { + $(window).off('scroll', this._onScrollHandler); + this._onScrollHandler = null; + } + },*/ + + _onScroll: function() { + if (!Support.mobileTouch) return; + // the timeout is a hack for iOS not responding + this.timers.set("scroll", $.proxy(this.adjustToScroll, this), 0); + }, + + _onWindowResize: function() { + var page; + if (!(page = Pages.page)) return; + + Thumbnails.fitToViewport(); + + this.updateBoxDimensions(); + + page.fitToBox(); + + // update the UI to the current size + UI.update(); + + // instantly update previous/next + UI.adjustPrevNext(null, 0); + + // reposition spinner + Spinner.center(); + + Overlay.max(); // IE7-8 + + // show UI for touch on resize + UI._onWindowResize(); + + this._onScroll(); + }, + + adjustToScroll: function() { + if (!Support.mobileTouch) return; + + this.element.css({ + //left: $(window).scrollLeft(), + top: $(window).scrollTop() + }); + }, + + getBoxDimensions: function() { + return this._boxDimensions; + }, + + updateBoxDimensions: function() { + var page; + if (!(page = Pages.page)) return; + + var viewport = Bounds.viewport(), + thumbnails = Thumbnails.getDimensions(); + + var isHorizontal = Thumbnails._orientation === "horizontal"; + + this._boxDimensions = { + width: isHorizontal ? viewport.width : viewport.width - thumbnails.width, + height: isHorizontal + ? viewport.height - thumbnails.height + : viewport.height + }; + + // resize + this._boxPosition = { + top: 0, + left: isHorizontal ? 0 : thumbnails.width + }; + + this._box.css($.extend({}, this._boxDimensions, this._boxPosition)); + }, + + show: function(callback, alternateDuration) { + if (this.visible) { + if (callback) callback(); + return; + } + + this.visible = true; + this.opening = true; + + this.attach(); + + // clear timers that possible break toggling between show/hide() + this.timers.clear("show-window"); + this.timers.clear("hide-overlay"); + + // position the window at the top if mobile touch + this.adjustToScroll(); + + var duration = + ($.type(alternateDuration) === "number" + ? alternateDuration + : Pages.page && Pages.page.view.options.effects.window.show) || 0; + + var fx = 2; + + // overlay + Overlay[Pages.page && Pages.page.view.options.overlay ? "show" : "hide"]( + function() { + if (callback && --fx < 1) callback(); + }, + duration + ); + + // window + // using a timeout here removes a sharp visible edge of the window while fading in + // because the fading happens on top of a solid area + this.timers.set( + "show-window", + $.proxy(function() { + this._show( + $.proxy(function() { + this.opening = false; + if (callback && --fx < 1) callback(); + }, this), + duration + ); + }, this), + duration > 1 ? Math.min(duration * 0.5, 50) : 1 + ); + }, + + _show: function(callback, alternateDuration) { + var duration = + ($.type(alternateDuration) === "number" + ? alternateDuration + : Pages.page && Pages.page.view.options.effects.window.show) || 0; + + this.element.stop(true).fadeTo(duration, 1, callback); + }, + + hide: function(callback) { + if (!this.view) return; + + var hideQueue = this.queues.hide; + hideQueue.queue([]); // clear queue + + // clear timers that possible break toggling between show/hide() + this.timers.clear("show-window"); + this.timers.clear("hide-overlay"); + + var duration = Pages.page ? Pages.page.view.options.effects.window.hide : 0; + + hideQueue.queue( + $.proxy(function(next_stop) { + Pages.stop(); + + // hide the spinner here so its effect ends early enough + Spinner.hide(); + + next_stop(); + }, this) + ); + + hideQueue.queue( + $.proxy(function(next_unbinds) { + // ui + UI.disable(); + UI.hide(null, duration); + + // keyboard + Keyboard.disable(); + + next_unbinds(); + }, this) + ); + + hideQueue.queue( + $.proxy(function(next_hidden) { + var fx = 2; + + this._hide(function() { + if (--fx < 1) next_hidden(); + }, duration); + + // using a timeout here removes a sharp visible edge of the window while fading out + this.timers.set( + "hide-overlay", + $.proxy(function() { + Overlay.hide(function() { + if (--fx < 1) next_hidden(); + }, duration); + }, this), + duration > 1 ? Math.min(duration * 0.5, 150) : 1 + ); + + // after we initiate hide, the next show() should bring up the UI + // we to this using a flag + this._first = true; + }, this) + ); + + // callbacks after resize in a separate queue + // so we can stop the hideQueue without stopping the resize + hideQueue.queue( + $.proxy(function(next_after_resize) { + this._reset(); + + // all of the below we cannot safely call safely + this.stopObservingResize(); + //this.stopObservingScroll(); + + Pages.removeAll(); + + Thumbnails.clear(); + + this.timers.clear(); + + this._position = -1; + + // afterHide callback + var afterHide = Pages.page && Pages.page.view.options.afterHide; + if ($.type(afterHide) === "function") { + afterHide.call(Fresco); + } + + this.view = null; + this.opening = false; + this.closing = false; + + // remove from DOM + this.detach(); + + next_after_resize(); + }, this) + ); + + if ($.type(callback) === "function") { + hideQueue.queue( + $.proxy(function(next_callback) { + callback(); + next_callback(); + }, this) + ); + } + }, + + _hide: function(callback, alternateDuration) { + var duration = + ($.type(alternateDuration) === "number" + ? alternateDuration + : Pages.page && Pages.page.view.options.effects.window.hide) || 0; + + this.element.stop(true).fadeOut(duration, callback); + }, + + // Load + load: function(views, position) { + this.views = views; + + // dimension and visibility based code needs + // the window attached + this.attach(); + + Thumbnails.load(views); + + Pages.load(views); + + this.startObservingResize(); + //this.startObservingScroll(); + + if (position) { + this.setPosition(position); + } + }, + + // loading indicator + /* + startLoading: function() { + if (!Spinner.supported) return; + + Spinner.show(); + Spinner.center(); + }, + + stopLoading: function() { + if (!Spinner.supported) return; + + // we only stop loading if there are no loading pages anymore + var loadingCount = Pages.getLoadingCount(); + + if (loadingCount < 1) { + Spinner.hide(); + } + },*/ + + setPosition: function(position, callback) { + this._position = position; + + // store the current view + this.view = this.views[position - 1]; + + // we need to make sure that a possible hide effect doesn't + // trigger its callbacks, as that would cancel the showing/loading + // of the page started below + this.stopHideQueue(); + + // store the page and show it + this.page = Pages.show( + position, + $.proxy(function() { + if (callback) callback(); + }, this) + ); + }, + + // stop all callbacks possibly queued up into a hide animation + // this allows the hide animation to finish as we start showing/loading + // a new page, a callback could otherwise interrupt this + stopHideQueue: function() { + this.queues.hide.queue([]); + }, + + _reset: function() { + this.visible = false; + UI.hide(null, 0); + UI.reset(); + }, + + // Previous / Next + mayPrevious: function() { + return ( + (this.view && + this.view.options.loop && + this.views && + this.views.length > 1) || + this._position !== 1 + ); + }, + + previous: function(force) { + var mayPrevious = this.mayPrevious(); + + if (force || mayPrevious) { + this.setPosition(this.getSurroundingIndexes().previous); + } + }, + + mayNext: function() { + var hasViews = this.views && this.views.length > 1; + + return ( + (this.view && this.view.options.loop && hasViews) || + (hasViews && this.getSurroundingIndexes().next !== 1) + ); + }, + + next: function(force) { + var mayNext = this.mayNext(); + + if (force || mayNext) { + this.setPosition(this.getSurroundingIndexes().next); + } + }, + + // surrounding + getSurroundingIndexes: function() { + if (!this.views) return {}; + + var pos = this._position, + length = this.views.length; + + var previous = pos <= 1 ? length : pos - 1, + next = pos >= length ? 1 : pos + 1; + + return { + previous: previous, + next: next + }; + } +}; + +// Keyboard +// keeps track of keyboard events when enabled +var Keyboard = { + enabled: false, + + keyCode: { + left: 37, + right: 39, + esc: 27 + }, + + // enable is passed the keyboard option of a page, which can be false + // or contains multiple buttons to toggle + enable: function(enabled) { + this.disable(); + + if (!enabled) return; + + $(document) + .on("keydown", (this._onKeyDownHandler = $.proxy(this.onKeyDown, this))) + .on("keyup", (this._onKeyUpHandler = $.proxy(this.onKeyUp, this))); + + this.enabled = enabled; + }, + + disable: function() { + this.enabled = false; + + if (this._onKeyUpHandler) { + $(document) + .off("keyup", this._onKeyUpHandler) + .off("keydown", this._onKeyDownHandler); + this._onKeyUpHandler = this._onKeyDownHandler = null; + } + }, + + onKeyDown: function(event) { + if (!this.enabled) return; + + var key = this.getKeyByKeyCode(event.keyCode); + + if (!key || (key && this.enabled && !this.enabled[key])) return; + + event.preventDefault(); + event.stopPropagation(); + + switch (key) { + case "left": + Window.previous(); + break; + case "right": + Window.next(); + break; + } + }, + + onKeyUp: function(event) { + if (!this.enabled) return; + + var key = this.getKeyByKeyCode(event.keyCode); + + if (!key || (key && this.enabled && !this.enabled[key])) return; + + switch (key) { + case "esc": + Window.hide(); + break; + } + }, + + getKeyByKeyCode: function(keyCode) { + for (var key in this.keyCode) { + if (this.keyCode[key] === keyCode) return key; + } + return null; + } +}; + +var Page = (function() { + var _uid = 0, + _loadedUrlCache = {}, + // a group of elements defining the 1px stroke, cloned later on + _strokes = $("
") + .addClass("fr-stroke fr-stroke-top fr-stroke-horizontal") + .append($("
").addClass("fr-stroke-color")) + .add( + $("
") + .addClass("fr-stroke fr-stroke-bottom fr-stroke-horizontal") + .append($("
").addClass("fr-stroke-color")) + ) + .add( + $("
") + .addClass("fr-stroke fr-stroke-left fr-stroke-vertical") + .append($("
").addClass("fr-stroke-color")) + ) + .add( + $("
") + .addClass("fr-stroke fr-stroke-right fr-stroke-vertical") + .append($("
").addClass("fr-stroke-color")) + ); + + function Page() { + return this.initialize.apply(this, _slice.call(arguments)); + } + $.extend(Page.prototype, { + initialize: function(view, position, total) { + this.view = view; + this.dimensions = { width: 0, height: 0 }; + this.uid = _uid++; + + // store position/total views for later use + this._position = position; + this._total = total; + this._fullClick = false; + + this._visible = false; + + this.queues = {}; + this.queues.showhide = $({}); + }, + + // create the page, this doesn't mean it's loaded + // should happen instantly + create: function() { + if (this._created) return; + + Pages.element.append( + (this.element = $("
") + .addClass("fr-page") + .append((this.container = $("
").addClass("fr-container"))) + .css({ opacity: 0 }) + .hide()) + ); + + // check if we have a position + var hasPosition = this.view.options.position && this._total > 1; + if (hasPosition) { + // mark it if so + this.element.addClass("fr-has-position"); + } + + // info (caption/position) + if (this.view.caption || hasPosition) { + this.element.append( + (this.info = $("
") + .addClass("fr-info") + .append($("
").addClass("fr-info-background")) + .append(_strokes.clone(true)) + .append((this.infoPadder = $("
").addClass("fr-info-padder")))) + ); + + // insert position first because it floats right + if (hasPosition) { + this.element.addClass("fr-has-position"); + + this.infoPadder.append( + (this.pos = $("
") + .addClass("fr-position") + .append( + $("") + .addClass("fr-position-text") + .html(this._position + " / " + this._total) + )) + ); + } + + if (this.view.caption) { + this.infoPadder.append( + (this.caption = $("
") + .addClass("fr-caption") + .html(this.view.caption)) + ); + } + } + + // background + this.container + .append( + (this.background = $("
").addClass("fr-content-background")) + ) + .append((this.content = $("
").addClass("fr-content"))); + + // append images instantly + if (this.view.type == "image") { + this.content.append( + (this.image = $("") + .addClass("fr-content-element") + .attr({ src: this.view.url })) + ); + + this.content.append(_strokes.clone(true)); + } + + // ui:outside needs a position outside of the info bar + if (hasPosition && this.view.options.ui == "outside") { + this.container.append( + (this.positionOutside = $("
") + .addClass("fr-position-outside") + .append($("
").addClass("fr-position-background")) + .append( + $("") + .addClass("fr-position-text") + .html(this._position + " / " + this._total) + )) + ); + } + + // ui:inside has everything inside the content + if (this.view.options.ui == "inside") { + // buttons + this.content + // < previous + .append( + (this.previousInside = $("
") + .addClass("fr-side fr-side-previous fr-toggle-ui") + .append( + $("
") + .addClass("fr-side-button") + .append($("
").addClass("fr-side-button-background")) + .append($("
").addClass("fr-side-button-icon")) + )) + ) + // > next + .append( + (this.nextInside = $("
") + .addClass("fr-side fr-side-next fr-toggle-ui") + .append( + $("
") + .addClass("fr-side-button") + .append($("
").addClass("fr-side-button-background")) + .append($("
").addClass("fr-side-button-icon")) + )) + ) + + // X close + .append( + (this.closeInside = $("
") + .addClass("fr-close fr-toggle-ui") + .append($("
").addClass("fr-close-background")) + .append($("
").addClass("fr-close-icon"))) + ); + + // info (only inserted when there is a caption) + // if there is no caption we insert a separate position element below + // but if 1 item in the group has a caption we insert the info bar + if (this.view.caption || (hasPosition && this.view.grouped.caption)) { + this.content.append( + (this.infoInside = $("
") + .addClass("fr-info fr-toggle-ui") + .append($("
").addClass("fr-info-background")) + .append(_strokes.clone(true)) + .append( + (this.infoPadderInside = $("
").addClass("fr-info-padder")) + )) + ); + + // insert position first because it floats right + if (hasPosition) { + this.infoPadderInside.append( + (this.posInside = $("
") + .addClass("fr-position") + .append( + $("") + .addClass("fr-position-text") + .html(this._position + " / " + this._total) + )) + ); + } + + if (this.view.caption) { + this.infoPadderInside.append( + (this.captionInside = $("
") + .addClass("fr-caption") + .html(this.view.caption)) + ); + } + } + + // insert a separate position for when there's no caption + // avoid adding it when the group has at least one caption, + // the info bar is shown then + if (!this.view.caption && hasPosition && !this.view.grouped.caption) { + this.content.append( + (this.positionInside = $("
") + .addClass("fr-position-inside fr-toggle-ui") + .append($("
").addClass("fr-position-background")) + .append( + $("") + .addClass("fr-position-text") + .html(this._position + " / " + this._total) + )) + ); + } + + // disabled states on buttons + var mayPrevious = + (this.view.options.loop && this._total > 1) || this._position != 1, + mayNext = + (this.view.options.loop && this._total > 1) || + this._position < this._total; + this.previousInside[(mayPrevious ? "remove" : "add") + "Class"]( + "fr-side-disabled" + ); + this.nextInside[(mayNext ? "remove" : "add") + "Class"]( + "fr-side-disabled" + ); + } + + // overlap (this affects padding) + $.each( + ["x", "y"], + $.proxy(function(i, z) { + if (this.view.options.overflow[z]) { + this.element.addClass("fr-overflow-" + z); + } + }, this) + ); + + // add the type + this.element.addClass("fr-type-" + this.view.type); + // add type-video + if (Type.isVideo(this.view.type)) { + this.element.addClass("fr-type-video"); + } + + // no sides + if (this._total < 2) { + this.element.addClass("fr-no-sides"); + } + + this._created = true; + }, + + //surrounding + _getSurroundingPages: function() { + var preload; + if (!(preload = this.view.options.preload)) return []; + + var pages = [], + begin = Math.max(1, this._position - preload[0]), + end = Math.min(this._position + preload[1], this._total), + pos = this._position; + + // add the pages after this one first for the preloading order + for (var i = pos; i <= end; i++) { + var page = Pages.pages[i - 1]; + if (page._position != pos) pages.push(page); + } + + for (var i = pos; i >= begin; i--) { + var page = Pages.pages[i - 1]; + if (page._position != pos) pages.push(page); + } + + return pages; + }, + + preloadSurroundingImages: function() { + var pages = this._getSurroundingPages(); + + $.each( + pages, + $.proxy(function(i, page) { + page.preload(); + }, this) + ); + }, + + // preload is a non-abortable preloader, + // so that it doesn't interfere with our regular load + preload: function() { + if ( + this.preloading || + this.preloaded || + this.view.type != "image" || + !this.view.options.preload || + this.loaded // page might be loaded before it's preloaded so also stop there + ) { + return; + } + + // make sure the page is created + this.create(); + + this.preloading = true; + + this.preloadReady = new ImageReady( + this.image[0], + $.proxy(function(imageReady) { + // mark this page as loaded, without hiding the spinner + this.loaded = true; + _loadedUrlCache[this.view.url] = true; + + this.preloading = false; + this.preloaded = true; + + this.dimensions = { + width: imageReady.img.naturalWidth, + height: imageReady.img.naturalHeight + }; + }, this), + null, + { + // have the preload always use naturalWidth, + // this avoid an extra new Image() request + method: "naturalWidth" + } + ); + }, + + // the purpose of load is to set dimensions + // we use it to set dimensions even for content that doesn't load like youtube + load: function(callback, isPreload) { + // make sure the page is created + this.create(); + // exit early if already loaded + if (this.loaded) { + if (callback) callback(); + return; + } + + // abort possible previous (pre)load + this.abort(); + + // mark as loading + this.loading = true; + + // start the spinner after waiting for some duration + if (this.view.options.spinner) { + // && !_loadedUrlCache[this.view.url] + this._spinnerDelay = setTimeout( + $.proxy(function() { + Spinner.show(); + }, this), + this.view.options.spinnerDelay || 0 + ); + } + + switch (this.view.type) { + case "image": + // if we had an error before just go through + if (this.error) { + if (callback) callback(); + return; + } + + this.imageReady = new ImageReady( + this.image[0], + $.proxy(function(imageReady) { + // mark as loaded + this._markAsLoaded(); + + this.setDimensions({ + width: imageReady.img.naturalWidth, + height: imageReady.img.naturalHeight + }); + + if (callback) callback(); + }, this), + $.proxy(function() { + // mark as loaded + this._markAsLoaded(); + + this.image.hide(); + this.content.prepend( + (this.error = $("
") + .addClass("fr-error fr-content-element") + .append($("
").addClass("fr-error-icon"))) + ); + this.element.addClass("fr-has-error"); + + this.setDimensions({ + width: this.error.outerWidth(), + height: this.error.outerHeight() + }); + + // allow resizing + this.error.css({ width: "100%", height: "100%" }); + + if (callback) callback(); + }, this), + { + method: this.view.options.loadedMethod + } + ); + + break; + + case "vimeo": + this.vimeoReady = new VimeoReady( + this.view.url, + $.proxy(function(data) { + // mark as loaded + this._markAsLoaded(); + + this.setDimensions({ + width: data.dimensions.width, + height: data.dimensions.height + }); + + if (callback) callback(); + }, this) + ); + break; + + case "youtube": + // mark as loaded + this._markAsLoaded(); + + this.setDimensions({ + width: this.view.options.width, + height: this.view.options.height + }); + + if (callback) callback(); + break; + } + }, + + // sets dimensions taking maxWidth/Height into account + setDimensions: function(dimensions) { + this.dimensions = dimensions; + + if (this.view.options.maxWidth || this.view.options.maxHeight) { + var opts = this.view.options, + bounds = { + width: opts.maxWidth ? opts.maxWidth : this.dimensions.width, + height: opts.maxHeight ? opts.maxHeight : this.dimensions.height + }; + + this.dimensions = Fit.within(bounds, this.dimensions); + } + }, + + // helper for load() + _markAsLoaded: function() { + this._abortSpinnerDelay(); + + this.loading = false; + this.loaded = true; + + // mark url as loaded so we can avoid + // showing the spinner again + _loadedUrlCache[this.view.url] = true; + + Spinner.hide(null, null, this._position); + }, + + isVideo: function() { + return Type.isVideo(this.view.type); + }, + + insertVideo: function(callback) { + // don't insert a video twice + // and stop if not a video + if (this.playerIframe || !this.isVideo()) { + if (callback) callback(); + return; + } + + var protocol = + "http" + + (window.location && window.location.protocol === "https:" ? "s" : "") + + ":"; + + var playerVars = $.extend({}, this.view.options[this.view.type] || {}), + queryString = $.param(playerVars), + urls = { + vimeo: protocol + "//player.vimeo.com/video/{id}?{queryString}", + youtube: protocol + "//www.youtube.com/embed/{id}?{queryString}" + }, + src = urls[this.view.type] + .replace("{id}", this.view._data.id) + .replace("{queryString}", queryString); + + this.content.prepend( + (this.playerIframe = $( + "