From 38d64b5f0e9d7ddb59cf7d8cd73d2ddff0a5bd55 Mon Sep 17 00:00:00 2001 From: Nick Stakenburg Date: Mon, 6 Sep 2021 18:58:04 +0200 Subject: [PATCH] - replaced deprecated jQuery functions (`$.proxy` and `$.type`) - updated README with documentation so we no longer need the website - updated dependencies --- .gitignore | 1 + Gruntfile.js | 51 +- README.md | 657 +- dist/css/strip.css | 383 +- dist/js/strip.pkgd.js | 5389 ++++++++--------- dist/js/strip.pkgd.min.js | 2070 +------ example/css/style.css | 215 + example/images/art/Rembrandt.jpg | Bin 0 -> 217006 bytes example/images/art/andrew_3.jpg | Bin 0 -> 341212 bytes example/images/art/andrew_wyeth.jpg | Bin 0 -> 374277 bytes example/images/art/charles-le-brun.jpg | Bin 0 -> 195970 bytes example/images/art/frans_hals.jpg | Bin 0 -> 229806 bytes .../art/henry_scott_tuke-compressed.jpg | Bin 0 -> 95149 bytes example/images/art/henry_scott_tuke.jpg | Bin 0 -> 162364 bytes example/images/art/john_3.jpg | Bin 0 -> 148610 bytes example/images/art/john_4.jpg | Bin 0 -> 117950 bytes .../images/art/john_singer_2-compressed.jpg | Bin 0 -> 80859 bytes example/images/art/john_singer_2.jpg | Bin 0 -> 325401 bytes example/images/art/john_singer_sargent.jpg | Bin 0 -> 150688 bytes example/images/art/john_singer_sargent_.png | Bin 0 -> 2549211 bytes example/images/art/reynolds.jpg | Bin 0 -> 178600 bytes .../art/wyethwindfromthesea-compressed.jpg | Bin 0 -> 173689 bytes example/images/art/wyethwindfromthesea.jpg | Bin 0 -> 175678 bytes example/images/dm_fw/dm1.jpg | Bin 0 -> 171410 bytes example/images/dm_fw/dm2.jpg | Bin 0 -> 253278 bytes example/images/dm_fw/dm3.jpg | Bin 0 -> 234577 bytes example/images/dm_fw/dm4.jpg | Bin 0 -> 266143 bytes example/images/logo.png | Bin 0 -> 10509 bytes example/images/thumbnails/vimeo.png | Bin 0 -> 669819 bytes example/images/thumbnails/youtube.png | Bin 0 -> 392346 bytes example/index.html | 139 + package-lock.json | 4311 +++++++++++++ package.json | 23 +- src/css/strip.css | 379 +- src/js/api.js | 65 +- src/js/helpers/bounds.js | 6 +- src/js/helpers/browser.js | 4 +- src/js/helpers/helpers.js | 16 +- src/js/helpers/imageready.js | 118 +- src/js/helpers/spinner.js | 24 +- src/js/helpers/support.js | 8 +- src/js/helpers/timers.js | 14 +- src/js/helpers/url.js | 42 +- src/js/helpers/vimeoready.js | 28 +- src/js/keyboard.js | 18 +- src/js/options.js | 37 +- src/js/page.js | 144 +- src/js/pages.js | 82 +- src/js/setup.js | 2 +- src/js/skins.js | 2 +- src/js/umd-head.js | 2 +- src/js/view.js | 8 +- src/js/window.js | 206 +- 53 files changed, 9005 insertions(+), 5439 deletions(-) create mode 100644 example/css/style.css create mode 100644 example/images/art/Rembrandt.jpg create mode 100644 example/images/art/andrew_3.jpg create mode 100644 example/images/art/andrew_wyeth.jpg create mode 100644 example/images/art/charles-le-brun.jpg create mode 100644 example/images/art/frans_hals.jpg create mode 100644 example/images/art/henry_scott_tuke-compressed.jpg create mode 100644 example/images/art/henry_scott_tuke.jpg create mode 100644 example/images/art/john_3.jpg create mode 100644 example/images/art/john_4.jpg create mode 100644 example/images/art/john_singer_2-compressed.jpg create mode 100644 example/images/art/john_singer_2.jpg create mode 100644 example/images/art/john_singer_sargent.jpg create mode 100644 example/images/art/john_singer_sargent_.png create mode 100644 example/images/art/reynolds.jpg create mode 100644 example/images/art/wyethwindfromthesea-compressed.jpg create mode 100644 example/images/art/wyethwindfromthesea.jpg create mode 100644 example/images/dm_fw/dm1.jpg create mode 100644 example/images/dm_fw/dm2.jpg create mode 100644 example/images/dm_fw/dm3.jpg create mode 100644 example/images/dm_fw/dm4.jpg create mode 100644 example/images/logo.png create mode 100644 example/images/thumbnails/vimeo.png create mode 100644 example/images/thumbnails/youtube.png create mode 100644 example/index.html create mode 100644 package-lock.json diff --git a/.gitignore b/.gitignore index 1fe5977..6f5f4fa 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules/ strip-*.zip *.log .idea +.history \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js index c3a58b4..b4ffc32 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,9 +1,9 @@ -module.exports = function(grunt) { +module.exports = function (grunt) { // Config grunt.initConfig({ pkg: grunt.file.readJSON("package.json"), dirs: { - dest: "dist" + dest: "dist", }, vars: {}, @@ -11,7 +11,7 @@ module.exports = function(grunt) { concat: { js: { options: { - process: true + process: true, }, src: [ "src/js/umd-head.js", @@ -39,17 +39,17 @@ module.exports = function(grunt) { "src/js/api.js", - "src/js/umd-tail.js" + "src/js/umd-tail.js", ], - dest: "<%= dirs.dest %>/js/strip.pkgd.js" + dest: "<%= dirs.dest %>/js/strip.pkgd.js", }, css: { options: { - process: true + process: true, }, src: ["src/css/strip.css"], - dest: "<%= dirs.dest %>/css/strip.css" - } + dest: "<%= dirs.dest %>/css/strip.css", + }, }, copy: { @@ -59,22 +59,22 @@ module.exports = function(grunt) { expand: true, cwd: "src/css/strip-skins/", src: ["**"], - dest: "<%= dirs.dest %>/css/strip-skins/" - } - ] - } + dest: "<%= dirs.dest %>/css/strip-skins/", + }, + ], + }, }, uglify: { js: { options: { output: { - comments: "some" - } + comments: "some", + }, }, src: ["<%= dirs.dest %>/js/strip.pkgd.js"], - dest: "<%= dirs.dest %>/js/strip.pkgd.min.js" - } + dest: "<%= dirs.dest %>/js/strip.pkgd.min.js", + }, }, svgmin: { @@ -82,8 +82,8 @@ module.exports = function(grunt) { plugins: [ { removeViewBox: false }, { removeUselessStrokeAndFill: false }, - { removeEmptyAttrs: false } - ] + { removeEmptyAttrs: false }, + ], }, dist: { files: [ @@ -91,15 +91,15 @@ module.exports = function(grunt) { expand: true, cwd: "src/css/", src: ["**/*.svg"], - dest: "src/css/" - } - ] - } + dest: "src/css/", + }, + ], + }, }, clean: { - dest: "<%= dirs.dest %>/*" - } + dest: "<%= dirs.dest %>/*", + }, }); // Load plugins @@ -108,7 +108,6 @@ module.exports = function(grunt) { grunt.loadNpmTasks("grunt-contrib-uglify"); grunt.loadNpmTasks("grunt-contrib-clean"); grunt.loadNpmTasks("grunt-svgmin"); - grunt.loadNpmTasks("grunt-sync"); // Tasks grunt.registerTask("default", [ @@ -116,7 +115,7 @@ module.exports = function(grunt) { "concat:js", "uglify:js", "concat:css", - "copy:css" + "copy:css", ]); grunt.registerTask("svg", ["svgmin"]); diff --git a/README.md b/README.md index 608a358..dc131be 100644 --- a/README.md +++ b/README.md @@ -2,52 +2,667 @@ _An Unobtrusive Responsive Lightbox_ -Strip is a Lightbox that only partially covers the page. This is less intrusive and leaves room to interact with the page on larger screens while giving smaller mobile devices the classic Lightbox experience. +Strip is a Lightbox that only partially covers the page. This leaves room to interact with the page on larger screens while giving smaller devices the classic Lightbox experience. -See [stripjs.com](http://www.stripjs.com) for demos and docs. +![screenshot](https://cloud.githubusercontent.com/assets/5575/4969788/ec4fc80e-686c-11e4-8406-614db6980325.jpg) -[![screenshot](https://cloud.githubusercontent.com/assets/5575/4969788/ec4fc80e-686c-11e4-8406-614db6980325.jpg)](http://www.stripjs.com) +- [Strip](#strip) + - [Usage](#usage) + - [Installation](#installation) + - [Basic Usage](#basic-usage) + - [Groups](#groups) + - [Media types](#media-types) + - [Image](#image) + - [Youtube](#youtube) + - [Vimeo](#vimeo) + - [Javascript API](#javascript-api) + - [Groups](#groups-1) + - [Links](#links) + - [Options](#options) + - [Callbacks](#callbacks) + - [Skins](#skins) + - [Default options](#default-options) + - [Changing the default skin](#changing-the-default-skin) -## Install +## Usage -[Install with npm](http://npmjs.com/package/@staaky/strip): `npm install @staaky/strip` +### Installation -## License +Include Strip below the latest 3.x release of [jQuery](https://www.jquery.com/): -Strip may be used in commercial projects and applications with the one-time purchase of a commercial license. If you are paid to do your job, and part of your job is implementing Strip, a commercial license is required. +```html + + + +``` + +Alternatively Strip can be installed using [npm](https://npmjs.com/package/@staaky/strip): + +```bash +npm install @staaky/strip +``` + +### Basic Usage + +The most basic way to use Strip is by adding the class `strip` to a link: + +```html +Show image +``` + +A caption can be added using the data-strip-caption attribute: + +```html +Caption +``` + +Or customize Strip by putting [options](#options) on the `data-strip-options` attribute: + +```html +Options +``` + +### Groups + +Create groups by giving links a `data-strip-group` attribute with a unique name: + +```html +Image 1 +Image 2 +``` + +The `data-strip-group-options` attribute can be used to set options for all items in the group: + +```html +This group +has shared options +``` + +## Media types + +Strip attempts to automatically detect the media type using the given url. The type can also be set to one of the following values using the `data-strip-type` attribute: `image`, `youtube` or `vimeo`. + +### Image + +Most of the time setting the type will not be required for images, it will be required in cases where Strip cannot detect it based on the url: + +```html +Image +``` + +### Youtube + +Strip will detect Youtube links and automatically embed them: + +```html +Youtube +``` + +Options can be set using the `youtube` option, see [YouTube Embedded Players and Player Parameters](https://developers.google.com/youtube/player_parameters?playerVersion=HTML5) for all the available options. + +```html +Youtube - Dimensions and options +``` + +### Vimeo + +Strips embeds Vimeo videos using the Vimeo Player API. + +```html +Vimeo +``` + +Options can be set using the `vimeo` option, see [Vimeo Player Embedding](https://developer.vimeo.com/player/embedding) for all the available options. + +## Javascript API + +The API allows Strip to be used with Javascript, as an alternative to using the `strip` class on links. + + + + + + + + + + + + + + + + +
Method
+ +`Strip.show()` + + + +A single item can be shown by giving `Strip.show()` a url: + +```js +Strip.show("image.jpg"); +``` + +Add a caption by using an object instead: + +```js +Strip.show({ + url: "image.jpg", + caption: "Caption for this image", +}); +``` + +This object also accepts [options](#options) to customize Strip: + +```js +Strip.show({ + url: "http://vimeo.com/32071937", + options: { + side: "top", + }, +}); +``` + +#### Groups + +Groups can be shown by giving `Strip.show()` an array with multiple items: + +```js +// use urls +Strip.show(["image1.jpg", "image2.jpg"]); + +// or objects +Strip.show([ + { url: "image1.jpg", caption: "Caption for this image" }, + { url: "image2.jpg", caption: "Another caption" }, +]); +``` + +[Options](#options) for the entire group can be set using the second argument: + +```js +Strip.show(["image1.jpg", "image2.jpg"], { + loop: false, + maxWidth: 500, +}); +``` -[http://www.stripjs.com/license](http://www.stripjs.com/license) +Open Strip at a specific position by setting a number as the last argument: -For non-commercial projects and applications, you may use Strip under the terms of the [Creative Commons BY-NC-ND 3.0 License](http://creativecommons.org/licenses/by-nc-nd/3.0/) for free. +```js +Strip.show(["image1.jpg", "image2.jpg"], 2); +``` + +#### Links + +Links that use the strip class can also be opened by passing `Strip.show()` an element: + +```js +Strip.show($("#elementid")[0]); +``` + +
-## Build +`Strip.hide()` -The latest release can be found on [stripjs.com/download](http://www.stripjs.com/download). + -To build Strip yourself start by cloning a copy of the main Strip git repo by running: +Close Strip at any time by calling `Strip.hide()`: +```js +Strip.hide(); ``` -git clone git://github.com/staaky/strip.git + +
+ +`Strip.disable()` + + + +Disables Strip. When disabled, links using the `strip` class will no longer open Strip but work as regular links. Calls to `Strip.show()` will use a fallback to make them behave like regular links. + +```js +Strip.disable(); ``` -Go inside the strip folder that was just fetched and install dependencies: +Use `Strip.fallback(false)` should you need to disable this fallback as well: +```js +Strip.fallback(false).disable(); ``` -cd strip && npm install + +
+ +`Strip.enable()` + + + +Enable Strip if it was previously disabled. + +```js +Strip.enable(); ``` -Make sure the [grunt command line interface](https://github.com/gruntjs/grunt-cli) is installed as a global package: +
+`Strip.fallback()` + + + +When Strip is disabled it will fallback to making `Strip.show()` calls open as regular links. By disabling this fallback API calls will do nothing at all. + +```js +Strip.fallback(false); ``` -npm install -g grunt-cli + +
+ +`Strip.setDefaultSkin()` + + + +Sets the name of the default skin, this skin will be used when no `skin` option is provided. + +```js +Strip.setDefaultSkin("custom"); ``` -Now run the `grunt` command, this updates files in `dist` to include the latest changes made in the `src` folder: +
+ +## Options + + + + + + + + + + + + + + + + + + + + + + + + + + +
Option
+ +`effects` + + + +Sets the duration of individual effects, or disables them when set to _false_. + +``` +effects: false ``` -grunt + +These are all available effects: + +```js +effects: { + spinner: { show: 200, hide: 200 }, + transition: { min: 175, max: 250 }, + ui: { show: 0, hide: 200 }, + window: { show: 300, hide: 300 } +} +``` + +
+ +`hideOnClickOutside` + + + +Hide Strip when clicking outside of it or an element that could open it, enabled by default. + +``` +hideOnClickOutside: false +``` + +
+ +`keyboard` + + + +Enable or disable individual keyboard buttons or all of them when set to _false_. + +``` +keyboard: false +``` + +Or use an object to toggle individual buttons: + +```js +keyboard: { + left: true, + right: true, + esc: false +} +``` + +**Note:** `youtube` and `vimeo` will always have left and right disabled, because a video could require these keys. + +
+ +`loop` + + + +When set to true a group becomes a loop, making it possible to move between the first and last item: + +``` +loop: true +``` + +
+ +`maxHeight` + + + +Sets a maximum height for the content. + +``` +maxHeight: 500 +``` + +
+ +`maxWidth` + + + +Sets a maximum width for the content. + +``` +maxWidth: 500 +``` + +
+ +`overlap` + + + +Allows buttons to overlap the content when set to _true_, which is the default. Disabling overlap will cause buttons to be positioned outside of the content. + +``` +overlap: false +``` + +**Note:** Vimeo and Youtube always have `overlap: false` because overlapping buttons could otherwise prevent interaction with the video. + +
+ +`position` + + + +Show or hide the position indicator. + +``` +position: false +``` + +
+ +`preload` + + + +Sets the items to preload before and after the current item, or disables preloading when set to _false_. + +``` +preload: [1,2] // preload 1 before and 2 after +``` + +``` +preload: false // disables preloading +``` + +
+ +`side` + + + +Set the side to position Strip on to `top`, `bottom`, `left` or `right`. + +``` +side: 'top' +``` + +
+ +`skin` + + + +Sets the skin. If you've provided default options for this skin they'll be applied as a starting point for other options. The default skin is `strip`. + +``` +skin: 'custom' +``` + +
+ +`spinner` + + + +Disables the loading icon when set to _false_. + +``` +spinner: false +``` + +
+ +`toggle` + + + +Clicking elements will toggle Strip, this behavior can be disabed by setting `toggle` to _false_. Doing so will keep Strip open even if an element is clicked twice. + +``` +toggle: false +``` + +**Note:** `Strip.show()` calls don't use toggle behavior, this only works for elements with `class='strip'` + +
+ +`vimeo` + + + +Sets the player parameters of a Vimeo video, available options can be found in the Vimeo documentation: [Vimeo Player Embedding](https://developer.vimeo.com/player/embedding). + +```js +vimeo: { + autoplay: 1, + title: 1, + byline: 1, + portrait: 0, + loop: 0 +} +``` + +
+ +`youtube` + + + +youtubeSets the player parameters of a Youtube video, available options can be found in the Youtube documentation: [YouTube Embedded Players and Player Parameters](https://developers.google.com/youtube/player_parameters?playerVersion=HTML5). + +```js +youtube: { + autohide: 1, + autoplay: 1, + controls: 1, + enablejsapi: 1, + hd: 1, + iv_load_policy: 3, + loop: 0, + modestbranding: 1, + rel: 0 +} +``` + +
+ +## Callbacks + +Callbacks can be used alongside other [Options](#options). + + + + + + + + + + + + + +
Callback
+ +`afterPosition` + + + +A function to call after the position changed. The first argument is the current position within the group. + +```js +afterPosition: function(position) { + console.log("You've reached position " + position); +} +``` + +
+ +`afterHide` + + + +A function to call after Strip is fully hidden. + +```js +afterHide: function() { + console.log('Strip is no longer visible'); +} +``` + +
+ +`onResize` + + + +This callback allows you to respond to Strip as it's resizing and make adjustments to your page. You could for example slide your page along or adjust margins. The parameters of onResize give you everything needed to make these adjustments. + +```js +onResize: function(fxProperty, fxValue, side) { + console.log(fxProperty, fxValue, side); + // logs: 'width', 0, 'right' when starting the animation + // and adjusts fxValue for each step in the animation +} +``` + +`fxProperty` is the property currently animated, which can be _'width'_ or _'height'_. `fxValue` is the value of that property at the current step in the animation. side is the current side Strip is positioned on, which can be _'top'_, _'bottom'_, _'left'_ or _'right'_. + +
+ +`onShow` + + + +A function to call when Strip comes into view. + +```js +onShow: function() { + console.log('Strip is visible'); +} +``` + +
+ +## Skins + +Custom skins can be added by copying the existing default skin called `strip`, rename it and modify it to your needs. It's recommended to do this in a separate css file so that `strip.css` can be upgraded without losing anything. + +Once you have a skin in place it can be used with the `skin` option: + +``` +skin: 'custom' +``` + +### Default options + +Default options can be provided for a skin by extending `Strip.Skins` with options for your custom skin: + +```js +$.extend(Strip.Skins, { + custom: { + loop: false, + }, +}); +``` + +### Changing the default skin + +The default skin can be changed using `Strip.setDefaultSkin()`. + +```js +Strip.setDefaultSkin("custom"); ``` -* * * +--- -By [Nick Stakenburg](http://www.nickstakenburg.com) +By [Nick Stakenburg](https://github.com/staaky) diff --git a/dist/css/strip.css b/dist/css/strip.css index 25625ad..7382013 100644 --- a/dist/css/strip.css +++ b/dist/css/strip.css @@ -1,21 +1,20 @@ /*! - * Strip - An Unobtrusive Responsive Lightbox - v1.7.0 - * (c) 2014-2019 Nick Stakenburg + * Strip - An Unobtrusive Responsive Lightbox - v1.8.0 + * (c) 2014-2021 Nick Stakenburg * - * http://www.stripjs.com + * https://github.com/staaky/strip * - * Licensing: - * - Commercial: http://www.stripjs.com/license - * - Non-commercial: http://creativecommons.org/licenses/by-nc-nd/3.0 + * @license: https://creativecommons.org/licenses/by/4.0 */ .strp-window { - position: fixed; - width: 0; - height: 100%; + position: fixed; + width: 0; + height: 100%; overflow: hidden; background: #292929; - font: 13px/20px "Lucida Sans", "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, sans-serif; + font: 13px/20px "Lucida Sans", "Lucida Sans Unicode", "Lucida Grande", Verdana, + Arial, sans-serif; } .strp-window.strp-vertical { @@ -26,23 +25,31 @@ /* margin is added around the window to keep a visual reference * to the underlying page at all times. */ -.strp-window { margin-left: 40px; } -.strp-window.strp-vertical { margin-left: 0; margin-bottom: 40px; } +.strp-window { + margin-left: 40px; +} +.strp-window.strp-vertical { + margin-left: 0; + margin-bottom: 40px; +} /* fullscreen on smaller screens (iPhone 6+ and smaller) * since min-width is used to make this work it won't show on IE8, but * we're not expecting that browser to have a mobile sized screen anyway */ @media all and (max-width: 414px) and (orientation: portrait), - all and (max-width: 736px) and (max-height: 414px) { - .strp-window.strp-horizontal { min-width: 100%; } + all and (max-width: 736px) and (max-height: 414px) { + .strp-window.strp-horizontal { + min-width: 100%; + } } @media all and (max-height: 414px) and (orientation: landscape), - all and (max-height: 736px) and (max-width: 414px){ - .strp-window.strp-vertical { min-height: 100%; } + all and (max-height: 736px) and (max-width: 414px) { + .strp-window.strp-vertical { + min-height: 100%; + } } - /* z-index */ .strp-window, .strp-spinner-move { @@ -51,14 +58,16 @@ /* reset box-sizing */ .strp-window, -.strp-window [class^='strp-'], +.strp-window [class^="strp-"], .strp-spinner-move, -.strp-spinner-move [class^='strp-'] { +.strp-spinner-move [class^="strp-"] { box-sizing: border-box; } /* Chrome hack, this fixes a visual glitch when quickly toggling a video */ -.strp-window { transform: translateZ(0px); } +.strp-window { + transform: translateZ(0px); +} /* some properties on the window are used to toggle things * like margin and the fullscreen mode, @@ -78,25 +87,73 @@ } /* sides */ -.strp-side-right { top: 0; right: 0; } -.strp-side-right .strp-pages { top: 0; right: 0; } -.strp-side-right .strp-page { top: 0; right: 0; } -.strp-side-right .strp-close { top: 0; right: 0; } - -.strp-side-left { top: 0; left: 0; } -.strp-side-left .strp-pages { top: 0; left: 0; } -.strp-side-left .strp-page { top: 0; left: 0; } -.strp-side-left .strp-close { top: 0; right: 0; } - -.strp-side-top { top: 0; left: 0; } -.strp-side-top .strp-pages { top: 0; left: 0; } -.strp-side-top .strp-page { top: 0; left: 0; } -.strp-side-top .strp-close { top: 0; right: 0; } - -.strp-side-bottom { bottom: 0; left: 0; } -.strp-side-bottom .strp-pages { bottom: 0; left: 0; } -.strp-side-bottom .strp-page { bottom: 0; left: 0; } -.strp-side-bottom .strp-close { top: 0; right: 0; } +.strp-side-right { + top: 0; + right: 0; +} +.strp-side-right .strp-pages { + top: 0; + right: 0; +} +.strp-side-right .strp-page { + top: 0; + right: 0; +} +.strp-side-right .strp-close { + top: 0; + right: 0; +} + +.strp-side-left { + top: 0; + left: 0; +} +.strp-side-left .strp-pages { + top: 0; + left: 0; +} +.strp-side-left .strp-page { + top: 0; + left: 0; +} +.strp-side-left .strp-close { + top: 0; + right: 0; +} + +.strp-side-top { + top: 0; + left: 0; +} +.strp-side-top .strp-pages { + top: 0; + left: 0; +} +.strp-side-top .strp-page { + top: 0; + left: 0; +} +.strp-side-top .strp-close { + top: 0; + right: 0; +} + +.strp-side-bottom { + bottom: 0; + left: 0; +} +.strp-side-bottom .strp-pages { + bottom: 0; + left: 0; +} +.strp-side-bottom .strp-page { + bottom: 0; + left: 0; +} +.strp-side-bottom .strp-close { + top: 0; + right: 0; +} .strp-page { position: absolute; @@ -124,14 +181,23 @@ navbutton = 72 = 54 + (2 * 9 margin) closebutton = 48 */ -.strp-no-overlap .strp-container { padding: 48px 72px; } -.strp-no-overlap.strp-no-sides .strp-container { padding: 48px 0; } - -.strp-vertical .strp-no-overlap .strp-container { padding: 0 72px; } -.strp-vertical .strp-no-overlap.strp-no-sides .strp-container { padding: 0 48px; } +.strp-no-overlap .strp-container { + padding: 48px 72px; +} +.strp-no-overlap.strp-no-sides .strp-container { + padding: 48px 0; +} -.strp-hovering-clickable .strp-container { cursor: pointer; } +.strp-vertical .strp-no-overlap .strp-container { + padding: 0 72px; +} +.strp-vertical .strp-no-overlap.strp-no-sides .strp-container { + padding: 0 48px; +} +.strp-hovering-clickable .strp-container { + cursor: pointer; +} .strp-content-element { position: absolute; @@ -196,9 +262,9 @@ background: none; text-decoration: underline; } -.strp-info a:hover { color: #eee; } - - +.strp-info a:hover { + color: #eee; +} /* < > */ .strp-nav { @@ -213,10 +279,17 @@ -moz-user-select: none; user-select: none; } -.strp-nav-previous { left: 0; } -.strp-nav-next { right: 0; left: auto; } +.strp-nav-previous { + left: 0; +} +.strp-nav-next { + right: 0; + left: auto; +} -.strp-nav-disabled { cursor: default; } +.strp-nav-disabled { + cursor: default; +} .strp-nav-button { float: left; @@ -243,7 +316,6 @@ background-repeat: no-repeat; } - /* X */ .strp-close { position: absolute; @@ -268,13 +340,16 @@ .strp-close-background { filter: alpha(opacity=80); - opacity: .8; + opacity: 0.8; background-color: #101010; } -.strp-close:hover .strp-close-background { background-color: #161616; } - +.strp-close:hover .strp-close-background { + background-color: #161616; +} -.strp-has-error .strp-container { background-color: #ca3434; } +.strp-has-error .strp-container { + background-color: #ca3434; +} .strp-error { position: absolute; top: 50%; @@ -285,9 +360,11 @@ margin-top: -120px; background-position: 50% 50%; background-repeat: no-repeat; - background-image: url('strip-skins/strip/error.svg'); + background-image: url("strip-skins/strip/error.svg"); +} +.strp-no-svg .strp-error { + background-image: url("strip-skins/strip/error.png"); } -.strp-no-svg .strp-error { background-image: url('strip-skins/strip/error.png'); } /* Spinner - loading icon, wrapped by a div that moves it */ .strp-spinner-move { @@ -299,7 +376,10 @@ /* ensure overflow during a jQuery animation */ overflow: visible !important; } -.strp-spinner-move.strp-vertical { width: 48px; height: 0; } +.strp-spinner-move.strp-vertical { + width: 48px; + height: 0; +} .strp-spinner { width: 48px; @@ -309,18 +389,52 @@ } /* different sides */ -.strp-spinner-move.strp-side-top { top: 0; right: 0; bottom: auto; left: auto; } -.strp-spinner-move.strp-side-bottom { top: auto; right: 0; bottom: 0; left: auto; } - -.strp-spinner-move.strp-side-right .strp-spinner { margin-left: -48px; } -.strp-spinner-move.strp-side-left .strp-spinner { margin-right: -48px; float: right; } -.strp-spinner-move.strp-side-bottom .strp-spinner { margin-top: -48px; } -.strp-spinner-move.strp-side-top .strp-spinner { position: absolute; bottom: 0; right: 0; margin-bottom: -48px; } +.strp-spinner-move.strp-side-top { + top: 0; + right: 0; + bottom: auto; + left: auto; +} +.strp-spinner-move.strp-side-bottom { + top: auto; + right: 0; + bottom: 0; + left: auto; +} +.strp-spinner-move.strp-side-right .strp-spinner { + margin-left: -48px; +} +.strp-spinner-move.strp-side-left .strp-spinner { + margin-right: -48px; + float: right; +} +.strp-spinner-move.strp-side-bottom .strp-spinner { + margin-top: -48px; +} +.strp-spinner-move.strp-side-top .strp-spinner { + position: absolute; + bottom: 0; + right: 0; + margin-bottom: -48px; +} -@-moz-keyframes strp-spinner-spin { 100% { -moz-transform: rotate(360deg); } } -@-webkit-keyframes strp-spinner-spin { 100% { -webkit-transform: rotate(360deg); } } -@keyframes strp-spinner-spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } } +@-moz-keyframes strp-spinner-spin { + 100% { + -moz-transform: rotate(360deg); + } +} +@-webkit-keyframes strp-spinner-spin { + 100% { + -webkit-transform: rotate(360deg); + } +} +@keyframes strp-spinner-spin { + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} .strp-spinner-rotate, .strp-spinner-frame { @@ -343,34 +457,62 @@ color: inherit; /* color of the dots, inherited from text on the page */ } - - /* * ===== Skin: strip ===== */ - /* < > */ -.strp-window-skin-strip .strp-nav-button-background { background-color: transparent; } -.strp-window-skin-strip .strp-nav-previous .strp-nav-button-icon { background-image: url('strip-skins/strip/previous.svg'); } -.strp-window-skin-strip .strp-nav-next .strp-nav-button-icon { background-image: url('strip-skins/strip/next.svg'); } +/* < > */ +.strp-window-skin-strip .strp-nav-button-background { + background-color: transparent; +} +.strp-window-skin-strip .strp-nav-previous .strp-nav-button-icon { + background-image: url("strip-skins/strip/previous.svg"); +} +.strp-window-skin-strip .strp-nav-next .strp-nav-button-icon { + background-image: url("strip-skins/strip/next.svg"); +} /* IE7-8/no-svg (using a faded png) */ -.strp-window-skin-strip.strp-no-svg .strp-nav-previous .strp-nav-button-icon { background-image: url('strip-skins/strip/previous-faded.png'); opacity: 1; } -.strp-window-skin-strip.strp-no-svg .strp-nav-next .strp-nav-button-icon { background-image: url('strip-skins/strip/next-faded.png'); opacity: 1; } +.strp-window-skin-strip.strp-no-svg .strp-nav-previous .strp-nav-button-icon { + background-image: url("strip-skins/strip/previous-faded.png"); + opacity: 1; +} +.strp-window-skin-strip.strp-no-svg .strp-nav-next .strp-nav-button-icon { + background-image: url("strip-skins/strip/next-faded.png"); + opacity: 1; +} -.strp-window-skin-strip .strp-nav .strp-nav-button-icon { opacity: .6; } /* normal state */ -.strp-window-skin-strip.strp-mobile-touch .strp-nav .strp-nav-button-icon { opacity: 1; } /* mobile-touch always has normal states at full opacity */ +.strp-window-skin-strip .strp-nav .strp-nav-button-icon { + opacity: 0.6; +} /* normal state */ +.strp-window-skin-strip.strp-mobile-touch .strp-nav .strp-nav-button-icon { + opacity: 1; +} /* mobile-touch always has normal states at full opacity */ /* < > : hover */ .strp-window-skin-strip .strp-nav:hover .strp-nav-button-icon, -.strp-window-skin-strip .strp-nav-hover .strp-nav-button-icon { opacity: 1; } +.strp-window-skin-strip .strp-nav-hover .strp-nav-button-icon { + opacity: 1; +} /* IE7-8/no-svg (unfaded png) */ -.strp-window-skin-strip.strp-no-svg .strp-nav.strp-nav-previous:hover .strp-nav-button-icon, -.strp-window-skin-strip.strp-no-svg .strp-nav-hover.strp-nav-previous .strp-nav-button-icon { background-image: url('strip-skins/strip/previous.png'); } -.strp-window-skin-strip.strp-no-svg .strp-nav.strp-nav-next:hover .strp-nav-button-icon, -.strp-window-skin-strip.strp-no-svg .strp-nav-hover.strp-nav-next .strp-nav-button-icon { background-image: url('strip-skins/strip/next.png'); } +.strp-window-skin-strip.strp-no-svg + .strp-nav.strp-nav-previous:hover + .strp-nav-button-icon, +.strp-window-skin-strip.strp-no-svg + .strp-nav-hover.strp-nav-previous + .strp-nav-button-icon { + background-image: url("strip-skins/strip/previous.png"); +} +.strp-window-skin-strip.strp-no-svg + .strp-nav.strp-nav-next:hover + .strp-nav-button-icon, +.strp-window-skin-strip.strp-no-svg + .strp-nav-hover.strp-nav-next + .strp-nav-button-icon { + background-image: url("strip-skins/strip/next.png"); +} /* Reduce < > button size on narrow screens (iPhone 6 and smaller) */ @media all and (max-width: 375px) and (orientation: portrait), - all and (max-height: 375px) and (max-width: 667px) { + all and (max-height: 375px) and (max-width: 667px) { .strp-window-skin-strip .strp-nav { width: 48px; height: 60px; @@ -379,41 +521,78 @@ } /* < > */ - .strp-window-skin-strip .strp-nav-previous .strp-nav-button-icon { background-image: url('strip-skins/strip/previous-small.svg'); } - .strp-window-skin-strip .strp-nav-next .strp-nav-button-icon { background-image: url('strip-skins/strip/next-small.svg'); } + .strp-window-skin-strip .strp-nav-previous .strp-nav-button-icon { + background-image: url("strip-skins/strip/previous-small.svg"); + } + .strp-window-skin-strip .strp-nav-next .strp-nav-button-icon { + background-image: url("strip-skins/strip/next-small.svg"); + } /* IE7-8/no-svg (using a faded png) */ - .strp-window-skin-strip.strp-no-svg .strp-nav-previous .strp-nav-button-icon { background-image: url('strip-skins/strip/previous-small-faded.png'); } - .strp-window-skin-strip.strp-no-svg .strp-nav-next .strp-nav-button-icon { background-image: url('strip-skins/strip/next-small-faded.png'); } + .strp-window-skin-strip.strp-no-svg .strp-nav-previous .strp-nav-button-icon { + background-image: url("strip-skins/strip/previous-small-faded.png"); + } + .strp-window-skin-strip.strp-no-svg .strp-nav-next .strp-nav-button-icon { + background-image: url("strip-skins/strip/next-small-faded.png"); + } /* IE7-8/no-svg (unfaded png) */ - .strp-window-skin-strip.strp-no-svg .strp-nav.strp-nav-previous:hover .strp-nav-button-icon, - .strp-window-skin-strip.strp-no-svg .strp-nav-hover.strp-nav-previous .strp-nav-button-icon { background-image: url('strip-skins/strip/previous-small.png'); } - .strp-window-skin-strip.strp-no-svg .strp-nav.strp-nav-next:hover .strp-nav-button-icon, - .strp-window-skin-strip.strp-no-svg .strp-nav-hover.strp-nav-next .strp-nav-button-icon { background-image: url('strip-skins/strip/next-small.png'); } + .strp-window-skin-strip.strp-no-svg + .strp-nav.strp-nav-previous:hover + .strp-nav-button-icon, + .strp-window-skin-strip.strp-no-svg + .strp-nav-hover.strp-nav-previous + .strp-nav-button-icon { + background-image: url("strip-skins/strip/previous-small.png"); + } + .strp-window-skin-strip.strp-no-svg + .strp-nav.strp-nav-next:hover + .strp-nav-button-icon, + .strp-window-skin-strip.strp-no-svg + .strp-nav-hover.strp-nav-next + .strp-nav-button-icon { + background-image: url("strip-skins/strip/next-small.png"); + } /* also reduce padding navbutton = 58 = 48 + (2 * 5 margin) closebutton = 48 */ - .strp-no-overlap .strp-container { padding: 48px 58px; } - .strp-no-overlap.strp-no-sides .strp-container { padding: 48px 0; } + .strp-no-overlap .strp-container { + padding: 48px 58px; + } + .strp-no-overlap.strp-no-sides .strp-container { + padding: 48px 0; + } - .strp-vertical .strp-no-overlap .strp-container { padding: 0 58px; } - .strp-vertical .strp-no-overlap.strp-no-sides .strp-container { padding: 0 48px; } + .strp-vertical .strp-no-overlap .strp-container { + padding: 0 58px; + } + .strp-vertical .strp-no-overlap.strp-no-sides .strp-container { + padding: 0 48px; + } } /* X */ -.strp-window-skin-strip .strp-close .strp-close-icon { background-image: url('strip-skins/strip/close.svg'); opacity: .8; } -.strp-window-skin-strip .strp-close:hover .strp-close-icon { opacity: 1; } +.strp-window-skin-strip .strp-close .strp-close-icon { + background-image: url("strip-skins/strip/close.svg"); + opacity: 0.8; +} +.strp-window-skin-strip .strp-close:hover .strp-close-icon { + opacity: 1; +} /* iOS 8.4.1 bug: when opacity changes it'll require 2 taps force a single opacity to fix this */ .strp-window-skin-strip.strp-mobile-touch .strp-close .strp-close-icon, -.strp-window-skin-strip.strp-mobile-touch .strp-close:hover .strp-close-icon { opacity: 1; } +.strp-window-skin-strip.strp-mobile-touch .strp-close:hover .strp-close-icon { + opacity: 1; +} /* IE7-8/no-svg */ -.strp-window-skin-strip.strp-no-svg .strp-close .strp-close-icon { background-image: url('strip-skins/strip/close.png'); opacity: 1; } - +.strp-window-skin-strip.strp-no-svg .strp-close .strp-close-icon { + background-image: url("strip-skins/strip/close.png"); + opacity: 1; +} /* here's how to have content slide in with the window when opening and closing */ /* diff --git a/dist/js/strip.pkgd.js b/dist/js/strip.pkgd.js index 2d15587..f600f48 100644 --- a/dist/js/strip.pkgd.js +++ b/dist/js/strip.pkgd.js @@ -1,561 +1,582 @@ /*! - * Strip - An Unobtrusive Responsive Lightbox - v1.7.0 - * (c) 2014-2019 Nick Stakenburg + * Strip - An Unobtrusive Responsive Lightbox - v1.8.0 + * (c) 2014-2021 Nick Stakenburg * - * http://www.stripjs.com + * https://github.com/staaky/strip * * @license: https://creativecommons.org/licenses/by/4.0 */ // UMD wrapper (function(root, factory) { - if (typeof define === "function" && define.amd) { + if (typeof define === 'function' && define.amd) { // AMD - define(["jquery"], factory); - } else if (typeof module === "object" && module.exports) { + define(['jquery'], factory); + } else if (typeof module === 'object' && module.exports) { // Node/CommonJS - module.exports = factory(require("jquery")); + module.exports = factory(require('jquery')); } else { // Browser global root.Strip = factory(jQuery); } -})(this, function($) { - var Strip = { - version: "1.7.0" - }; - - Strip.Skins = { - // the default skin - strip: {} - }; - - var Browser = (function(uA) { - function getVersion(identifier) { - var version = new RegExp(identifier + "([\\d.]+)").exec(uA); - return version ? parseFloat(version[1]) : true; - } +}(this, function($) { - 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 Strip = { + version: "1.8.0", +}; - var _slice = Array.prototype.slice; +Strip.Skins = { + // the default skin + strip: {}, +}; - // Fit - var Fit = { - within: function(bounds, dimensions) { - var options = $.extend( - { - height: true, - width: true - }, - arguments[2] || {} - ); +var Browser = (function (uA) { + function getVersion(identifier) { + var version = new RegExp(identifier + "([\\d.]+)").exec(uA); + return version ? parseFloat(version[1]) : true; + } - var size = $.extend({}, dimensions), - scale = 1, - attempts = 5; + 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 fit = { width: options.width, height: options.height }; +var _slice = Array.prototype.slice; - // 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; +// Fit +var Fit = { + within: function (bounds, dimensions) { + var options = $.extend( + { + height: true, + width: true, + }, + arguments[2] || {} + ); - if (fit.width && size.width > bounds.width) { - scaleX = bounds.width / size.width; - } - if (fit.height && size.height > bounds.height) { - scaleY = bounds.height / size.height; - } + var size = $.extend({}, dimensions), + scale = 1, + attempts = 5; - // we'll end up using the largest scaled down factor - var scale = Math.min(scaleX, scaleY); + var fit = { width: options.width, height: options.height }; - // adjust current size, based on original dimensions - size = { - width: Math.round(dimensions.width * scale), - height: Math.round(dimensions.height * scale) - }; - //} + // 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; - attempts--; + 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 + var scale = Math.min(scaleX, scaleY); - // make sure size is never pressed into negative - size.width = Math.max(size.width, 0); - size.height = Math.max(size.height, 0); + // adjust current size, based on original dimensions + size = { + width: Math.round(dimensions.width * scale), + height: Math.round(dimensions.height * scale), + }; + //} - return size; + attempts--; } - }; - // we only uses some of the jQueryUI easing functions - // add those with a prefix to prevent conflicts - $.extend($.easing, { - stripEaseInCubic: function(x, t, b, c, d) { - return c * (t /= d) * t * t + b; - }, + // make sure size is never pressed into negative + size.width = Math.max(size.width, 0); + size.height = Math.max(size.height, 0); - stripEaseInSine: function(x, t, b, c, d) { - return -c * Math.cos((t / d) * (Math.PI / 2)) + c + b; - }, + return size; + }, +}; - stripEaseOutSine: function(x, t, b, c, d) { - return c * Math.sin((t / d) * (Math.PI / 2)) + b; - } - }); +// we only uses some of the jQueryUI easing functions +// add those with a prefix to prevent conflicts +$.extend($.easing, { + stripEaseInCubic: function (x, t, b, c, d) { + return c * (t /= d) * t * t + b; + }, - var Support = (function() { - var testElement = document.createElement("div"), - domPrefixes = "Webkit Moz O ms Khtml".split(" "); + stripEaseInSine: function (x, t, b, c, d) { + return -c * Math.cos((t / d) * (Math.PI / 2)) + c + b; + }, - function prefixed(property) { - return testAllProperties(property, "prefix"); - } + stripEaseOutSine: function (x, t, b, c, d) { + return c * Math.sin((t / d) * (Math.PI / 2)) + b; + }, +}); - function testProperties(properties, prefixed) { - for (var i in properties) { - if (testElement.style[properties[i]] !== undefined) { - return prefixed == "prefix" ? properties[i] : true; - } - } - return false; - } +var Support = (function () { + var testElement = document.createElement("div"), + domPrefixes = "Webkit Moz O ms Khtml".split(" "); - function testAllProperties(property, prefixed) { - var ucProperty = property.charAt(0).toUpperCase() + property.substr(1), - properties = ( - property + - " " + - domPrefixes.join(ucProperty + " ") + - ucProperty - ).split(" "); + function prefixed(property) { + return testAllProperties(property, "prefix"); + } - return testProperties(properties, prefixed); + function testProperties(properties, prefixed) { + for (var i in properties) { + if (testElement.style[properties[i]] !== undefined) { + return prefixed == "prefix" ? properties[i] : true; + } } + return false; + } - // feature detect - return { - css: { - animation: testAllProperties("animation"), - transform: testAllProperties("transform"), - prefixed: prefixed - }, + function testAllProperties(property, prefixed) { + var ucProperty = property.charAt(0).toUpperCase() + property.substr(1), + properties = ( + property + + " " + + domPrefixes.join(ucProperty + " ") + + ucProperty + ).split(" "); - 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.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 - - var Bounds = { - viewport: function() { - var dimensions = { - width: $(window).width() - }; + return testProperties(properties, prefixed); + } - // 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(); + // feature detect + return { + 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.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 + +var Bounds = { + viewport: function () { + var dimensions = { + width: $(window).width(), + }; - return dimensions; + // 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; + }, +}; + +/* ImageReady (standalone) - part of VoilĂ  + * http://voila.nickstakenburg.com + * MIT License + */ +var ImageReady = (function ($) { + var Poll = function () { + return this.initialize.apply(this, Array.prototype.slice.call(arguments)); }; + $.extend(Poll.prototype, { + initialize: function () { + this.options = $.extend( + { + test: function () {}, + success: function () {}, + timeout: function () {}, + callAt: false, + intervals: [ + [0, 0], + [1 * 1000, 10], + [2 * 1000, 50], + [4 * 1000, 100], + [20 * 1000, 500], + ], + }, + arguments[0] || {} + ); - /* ImageReady (standalone) - part of VoilĂ  - * http://voila.nickstakenburg.com - * MIT License - */ - var ImageReady = (function($) { - var Poll = function() { - return this.initialize.apply(this, Array.prototype.slice.call(arguments)); - }; - $.extend(Poll.prototype, { - initialize: function() { - this.options = $.extend( - { - test: function() {}, - success: function() {}, - timeout: function() {}, - callAt: false, - intervals: [ - [0, 0], - [1 * 1000, 10], - [2 * 1000, 50], - [4 * 1000, 100], - [20 * 1000, 500] - ] - }, - arguments[0] || {} - ); + this._test = this.options.test; + this._success = this.options.success; + this._timeout = this.options.timeout; - this._test = this.options.test; - this._success = this.options.success; - this._timeout = this.options.timeout; + this._ipos = 0; + this._time = 0; + this._delay = this.options.intervals[this._ipos][1]; + this._callTimeouts = []; - this._ipos = 0; - this._time = 0; - this._delay = this.options.intervals[this._ipos][1]; - this._callTimeouts = []; + this.poll(); + this._createCallsAt(); + }, - this.poll(); - this._createCallsAt(); - }, + poll: function () { + this._polling = setTimeout( + function () { + if (this._test()) { + this.success(); + return; + } + + // update time + this._time += this._delay; - poll: function() { - this._polling = setTimeout( - $.proxy(function() { - if (this._test()) { - this.success(); + // next i within the interval + if (this._time >= this.options.intervals[this._ipos][0]) { + // timeout when no next interval + if (!this.options.intervals[this._ipos + 1]) { + if (typeof this._timeout === "function") { + this._timeout(); + } return; } - // update time - this._time += this._delay; + this._ipos++; - // next i within the interval - if (this._time >= this.options.intervals[this._ipos][0]) { - // timeout when no next interval - if (!this.options.intervals[this._ipos + 1]) { - if ($.type(this._timeout) == "function") { - this._timeout(); - } - return; - } + // update to the new bracket + this._delay = this.options.intervals[this._ipos][1]; + } - this._ipos++; + this.poll(); + }.bind(this), + this._delay + ); + }, - // update to the new bracket - this._delay = this.options.intervals[this._ipos][1]; - } + success: function () { + this.abort(); + this._success(); + }, - this.poll(); - }, this), - this._delay - ); - }, + _createCallsAt: function () { + if (!this.options.callAt) return; - success: function() { - this.abort(); - this._success(); - }, + // start a timer for each call + $.each( + this.options.callAt, + function (i, at) { + var time = at[0], + fn = at[1]; + + var timeout = setTimeout( + function () { + fn(); + }.bind(this), + time + ); - _createCallsAt: function() { - if (!this.options.callAt) return; + this._callTimeouts.push(timeout); + }.bind(this) + ); + }, - // start a timer for each call - $.each( - this.options.callAt, - $.proxy(function(i, at) { - var time = at[0], - fn = at[1]; - - var timeout = setTimeout( - $.proxy(function() { - fn(); - }, this), - time - ); + _stopCallTimeouts: function () { + $.each(this._callTimeouts, function (i, timeout) { + clearTimeout(timeout); + }); + this._callTimeouts = []; + }, - this._callTimeouts.push(timeout); - }, this) - ); - }, + abort: function () { + this._stopCallTimeouts(); - _stopCallTimeouts: function() { - $.each(this._callTimeouts, function(i, timeout) { - clearTimeout(timeout); - }); - this._callTimeouts = []; - }, + if (this._polling) { + clearTimeout(this._polling); + this._polling = null; + } + }, + }); - abort: function() { - this._stopCallTimeouts(); + var ImageReady = function () { + return this.initialize.apply(this, Array.prototype.slice.call(arguments)); + }; + $.extend(ImageReady.prototype, { + supports: { + naturalWidth: (function () { + return "naturalWidth" in new Image(); + })(), + }, - if (this._polling) { - clearTimeout(this._polling); - this._polling = null; - } + // 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: "onload", + pollFallbackAfter: 1000, + }, + arguments[3] || {} + ); + + // onload and a fallback for no naturalWidth support (IE6-7) + if (this.options.method == "onload" || !this.supports.naturalWidth) { + this.load(); + return; } - }); - var ImageReady = function() { - return this.initialize.apply(this, Array.prototype.slice.call(arguments)); - }; - $.extend(ImageReady.prototype, { - supports: { - naturalWidth: (function() { - return "naturalWidth" in new Image(); - })() - }, + // start polling + this.poll(); + }, + + // NOTE: Polling for naturalWidth is only reliable if the + // .src never changes. naturalWidth isn't always reset + // to 0 after the src changes (depending on how the spec + // was implemented). The spec even seems to be against + // this, making polling unreliable in those cases. + poll: function () { + this._poll = new Poll({ + test: function () { + return this.img.naturalWidth > 0; + }.bind(this), + + success: function () { + this.success(); + }.bind(this), + + timeout: function () { + // error on timeout + this.error(); + }.bind(this), + + callAt: [ + [ + this.options.pollFallbackAfter, + function () { + this.load(); + }.bind(this), + ], + ], + }); + }, - // 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: "onload", - pollFallbackAfter: 1000 - }, - arguments[3] || {} - ); + load: function () { + this._loading = setTimeout( + function () { + var image = new Image(); + this._onloadImage = image; - // onload and a fallback for no naturalWidth support (IE6-7) - if (this.options.method == "onload" || !this.supports.naturalWidth) { - this.load(); - return; - } + image.onload = function () { + image.onload = function () {}; - // start polling - this.poll(); - }, + if (!this.supports.naturalWidth) { + this.img.naturalWidth = image.width; + this.img.naturalHeight = image.height; + image.naturalWidth = image.width; + image.naturalHeight = image.height; + } - // NOTE: Polling for naturalWidth is only reliable if the - // .src never changes. naturalWidth isn't always reset - // to 0 after the src changes (depending on how the spec - // was implemented). The spec even seems to be against - // this, making polling unreliable in those cases. - poll: function() { - this._poll = new Poll({ - test: $.proxy(function() { - return this.img.naturalWidth > 0; - }, this), - - success: $.proxy(function() { this.success(); - }, this), - - timeout: $.proxy(function() { - // error on timeout - this.error(); - }, this), - - callAt: [ - [ - this.options.pollFallbackAfter, - $.proxy(function() { - this.load(); - }, this) - ] - ] - }); - }, + }.bind(this); - load: function() { - this._loading = setTimeout( - $.proxy(function() { - var image = new Image(); - this._onloadImage = image; + image.onerror = this.error.bind(this); - image.onload = $.proxy(function() { - image.onload = function() {}; + image.src = this.img.src; + }.bind(this) + ); + }, - if (!this.supports.naturalWidth) { - this.img.naturalWidth = image.width; - this.img.naturalHeight = image.height; - image.naturalWidth = image.width; - image.naturalHeight = image.height; - } + success: function () { + if (this._calledSuccess) return; - this.success(); - }, this); + this._calledSuccess = true; - image.onerror = $.proxy(this.error, this); + // stop loading/polling + this.abort(); - image.src = this.img.src; - }, this) - ); - }, + // some time to allow layout updates, IE requires this! + this.waitForRender( + function () { + this.isLoaded = true; + this.successCallback(this); + }.bind(this) + ); + }, - success: function() { - if (this._calledSuccess) return; + error: function () { + if (this._calledError) return; - this._calledSuccess = true; + this._calledError = true; - // stop loading/polling - this.abort(); + // stop loading/polling + this.abort(); - // some time to allow layout updates, IE requires this! - this.waitForRender( - $.proxy(function() { - this.isLoaded = true; - this.successCallback(this); - }, this) - ); - }, + // don't wait for an actual render on error, just timeout + // to give the browser some time to render a broken image icon + this._errorRenderTimeout = setTimeout( + function () { + if (this.errorCallback) this.errorCallback(this); + }.bind(this) + ); + }, - error: function() { - if (this._calledError) return; + abort: function () { + this.stopLoading(); + this.stopPolling(); + this.stopWaitingForRender(); + }, - this._calledError = true; + stopPolling: function () { + if (this._poll) { + this._poll.abort(); + this._poll = null; + } + }, - // stop loading/polling - this.abort(); + stopLoading: function () { + if (this._loading) { + clearTimeout(this._loading); + this._loading = null; + } - // don't wait for an actual render on error, just timeout - // to give the browser some time to render a broken image icon - this._errorRenderTimeout = setTimeout( - $.proxy(function() { - if (this.errorCallback) this.errorCallback(this); - }, this) - ); - }, + if (this._onloadImage) { + this._onloadImage.onload = function () {}; + this._onloadImage.onerror = function () {}; + } + }, - abort: function() { - this.stopLoading(); - this.stopPolling(); - this.stopWaitingForRender(); - }, + // used by success() only + waitForRender: function (callback) { + this._renderTimeout = setTimeout(callback); + }, - stopPolling: function() { - if (this._poll) { - this._poll.abort(); - this._poll = null; - } - }, + stopWaitingForRender: function () { + if (this._renderTimeout) { + clearTimeout(this._renderTimeout); + this._renderTimeout = null; + } - stopLoading: function() { - if (this._loading) { - clearTimeout(this._loading); - this._loading = null; - } + if (this._errorRenderTimeout) { + clearTimeout(this._errorRenderTimeout); + this._errorRenderTimeout = null; + } + }, + }); - if (this._onloadImage) { - this._onloadImage.onload = function() {}; - this._onloadImage.onerror = function() {}; - } - }, + return ImageReady; +})(jQuery); - // used by success() only - waitForRender: function(callback) { - this._renderTimeout = setTimeout(callback); - }, +// Spinner +// Create pure CSS based spinners +function Spinner() { + return this.initialize.apply(this, _slice.call(arguments)); +} - stopWaitingForRender: function() { - if (this._renderTimeout) { - clearTimeout(this._renderTimeout); - this._renderTimeout = null; - } +// mark as supported +Spinner.supported = Support.css.transform && Support.css.animation; - if (this._errorRenderTimeout) { - clearTimeout(this._errorRenderTimeout); - this._errorRenderTimeout = null; - } - } - }); +$.extend(Spinner.prototype, { + initialize: function (element) { + this.element = $(element); + if (!this.element[0]) return; - return ImageReady; - })(jQuery); + this.classPrefix = "strp-"; - // Spinner - // Create pure CSS based spinners - function Spinner() { - return this.initialize.apply(this, _slice.call(arguments)); - } + this.setOptions(arguments[1] || {}); - // mark as supported - Spinner.supported = Support.css.transform && Support.css.animation; + this.element.addClass(this.classPrefix + "spinner"); + this.element.append( + (this._rotate = $("
").addClass(this.classPrefix + "spinner-rotate")) + ); - $.extend(Spinner.prototype, { - initialize: function(element) { - this.element = $(element); - if (!this.element[0]) return; + this.build(); + this.start(); + }, - this.classPrefix = "strp-"; + setOptions: function (options) { + this.options = $.extend( + { + show: 200, + hide: 200, + }, + options || {} + ); + }, - this.setOptions(arguments[1] || {}); + build: function () { + if (this._build) return; - this.element.addClass(this.classPrefix + "spinner"); - this.element.append( - (this._rotate = $("
").addClass( - this.classPrefix + "spinner-rotate" - )) - ); + this._rotate.html(""); - this.build(); - this.start(); - }, + var d = (this.options.length + this.options.radius) * 2, + dimensions = { height: d, width: d }; - setOptions: function(options) { - this.options = $.extend( - { - show: 200, - hide: 200 - }, - options || {} - ); - }, + // we parse stuff below so make sure that happens with a visible spinner + var is_vis = this.element.is(":visible"); + if (!is_vis) this.element.show(); - build: function() { - if (this._build) return; + // find the amount of lines + var frame, line; + this._rotate.append( + (frame = $("
") + .addClass(this.classPrefix + "spinner-frame") + .append( + (line = $("
").addClass(this.classPrefix + "spinner-line")) + )) + ); - this._rotate.html(""); + var lines = parseInt($(line).css("z-index")); + this.lines = lines; + // now reset that z-index + line.css({ "z-index": "inherit" }); - var d = (this.options.length + this.options.radius) * 2, - dimensions = { height: d, width: d }; + frame.remove(); - // we parse stuff below so make sure that happens with a visible spinner - var is_vis = this.element.is(":visible"); - if (!is_vis) this.element.show(); + // reset visibility + if (!is_vis) this.element.hide(); - // find the amount of lines + // insert frames + var color; + for (var i = 0; i < lines; i++) { var frame, line; this._rotate.append( (frame = $("
") @@ -565,2656 +586,2618 @@ )) ); - var lines = parseInt($(line).css("z-index")); - this.lines = lines; - // now reset that z-index - line.css({ "z-index": "inherit" }); + color = color || line.css("color"); + line.css({ background: color }); - frame.remove(); + frame.css({ opacity: ((1 / lines) * (i + 1)).toFixed(2) }); - // reset visibility - if (!is_vis) this.element.hide(); - - // insert frames - var color; - for (var i = 0; i < lines; i++) { - var frame, line; - this._rotate.append( - (frame = $("
") - .addClass(this.classPrefix + "spinner-frame") - .append( - (line = $("
").addClass(this.classPrefix + "spinner-line")) - )) - ); - - color = color || line.css("color"); - line.css({ background: color }); + var transformCSS = {}; + transformCSS[Support.css.prefixed("transform")] = + "rotate(" + (360 / lines) * (i + 1) + "deg)"; + frame.css(transformCSS); + } - frame.css({ opacity: ((1 / lines) * (i + 1)).toFixed(2) }); + this._build = true; + }, + + start: function () { + var rotateCSS = {}; + rotateCSS[Support.css.prefixed("animation")] = + this.classPrefix + "spinner-spin 1s infinite steps(" + this.lines + ")"; + this._rotate.css(rotateCSS); + }, + + stop: function () { + var rotateCSS = {}; + rotateCSS[Support.css.prefixed("animation")] = "none"; + this._rotate.css(rotateCSS); + }, + + show: function (callback) { + this.build(); + this.start(); + + this.element.stop(true).fadeTo(this.options.show, 1, callback); //deferred.resolve); + }, + + hide: function (callback) { + this.element.stop(true).fadeOut( + this.options.hide, + function () { + this.stop(); + if (callback) callback(); + }.bind(this) + ); + }, + + refresh: function () { + this._build = false; + this.build(); + }, +}); - var transformCSS = {}; - transformCSS[Support.css.prefixed("transform")] = - "rotate(" + (360 / lines) * (i + 1) + "deg)"; - frame.css(transformCSS); +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]; } - - this._build = true; - }, - - start: function() { - var rotateCSS = {}; - rotateCSS[Support.css.prefixed("animation")] = - this.classPrefix + "spinner-spin 1s infinite steps(" + this.lines + ")"; - this._rotate.css(rotateCSS); - }, - - stop: function() { - var rotateCSS = {}; - rotateCSS[Support.css.prefixed("animation")] = "none"; - this._rotate.css(rotateCSS); - }, - - show: function(callback) { - this.build(); - this.start(); - - this.element.stop(true).fadeTo(this.options.show, 1, callback); //deferred.resolve); - }, - - hide: function(callback) { - this.element.stop(true).fadeOut( - this.options.hide, - $.proxy(function() { - this.stop(); - if (callback) callback(); - }, this) - ); - }, - - refresh: function() { - this._build = false; - this.build(); + } else { + this.clearAll(); } - }); - - 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 = {}; + }, +}); - 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; } }); - // 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; - } + return result; +} - function detectExtension(url) { - var ext = (url || "").replace(/\?.*/g, "").match(/\.([^.]{3,4})$/); - return ext ? ext[1].toLowerCase() : null; - } +function detectExtension(url) { + var ext = (url || "").replace(/\?.*/g, "").match(/\.([^.]{3,4})$/); + return ext ? ext[1].toLowerCase() : null; +} - 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; +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) - }; - } + return { + extension: detectExtension(url), + }; }, + }, - youtube: { - detect: function(url) { - var res = /(youtube\.com|youtu\.be)\/watch\?(?=.*vi?=([a-zA-Z0-9-_]+))(?:\S+)?$/.exec( + 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]; + if (res && res[2]) return res[2]; - res = /(youtube\.com|youtu\.be)\/(vi?\/|u\/|embed\/)?([a-zA-Z0-9-_]+)(?:\S+)?$/i.exec( + res = + /(youtube\.com|youtu\.be)\/(vi?\/|u\/|embed\/)?([a-zA-Z0-9-_]+)(?:\S+)?$/i.exec( url ); - if (res && res[3]) return res[3]; + if (res && res[3]) return res[3]; - return false; - }, - data: function(url) { - var id = this.detect(url); - if (!id) return false; + return false; + }, + data: function (url) { + var id = this.detect(url); + if (!id) return false; - return { - id: id - }; - } + return { + id: id, + }; }, + }, - vimeo: { - detect: function(url) { - var res = /(vimeo\.com)\/([a-zA-Z0-9-_]+)(?:\S+)?$/i.exec(url); - if (res && res[2]) return res[2]; + 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 false; + }, + data: function (url) { + var id = this.detect(url); + if (!id) return false; - return { - id: id - }; - } - } - }; + return { + id: id, + }; + }, + }, +}; - 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; +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(); - }, + this.load(); + }, - load: function() { - // first try the cache - var cache = Cache.get(this.url); + load: function () { + // first try the cache + var cache = Cache.get(this.url); - if (cache) { - return this.callback(cache.data); - } + if (cache) { + return this.callback(cache.data); + } - var protocol = - "http" + - (window.location && window.location.protocol == "https:" - ? "s" - : "") + - ":", - video_id = getURIData(this.url).id; + var protocol = + "http" + + (window.location && window.location.protocol == "https:" ? "s" : "") + + ":", + video_id = getURIData(this.url).id; - this._xhr = $.getJSON( + this._xhr = $.getJSON( + protocol + + "//vimeo.com/api/oembed.json?url=" + 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) - ); - }, + "//vimeo.com/" + + video_id + + "&maxwidth=9999999&maxheight=9999999&callback=?", + function (_data) { + var data = { + dimensions: { + width: _data.width, + height: _data.height, + }, + }; + + Cache.set(this.url, data); + + if (this.callback) this.callback(data); + }.bind(this) + ); + }, - abort: function() { - if (this._xhr) { - this._xhr.abort(); - this._xhr = null; - } + abort: function () { + if (this._xhr) { + this._xhr.abort(); + this._xhr = null; } - }); + }, + }); - var Cache = { - cache: [], + 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; - }, + 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 }); - }, + 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]; - } + 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: { - spinner: { show: 200, hide: 200 }, - transition: { min: 175, max: 250 }, - ui: { show: 0, hide: 200 }, - window: { show: 300, hide: 300 } - }, - hideOnClickOutside: true, - keyboard: { - left: true, - right: true, - esc: true - }, - loop: true, - overlap: true, - preload: [1, 2], - position: true, - skin: "strip", - side: "right", - spinner: true, - toggle: true, - uiDelay: 3000, + return VimeoReady; +})(); + +var Options = { + defaults: { + effects: { + spinner: { show: 200, hide: 200 }, + transition: { min: 175, max: 250 }, + ui: { show: 0, hide: 200 }, + window: { show: 300, hide: 300 }, + }, + hideOnClickOutside: true, + keyboard: { + left: true, + right: true, + esc: true, + }, + loop: true, + overlap: true, + preload: [1, 2], + position: true, + skin: "strip", + side: "right", + spinner: true, + toggle: true, + uiDelay: 3000, + vimeo: { + autoplay: 1, + api: 1, + title: 1, + byline: 1, + portrait: 0, + loop: 0, + }, + youtube: { + autoplay: 1, + controls: 1, + 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: { - autoplay: 1, - api: 1, - title: 1, - byline: 1, - portrait: 0, - loop: 0 + 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: { - autoplay: 1, - controls: 1, - enablejsapi: 1, - hd: 1, - iv_load_policy: 3, - loop: 0, - modestbranding: 1, - rel: 0, - vq: "hd1080" // force hd: http://stackoverflow.com/a/12467865 + width: 1280, + height: 720, }, - - 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 || {}; + create: function (opts, type, data) { + opts = opts || {}; + data = data || {}; - opts.skin = opts.skin || this.defaults.skin; + opts.skin = opts.skin || this.defaults.skin; - var selected = opts.skin - ? $.extend( - {}, - Strip.Skins[opts.skin] || Strip.Skins[this.defaults.skin] - ) - : {}, - merged = $.extend(true, {}, this.defaults, selected); + var selected = opts.skin + ? $.extend( + {}, + Strip.Skins[opts.skin] || Strip.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; + // 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); + // safe options to work with + var options = $.extend(true, {}, merged, opts); - // 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 - ) { + // 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 the spinner when effects are disabled + options.spinner = false; + } - // 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 }); - } + // keyboard + if (options.keyboard) { + // when keyboard is true, enable all keys + if (typeof options.keyboard === "boolean") { + options.keyboard = {}; + $.each(this.defaults.keyboard, function (key, bool) { + options.keyboard[key] = true; + }); } - // vimeo & youtube always have no overlap + // disable left and right keys for video, because players like + // youtube use these keys if (type === "vimeo" || type === "youtube") { - options.overlap = false; + $.extend(options.keyboard, { left: false, right: false }); } - - return options; } - }; - - function View() { - this.initialize.apply(this, _slice.call(arguments)); - } - $.extend(View.prototype, { - initialize: function(object) { - var options = arguments[1] || {}, - data = {}; - - // string -> element - if ($.type(object) === "string") { - // turn the string into an element - object = { url: object }; - } - // element -> object - else if (object && object.nodeType === 1) { - var element = $(object); - - object = { - element: element[0], - url: element.attr("href"), - caption: element.attr("data-strip-caption"), - group: element.attr("data-strip-group"), - extension: element.attr("data-strip-extension"), - type: element.attr("data-strip-type"), - options: - (element.attr("data-strip-options") && - eval("({" + element.attr("data-strip-options") + "})")) || - {} - }; - } + // vimeo & youtube always have no overlap + if (type === "vimeo" || type === "youtube") { + options.overlap = false; + } - if (object) { - // detect type if none is set - if (!object.extension) { - object.extension = detectExtension(object.url); - } + return options; + }, +}; + +function View() { + this.initialize.apply(this, _slice.call(arguments)); +} +$.extend(View.prototype, { + initialize: function (object) { + var options = arguments[1] || {}, + data = {}; + + // string -> element + if (typeof object === "string") { + // turn the string into an element + object = { url: object }; + } - if (!object.type) { - var data = getURIData(object.url); - object._data = data; - object.type = data.type; - } - } + // element -> object + else if (object && object.nodeType === 1) { + var element = $(object); + + object = { + element: element[0], + url: element.attr("href"), + caption: element.attr("data-strip-caption"), + group: element.attr("data-strip-group"), + extension: element.attr("data-strip-extension"), + type: element.attr("data-strip-type"), + options: + (element.attr("data-strip-options") && + eval("({" + element.attr("data-strip-options") + "})")) || + {}, + }; + } - if (!object._data) { - object._data = getURIData(object.url); + if (object) { + // detect type if none is set + if (!object.extension) { + object.extension = detectExtension(object.url); } - if (object && object.options) { - object.options = $.extend( - true, - $.extend({}, options), - $.extend({}, object.options) - ); - } else { - object.options = $.extend({}, options); + if (!object.type) { + var data = getURIData(object.url); + object._data = data; + object.type = data.type; } - // extend the options - object.options = Options.create( - object.options, - object.type, - object._data - ); - - // extend this with data - $.extend(this, object); - - return this; } - }); - - var Pages = { - initialize: function(element) { - this.element = element; - this.pages = {}; - this.uid = 1; - }, - - add: function(views) { - this.uid++; - - this.views = views; - - this.pages[this.uid] = []; // create room for these pages - // switched pages, so show the UI on the next resize - Window._showUIOnResize = true; + if (!object._data) { + object._data = getURIData(object.url); + } - // add pages for all these views - $.each( - views, - $.proxy(function(i, view) { - this.pages[this.uid].push(new Page(view, i + 1, this.views.length)); - }, this) + if (object && object.options) { + object.options = $.extend( + true, + $.extend({}, options), + $.extend({}, object.options) ); - }, + } else { + object.options = $.extend({}, options); + } + // extend the options + object.options = Options.create(object.options, object.type, object._data); - show: function(position, callback) { - var page = this.pages[this.uid][position - 1]; - - // never try to reload the exact same frame - if (this.page && this.page.uid == page.uid) { - // also hide the window if toggle is enabled - if (page.view.options.toggle) { - Window.hide(); - // clear the page so double clicking when hiding will - // re-open the window even if it's in a hide animation - this.page = null; - } + // extend this with data + $.extend(this, object); - return; + return this; + }, +}); + +var Pages = { + initialize: function (element) { + this.element = element; + this.pages = {}; + this.uid = 1; + }, + + add: function (views) { + this.uid++; + + this.views = views; + + this.pages[this.uid] = []; // create room for these pages + + // switched pages, so show the UI on the next resize + Window._showUIOnResize = true; + + // add pages for all these views + $.each( + views, + function (i, view) { + this.pages[this.uid].push(new Page(view, i + 1, this.views.length)); + }.bind(this) + ); + }, + + show: function (position, callback) { + var page = this.pages[this.uid][position - 1]; + + // never try to reload the exact same frame + if (this.page && this.page.uid == page.uid) { + // also hide the window if toggle is enabled + if (page.view.options.toggle) { + Window.hide(); + // clear the page so double clicking when hiding will + // re-open the window even if it's in a hide animation + this.page = null; } - // set class names to indicate active state - Pages.setActiveClass(page); + return; + } - // update the page - this.page = page; + // set class names to indicate active state + Pages.setActiveClass(page); - this.removeHiddenAndLoadingInactive(); - page.show( - $.proxy(function() { - // once a page has been fully shown we mark Pages as not being switched anymore - this._switched = false; - if (callback) callback(); - }, this) - ); - }, + // update the page + this.page = page; - getLoadingCount: function() { - // we only stop loading if all the frames we have are not loading anymore - var count = 0; - $.each(this.pages, function(id, pages) { - $.each(pages, function(j, page) { - if (page.loading) count++; - }); + this.removeHiddenAndLoadingInactive(); + page.show( + function () { + // once a page has been fully shown we mark Pages as not being switched anymore + this._switched = false; + if (callback) callback(); + }.bind(this) + ); + }, + + getLoadingCount: function () { + // we only stop loading if all the frames we have are not loading anymore + var count = 0; + $.each(this.pages, function (_id, pages) { + $.each(pages, function (_j, page) { + if (page.loading) count++; }); - return count; - }, - - // used by the API when opening - // checks if the page is in the currently open group - getPositionInActivePageGroup: function(element) { - var position = 0, - activeGroup = this.pages[this.uid]; - - if (activeGroup) { - $.each(activeGroup, function(i, page) { - if (page.view.element && page.view.element == element) { - position = i + 1; - } - }); - } - - return position; - }, - - // remove pages not matching the current id - removeExpired: function(instantly) { - $.each(this.pages, function(id, pages) { - if (id != this._id) { - $.each(pages, function(j, page) { - page.remove(instantly); - }); + }); + return count; + }, + + // used by the API when opening + // checks if the page is in the currently open group + getPositionInActivePageGroup: function (element) { + var position = 0, + activeGroup = this.pages[this.uid]; + + if (activeGroup) { + $.each(activeGroup, function (i, page) { + if (page.view.element && page.view.element == element) { + position = i + 1; } }); - }, - - // Window.hide will call this when fully closed - removeAll: function() { - $.each(this.pages, function(id, pages) { - $.each(pages, function(j, page) { - page.remove(); - }); - }); - - // empty out pages - this.pages = {}; - }, - - hideVisibleInactive: function(alternateDuration) { - $.each( - this.pages, - $.proxy(function(id, pages) { - $.each( - pages, - $.proxy(function(j, page) { - if (page.uid != this.page.uid) { - page.hide(null, alternateDuration); - } - }, this) - ); - }, this) - ); - }, - - stopInactive: function() { - $.each( - this.pages, - $.proxy(function(id, pages) { - $.each( - pages, - $.proxy(function(j, page) { - if (page.uid != this.page.uid && !page.preloading) { - page.stop(); - } - }, this) - ); - }, this) - ); - }, - - // TODO: might be nice to have a hide animation before removal, it's instant now - removeHiddenAndLoadingInactive: function() { - // track which inactive page groups are empty - var empty = []; - - $.each( - this.pages, - $.proxy(function(uid, pages) { - // only remove pages in the groups that are currently not active - if (uid != this.uid) { - var removed = 0; - - $.each( - pages, - $.proxy(function(j, page) { - // remove hidden or loading, but dont'remove frames in animation - if ((!page.visible || page.loading) && !page.animatingWindow) { - page.remove(); - } - - if (page.removed) removed++; // count all, not those we remove now - }, this) - ); + } - // if we've removed all pages from this group it's safe to remove it - // we don't do this in the loop but below - if (removed == pages.length) { - empty.push(uid); - } - } - }, this) - ); + return position; + }, - // now remove all empty page groups - $.each( - empty, - $.proxy(function(i, uid) { - delete this.pages[uid]; - }, this) - ); - }, - - stop: function() { - $.each(this.pages, function(id, pages) { - $.each(pages, function(j, page) { - page.stop(); + // remove pages not matching the current id + removeExpired: function (instantly) { + $.each(this.pages, function (id, pages) { + if (id != this._id) { + $.each(pages, function (_j, page) { + page.remove(instantly); }); - }); - }, - - setActiveClass: function(page) { - // switch the active element class - this.removeActiveClasses(); - - // add the active class if the new page is bound to an element - var element = page.view.element; - if (element) { - $(element).addClass("strip-active-element strip-active-group"); - - // also give other items in the group the active group class - var group = $(element).attr("data-strip-group"); - if (group) { - $('.strip[data-strip-group="' + group + '"]').addClass( - "strip-active-group" - ); - } } - }, - - removeActiveClasses: function() { - $(".strip-active-group").removeClass( - "strip-active-group strip-active-element" - ); - } - }; - - var Page = (function() { - var uid = 0, - loadedUrlCache = {}; - - 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.animated = 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("strp-page") - .append((this.container = $("
").addClass("strp-container"))) - .css({ opacity: 0 }) - .hide()) - ); - - var hasPosition = this.view.options.position && this._total > 1; - if (this.view.caption || hasPosition) { - this.element.append( - (this.info = $("
") - .addClass("strp-info") - .append( - (this.info_padder = $("
").addClass("strp-info-padder")) - )) - ); - - // insert caption first because it floats right - if (hasPosition) { - this.element.addClass("strp-has-position"); - - this.info_padder.append( - $("
") - .addClass("strp-position") - .html(this._position + " / " + this._total) - ); - } - - if (this.view.caption) { - this.info_padder.append( - (this.caption = $("
") - .addClass("strp-caption") - .html(this.view.caption)) - ); - } - } - - switch (this.view.type) { - case "image": - this.container.append( - (this.content = $("").attr({ src: this.view.url })) - ); - break; - - case "vimeo": - case "youtube": - this.container.append((this.content = $("
"))); - break; - } - - // ui - this.element.addClass( - "strp" + (this.view.options.overlap ? "" : "-no") + "-overlap" - ); - - // no sides - if (this._total < 2) { - this.element.addClass("strp-no-sides"); - } - - this.content.addClass("strp-content-element"); - - 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[Pages.uid][i - 1]; - if (page._position != pos) pages.push(page); - } - - for (var i = pos; i >= begin; i--) { - var page = Pages.pages[Pages.uid][i - 1]; - if (page._position != pos) pages.push(page); - } + }); + }, - return pages; - }, + // Window.hide will call this when fully closed + removeAll: function () { + $.each(this.pages, function (_id, pages) { + $.each(pages, function (_j, page) { + page.remove(); + }); + }); - preloadSurroundingImages: function() { - var pages = this._getSurroundingPages(); + // empty out pages + this.pages = {}; + }, + hideVisibleInactive: function (alternateDuration) { + $.each( + this.pages, + function (_id, pages) { $.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; - - new ImageReady( - this.content[0], - $.proxy(function(imageReady) { - this.loaded = true; - this.preloading = false; - this.preloaded = true; - - this.dimensions = { - width: imageReady.img.naturalWidth, - height: imageReady.img.naturalHeight - }; - }, this), - null, - { method: "naturalWidth" } + function (_j, page) { + if (page.uid != this.page.uid) { + page.hide(null, alternateDuration); + } + }.bind(this) ); - }, - - // 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(); - - // loading indicator, we don't show it when preloading frames - this.loading = true; - - // start spinner - // only when this url hasn't been loaded before - if (this.view.options.spinner && !loadedUrlCache[this.view.url]) { - Window.startLoading(); - } - - switch (this.view.type) { - case "image": - // if we had an error before just go through - if (this.error) { - if (callback) callback(); - return; + }.bind(this) + ); + }, + + stopInactive: function () { + $.each( + this.pages, + function (_id, pages) { + $.each( + pages, + function (_j, page) { + if (page.uid != this.page.uid && !page.preloading) { + page.stop(); } - - this.imageReady = new ImageReady( - this.content[0], - $.proxy(function(imageReady) { - // mark as loaded - this._markAsLoaded(); - - this.dimensions = { - width: imageReady.img.naturalWidth, - height: imageReady.img.naturalHeight - }; - - if (callback) callback(); - }, this), - $.proxy(function() { - // mark as loaded - this._markAsLoaded(); - - this.content.hide(); - this.container.append( - (this.error = $("
").addClass("strp-error")) - ); - this.element.addClass("strp-has-error"); - - this.dimensions = { - width: this.error.outerWidth(), - height: this.error.outerHeight() - }; - - if (callback) callback(); - }, this), - { method: "naturalWidth" } - ); - - break; - - case "vimeo": - this.vimeoReady = new VimeoReady( - this.view.url, - $.proxy(function(data) { - // mark as loaded - this._markAsLoaded(); - - this.dimensions = { - width: data.dimensions.width, - height: data.dimensions.height - }; - - if (callback) callback(); - }, this) - ); - - break; - - case "youtube": - // mark as loaded - this._markAsLoaded(); - - this.dimensions = { - width: this.view.options.width, - height: this.view.options.height - }; - - if (callback) callback(); - break; - } - }, - - // helper for load() - _markAsLoaded: function() { - this.loading = false; - this.loaded = true; - - // mark url as loaded so we can avoid - // showing the spinner again - loadedUrlCache[this.view.url] = true; - - Window.stopLoading(); - }, - - isVideo: function() { - return /^(youtube|vimeo)$/.test(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" - : "") + - ":", - 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.append( - (this.playerIframe = $( - "