diff --git a/etemplate/js/et2_widget_vfs.js b/etemplate/js/et2_widget_vfs.js
index 2e3238247a..7675079b18 100644
--- a/etemplate/js/et2_widget_vfs.js
+++ b/etemplate/js/et2_widget_vfs.js
@@ -18,6 +18,7 @@
et2_core_valueWidget;
et2_widget_description;
et2_widget_file;
+ /etemplate/js/expose.js;
*/
/**
@@ -245,7 +246,7 @@ et2_register_widget(et2_vfsName_ro, ["vfs-name_ro"]);
*
* @augments et2_valueWidget
*/
-var et2_vfsMime = et2_valueWidget.extend([et2_IDetachedDOM],
+var et2_vfsMime = expose(et2_valueWidget.extend([et2_IDetachedDOM],
{
attributes: {
"value": {
@@ -257,6 +258,12 @@ var et2_vfsMime = et2_valueWidget.extend([et2_IDetachedDOM],
"type": "integer",
"description": "Size of icon / thumbnail, in pixels",
"default": et2_no_init
+ },
+ expose_callback:{
+ "name": "expose_callback",
+ "type": "js",
+ "default": et2_no_init,
+ "description": "JS code which is executed when expose slides."
}
},
@@ -272,9 +279,57 @@ var et2_vfsMime = et2_valueWidget.extend([et2_IDetachedDOM],
this.iconOverlayContainer = jQuery(document.createElement('span')).addClass('iconOverlayContainer');
this.image = jQuery(document.createElement("img"));
this.image.addClass("et2_vfs vfsMimeIcon");
+ var self= this;
+ if (this.options.expose_callback)
+ {
+ jQuery(this.expose_options.container).on ('slide', function (event) {
+ if (this.getIndex() - this.getNum() -1 == 0 && typeof self.expose_callback == 'function')
+ {
+ //Call the callback to load more items
+ var content = self.expose_callback.call(this,{})
+ if (content) this.add(content);
+ }
+ });
+ }
this.iconOverlayContainer.append(this.image);
this.setDOMNode(this.iconOverlayContainer[0]);
},
+
+ /**
+ * Function to get media content to feed the expose
+ * @param {type} _value
+ * @returns {Array|Array.getMedia.mediaContent}
+ */
+ getMedia: function (_value)
+ {
+ var base_url = egw(window).window.location.origin + egw.webserverUrl;
+ _value.mime.match(/video|audio|media|image/,'ig')
+ var mediaContent = [];
+ if (_value.mime.match(/video/,'ig'))
+ {
+ mediaContent = [{
+ title: _value.name,
+ type: 'video/*',
+ sources:[{
+ href: base_url + _value.download_url,
+ type: _value.mime,
+ }]
+
+ }];
+
+ }
+ else
+ {
+ mediaContent = [{
+ title: _value.name,
+ href: base_url + _value.download_url,
+ type: _value.mime,
+ thumbnail: base_url+this.image.attr('src')
+ }];
+ }
+ return mediaContent;
+ },
+
set_value: function(_value) {
if (typeof _value !== 'object')
{
@@ -337,7 +392,7 @@ var et2_vfsMime = et2_valueWidget.extend([et2_IDetachedDOM],
this.set_value(_values['value']);
}
}
-});
+}));
et2_register_widget(et2_vfsMime, ["vfs-mime"]);
/**
diff --git a/etemplate/templates/default/etemplate2.css b/etemplate/templates/default/etemplate2.css
index 1b17332b58..daa35c37d3 100644
--- a/etemplate/templates/default/etemplate2.css
+++ b/etemplate/templates/default/etemplate2.css
@@ -7,6 +7,8 @@
* Magic suggest / tag list widget
*/
@import url("../../../phpgwapi/js/jquery/magicsuggest/magicsuggest.css");
+@import url("../../../phpgwapi/js/jquery/blueimp/css/blueimp-gallery.min.css");
+
/**
diff --git a/phpgwapi/js/jquery/blueimp/Gruntfile.js b/phpgwapi/js/jquery/blueimp/Gruntfile.js
new file mode 100644
index 0000000000..b7ae4eb05b
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/Gruntfile.js
@@ -0,0 +1,84 @@
+/*
+ * blueimp Gallery Gruntfile
+ * https://github.com/blueimp/grunt-locales
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* global module */
+
+module.exports = function (grunt) {
+ 'use strict';
+
+ grunt.initConfig({
+ jshint: {
+ options: {
+ jshintrc: '.jshintrc'
+ },
+ all: [
+ 'Gruntfile.js',
+ 'js/blueimp-helper.js',
+ 'js/blueimp-gallery.js',
+ 'js/blueimp-gallery-fullscreen.js',
+ 'js/blueimp-gallery-indicator.js',
+ 'js/blueimp-gallery-video.js',
+ 'js/blueimp-gallery-vimeo.js',
+ 'js/blueimp-gallery-youtube.js',
+ 'js/jquery.blueimp-gallery.js',
+ 'js/demo.js'
+ ]
+ },
+ uglify: {
+ standalone: {
+ src: [
+ 'js/blueimp-helper.js',
+ 'js/blueimp-gallery.js',
+ 'js/blueimp-gallery-fullscreen.js',
+ 'js/blueimp-gallery-indicator.js',
+ 'js/blueimp-gallery-video.js',
+ 'js/blueimp-gallery-vimeo.js',
+ 'js/blueimp-gallery-youtube.js'
+ ],
+ dest: 'js/blueimp-gallery.min.js'
+ },
+ jqueryPlugin: {
+ src: [
+ 'js/blueimp-gallery.js',
+ 'js/blueimp-gallery-fullscreen.js',
+ 'js/blueimp-gallery-indicator.js',
+ 'js/blueimp-gallery-video.js',
+ 'js/blueimp-gallery-vimeo.js',
+ 'js/blueimp-gallery-youtube.js',
+ 'js/jquery.blueimp-gallery.js'
+ ],
+ dest: 'js/jquery.blueimp-gallery.min.js'
+ }
+ },
+ less: {
+ production: {
+ options: {
+ cleancss: true
+ },
+ src: [
+ 'css/blueimp-gallery.css',
+ 'css/blueimp-gallery-indicator.css',
+ 'css/blueimp-gallery-video.css'
+ ],
+ dest: 'css/blueimp-gallery.min.css'
+ }
+ }
+ });
+
+ grunt.loadNpmTasks('grunt-contrib-jshint');
+ grunt.loadNpmTasks('grunt-contrib-less');
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+ grunt.loadNpmTasks('grunt-bump-build-git');
+
+ grunt.registerTask('test', ['jshint']);
+ grunt.registerTask('default', ['test', 'less', 'uglify']);
+
+};
diff --git a/phpgwapi/js/jquery/blueimp/README.md b/phpgwapi/js/jquery/blueimp/README.md
new file mode 100644
index 0000000000..e8356147ec
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/README.md
@@ -0,0 +1,984 @@
+# blueimp Gallery
+
+- [Demo](#demo)
+- [Description](#description)
+- [Setup](#setup)
+ - [Lightbox setup](#lightbox-setup)
+ - [Controls](#controls)
+ - [Carousel setup](#carousel-setup)
+- [Keyboard shortcuts](#keyboard-shortcuts)
+- [Options](#options)
+ - [Default options](#default-options)
+ - [Event callbacks](#event-callbacks)
+ - [Carousel options](#carousel-options)
+ - [Indicator options](#indicator-options)
+ - [Fullscreen options](#fullscreen-options)
+ - [Video options](#video-options)
+ - [Video factory options](#video-factory-options)
+ - [YouTube options](#youtube-options)
+ - [Vimeo options](#vimeo-options)
+ - [Container and element options](#container-and-element-options)
+ - [Property options](#property-options)
+- [API](#api)
+ - [Initialization](#initialization)
+ - [API methods](#api-methods)
+ - [Videos](#videos)
+ - [HTML5 video player](#html5-video-player)
+ - [Multiple video sources](#multiple-video-sources)
+ - [YouTube](#youtube)
+ - [Vimeo](#vimeo)
+ - [Additional Gallery elements](#additional-gallery-elements)
+ - [Additional content types](#additional-content-types)
+ - [Example HTML text factory implementation](#example-html-text-factory-implementation)
+ - [jQuery plugin](#jquery-plugin)
+ - [jQuery plugin setup](#jquery-plugin-setup)
+ - [HTML5 data-attributes](#html5-data-attributes)
+ - [Container ids and link grouping](#container-ids-and-link-grouping)
+ - [Gallery object](#gallery-object)
+ - [jQuery events](#jquery-events)
+- [Requirements](#requirements)
+- [Browsers](#browsers)
+ - [Desktop browsers](#desktop-browsers)
+ - [Mobile browsers](#mobile-browsers)
+- [License](#license)
+- [Credits](#credits)
+
+## Demo
+[blueimp Gallery Demo](https://blueimp.github.io/Gallery/)
+
+## Description
+blueimp Gallery is a touch-enabled, responsive and customizable image and video gallery, carousel and lightbox, optimized for both mobile and desktop web browsers.
+It features swipe, mouse and keyboard navigation, transition effects, slideshow functionality, fullscreen support and on-demand content loading and can be extended to display additional content types.
+
+## Setup
+
+### Lightbox setup
+Copy the **css**, **img** and **js** directories to your website.
+
+Include the Gallery stylesheet in the head section of your webpage:
+
+```html
+
+```
+
+Add the following HTML snippet with the Gallery widget to the body of your webpage:
+
+```html
+
+
')
+ .addClass('text-content')
+ .attr('title', obj.title);
+ $.get(obj.href)
+ .done(function (result) {
+ $element.html(result);
+ callback({
+ type: 'load',
+ target: $element[0]
+ });
+ })
+ .fail(function () {
+ callback({
+ type: 'error',
+ target: $element[0]
+ });
+ });
+ return $element[0];
+};
+```
+
+Next, add the **text-content** class to the Gallery CSS:
+
+```css
+.blueimp-gallery > .slides > .slide > .text-content {
+ overflow: auto;
+ margin: 60px auto;
+ padding: 0 60px;
+ max-width: 920px;
+ text-align: left;
+}
+```
+
+With the previous changes in place, the Gallery can now handle HTML content types:
+
+```js
+blueimp.Gallery([
+ {
+ title: 'Noodle soup',
+ href: 'https://example.org/text/noodle-soup.html',
+ type: 'text/html'
+ },
+ {
+ title: 'Tomato salad',
+ href: 'https://example.org/text/tomato-salad.html',
+ type: 'text/html'
+ }
+]);
+```
+
+### jQuery plugin
+
+#### jQuery plugin setup
+The blueimp Gallery jQuery plugin registers a global click handler to open links with **data-gallery** attribute in the Gallery lightbox.
+
+To use it, follow the [lightbox setup](#lightbox-setup) guide, but replace the minified Gallery script with the jQuery plugin version and include it after including [jQuery](https://jquery.com/):
+
+```html
+
+
+```
+
+Next, add the attribute **data-gallery** to your Gallery links:
+
+```html
+
+```
+
+The onclick handler from the [lightbox setup](#lightbox-setup) guide is not required and can be removed.
+
+#### HTML5 data-attributes
+Options for the Gallery lightbox opened via the jQuery plugin can be defined as [HTML5 data-attributes](https://api.jquery.com/data/#data-html5) on the Gallery widget container.
+
+The jQuery plugin also introduces the additional **filter** option, which is applied to the Gallery links via [jQuery's filter method](https://api.jquery.com/filter/) and allows to remove duplicates from the list:
+
+```html
+
+```
+
+This will initialize the Gallery with the option **startSlideshow** set to **true**.
+It will also filter the Gallery links so that only links with an even index number will be included.
+
+#### Container ids and link grouping
+If the **data-gallery** attribute value is a valid id string (e.g. "#blueimp-gallery"), it is used as container option.
+Setting **data-gallery** to a non-empty string also allows to group links into different sets of Gallery images:
+
+```html
+
+
+```
+
+This will open the links with the **data-gallery** attribute **#blueimp-gallery-fruits** in the Gallery widget with the id **blueimp-gallery-fruits**, and the links with the **data-gallery** attribute **#blueimp-gallery-vegetables** in the Gallery widget with the id **blueimp-gallery-vegetables**.
+
+#### Gallery object
+The gallery object is stored via [jQuery data storage](https://api.jquery.com/category/miscellaneous/data-storage/) on the Gallery widget when the Gallery is opened and can be retrieved the following way:
+
+```js
+var gallery = $('#blueimp-gallery').data('gallery');
+```
+
+This gallery object provides all methods outlined in the API methods section.
+
+#### jQuery events
+The jQuery plugin triggers Gallery events on the widget container, with event names equivalent to the gallery [event callbacks](#event-callbacks):
+
+```js
+$('#blueimp-gallery')
+ .on('open', function (event) {
+ // Gallery open event handler
+ })
+ .on('opened', function (event) {
+ // Gallery opened event handler
+ })
+ .on('slide', function (event, index, slide) {
+ // Gallery slide event handler
+ })
+ .on('slideend', function (event, index, slide) {
+ // Gallery slideend event handler
+ })
+ .on('slidecomplete', function (event, index, slide) {
+ // Gallery slidecomplete event handler
+ })
+ .on('close', function (event) {
+ // Gallery close event handler
+ })
+ .on('closed', function (event) {
+ // Gallery closed event handler
+ });
+```
+
+## Requirements
+blueimp Gallery doesn't require any other libraries and can be used standalone without any dependencies.
+
+You can also use the individual source files instead of the standalone minified version:
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+```
+
+The helper script can be replaced by [jQuery](https://jquery.com/) v. 1.7+.
+The fullscreen, indicator, video, youtube and vimeo source files are optional if their functionality is not required.
+
+The [jQuery plugin](#jquery-plugin) requires [jQuery](https://jquery.com/) v. 1.7+ and the basic Gallery script, while the fullscreen, indicator, video, youtube and vimeo source files are also optional:
+
+```html
+
+
+
+
+
+
+
+
+```
+
+Please note that the jQuery plugin is an optional extension and not required for the Gallery functionality.
+
+## Browsers
+blueimp Gallery has been tested with and supports the following browsers:
+
+### Desktop browsers
+
+* Google Chrome 14.0+
+* Apple Safari 4.0+
+* Mozilla Firefox 4.0+
+* Opera 10.0+
+* Microsoft Internet Explorer 7.0+
+
+### Mobile browsers
+
+* Apple Safari on iOS 6.0+
+* Google Chrome on iOS 6.0+
+* Google Chrome on Android 4.0+
+* Default Browser on Android 2.3+
+* Opera Mobile 12.0+
+
+## License
+Released under the [MIT license](http://www.opensource.org/licenses/MIT).
+
+## Credits
+The swipe implementation is based on code from the [Swipe](http://swipejs.com/) library.
diff --git a/phpgwapi/js/jquery/blueimp/blueimp-gallery.jquery.json b/phpgwapi/js/jquery/blueimp/blueimp-gallery.jquery.json
new file mode 100644
index 0000000000..386c81d5d4
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/blueimp-gallery.jquery.json
@@ -0,0 +1,48 @@
+{
+ "name": "blueimp-gallery",
+ "version": "2.15.2",
+ "title": "blueimp Gallery",
+ "description": "blueimp Gallery is a touch-enabled, responsive and customizable image and video gallery, carousel and lightbox, optimized for both mobile and desktop web browsers. It features swipe, mouse and keyboard navigation, transition effects, slideshow functionality, fullscreen support and on-demand content loading and can be extended to display additional content types.",
+ "keywords": [
+ "image",
+ "video",
+ "gallery",
+ "carousel",
+ "lightbox",
+ "mobile",
+ "desktop",
+ "touch",
+ "responsive",
+ "swipe",
+ "mouse",
+ "keyboard",
+ "navigation",
+ "transition",
+ "effects",
+ "slideshow",
+ "fullscreen"
+ ],
+ "homepage": "https://github.com/blueimp/Gallery",
+ "author": {
+ "name": "Sebastian Tschan",
+ "url": "https://blueimp.net"
+ },
+ "maintainers": [
+ {
+ "name": "Sebastian Tschan",
+ "url": "https://blueimp.net"
+ }
+ ],
+ "bugs": "https://github.com/blueimp/Gallery/issues",
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "http://www.opensource.org/licenses/MIT"
+ }
+ ],
+ "dependencies": {
+ "jquery": ">=1.7"
+ },
+ "docs": "https://github.com/blueimp/Gallery/blob/master/README.md",
+ "demo": "https://blueimp.github.io/Gallery/"
+}
diff --git a/phpgwapi/js/jquery/blueimp/bower.json b/phpgwapi/js/jquery/blueimp/bower.json
new file mode 100644
index 0000000000..f1a9006310
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/bower.json
@@ -0,0 +1,71 @@
+{
+ "name": "blueimp-gallery",
+ "version": "2.15.2",
+ "title": "blueimp Gallery",
+ "description": "blueimp Gallery is a touch-enabled, responsive and customizable image and video gallery, carousel and lightbox, optimized for both mobile and desktop web browsers. It features swipe, mouse and keyboard navigation, transition effects, slideshow functionality, fullscreen support and on-demand content loading and can be extended to display additional content types.",
+ "keywords": [
+ "image",
+ "video",
+ "gallery",
+ "carousel",
+ "lightbox",
+ "mobile",
+ "desktop",
+ "touch",
+ "responsive",
+ "swipe",
+ "mouse",
+ "keyboard",
+ "navigation",
+ "transition",
+ "effects",
+ "slideshow",
+ "fullscreen"
+ ],
+ "homepage": "https://github.com/blueimp/Gallery",
+ "author": {
+ "name": "Sebastian Tschan",
+ "url": "https://blueimp.net"
+ },
+ "maintainers": [
+ {
+ "name": "Sebastian Tschan",
+ "url": "https://blueimp.net"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/blueimp/Gallery.git"
+ },
+ "bugs": "https://github.com/blueimp/Gallery/issues",
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "http://www.opensource.org/licenses/MIT"
+ }
+ ],
+ "main": [
+ "css/blueimp-gallery.css",
+ "css/blueimp-gallery-indicator.css",
+ "css/blueimp-gallery-video.css",
+ "img/error.png",
+ "img/error.svg",
+ "img/loading.gif",
+ "img/play-pause.png",
+ "img/play-pause.svg",
+ "img/video-play.png",
+ "img/video-play.svg",
+ "js/blueimp-helper.js",
+ "js/blueimp-gallery.js",
+ "js/blueimp-gallery-fullscreen.js",
+ "js/blueimp-gallery-indicator.js",
+ "js/blueimp-gallery-video.js",
+ "js/blueimp-gallery-vimeo.js",
+ "js/blueimp-gallery-youtube.js"
+ ],
+ "ignore": [
+ "/*.*",
+ "css/demo.css",
+ "js/demo.js"
+ ]
+}
diff --git a/phpgwapi/js/jquery/blueimp/css/blueimp-gallery-indicator.css b/phpgwapi/js/jquery/blueimp/css/blueimp-gallery-indicator.css
new file mode 100644
index 0000000000..e47171a3c6
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/css/blueimp-gallery-indicator.css
@@ -0,0 +1,71 @@
+@charset "UTF-8";
+/*
+ * blueimp Gallery Indicator CSS 1.1.0
+ * https://github.com/blueimp/Gallery
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+.blueimp-gallery > .indicator {
+ position: absolute;
+ top: auto;
+ right: 15px;
+ bottom: 15px;
+ left: 15px;
+ margin: 0 40px;
+ padding: 0;
+ list-style: none;
+ text-align: center;
+ line-height: 10px;
+ display: none;
+}
+.blueimp-gallery > .indicator > li {
+ display: inline-block;
+ width: 9px;
+ height: 9px;
+ margin: 6px 3px 0 3px;
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ border: 1px solid transparent;
+ background: #ccc;
+ background: rgba(255, 255, 255, 0.25) center no-repeat;
+ border-radius: 5px;
+ box-shadow: 0 0 2px #000;
+ opacity: 0.5;
+ cursor: pointer;
+}
+.blueimp-gallery > .indicator > li:hover,
+.blueimp-gallery > .indicator > .active {
+ background-color: #fff;
+ border-color: #fff;
+ opacity: 1;
+}
+.blueimp-gallery-controls > .indicator {
+ display: block;
+ /* Fix z-index issues (controls behind slide element) on Android: */
+ -webkit-transform: translateZ(0);
+ -moz-transform: translateZ(0);
+ -ms-transform: translateZ(0);
+ -o-transform: translateZ(0);
+ transform: translateZ(0);
+}
+.blueimp-gallery-single > .indicator {
+ display: none;
+}
+.blueimp-gallery > .indicator {
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+/* IE7 fixes */
+*+html .blueimp-gallery > .indicator > li {
+ display: inline;
+}
diff --git a/phpgwapi/js/jquery/blueimp/css/blueimp-gallery-video.css b/phpgwapi/js/jquery/blueimp/css/blueimp-gallery-video.css
new file mode 100644
index 0000000000..59695647ae
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/css/blueimp-gallery-video.css
@@ -0,0 +1,87 @@
+@charset "UTF-8";
+/*
+ * blueimp Gallery Video Factory CSS 1.3.0
+ * https://github.com/blueimp/Gallery
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+.blueimp-gallery > .slides > .slide > .video-content > img {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ margin: auto;
+ width: auto;
+ height: auto;
+ max-width: 100%;
+ max-height: 100%;
+ /* Prevent artifacts in Mozilla Firefox: */
+ -moz-backface-visibility: hidden;
+}
+.blueimp-gallery > .slides > .slide > .video-content > video {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+.blueimp-gallery > .slides > .slide > .video-content > iframe {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ border: none;
+}
+.blueimp-gallery > .slides > .slide > .video-playing > iframe {
+ top: 0;
+}
+.blueimp-gallery > .slides > .slide > .video-content > a {
+ position: absolute;
+ top: 50%;
+ right: 0;
+ left: 0;
+ margin: -64px auto 0;
+ width: 128px;
+ height: 128px;
+ background: url(../img/video-play.png) center no-repeat;
+ opacity: 0.8;
+ cursor: pointer;
+}
+.blueimp-gallery > .slides > .slide > .video-content > a:hover {
+ opacity: 1;
+}
+.blueimp-gallery > .slides > .slide > .video-playing > a,
+.blueimp-gallery > .slides > .slide > .video-playing > img {
+ display: none;
+}
+.blueimp-gallery > .slides > .slide > .video-content > video {
+ display: none;
+}
+.blueimp-gallery > .slides > .slide > .video-playing > video {
+ display: block;
+}
+.blueimp-gallery > .slides > .slide > .video-loading > a {
+ background: url(../img/loading.gif) center no-repeat;
+ background-size: 64px 64px;
+}
+
+/* Replace PNGs with SVGs for capable browsers (excluding IE<9) */
+body:last-child .blueimp-gallery > .slides > .slide > .video-content:not(.video-loading) > a {
+ background-image: url(../img/video-play.svg);
+}
+
+/* IE7 fixes */
+*+html .blueimp-gallery > .slides > .slide > .video-content {
+ height: 100%;
+}
+*+html .blueimp-gallery > .slides > .slide > .video-content > a {
+ left: 50%;
+ margin-left: -64px;
+}
diff --git a/phpgwapi/js/jquery/blueimp/css/blueimp-gallery.css b/phpgwapi/js/jquery/blueimp/css/blueimp-gallery.css
new file mode 100644
index 0000000000..b860d78646
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/css/blueimp-gallery.css
@@ -0,0 +1,226 @@
+@charset "UTF-8";
+/*
+ * blueimp Gallery CSS 2.11.1
+ * https://github.com/blueimp/Gallery
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+.blueimp-gallery,
+.blueimp-gallery > .slides > .slide > .slide-content {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ /* Prevent artifacts in Mozilla Firefox: */
+ -moz-backface-visibility: hidden;
+}
+.blueimp-gallery > .slides > .slide > .slide-content {
+ margin: auto;
+ width: auto;
+ height: auto;
+ max-width: 100%;
+ max-height: 100%;
+ opacity: 1;
+}
+.blueimp-gallery {
+ position: fixed;
+ z-index: 999999;
+ overflow: hidden;
+ background: #000;
+ background: rgba(0, 0, 0, 0.9);
+ opacity: 0;
+ display: none;
+ direction: ltr;
+ -ms-touch-action: none;
+ touch-action: none;
+}
+.blueimp-gallery-carousel {
+ position: relative;
+ z-index: auto;
+ margin: 1em auto;
+ /* Set the carousel width/height ratio to 16/9: */
+ padding-bottom: 56.25%;
+ box-shadow: 0 0 10px #000;
+ -ms-touch-action: pan-y;
+ touch-action: pan-y;
+}
+.blueimp-gallery-display {
+ display: block;
+ opacity: 1;
+}
+.blueimp-gallery > .slides {
+ position: relative;
+ height: 100%;
+ overflow: hidden;
+}
+.blueimp-gallery-carousel > .slides {
+ position: absolute;
+}
+.blueimp-gallery > .slides > .slide {
+ position: relative;
+ float: left;
+ height: 100%;
+ text-align: center;
+ -webkit-transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000);
+ -moz-transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000);
+ -ms-transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000);
+ -o-transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000);
+ transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000);
+}
+.blueimp-gallery,
+.blueimp-gallery > .slides > .slide > .slide-content {
+ -webkit-transition: opacity 0.5s linear;
+ -moz-transition: opacity 0.5s linear;
+ -ms-transition: opacity 0.5s linear;
+ -o-transition: opacity 0.5s linear;
+ transition: opacity 0.5s linear;
+}
+.blueimp-gallery > .slides > .slide-loading {
+ background: url(../img/loading.gif) center no-repeat;
+ background-size: 64px 64px;
+}
+.blueimp-gallery > .slides > .slide-loading > .slide-content {
+ opacity: 0;
+}
+.blueimp-gallery > .slides > .slide-error {
+ background: url(../img/error.png) center no-repeat;
+}
+.blueimp-gallery > .slides > .slide-error > .slide-content {
+ display: none;
+}
+.blueimp-gallery > .prev,
+.blueimp-gallery > .next {
+ position: absolute;
+ top: 50%;
+ left: 15px;
+ width: 40px;
+ height: 40px;
+ margin-top: -23px;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 60px;
+ font-weight: 100;
+ line-height: 30px;
+ color: #fff;
+ text-decoration: none;
+ text-shadow: 0 0 2px #000;
+ text-align: center;
+ background: #222;
+ background: rgba(0, 0, 0, 0.5);
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ border: 3px solid #fff;
+ -webkit-border-radius: 23px;
+ -moz-border-radius: 23px;
+ border-radius: 23px;
+ opacity: 0.5;
+ cursor: pointer;
+ display: none;
+}
+.blueimp-gallery > .next {
+ left: auto;
+ right: 15px;
+}
+.blueimp-gallery > .close,
+.blueimp-gallery > .title {
+ position: absolute;
+ top: 15px;
+ left: 15px;
+ margin: 0 40px 0 0;
+ font-size: 20px;
+ line-height: 30px;
+ color: #fff;
+ text-shadow: 0 0 2px #000;
+ opacity: 0.8;
+ display: none;
+}
+.blueimp-gallery > .close {
+ padding: 15px;
+ right: 15px;
+ left: auto;
+ margin: -15px;
+ font-size: 30px;
+ text-decoration: none;
+ cursor: pointer;
+}
+.blueimp-gallery > .play-pause {
+ position: absolute;
+ right: 15px;
+ bottom: 15px;
+ width: 15px;
+ height: 15px;
+ background: url(../img/play-pause.png) 0 0 no-repeat;
+ cursor: pointer;
+ opacity: 0.5;
+ display: none;
+}
+.blueimp-gallery-playing > .play-pause {
+ background-position: -15px 0;
+}
+.blueimp-gallery > .prev:hover,
+.blueimp-gallery > .next:hover,
+.blueimp-gallery > .close:hover,
+.blueimp-gallery > .title:hover,
+.blueimp-gallery > .play-pause:hover {
+ color: #fff;
+ opacity: 1;
+}
+.blueimp-gallery-controls > .prev,
+.blueimp-gallery-controls > .next,
+.blueimp-gallery-controls > .close,
+.blueimp-gallery-controls > .title,
+.blueimp-gallery-controls > .play-pause {
+ display: block;
+ /* Fix z-index issues (controls behind slide element) on Android: */
+ -webkit-transform: translateZ(0);
+ -moz-transform: translateZ(0);
+ -ms-transform: translateZ(0);
+ -o-transform: translateZ(0);
+ transform: translateZ(0);
+}
+.blueimp-gallery-single > .prev,
+.blueimp-gallery-left > .prev,
+.blueimp-gallery-single > .next,
+.blueimp-gallery-right > .next,
+.blueimp-gallery-single > .play-pause {
+ display: none;
+}
+.blueimp-gallery > .slides > .slide > .slide-content,
+.blueimp-gallery > .prev,
+.blueimp-gallery > .next,
+.blueimp-gallery > .close,
+.blueimp-gallery > .play-pause {
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+/* Replace PNGs with SVGs for capable browsers (excluding IE<9) */
+body:last-child .blueimp-gallery > .slides > .slide-error {
+ background-image: url(../img/error.svg);
+}
+body:last-child .blueimp-gallery > .play-pause {
+ width: 20px;
+ height: 20px;
+ background-size: 40px 20px;
+ background-image: url(../img/play-pause.svg);
+}
+body:last-child .blueimp-gallery-playing > .play-pause {
+ background-position: -20px 0;
+}
+
+/* IE7 fixes */
+*+html .blueimp-gallery > .slides > .slide {
+ min-height: 300px;
+}
+*+html .blueimp-gallery > .slides > .slide > .slide-content {
+ position: relative;
+}
diff --git a/phpgwapi/js/jquery/blueimp/css/blueimp-gallery.min.css b/phpgwapi/js/jquery/blueimp/css/blueimp-gallery.min.css
new file mode 100644
index 0000000000..c65eaa0515
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/css/blueimp-gallery.min.css
@@ -0,0 +1 @@
+@charset "UTF-8";.blueimp-gallery,.blueimp-gallery>.slides>.slide>.slide-content{position:absolute;top:0;right:0;bottom:0;left:0;-moz-backface-visibility:hidden}.blueimp-gallery>.slides>.slide>.slide-content{margin:auto;width:auto;height:auto;max-width:100%;max-height:100%;opacity:1}.blueimp-gallery{position:fixed;z-index:999999;overflow:hidden;background:#000;background:rgba(0,0,0,.9);opacity:0;display:none;direction:ltr;-ms-touch-action:none;touch-action:none}.blueimp-gallery-carousel{position:relative;z-index:auto;margin:1em auto;padding-bottom:56.25%;box-shadow:0 0 10px #000;-ms-touch-action:pan-y;touch-action:pan-y}.blueimp-gallery-display{display:block;opacity:1}.blueimp-gallery>.slides{position:relative;height:100%;overflow:hidden}.blueimp-gallery-carousel>.slides{position:absolute}.blueimp-gallery>.slides>.slide{position:relative;float:left;height:100%;text-align:center;-webkit-transition-timing-function:cubic-bezier(0.645,.045,.355,1);-moz-transition-timing-function:cubic-bezier(0.645,.045,.355,1);-ms-transition-timing-function:cubic-bezier(0.645,.045,.355,1);-o-transition-timing-function:cubic-bezier(0.645,.045,.355,1);transition-timing-function:cubic-bezier(0.645,.045,.355,1)}.blueimp-gallery,.blueimp-gallery>.slides>.slide>.slide-content{-webkit-transition:opacity .5s linear;-moz-transition:opacity .5s linear;-ms-transition:opacity .5s linear;-o-transition:opacity .5s linear;transition:opacity .5s linear}.blueimp-gallery>.slides>.slide-loading{background:url(../img/loading.gif) center no-repeat;background-size:64px 64px}.blueimp-gallery>.slides>.slide-loading>.slide-content{opacity:0}.blueimp-gallery>.slides>.slide-error{background:url(../img/error.png) center no-repeat}.blueimp-gallery>.slides>.slide-error>.slide-content{display:none}.blueimp-gallery>.prev,.blueimp-gallery>.next{position:absolute;top:50%;left:15px;width:40px;height:40px;margin-top:-23px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-decoration:none;text-shadow:0 0 2px #000;text-align:center;background:#222;background:rgba(0,0,0,.5);-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;cursor:pointer;display:none}.blueimp-gallery>.next{left:auto;right:15px}.blueimp-gallery>.close,.blueimp-gallery>.title{position:absolute;top:15px;left:15px;margin:0 40px 0 0;font-size:20px;line-height:30px;color:#fff;text-shadow:0 0 2px #000;opacity:.8;display:none}.blueimp-gallery>.close{padding:15px;right:15px;left:auto;margin:-15px;font-size:30px;text-decoration:none;cursor:pointer}.blueimp-gallery>.play-pause{position:absolute;right:15px;bottom:15px;width:15px;height:15px;background:url(../img/play-pause.png) 0 0 no-repeat;cursor:pointer;opacity:.5;display:none}.blueimp-gallery-playing>.play-pause{background-position:-15px 0}.blueimp-gallery>.prev:hover,.blueimp-gallery>.next:hover,.blueimp-gallery>.close:hover,.blueimp-gallery>.title:hover,.blueimp-gallery>.play-pause:hover{color:#fff;opacity:1}.blueimp-gallery-controls>.prev,.blueimp-gallery-controls>.next,.blueimp-gallery-controls>.close,.blueimp-gallery-controls>.title,.blueimp-gallery-controls>.play-pause{display:block;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);-o-transform:translateZ(0);transform:translateZ(0)}.blueimp-gallery-single>.prev,.blueimp-gallery-left>.prev,.blueimp-gallery-single>.next,.blueimp-gallery-right>.next,.blueimp-gallery-single>.play-pause{display:none}.blueimp-gallery>.slides>.slide>.slide-content,.blueimp-gallery>.prev,.blueimp-gallery>.next,.blueimp-gallery>.close,.blueimp-gallery>.play-pause{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body:last-child .blueimp-gallery>.slides>.slide-error{background-image:url(../img/error.svg)}body:last-child .blueimp-gallery>.play-pause{width:20px;height:20px;background-size:40px 20px;background-image:url(../img/play-pause.svg)}body:last-child .blueimp-gallery-playing>.play-pause{background-position:-20px 0}*+html .blueimp-gallery>.slides>.slide{min-height:300px}*+html .blueimp-gallery>.slides>.slide>.slide-content{position:relative}@charset "UTF-8";.blueimp-gallery>.indicator{position:absolute;top:auto;right:15px;bottom:15px;left:15px;margin:0 40px;padding:0;list-style:none;text-align:center;line-height:10px;display:none}.blueimp-gallery>.indicator>li{display:inline-block;width:9px;height:9px;margin:6px 3px 0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;border:1px solid transparent;background:#ccc;background:rgba(255,255,255,.25)center no-repeat;border-radius:5px;box-shadow:0 0 2px #000;opacity:.5;cursor:pointer}.blueimp-gallery>.indicator>li:hover,.blueimp-gallery>.indicator>.active{background-color:#fff;border-color:#fff;opacity:1}.blueimp-gallery-controls>.indicator{display:block;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);-o-transform:translateZ(0);transform:translateZ(0)}.blueimp-gallery-single>.indicator{display:none}.blueimp-gallery>.indicator{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}*+html .blueimp-gallery>.indicator>li{display:inline}@charset "UTF-8";.blueimp-gallery>.slides>.slide>.video-content>img{position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;width:auto;height:auto;max-width:100%;max-height:100%;-moz-backface-visibility:hidden}.blueimp-gallery>.slides>.slide>.video-content>video{position:absolute;top:0;left:0;width:100%;height:100%}.blueimp-gallery>.slides>.slide>.video-content>iframe{position:absolute;top:100%;left:0;width:100%;height:100%;border:none}.blueimp-gallery>.slides>.slide>.video-playing>iframe{top:0}.blueimp-gallery>.slides>.slide>.video-content>a{position:absolute;top:50%;right:0;left:0;margin:-64px auto 0;width:128px;height:128px;background:url(../img/video-play.png) center no-repeat;opacity:.8;cursor:pointer}.blueimp-gallery>.slides>.slide>.video-content>a:hover{opacity:1}.blueimp-gallery>.slides>.slide>.video-playing>a,.blueimp-gallery>.slides>.slide>.video-playing>img{display:none}.blueimp-gallery>.slides>.slide>.video-content>video{display:none}.blueimp-gallery>.slides>.slide>.video-playing>video{display:block}.blueimp-gallery>.slides>.slide>.video-loading>a{background:url(../img/loading.gif) center no-repeat;background-size:64px 64px}body:last-child .blueimp-gallery>.slides>.slide>.video-content:not(.video-loading)>a{background-image:url(../img/video-play.svg)}*+html .blueimp-gallery>.slides>.slide>.video-content{height:100%}*+html .blueimp-gallery>.slides>.slide>.video-content>a{left:50%;margin-left:-64px}
\ No newline at end of file
diff --git a/phpgwapi/js/jquery/blueimp/css/demo.css b/phpgwapi/js/jquery/blueimp/css/demo.css
new file mode 100644
index 0000000000..7ed6bcc4d7
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/css/demo.css
@@ -0,0 +1,51 @@
+/*
+ * blueimp Gallery Demo CSS 2.0.0
+ * https://github.com/blueimp/Gallery
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+body {
+ max-width: 750px;
+ margin: 0 auto;
+ padding: 1em;
+ font-family: 'Lucida Grande', 'Lucida Sans Unicode', Arial, sans-serif;
+ font-size: 1em;
+ line-height: 1.4em;
+ background: #222;
+ color: #fff;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+}
+a {
+ color: orange;
+ text-decoration: none;
+}
+img {
+ border: 0;
+ vertical-align: middle;
+}
+h1 {
+ line-height: 1em;
+}
+h2,
+.links {
+ text-align: center;
+}
+
+@media (min-width: 481px) {
+ .navigation {
+ list-style: none;
+ padding: 0;
+ }
+ .navigation li {
+ display: inline-block;
+ }
+ .navigation li:not(:first-child):before {
+ content: '| ';
+ }
+}
diff --git a/phpgwapi/js/jquery/blueimp/img/error.png b/phpgwapi/js/jquery/blueimp/img/error.png
new file mode 100644
index 0000000000..a5577c33ab
Binary files /dev/null and b/phpgwapi/js/jquery/blueimp/img/error.png differ
diff --git a/phpgwapi/js/jquery/blueimp/img/error.svg b/phpgwapi/js/jquery/blueimp/img/error.svg
new file mode 100644
index 0000000000..184206a144
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/img/error.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/phpgwapi/js/jquery/blueimp/img/loading.gif b/phpgwapi/js/jquery/blueimp/img/loading.gif
new file mode 100644
index 0000000000..90f28cbdbb
Binary files /dev/null and b/phpgwapi/js/jquery/blueimp/img/loading.gif differ
diff --git a/phpgwapi/js/jquery/blueimp/img/play-pause.png b/phpgwapi/js/jquery/blueimp/img/play-pause.png
new file mode 100644
index 0000000000..ece6cfb9b7
Binary files /dev/null and b/phpgwapi/js/jquery/blueimp/img/play-pause.png differ
diff --git a/phpgwapi/js/jquery/blueimp/img/play-pause.svg b/phpgwapi/js/jquery/blueimp/img/play-pause.svg
new file mode 100644
index 0000000000..a7f1f50cd3
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/img/play-pause.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/phpgwapi/js/jquery/blueimp/img/video-play.png b/phpgwapi/js/jquery/blueimp/img/video-play.png
new file mode 100644
index 0000000000..353e3a592d
Binary files /dev/null and b/phpgwapi/js/jquery/blueimp/img/video-play.png differ
diff --git a/phpgwapi/js/jquery/blueimp/img/video-play.svg b/phpgwapi/js/jquery/blueimp/img/video-play.svg
new file mode 100644
index 0000000000..b5ea206dde
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/img/video-play.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/phpgwapi/js/jquery/blueimp/index.html b/phpgwapi/js/jquery/blueimp/index.html
new file mode 100644
index 0000000000..be868745cd
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/index.html
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
blueimp Gallery
+
+
+
+
+
+
+
+
+
blueimp Gallery
+
blueimp Gallery is a touch-enabled, responsive and customizable image & video gallery, carousel and lightbox, optimized for both mobile and desktop web browsers.
+It features swipe, mouse and keyboard navigation, transition effects, slideshow functionality, fullscreen support and on-demand content loading and can be extended to display additional content types.
+
+
blueimp Gallery is based on Swipe .
+
+
Carousel image gallery
+
+
+
+
Carousel video gallery
+
+
+
+
Lightbox image gallery
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/phpgwapi/js/jquery/blueimp/js/blueimp-gallery-fullscreen.js b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery-fullscreen.js
new file mode 100644
index 0000000000..eb0de11363
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery-fullscreen.js
@@ -0,0 +1,90 @@
+/*
+ * blueimp Gallery Fullscreen JS 1.2.0
+ * https://github.com/blueimp/Gallery
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* global define, window, document */
+
+(function (factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ // Register as an anonymous AMD module:
+ define([
+ './blueimp-helper',
+ './blueimp-gallery'
+ ], factory);
+ } else {
+ // Browser globals:
+ factory(
+ window.blueimp.helper || window.jQuery,
+ window.blueimp.Gallery
+ );
+ }
+}(function ($, Gallery) {
+ 'use strict';
+
+ $.extend(Gallery.prototype.options, {
+ // Defines if the gallery should open in fullscreen mode:
+ fullScreen: false
+ });
+
+ var initialize = Gallery.prototype.initialize,
+ close = Gallery.prototype.close;
+
+ $.extend(Gallery.prototype, {
+
+ getFullScreenElement: function () {
+ return document.fullscreenElement ||
+ document.webkitFullscreenElement ||
+ document.mozFullScreenElement ||
+ document.msFullscreenElement;
+ },
+
+ requestFullScreen: function (element) {
+ if (element.requestFullscreen) {
+ element.requestFullscreen();
+ } else if (element.webkitRequestFullscreen) {
+ element.webkitRequestFullscreen();
+ } else if (element.mozRequestFullScreen) {
+ element.mozRequestFullScreen();
+ } else if (element.msRequestFullscreen) {
+ element.msRequestFullscreen();
+ }
+ },
+
+ exitFullScreen: function () {
+ if (document.exitFullscreen) {
+ document.exitFullscreen();
+ } else if (document.webkitCancelFullScreen) {
+ document.webkitCancelFullScreen();
+ } else if (document.mozCancelFullScreen) {
+ document.mozCancelFullScreen();
+ } else if (document.msExitFullscreen) {
+ document.msExitFullscreen();
+ }
+ },
+
+ initialize: function () {
+ initialize.call(this);
+ if (this.options.fullScreen && !this.getFullScreenElement()) {
+ this.requestFullScreen(this.container[0]);
+ }
+ },
+
+ close: function () {
+ if (this.getFullScreenElement() === this.container[0]) {
+ this.exitFullScreen();
+ }
+ close.call(this);
+ }
+
+ });
+
+ return Gallery;
+}));
diff --git a/phpgwapi/js/jquery/blueimp/js/blueimp-gallery-indicator.js b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery-indicator.js
new file mode 100644
index 0000000000..cd0cf7d5ea
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery-indicator.js
@@ -0,0 +1,153 @@
+/*
+ * blueimp Gallery Indicator JS 1.1.0
+ * https://github.com/blueimp/Gallery
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* global define, window, document */
+
+(function (factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ // Register as an anonymous AMD module:
+ define([
+ './blueimp-helper',
+ './blueimp-gallery'
+ ], factory);
+ } else {
+ // Browser globals:
+ factory(
+ window.blueimp.helper || window.jQuery,
+ window.blueimp.Gallery
+ );
+ }
+}(function ($, Gallery) {
+ 'use strict';
+
+ $.extend(Gallery.prototype.options, {
+ // The tag name, Id, element or querySelector of the indicator container:
+ indicatorContainer: 'ol',
+ // The class for the active indicator:
+ activeIndicatorClass: 'active',
+ // The list object property (or data attribute) with the thumbnail URL,
+ // used as alternative to a thumbnail child element:
+ thumbnailProperty: 'thumbnail',
+ // Defines if the gallery indicators should display a thumbnail:
+ thumbnailIndicators: true
+ });
+
+ var initSlides = Gallery.prototype.initSlides,
+ addSlide = Gallery.prototype.addSlide,
+ resetSlides = Gallery.prototype.resetSlides,
+ handleClick = Gallery.prototype.handleClick,
+ handleSlide = Gallery.prototype.handleSlide,
+ handleClose = Gallery.prototype.handleClose;
+
+ $.extend(Gallery.prototype, {
+
+ createIndicator: function (obj) {
+ var indicator = this.indicatorPrototype.cloneNode(false),
+ title = this.getItemProperty(obj, this.options.titleProperty),
+ thumbnailProperty = this.options.thumbnailProperty,
+ thumbnailUrl,
+ thumbnail;
+ if (this.options.thumbnailIndicators) {
+ thumbnail = obj.getElementsByTagName && $(obj).find('img')[0];
+ if (thumbnail) {
+ thumbnailUrl = thumbnail.src;
+ } else if (thumbnailProperty) {
+ thumbnailUrl = this.getItemProperty(obj, thumbnailProperty);
+ }
+ if (thumbnailUrl) {
+ indicator.style.backgroundImage = 'url("' + thumbnailUrl + '")';
+ }
+ }
+ if (title) {
+ indicator.title = title;
+ }
+ return indicator;
+ },
+
+ addIndicator: function (index) {
+ if (this.indicatorContainer.length) {
+ var indicator = this.createIndicator(this.list[index]);
+ indicator.setAttribute('data-index', index);
+ this.indicatorContainer[0].appendChild(indicator);
+ this.indicators.push(indicator);
+ }
+ },
+
+ setActiveIndicator: function (index) {
+ if (this.indicators) {
+ if (this.activeIndicator) {
+ this.activeIndicator
+ .removeClass(this.options.activeIndicatorClass);
+ }
+ this.activeIndicator = $(this.indicators[index]);
+ this.activeIndicator
+ .addClass(this.options.activeIndicatorClass);
+ }
+ },
+
+ initSlides: function (reload) {
+ if (!reload) {
+ this.indicatorContainer = this.container.find(
+ this.options.indicatorContainer
+ );
+ if (this.indicatorContainer.length) {
+ this.indicatorPrototype = document.createElement('li');
+ this.indicators = this.indicatorContainer[0].children;
+ }
+ }
+ initSlides.call(this, reload);
+ },
+
+ addSlide: function (index) {
+ addSlide.call(this, index);
+ this.addIndicator(index);
+ },
+
+ resetSlides: function () {
+ resetSlides.call(this);
+ this.indicatorContainer.empty();
+ this.indicators = [];
+ },
+
+ handleClick: function (event) {
+ var target = event.target || event.srcElement,
+ parent = target.parentNode;
+ if (parent === this.indicatorContainer[0]) {
+ // Click on indicator element
+ this.preventDefault(event);
+ this.slide(this.getNodeIndex(target));
+ } else if (parent.parentNode === this.indicatorContainer[0]) {
+ // Click on indicator child element
+ this.preventDefault(event);
+ this.slide(this.getNodeIndex(parent));
+ } else {
+ return handleClick.call(this, event);
+ }
+ },
+
+ handleSlide: function (index) {
+ handleSlide.call(this, index);
+ this.setActiveIndicator(index);
+ },
+
+ handleClose: function () {
+ if (this.activeIndicator) {
+ this.activeIndicator
+ .removeClass(this.options.activeIndicatorClass);
+ }
+ handleClose.call(this);
+ }
+
+ });
+
+ return Gallery;
+}));
diff --git a/phpgwapi/js/jquery/blueimp/js/blueimp-gallery-video.js b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery-video.js
new file mode 100644
index 0000000000..ee0343554c
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery-video.js
@@ -0,0 +1,171 @@
+/*
+ * blueimp Gallery Video Factory JS 1.1.1
+ * https://github.com/blueimp/Gallery
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* global define, window, document */
+
+(function (factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ // Register as an anonymous AMD module:
+ define([
+ './blueimp-helper',
+ './blueimp-gallery'
+ ], factory);
+ } else {
+ // Browser globals:
+ factory(
+ window.blueimp.helper || window.jQuery,
+ window.blueimp.Gallery
+ );
+ }
+}(function ($, Gallery) {
+ 'use strict';
+
+ $.extend(Gallery.prototype.options, {
+ // The class for video content elements:
+ videoContentClass: 'video-content',
+ // The class for video when it is loading:
+ videoLoadingClass: 'video-loading',
+ // The class for video when it is playing:
+ videoPlayingClass: 'video-playing',
+ // The list object property (or data attribute) for the video poster URL:
+ videoPosterProperty: 'poster',
+ // The list object property (or data attribute) for the video sources array:
+ videoSourcesProperty: 'sources'
+ });
+
+ var handleSlide = Gallery.prototype.handleSlide;
+
+ $.extend(Gallery.prototype, {
+
+ handleSlide: function (index) {
+ handleSlide.call(this, index);
+ if (this.playingVideo) {
+ this.playingVideo.pause();
+ }
+ },
+
+ videoFactory: function (obj, callback, videoInterface) {
+ var that = this,
+ options = this.options,
+ videoContainerNode = this.elementPrototype.cloneNode(false),
+ videoContainer = $(videoContainerNode),
+ errorArgs = [{
+ type: 'error',
+ target: videoContainerNode
+ }],
+ video = videoInterface || document.createElement('video'),
+ url = this.getItemProperty(obj, options.urlProperty),
+ type = this.getItemProperty(obj, options.typeProperty),
+ title = this.getItemProperty(obj, options.titleProperty),
+ posterUrl = this.getItemProperty(obj, options.videoPosterProperty),
+ posterImage,
+ sources = this.getItemProperty(
+ obj,
+ options.videoSourcesProperty
+ ),
+ source,
+ playMediaControl,
+ isLoading,
+ hasControls;
+ videoContainer.addClass(options.videoContentClass);
+ if (title) {
+ videoContainerNode.title = title;
+ }
+ if (video.canPlayType) {
+ if (url && type && video.canPlayType(type)) {
+ video.src = url;
+ } else {
+ while (sources && sources.length) {
+ source = sources.shift();
+ url = this.getItemProperty(source, options.urlProperty);
+ type = this.getItemProperty(source, options.typeProperty);
+ if (url && type && video.canPlayType(type)) {
+ video.src = url;
+ break;
+ }
+ }
+ }
+ }
+ if (posterUrl) {
+ video.poster = posterUrl;
+ posterImage = this.imagePrototype.cloneNode(false);
+ $(posterImage).addClass(options.toggleClass);
+ posterImage.src = posterUrl;
+ posterImage.draggable = false;
+ videoContainerNode.appendChild(posterImage);
+ }
+ playMediaControl = document.createElement('a');
+ playMediaControl.setAttribute('target', '_blank');
+ if (!videoInterface) {
+ playMediaControl.setAttribute('download', title);
+ }
+ playMediaControl.href = url;
+ if (video.src) {
+ video.controls = true;
+ (videoInterface || $(video))
+ .on('error', function () {
+ that.setTimeout(callback, errorArgs);
+ })
+ .on('pause', function () {
+ isLoading = false;
+ videoContainer
+ .removeClass(that.options.videoLoadingClass)
+ .removeClass(that.options.videoPlayingClass);
+ if (hasControls) {
+ that.container.addClass(that.options.controlsClass);
+ }
+ delete that.playingVideo;
+ if (that.interval) {
+ that.play();
+ }
+ })
+ .on('playing', function () {
+ isLoading = false;
+ videoContainer
+ .removeClass(that.options.videoLoadingClass)
+ .addClass(that.options.videoPlayingClass);
+ if (that.container.hasClass(that.options.controlsClass)) {
+ hasControls = true;
+ that.container.removeClass(that.options.controlsClass);
+ } else {
+ hasControls = false;
+ }
+ })
+ .on('play', function () {
+ window.clearTimeout(that.timeout);
+ isLoading = true;
+ videoContainer.addClass(that.options.videoLoadingClass);
+ that.playingVideo = video;
+ });
+ $(playMediaControl).on('click', function (event) {
+ that.preventDefault(event);
+ if (isLoading) {
+ video.pause();
+ } else {
+ video.play();
+ }
+ });
+ videoContainerNode.appendChild(
+ (videoInterface && videoInterface.element) || video
+ );
+ }
+ videoContainerNode.appendChild(playMediaControl);
+ this.setTimeout(callback, [{
+ type: 'load',
+ target: videoContainerNode
+ }]);
+ return videoContainerNode;
+ }
+ });
+
+ return Gallery;
+}));
diff --git a/phpgwapi/js/jquery/blueimp/js/blueimp-gallery-vimeo.js b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery-vimeo.js
new file mode 100644
index 0000000000..6ffff646b9
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery-vimeo.js
@@ -0,0 +1,215 @@
+/*
+ * blueimp Gallery Vimeo Video Factory JS 1.2.0
+ * https://github.com/blueimp/Gallery
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* global define, window, document, location, $f */
+
+(function (factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ // Register as an anonymous AMD module:
+ define([
+ './blueimp-helper',
+ './blueimp-gallery-video'
+ ], factory);
+ } else {
+ // Browser globals:
+ factory(
+ window.blueimp.helper || window.jQuery,
+ window.blueimp.Gallery
+ );
+ }
+}(function ($, Gallery) {
+ 'use strict';
+
+ if (!window.postMessage) {
+ return Gallery;
+ }
+
+ $.extend(Gallery.prototype.options, {
+ // The list object property (or data attribute) with the Vimeo video id:
+ vimeoVideoIdProperty: 'vimeo',
+ // The URL for the Vimeo video player, can be extended with custom parameters:
+ // https://developer.vimeo.com/player/embedding
+ vimeoPlayerUrl: '//player.vimeo.com/video/VIDEO_ID?api=1&player_id=PLAYER_ID',
+ // The prefix for the Vimeo video player ID:
+ vimeoPlayerIdPrefix: 'vimeo-player-',
+ // Require a click on the native Vimeo player for the initial playback:
+ vimeoClickToPlay: true
+ });
+
+ var textFactory = Gallery.prototype.textFactory || Gallery.prototype.imageFactory,
+ VimeoPlayer = function (url, videoId, playerId, clickToPlay) {
+ this.url = url;
+ this.videoId = videoId;
+ this.playerId = playerId;
+ this.clickToPlay = clickToPlay;
+ this.element = document.createElement('div');
+ this.listeners = {};
+ },
+ counter = 0;
+
+ $.extend(VimeoPlayer.prototype, {
+
+ canPlayType: function () {
+ return true;
+ },
+
+ on: function (type, func) {
+ this.listeners[type] = func;
+ return this;
+ },
+
+ loadAPI: function () {
+ var that = this,
+ apiUrl = '//' + (location.protocol === 'https' ? 'secure-' : '') +
+ 'a.vimeocdn.com/js/froogaloop2.min.js',
+ scriptTags = document.getElementsByTagName('script'),
+ i = scriptTags.length,
+ scriptTag,
+ called,
+ callback = function () {
+ if (!called && that.playOnReady) {
+ that.play();
+ }
+ called = true;
+ };
+ while (i) {
+ i -= 1;
+ if (scriptTags[i].src === apiUrl) {
+ scriptTag = scriptTags[i];
+ break;
+ }
+ }
+ if (!scriptTag) {
+ scriptTag = document.createElement('script');
+ scriptTag.src = apiUrl;
+ }
+ $(scriptTag).on('load', callback);
+ scriptTags[0].parentNode.insertBefore(scriptTag, scriptTags[0]);
+ // Fix for cached scripts on IE 8:
+ if (/loaded|complete/.test(scriptTag.readyState)) {
+ callback();
+ }
+ },
+
+ onReady: function () {
+ var that = this;
+ this.ready = true;
+ this.player.addEvent('play', function () {
+ that.hasPlayed = true;
+ that.onPlaying();
+ });
+ this.player.addEvent('pause', function () {
+ that.onPause();
+ });
+ this.player.addEvent('finish', function () {
+ that.onPause();
+ });
+ if (this.playOnReady) {
+ this.play();
+ }
+ },
+
+ onPlaying: function () {
+ if (this.playStatus < 2) {
+ this.listeners.playing();
+ this.playStatus = 2;
+ }
+ },
+
+ onPause: function () {
+ this.listeners.pause();
+ delete this.playStatus;
+ },
+
+ insertIframe: function () {
+ var iframe = document.createElement('iframe');
+ iframe.src = this.url
+ .replace('VIDEO_ID', this.videoId)
+ .replace('PLAYER_ID', this.playerId);
+ iframe.id = this.playerId;
+ this.element.parentNode.replaceChild(iframe, this.element);
+ this.element = iframe;
+ },
+
+ play: function () {
+ var that = this;
+ if (!this.playStatus) {
+ this.listeners.play();
+ this.playStatus = 1;
+ }
+ if (this.ready) {
+ if (!this.hasPlayed && (this.clickToPlay || (window.navigator &&
+ /iP(hone|od|ad)/.test(window.navigator.platform)))) {
+ // Manually trigger the playing callback if clickToPlay
+ // is enabled and to workaround a limitation in iOS,
+ // which requires synchronous user interaction to start
+ // the video playback:
+ this.onPlaying();
+ } else {
+ this.player.api('play');
+ }
+ } else {
+ this.playOnReady = true;
+ if (!window.$f) {
+ this.loadAPI();
+ } else if (!this.player) {
+ this.insertIframe();
+ this.player = $f(this.element);
+ this.player.addEvent('ready', function () {
+ that.onReady();
+ });
+ }
+ }
+ },
+
+ pause: function () {
+ if (this.ready) {
+ this.player.api('pause');
+ } else if (this.playStatus) {
+ delete this.playOnReady;
+ this.listeners.pause();
+ delete this.playStatus;
+ }
+ }
+
+ });
+
+ $.extend(Gallery.prototype, {
+
+ VimeoPlayer: VimeoPlayer,
+
+ textFactory: function (obj, callback) {
+ var options = this.options,
+ videoId = this.getItemProperty(obj, options.vimeoVideoIdProperty);
+ if (videoId) {
+ if (this.getItemProperty(obj, options.urlProperty) === undefined) {
+ obj[options.urlProperty] = '//vimeo.com/' + videoId;
+ }
+ counter += 1;
+ return this.videoFactory(
+ obj,
+ callback,
+ new VimeoPlayer(
+ options.vimeoPlayerUrl,
+ videoId,
+ options.vimeoPlayerIdPrefix + counter,
+ options.vimeoClickToPlay
+ )
+ );
+ }
+ return textFactory.call(this, obj, callback);
+ }
+
+ });
+
+ return Gallery;
+}));
diff --git a/phpgwapi/js/jquery/blueimp/js/blueimp-gallery-youtube.js b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery-youtube.js
new file mode 100644
index 0000000000..da8831daa6
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery-youtube.js
@@ -0,0 +1,229 @@
+/*
+ * blueimp Gallery YouTube Video Factory JS 1.2.0
+ * https://github.com/blueimp/Gallery
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* global define, window, document, YT */
+
+(function (factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ // Register as an anonymous AMD module:
+ define([
+ './blueimp-helper',
+ './blueimp-gallery-video'
+ ], factory);
+ } else {
+ // Browser globals:
+ factory(
+ window.blueimp.helper || window.jQuery,
+ window.blueimp.Gallery
+ );
+ }
+}(function ($, Gallery) {
+ 'use strict';
+
+ if (!window.postMessage) {
+ return Gallery;
+ }
+
+ $.extend(Gallery.prototype.options, {
+ // The list object property (or data attribute) with the YouTube video id:
+ youTubeVideoIdProperty: 'youtube',
+ // Optional object with parameters passed to the YouTube video player:
+ // https://developers.google.com/youtube/player_parameters
+ youTubePlayerVars: {
+ wmode: 'transparent'
+ },
+ // Require a click on the native YouTube player for the initial playback:
+ youTubeClickToPlay: true
+ });
+
+ var textFactory = Gallery.prototype.textFactory || Gallery.prototype.imageFactory,
+ YouTubePlayer = function (videoId, playerVars, clickToPlay) {
+ this.videoId = videoId;
+ this.playerVars = playerVars;
+ this.clickToPlay = clickToPlay;
+ this.element = document.createElement('div');
+ this.listeners = {};
+ };
+
+ $.extend(YouTubePlayer.prototype, {
+
+ canPlayType: function () {
+ return true;
+ },
+
+ on: function (type, func) {
+ this.listeners[type] = func;
+ return this;
+ },
+
+ loadAPI: function () {
+ var that = this,
+ onYouTubeIframeAPIReady = window.onYouTubeIframeAPIReady,
+ apiUrl = '//www.youtube.com/iframe_api',
+ scriptTags = document.getElementsByTagName('script'),
+ i = scriptTags.length,
+ scriptTag;
+ window.onYouTubeIframeAPIReady = function () {
+ if (onYouTubeIframeAPIReady) {
+ onYouTubeIframeAPIReady.apply(this);
+ }
+ if (that.playOnReady) {
+ that.play();
+ }
+ };
+ while (i) {
+ i -= 1;
+ if (scriptTags[i].src === apiUrl) {
+ return;
+ }
+ }
+ scriptTag = document.createElement('script');
+ scriptTag.src = apiUrl;
+ scriptTags[0].parentNode.insertBefore(scriptTag, scriptTags[0]);
+ },
+
+ onReady: function () {
+ this.ready = true;
+ if (this.playOnReady) {
+ this.play();
+ }
+ },
+
+ onPlaying: function () {
+ if (this.playStatus < 2) {
+ this.listeners.playing();
+ this.playStatus = 2;
+ }
+ },
+
+ onPause: function () {
+ Gallery.prototype.setTimeout.call(
+ this,
+ this.checkSeek,
+ null,
+ 2000
+ );
+ },
+
+ checkSeek: function () {
+ if (this.stateChange === YT.PlayerState.PAUSED ||
+ this.stateChange === YT.PlayerState.ENDED) {
+ // check if current state change is actually paused
+ this.listeners.pause();
+ delete this.playStatus;
+ }
+ },
+
+ onStateChange: function (event) {
+ switch (event.data) {
+ case YT.PlayerState.PLAYING:
+ this.hasPlayed = true;
+ this.onPlaying();
+ break;
+ case YT.PlayerState.PAUSED:
+ case YT.PlayerState.ENDED:
+ this.onPause();
+ break;
+ }
+ // Save most recent state change to this.stateChange
+ this.stateChange = event.data;
+ },
+
+ onError: function (event) {
+ this.listeners.error(event);
+ },
+
+ play: function () {
+ var that = this;
+ if (!this.playStatus) {
+ this.listeners.play();
+ this.playStatus = 1;
+ }
+ if (this.ready) {
+ if (!this.hasPlayed && (this.clickToPlay || (window.navigator &&
+ /iP(hone|od|ad)/.test(window.navigator.platform)))) {
+ // Manually trigger the playing callback if clickToPlay
+ // is enabled and to workaround a limitation in iOS,
+ // which requires synchronous user interaction to start
+ // the video playback:
+ this.onPlaying();
+ } else {
+ this.player.playVideo();
+ }
+ } else {
+ this.playOnReady = true;
+ if (!(window.YT && YT.Player)) {
+ this.loadAPI();
+ } else if (!this.player) {
+ this.player = new YT.Player(this.element, {
+ videoId: this.videoId,
+ playerVars: this.playerVars,
+ events: {
+ onReady: function () {
+ that.onReady();
+ },
+ onStateChange: function (event) {
+ that.onStateChange(event);
+ },
+ onError: function (event) {
+ that.onError(event);
+ }
+ }
+ });
+ }
+ }
+ },
+
+ pause: function () {
+ if (this.ready) {
+ this.player.pauseVideo();
+ } else if (this.playStatus) {
+ delete this.playOnReady;
+ this.listeners.pause();
+ delete this.playStatus;
+ }
+ }
+
+ });
+
+ $.extend(Gallery.prototype, {
+
+ YouTubePlayer: YouTubePlayer,
+
+ textFactory: function (obj, callback) {
+ var options = this.options,
+ videoId = this.getItemProperty(obj, options.youTubeVideoIdProperty);
+ if (videoId) {
+ if (this.getItemProperty(obj, options.urlProperty) === undefined) {
+ obj[options.urlProperty] = '//www.youtube.com/watch?v=' + videoId;
+ }
+ if (this.getItemProperty(obj, options.videoPosterProperty) === undefined) {
+ obj[options.videoPosterProperty] = '//img.youtube.com/vi/' + videoId +
+ '/maxresdefault.jpg';
+ }
+ return this.videoFactory(
+ obj,
+ callback,
+ new YouTubePlayer(
+ videoId,
+ options.youTubePlayerVars,
+ options.youTubeClickToPlay
+ )
+ );
+ }
+ return textFactory.call(this, obj, callback);
+ }
+
+ });
+
+ return Gallery;
+}));
diff --git a/phpgwapi/js/jquery/blueimp/js/blueimp-gallery.js b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery.js
new file mode 100644
index 0000000000..d547ee10e8
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery.js
@@ -0,0 +1,1341 @@
+/*
+ * blueimp Gallery JS 2.14.1
+ * https://github.com/blueimp/Gallery
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Swipe implementation based on
+ * https://github.com/bradbirdsall/Swipe
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* global define, window, document, DocumentTouch */
+
+(function (factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ // Register as an anonymous AMD module:
+ define(['./blueimp-helper'], factory);
+ } else {
+ // Browser globals:
+ window.blueimp = window.blueimp || {};
+ window.blueimp.Gallery = factory(
+ window.blueimp.helper || window.jQuery
+ );
+ }
+}(function ($) {
+ 'use strict';
+
+ function Gallery(list, options) {
+ if (document.body.style.maxHeight === undefined) {
+ // document.body.style.maxHeight is undefined on IE6 and lower
+ return null;
+ }
+ if (!this || this.options !== Gallery.prototype.options) {
+ // Called as function instead of as constructor,
+ // so we simply return a new instance:
+ return new Gallery(list, options);
+ }
+ if (!list || !list.length) {
+ this.console.log(
+ 'blueimp Gallery: No or empty list provided as first argument.',
+ list
+ );
+ return;
+ }
+ this.list = list;
+ this.num = list.length;
+ this.initOptions(options);
+ this.initialize();
+ }
+
+ $.extend(Gallery.prototype, {
+
+ options: {
+ // The Id, element or querySelector of the gallery widget:
+ container: '#blueimp-gallery',
+ // The tag name, Id, element or querySelector of the slides container:
+ slidesContainer: 'div',
+ // The tag name, Id, element or querySelector of the title element:
+ titleElement: 'h3',
+ // The class to add when the gallery is visible:
+ displayClass: 'blueimp-gallery-display',
+ // The class to add when the gallery controls are visible:
+ controlsClass: 'blueimp-gallery-controls',
+ // The class to add when the gallery only displays one element:
+ singleClass: 'blueimp-gallery-single',
+ // The class to add when the left edge has been reached:
+ leftEdgeClass: 'blueimp-gallery-left',
+ // The class to add when the right edge has been reached:
+ rightEdgeClass: 'blueimp-gallery-right',
+ // The class to add when the automatic slideshow is active:
+ playingClass: 'blueimp-gallery-playing',
+ // The class for all slides:
+ slideClass: 'slide',
+ // The slide class for loading elements:
+ slideLoadingClass: 'slide-loading',
+ // The slide class for elements that failed to load:
+ slideErrorClass: 'slide-error',
+ // The class for the content element loaded into each slide:
+ slideContentClass: 'slide-content',
+ // The class for the "toggle" control:
+ toggleClass: 'toggle',
+ // The class for the "prev" control:
+ prevClass: 'prev',
+ // The class for the "next" control:
+ nextClass: 'next',
+ // The class for the "close" control:
+ closeClass: 'close',
+ // The class for the "play-pause" toggle control:
+ playPauseClass: 'play-pause',
+ // The list object property (or data attribute) with the object type:
+ typeProperty: 'type',
+ // The list object property (or data attribute) with the object title:
+ titleProperty: 'title',
+ // The list object property (or data attribute) with the object URL:
+ urlProperty: 'href',
+ // The gallery listens for transitionend events before triggering the
+ // opened and closed events, unless the following option is set to false:
+ displayTransition: true,
+ // Defines if the gallery slides are cleared from the gallery modal,
+ // or reused for the next gallery initialization:
+ clearSlides: true,
+ // Defines if images should be stretched to fill the available space,
+ // while maintaining their aspect ratio (will only be enabled for browsers
+ // supporting background-size="contain", which excludes IE < 9).
+ // Set to "cover", to make images cover all available space (requires
+ // support for background-size="cover", which excludes IE < 9):
+ stretchImages: false,
+ // Toggle the controls on pressing the Return key:
+ toggleControlsOnReturn: true,
+ // Toggle the automatic slideshow interval on pressing the Space key:
+ toggleSlideshowOnSpace: true,
+ // Navigate the gallery by pressing left and right on the keyboard:
+ enableKeyboardNavigation: true,
+ // Close the gallery on pressing the Esc key:
+ closeOnEscape: true,
+ // Close the gallery when clicking on an empty slide area:
+ closeOnSlideClick: true,
+ // Close the gallery by swiping up or down:
+ closeOnSwipeUpOrDown: true,
+ // Emulate touch events on mouse-pointer devices such as desktop browsers:
+ emulateTouchEvents: true,
+ // Stop touch events from bubbling up to ancestor elements of the Gallery:
+ stopTouchEventsPropagation: false,
+ // Hide the page scrollbars:
+ hidePageScrollbars: true,
+ // Stops any touches on the container from scrolling the page:
+ disableScroll: true,
+ // Carousel mode (shortcut for carousel specific options):
+ carousel: false,
+ // Allow continuous navigation, moving from last to first
+ // and from first to last slide:
+ continuous: true,
+ // Remove elements outside of the preload range from the DOM:
+ unloadElements: true,
+ // Start with the automatic slideshow:
+ startSlideshow: false,
+ // Delay in milliseconds between slides for the automatic slideshow:
+ slideshowInterval: 5000,
+ // The starting index as integer.
+ // Can also be an object of the given list,
+ // or an equal object with the same url property:
+ index: 0,
+ // The number of elements to load around the current index:
+ preloadRange: 2,
+ // The transition speed between slide changes in milliseconds:
+ transitionSpeed: 400,
+ // The transition speed for automatic slide changes, set to an integer
+ // greater 0 to override the default transition speed:
+ slideshowTransitionSpeed: undefined,
+ // The event object for which the default action will be canceled
+ // on Gallery initialization (e.g. the click event to open the Gallery):
+ event: undefined,
+ // Callback function executed when the Gallery is initialized.
+ // Is called with the gallery instance as "this" object:
+ onopen: undefined,
+ // Callback function executed when the Gallery has been initialized
+ // and the initialization transition has been completed.
+ // Is called with the gallery instance as "this" object:
+ onopened: undefined,
+ // Callback function executed on slide change.
+ // Is called with the gallery instance as "this" object and the
+ // current index and slide as arguments:
+ onslide: undefined,
+ // Callback function executed after the slide change transition.
+ // Is called with the gallery instance as "this" object and the
+ // current index and slide as arguments:
+ onslideend: undefined,
+ // Callback function executed on slide content load.
+ // Is called with the gallery instance as "this" object and the
+ // slide index and slide element as arguments:
+ onslidecomplete: undefined,
+ // Callback function executed when the Gallery is about to be closed.
+ // Is called with the gallery instance as "this" object:
+ onclose: undefined,
+ // Callback function executed when the Gallery has been closed
+ // and the closing transition has been completed.
+ // Is called with the gallery instance as "this" object:
+ onclosed: undefined
+ },
+
+ carouselOptions: {
+ hidePageScrollbars: false,
+ toggleControlsOnReturn: false,
+ toggleSlideshowOnSpace: false,
+ enableKeyboardNavigation: false,
+ closeOnEscape: false,
+ closeOnSlideClick: false,
+ closeOnSwipeUpOrDown: false,
+ disableScroll: false,
+ startSlideshow: true
+ },
+
+ console: window.console && typeof window.console.log === 'function' ?
+ window.console :
+ {log: function () {}},
+
+ // Detect touch, transition, transform and background-size support:
+ support: (function (element) {
+ var support = {
+ touch: window.ontouchstart !== undefined ||
+ (window.DocumentTouch && document instanceof DocumentTouch)
+ },
+ transitions = {
+ webkitTransition: {
+ end: 'webkitTransitionEnd',
+ prefix: '-webkit-'
+ },
+ MozTransition: {
+ end: 'transitionend',
+ prefix: '-moz-'
+ },
+ OTransition: {
+ end: 'otransitionend',
+ prefix: '-o-'
+ },
+ transition: {
+ end: 'transitionend',
+ prefix: ''
+ }
+ },
+ elementTests = function () {
+ var transition = support.transition,
+ prop,
+ translateZ;
+ document.body.appendChild(element);
+ if (transition) {
+ prop = transition.name.slice(0, -9) + 'ransform';
+ if (element.style[prop] !== undefined) {
+ element.style[prop] = 'translateZ(0)';
+ translateZ = window.getComputedStyle(element)
+ .getPropertyValue(transition.prefix + 'transform');
+ support.transform = {
+ prefix: transition.prefix,
+ name: prop,
+ translate: true,
+ translateZ: !!translateZ && translateZ !== 'none'
+ };
+ }
+ }
+ if (element.style.backgroundSize !== undefined) {
+ support.backgroundSize = {};
+ element.style.backgroundSize = 'contain';
+ support.backgroundSize.contain = window
+ .getComputedStyle(element)
+ .getPropertyValue('background-size') === 'contain';
+ element.style.backgroundSize = 'cover';
+ support.backgroundSize.cover = window
+ .getComputedStyle(element)
+ .getPropertyValue('background-size') === 'cover';
+ }
+ document.body.removeChild(element);
+ };
+ (function (support, transitions) {
+ var prop;
+ for (prop in transitions) {
+ if (transitions.hasOwnProperty(prop) &&
+ element.style[prop] !== undefined) {
+ support.transition = transitions[prop];
+ support.transition.name = prop;
+ break;
+ }
+ }
+ }(support, transitions));
+ if (document.body) {
+ elementTests();
+ } else {
+ $(document).on('DOMContentLoaded', elementTests);
+ }
+ return support;
+ // Test element, has to be standard HTML and must not be hidden
+ // for the CSS3 tests using window.getComputedStyle to be applicable:
+ }(document.createElement('div'))),
+
+ requestAnimationFrame: window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame,
+
+ initialize: function () {
+ this.initStartIndex();
+ if (this.initWidget() === false) {
+ return false;
+ }
+ this.initEventListeners();
+ // Load the slide at the given index:
+ this.onslide(this.index);
+ // Manually trigger the slideend event for the initial slide:
+ this.ontransitionend();
+ // Start the automatic slideshow if applicable:
+ if (this.options.startSlideshow) {
+ this.play();
+ }
+ },
+
+ slide: function (to, speed) {
+ window.clearTimeout(this.timeout);
+ var index = this.index,
+ direction,
+ naturalDirection,
+ diff;
+ if (index === to || this.num === 1) {
+ return;
+ }
+ if (!speed) {
+ speed = this.options.transitionSpeed;
+ }
+ if (this.support.transform) {
+ if (!this.options.continuous) {
+ to = this.circle(to);
+ }
+ // 1: backward, -1: forward:
+ direction = Math.abs(index - to) / (index - to);
+ // Get the actual position of the slide:
+ if (this.options.continuous) {
+ naturalDirection = direction;
+ direction = -this.positions[this.circle(to)] / this.slideWidth;
+ // If going forward but to < index, use to = slides.length + to
+ // If going backward but to > index, use to = -slides.length + to
+ if (direction !== naturalDirection) {
+ to = -direction * this.num + to;
+ }
+ }
+ diff = Math.abs(index - to) - 1;
+ // Move all the slides between index and to in the right direction:
+ while (diff) {
+ diff -= 1;
+ this.move(
+ this.circle((to > index ? to : index) - diff - 1),
+ this.slideWidth * direction,
+ 0
+ );
+ }
+ to = this.circle(to);
+ this.move(index, this.slideWidth * direction, speed);
+ this.move(to, 0, speed);
+ if (this.options.continuous) {
+ this.move(
+ this.circle(to - direction),
+ -(this.slideWidth * direction),
+ 0
+ );
+ }
+ } else {
+ to = this.circle(to);
+ this.animate(index * -this.slideWidth, to * -this.slideWidth, speed);
+ }
+ this.onslide(to);
+ },
+
+ getIndex: function () {
+ return this.index;
+ },
+
+ getNumber: function () {
+ return this.num;
+ },
+
+ prev: function () {
+ if (this.options.continuous || this.index) {
+ this.slide(this.index - 1);
+ }
+ },
+
+ next: function () {
+ if (this.options.continuous || this.index < this.num - 1) {
+ this.slide(this.index + 1);
+ }
+ },
+
+ play: function (time) {
+ var that = this;
+ window.clearTimeout(this.timeout);
+ this.interval = time || this.options.slideshowInterval;
+ if (this.elements[this.index] > 1) {
+ this.timeout = this.setTimeout(
+ (!this.requestAnimationFrame && this.slide) || function (to, speed) {
+ that.animationFrameId = that.requestAnimationFrame.call(
+ window,
+ function () {
+ that.slide(to, speed);
+ }
+ );
+ },
+ [this.index + 1, this.options.slideshowTransitionSpeed],
+ this.interval
+ );
+ }
+ this.container.addClass(this.options.playingClass);
+ },
+
+ pause: function () {
+ window.clearTimeout(this.timeout);
+ this.interval = null;
+ this.container.removeClass(this.options.playingClass);
+ },
+
+ add: function (list) {
+ var i;
+ if (!list.concat) {
+ // Make a real array out of the list to add:
+ list = Array.prototype.slice.call(list);
+ }
+ if (!this.list.concat) {
+ // Make a real array out of the Gallery list:
+ this.list = Array.prototype.slice.call(this.list);
+ }
+ this.list = this.list.concat(list);
+ this.num = this.list.length;
+ if (this.num > 2 && this.options.continuous === null) {
+ this.options.continuous = true;
+ this.container.removeClass(this.options.leftEdgeClass);
+ }
+ this.container
+ .removeClass(this.options.rightEdgeClass)
+ .removeClass(this.options.singleClass);
+ for (i = this.num - list.length; i < this.num; i += 1) {
+ this.addSlide(i);
+ this.positionSlide(i);
+ }
+ this.positions.length = this.num;
+ this.initSlides(true);
+ },
+
+ resetSlides: function () {
+ this.slidesContainer.empty();
+ this.slides = [];
+ },
+
+ handleClose: function () {
+ var options = this.options;
+ this.destroyEventListeners();
+ // Cancel the slideshow:
+ this.pause();
+ this.container[0].style.display = 'none';
+ this.container
+ .removeClass(options.displayClass)
+ .removeClass(options.singleClass)
+ .removeClass(options.leftEdgeClass)
+ .removeClass(options.rightEdgeClass);
+ if (options.hidePageScrollbars) {
+ document.body.style.overflow = this.bodyOverflowStyle;
+ }
+ if (this.options.clearSlides) {
+ this.resetSlides();
+ }
+ if (this.options.onclosed) {
+ this.options.onclosed.call(this);
+ }
+ },
+
+ close: function () {
+ var that = this,
+ closeHandler = function (event) {
+ if (event.target === that.container[0]) {
+ that.container.off(
+ that.support.transition.end,
+ closeHandler
+ );
+ that.handleClose();
+ }
+ };
+ if (this.options.onclose) {
+ this.options.onclose.call(this);
+ }
+ if (this.support.transition && this.options.displayTransition) {
+ this.container.on(
+ this.support.transition.end,
+ closeHandler
+ );
+ this.container.removeClass(this.options.displayClass);
+ } else {
+ this.handleClose();
+ }
+ },
+
+ circle: function (index) {
+ // Always return a number inside of the slides index range:
+ return (this.num + (index % this.num)) % this.num;
+ },
+
+ move: function (index, dist, speed) {
+ this.translateX(index, dist, speed);
+ this.positions[index] = dist;
+ },
+
+ translate: function (index, x, y, speed) {
+ var style = this.slides[index].style,
+ transition = this.support.transition,
+ transform = this.support.transform;
+ style[transition.name + 'Duration'] = speed + 'ms';
+ style[transform.name] = 'translate(' + x + 'px, ' + y + 'px)' +
+ (transform.translateZ ? ' translateZ(0)' : '');
+ },
+
+ translateX: function (index, x, speed) {
+ this.translate(index, x, 0, speed);
+ },
+
+ translateY: function (index, y, speed) {
+ this.translate(index, 0, y, speed);
+ },
+
+ animate: function (from, to, speed) {
+ if (!speed) {
+ this.slidesContainer[0].style.left = to + 'px';
+ return;
+ }
+ var that = this,
+ start = new Date().getTime(),
+ timer = window.setInterval(function () {
+ var timeElap = new Date().getTime() - start;
+ if (timeElap > speed) {
+ that.slidesContainer[0].style.left = to + 'px';
+ that.ontransitionend();
+ window.clearInterval(timer);
+ return;
+ }
+ that.slidesContainer[0].style.left = (((to - from) *
+ (Math.floor((timeElap / speed) * 100) / 100)) +
+ from) + 'px';
+ }, 4);
+ },
+
+ preventDefault: function (event) {
+ if (event.preventDefault) {
+ event.preventDefault();
+ } else {
+ event.returnValue = false;
+ }
+ },
+
+ stopPropagation: function (event) {
+ if (event.stopPropagation) {
+ event.stopPropagation();
+ } else {
+ event.cancelBubble = true;
+ }
+ },
+
+ onresize: function () {
+ this.initSlides(true);
+ },
+
+ onmousedown: function (event) {
+ // Trigger on clicks of the left mouse button only
+ // and exclude video elements:
+ if (event.which && event.which === 1 &&
+ event.target.nodeName !== 'VIDEO') {
+ // Preventing the default mousedown action is required
+ // to make touch emulation work with Firefox:
+ event.preventDefault();
+ (event.originalEvent || event).touches = [{
+ pageX: event.pageX,
+ pageY: event.pageY
+ }];
+ this.ontouchstart(event);
+ }
+ },
+
+ onmousemove: function (event) {
+ if (this.touchStart) {
+ (event.originalEvent || event).touches = [{
+ pageX: event.pageX,
+ pageY: event.pageY
+ }];
+ this.ontouchmove(event);
+ }
+ },
+
+ onmouseup: function (event) {
+ if (this.touchStart) {
+ this.ontouchend(event);
+ delete this.touchStart;
+ }
+ },
+
+ onmouseout: function (event) {
+ if (this.touchStart) {
+ var target = event.target,
+ related = event.relatedTarget;
+ if (!related || (related !== target &&
+ !$.contains(target, related))) {
+ this.onmouseup(event);
+ }
+ }
+ },
+
+ ontouchstart: function (event) {
+ if (this.options.stopTouchEventsPropagation) {
+ this.stopPropagation(event);
+ }
+ // jQuery doesn't copy touch event properties by default,
+ // so we have to access the originalEvent object:
+ var touches = (event.originalEvent || event).touches[0];
+ this.touchStart = {
+ // Remember the initial touch coordinates:
+ x: touches.pageX,
+ y: touches.pageY,
+ // Store the time to determine touch duration:
+ time: Date.now()
+ };
+ // Helper variable to detect scroll movement:
+ this.isScrolling = undefined;
+ // Reset delta values:
+ this.touchDelta = {};
+ },
+
+ ontouchmove: function (event) {
+ if (this.options.stopTouchEventsPropagation) {
+ this.stopPropagation(event);
+ }
+ // jQuery doesn't copy touch event properties by default,
+ // so we have to access the originalEvent object:
+ var touches = (event.originalEvent || event).touches[0],
+ scale = (event.originalEvent || event).scale,
+ index = this.index,
+ touchDeltaX,
+ indices;
+ // Ensure this is a one touch swipe and not, e.g. a pinch:
+ if (touches.length > 1 || (scale && scale !== 1)) {
+ return;
+ }
+ if (this.options.disableScroll) {
+ event.preventDefault();
+ }
+ // Measure change in x and y coordinates:
+ this.touchDelta = {
+ x: touches.pageX - this.touchStart.x,
+ y: touches.pageY - this.touchStart.y
+ };
+ touchDeltaX = this.touchDelta.x;
+ // Detect if this is a vertical scroll movement (run only once per touch):
+ if (this.isScrolling === undefined) {
+ this.isScrolling = this.isScrolling ||
+ Math.abs(touchDeltaX) < Math.abs(this.touchDelta.y);
+ }
+ if (!this.isScrolling) {
+ // Always prevent horizontal scroll:
+ event.preventDefault();
+ // Stop the slideshow:
+ window.clearTimeout(this.timeout);
+ if (this.options.continuous) {
+ indices = [
+ this.circle(index + 1),
+ index,
+ this.circle(index - 1)
+ ];
+ } else {
+ // Increase resistance if first slide and sliding left
+ // or last slide and sliding right:
+ this.touchDelta.x = touchDeltaX =
+ touchDeltaX /
+ (((!index && touchDeltaX > 0) ||
+ (index === this.num - 1 && touchDeltaX < 0)) ?
+ (Math.abs(touchDeltaX) / this.slideWidth + 1) : 1);
+ indices = [index];
+ if (index) {
+ indices.push(index - 1);
+ }
+ if (index < this.num - 1) {
+ indices.unshift(index + 1);
+ }
+ }
+ while (indices.length) {
+ index = indices.pop();
+ this.translateX(index, touchDeltaX + this.positions[index], 0);
+ }
+ } else if (this.options.closeOnSwipeUpOrDown) {
+ this.translateY(index, this.touchDelta.y + this.positions[index], 0);
+ }
+ },
+
+ ontouchend: function (event) {
+ if (this.options.stopTouchEventsPropagation) {
+ this.stopPropagation(event);
+ }
+ var index = this.index,
+ speed = this.options.transitionSpeed,
+ slideWidth = this.slideWidth,
+ isShortDuration = Number(Date.now() - this.touchStart.time) < 250,
+ // Determine if slide attempt triggers next/prev slide:
+ isValidSlide = (isShortDuration && Math.abs(this.touchDelta.x) > 20) ||
+ Math.abs(this.touchDelta.x) > slideWidth / 2,
+ // Determine if slide attempt is past start or end:
+ isPastBounds = (!index && this.touchDelta.x > 0) ||
+ (index === this.num - 1 && this.touchDelta.x < 0),
+ isValidClose = !isValidSlide && this.options.closeOnSwipeUpOrDown &&
+ ((isShortDuration && Math.abs(this.touchDelta.y) > 20) ||
+ Math.abs(this.touchDelta.y) > this.slideHeight / 2),
+ direction,
+ indexForward,
+ indexBackward,
+ distanceForward,
+ distanceBackward;
+ if (this.options.continuous) {
+ isPastBounds = false;
+ }
+ // Determine direction of swipe (true: right, false: left):
+ direction = this.touchDelta.x < 0 ? -1 : 1;
+ if (!this.isScrolling) {
+ if (isValidSlide && !isPastBounds) {
+ indexForward = index + direction;
+ indexBackward = index - direction;
+ distanceForward = slideWidth * direction;
+ distanceBackward = -slideWidth * direction;
+ if (this.options.continuous) {
+ this.move(this.circle(indexForward), distanceForward, 0);
+ this.move(this.circle(index - 2 * direction), distanceBackward, 0);
+ } else if (indexForward >= 0 &&
+ indexForward < this.num) {
+ this.move(indexForward, distanceForward, 0);
+ }
+ this.move(index, this.positions[index] + distanceForward, speed);
+ this.move(
+ this.circle(indexBackward),
+ this.positions[this.circle(indexBackward)] + distanceForward,
+ speed
+ );
+ index = this.circle(indexBackward);
+ this.onslide(index);
+ } else {
+ // Move back into position
+ if (this.options.continuous) {
+ this.move(this.circle(index - 1), -slideWidth, speed);
+ this.move(index, 0, speed);
+ this.move(this.circle(index + 1), slideWidth, speed);
+ } else {
+ if (index) {
+ this.move(index - 1, -slideWidth, speed);
+ }
+ this.move(index, 0, speed);
+ if (index < this.num - 1) {
+ this.move(index + 1, slideWidth, speed);
+ }
+ }
+ }
+ } else {
+ if (isValidClose) {
+ this.close();
+ } else {
+ // Move back into position
+ this.translateY(index, 0, speed);
+ }
+ }
+ },
+
+ ontouchcancel: function (event) {
+ if (this.touchStart) {
+ this.ontouchend(event);
+ delete this.touchStart;
+ }
+ },
+
+ ontransitionend: function (event) {
+ var slide = this.slides[this.index];
+ if (!event || slide === event.target) {
+ if (this.interval) {
+ this.play();
+ }
+ this.setTimeout(
+ this.options.onslideend,
+ [this.index, slide]
+ );
+ }
+ },
+
+ oncomplete: function (event) {
+ var target = event.target || event.srcElement,
+ parent = target && target.parentNode,
+ index;
+ if (!target || !parent) {
+ return;
+ }
+ index = this.getNodeIndex(parent);
+ $(parent).removeClass(this.options.slideLoadingClass);
+ if (event.type === 'error') {
+ $(parent).addClass(this.options.slideErrorClass);
+ this.elements[index] = 3; // Fail
+ } else {
+ this.elements[index] = 2; // Done
+ }
+ // Fix for IE7's lack of support for percentage max-height:
+ if (target.clientHeight > this.container[0].clientHeight) {
+ target.style.maxHeight = this.container[0].clientHeight;
+ }
+ if (this.interval && this.slides[this.index] === parent) {
+ this.play();
+ }
+ this.setTimeout(
+ this.options.onslidecomplete,
+ [index, parent]
+ );
+ },
+
+ onload: function (event) {
+ this.oncomplete(event);
+ },
+
+ onerror: function (event) {
+ this.oncomplete(event);
+ },
+
+ onkeydown: function (event) {
+ switch (event.which || event.keyCode) {
+ case 13: // Return
+ if (this.options.toggleControlsOnReturn) {
+ this.preventDefault(event);
+ this.toggleControls();
+ }
+ break;
+ case 27: // Esc
+ if (this.options.closeOnEscape) {
+ this.close();
+ }
+ break;
+ case 32: // Space
+ if (this.options.toggleSlideshowOnSpace) {
+ this.preventDefault(event);
+ this.toggleSlideshow();
+ }
+ break;
+ case 37: // Left
+ if (this.options.enableKeyboardNavigation) {
+ this.preventDefault(event);
+ this.prev();
+ }
+ break;
+ case 39: // Right
+ if (this.options.enableKeyboardNavigation) {
+ this.preventDefault(event);
+ this.next();
+ }
+ break;
+ }
+ },
+
+ handleClick: function (event) {
+ var options = this.options,
+ target = event.target || event.srcElement,
+ parent = target.parentNode,
+ isTarget = function (className) {
+ return $(target).hasClass(className) ||
+ $(parent).hasClass(className);
+ };
+ if (isTarget(options.toggleClass)) {
+ // Click on "toggle" control
+ this.preventDefault(event);
+ this.toggleControls();
+ } else if (isTarget(options.prevClass)) {
+ // Click on "prev" control
+ this.preventDefault(event);
+ this.prev();
+ } else if (isTarget(options.nextClass)) {
+ // Click on "next" control
+ this.preventDefault(event);
+ this.next();
+ } else if (isTarget(options.closeClass)) {
+ // Click on "close" control
+ this.preventDefault(event);
+ this.close();
+ } else if (isTarget(options.playPauseClass)) {
+ // Click on "play-pause" control
+ this.preventDefault(event);
+ this.toggleSlideshow();
+ } else if (parent === this.slidesContainer[0]) {
+ // Click on slide background
+ this.preventDefault(event);
+ if (options.closeOnSlideClick) {
+ this.close();
+ } else {
+ this.toggleControls();
+ }
+ } else if (parent.parentNode &&
+ parent.parentNode === this.slidesContainer[0]) {
+ // Click on displayed element
+ this.preventDefault(event);
+ this.toggleControls();
+ }
+ },
+
+ onclick: function (event) {
+ if (this.options.emulateTouchEvents &&
+ this.touchDelta && (Math.abs(this.touchDelta.x) > 20 ||
+ Math.abs(this.touchDelta.y) > 20)) {
+ delete this.touchDelta;
+ return;
+ }
+ return this.handleClick(event);
+ },
+
+ updateEdgeClasses: function (index) {
+ if (!index) {
+ this.container.addClass(this.options.leftEdgeClass);
+ } else {
+ this.container.removeClass(this.options.leftEdgeClass);
+ }
+ if (index === this.num - 1) {
+ this.container.addClass(this.options.rightEdgeClass);
+ } else {
+ this.container.removeClass(this.options.rightEdgeClass);
+ }
+ },
+
+ handleSlide: function (index) {
+ if (!this.options.continuous) {
+ this.updateEdgeClasses(index);
+ }
+ this.loadElements(index);
+ if (this.options.unloadElements) {
+ this.unloadElements(index);
+ }
+ this.setTitle(index);
+ },
+
+ onslide: function (index) {
+ this.index = index;
+ this.handleSlide(index);
+ this.setTimeout(this.options.onslide, [index, this.slides[index]]);
+ },
+
+ setTitle: function (index) {
+ var text = this.slides[index].firstChild.title,
+ titleElement = this.titleElement;
+ if (titleElement.length) {
+ this.titleElement.empty();
+ if (text) {
+ titleElement[0].appendChild(document.createTextNode(text));
+ }
+ }
+ },
+
+ setTimeout: function (func, args, wait) {
+ var that = this;
+ return func && window.setTimeout(function () {
+ func.apply(that, args || []);
+ }, wait || 0);
+ },
+
+ imageFactory: function (obj, callback) {
+ var that = this,
+ img = this.imagePrototype.cloneNode(false),
+ url = obj,
+ backgroundSize = this.options.stretchImages,
+ called,
+ element,
+ callbackWrapper = function (event) {
+ if (!called) {
+ event = {
+ type: event.type,
+ target: element
+ };
+ if (!element.parentNode) {
+ // Fix for IE7 firing the load event for
+ // cached images before the element could
+ // be added to the DOM:
+ return that.setTimeout(callbackWrapper, [event]);
+ }
+ called = true;
+ $(img).off('load error', callbackWrapper);
+ if (backgroundSize) {
+ if (event.type === 'load') {
+ element.style.background = 'url("' + url +
+ '") center no-repeat';
+ element.style.backgroundSize = backgroundSize;
+ }
+ }
+ callback(event);
+ }
+ },
+ title;
+ if (typeof url !== 'string') {
+ url = this.getItemProperty(obj, this.options.urlProperty);
+ title = this.getItemProperty(obj, this.options.titleProperty);
+ }
+ if (backgroundSize === true) {
+ backgroundSize = 'contain';
+ }
+ backgroundSize = this.support.backgroundSize &&
+ this.support.backgroundSize[backgroundSize] && backgroundSize;
+ if (backgroundSize) {
+ element = this.elementPrototype.cloneNode(false);
+ } else {
+ element = img;
+ img.draggable = false;
+ }
+ if (title) {
+ element.title = title;
+ }
+ $(img).on('load error', callbackWrapper);
+ img.src = url;
+ return element;
+ },
+
+ createElement: function (obj, callback) {
+ var type = obj && this.getItemProperty(obj, this.options.typeProperty),
+ factory = (type && this[type.split('/')[0] + 'Factory']) ||
+ this.imageFactory,
+ element = obj && factory.call(this, obj, callback);
+ if (!element) {
+ element = this.elementPrototype.cloneNode(false);
+ this.setTimeout(callback, [{
+ type: 'error',
+ target: element
+ }]);
+ }
+ $(element).addClass(this.options.slideContentClass);
+ return element;
+ },
+
+ loadElement: function (index) {
+ if (!this.elements[index]) {
+ if (this.slides[index].firstChild) {
+ this.elements[index] = $(this.slides[index])
+ .hasClass(this.options.slideErrorClass) ? 3 : 2;
+ } else {
+ this.elements[index] = 1; // Loading
+ $(this.slides[index]).addClass(this.options.slideLoadingClass);
+ this.slides[index].appendChild(this.createElement(
+ this.list[index],
+ this.proxyListener
+ ));
+ }
+ }
+ },
+
+ loadElements: function (index) {
+ var limit = Math.min(this.num, this.options.preloadRange * 2 + 1),
+ j = index,
+ i;
+ for (i = 0; i < limit; i += 1) {
+ // First load the current slide element (0),
+ // then the next one (+1),
+ // then the previous one (-2),
+ // then the next after next (+2), etc.:
+ j += i * (i % 2 === 0 ? -1 : 1);
+ // Connect the ends of the list to load slide elements for
+ // continuous navigation:
+ j = this.circle(j);
+ this.loadElement(j);
+ }
+ },
+
+ unloadElements: function (index) {
+ var i,
+ slide,
+ diff;
+ for (i in this.elements) {
+ if (this.elements.hasOwnProperty(i)) {
+ diff = Math.abs(index - i);
+ if (diff > this.options.preloadRange &&
+ diff + this.options.preloadRange < this.num) {
+ slide = this.slides[i];
+ slide.removeChild(slide.firstChild);
+ delete this.elements[i];
+ }
+ }
+ }
+ },
+
+ addSlide: function (index) {
+ var slide = this.slidePrototype.cloneNode(false);
+ slide.setAttribute('data-index', index);
+ this.slidesContainer[0].appendChild(slide);
+ this.slides.push(slide);
+ },
+
+ positionSlide: function (index) {
+ var slide = this.slides[index];
+ slide.style.width = this.slideWidth + 'px';
+ if (this.support.transform) {
+ slide.style.left = (index * -this.slideWidth) + 'px';
+ this.move(index, this.index > index ? -this.slideWidth :
+ (this.index < index ? this.slideWidth : 0), 0);
+ }
+ },
+
+ initSlides: function (reload) {
+ var clearSlides,
+ i;
+ if (!reload) {
+ this.positions = [];
+ this.positions.length = this.num;
+ this.elements = {};
+ this.imagePrototype = document.createElement('img');
+ this.elementPrototype = document.createElement('div');
+ this.slidePrototype = document.createElement('div');
+ $(this.slidePrototype).addClass(this.options.slideClass);
+ this.slides = this.slidesContainer[0].children;
+ clearSlides = this.options.clearSlides ||
+ this.slides.length !== this.num;
+ }
+ this.slideWidth = this.container[0].offsetWidth;
+ this.slideHeight = this.container[0].offsetHeight;
+ this.slidesContainer[0].style.width =
+ (this.num * this.slideWidth) + 'px';
+ if (clearSlides) {
+ this.resetSlides();
+ }
+ for (i = 0; i < this.num; i += 1) {
+ if (clearSlides) {
+ this.addSlide(i);
+ }
+ this.positionSlide(i);
+ }
+ // Reposition the slides before and after the given index:
+ if (this.options.continuous && this.support.transform) {
+ this.move(this.circle(this.index - 1), -this.slideWidth, 0);
+ this.move(this.circle(this.index + 1), this.slideWidth, 0);
+ }
+ if (!this.support.transform) {
+ this.slidesContainer[0].style.left =
+ (this.index * -this.slideWidth) + 'px';
+ }
+ },
+
+ toggleControls: function () {
+ var controlsClass = this.options.controlsClass;
+ if (this.container.hasClass(controlsClass)) {
+ this.container.removeClass(controlsClass);
+ } else {
+ this.container.addClass(controlsClass);
+ }
+ },
+
+ toggleSlideshow: function () {
+ if (!this.interval) {
+ this.play();
+ } else {
+ this.pause();
+ }
+ },
+
+ getNodeIndex: function (element) {
+ return parseInt(element.getAttribute('data-index'), 10);
+ },
+
+ getNestedProperty: function (obj, property) {
+ property.replace(
+ // Matches native JavaScript notation in a String,
+ // e.g. '["doubleQuoteProp"].dotProp[2]'
+ /\[(?:'([^']+)'|"([^"]+)"|(\d+))\]|(?:(?:^|\.)([^\.\[]+))/g,
+ function (str, singleQuoteProp, doubleQuoteProp, arrayIndex, dotProp) {
+ var prop = dotProp || singleQuoteProp || doubleQuoteProp ||
+ (arrayIndex && parseInt(arrayIndex, 10));
+ if (str && obj) {
+ obj = obj[prop];
+ }
+ }
+ );
+ return obj;
+ },
+
+ getDataProperty: function (obj, property) {
+ if (obj.getAttribute) {
+ var prop = obj.getAttribute('data-' +
+ property.replace(/([A-Z])/g, '-$1').toLowerCase());
+ if (typeof prop === 'string') {
+ if (/^(true|false|null|-?\d+(\.\d+)?|\{[\s\S]*\}|\[[\s\S]*\])$/
+ .test(prop)) {
+ try {
+ return $.parseJSON(prop);
+ } catch (ignore) {}
+ }
+ return prop;
+ }
+ }
+ },
+
+ getItemProperty: function (obj, property) {
+ var prop = obj[property];
+ if (prop === undefined) {
+ prop = this.getDataProperty(obj, property);
+ if (prop === undefined) {
+ prop = this.getNestedProperty(obj, property);
+ }
+ }
+ return prop;
+ },
+
+ initStartIndex: function () {
+ var index = this.options.index,
+ urlProperty = this.options.urlProperty,
+ i;
+ // Check if the index is given as a list object:
+ if (index && typeof index !== 'number') {
+ for (i = 0; i < this.num; i += 1) {
+ if (this.list[i] === index ||
+ this.getItemProperty(this.list[i], urlProperty) ===
+ this.getItemProperty(index, urlProperty)) {
+ index = i;
+ break;
+ }
+ }
+ }
+ // Make sure the index is in the list range:
+ this.index = this.circle(parseInt(index, 10) || 0);
+ },
+
+ initEventListeners: function () {
+ var that = this,
+ slidesContainer = this.slidesContainer,
+ proxyListener = function (event) {
+ var type = that.support.transition &&
+ that.support.transition.end === event.type ?
+ 'transitionend' : event.type;
+ that['on' + type](event);
+ };
+ $(window).on('resize', proxyListener);
+ $(document.body).on('keydown', proxyListener);
+ this.container.on('click', proxyListener);
+ if (this.support.touch) {
+ slidesContainer
+ .on('touchstart touchmove touchend touchcancel', proxyListener);
+ } else if (this.options.emulateTouchEvents &&
+ this.support.transition) {
+ slidesContainer
+ .on('mousedown mousemove mouseup mouseout', proxyListener);
+ }
+ if (this.support.transition) {
+ slidesContainer.on(
+ this.support.transition.end,
+ proxyListener
+ );
+ }
+ this.proxyListener = proxyListener;
+ },
+
+ destroyEventListeners: function () {
+ var slidesContainer = this.slidesContainer,
+ proxyListener = this.proxyListener;
+ $(window).off('resize', proxyListener);
+ $(document.body).off('keydown', proxyListener);
+ this.container.off('click', proxyListener);
+ if (this.support.touch) {
+ slidesContainer
+ .off('touchstart touchmove touchend touchcancel', proxyListener);
+ } else if (this.options.emulateTouchEvents &&
+ this.support.transition) {
+ slidesContainer
+ .off('mousedown mousemove mouseup mouseout', proxyListener);
+ }
+ if (this.support.transition) {
+ slidesContainer.off(
+ this.support.transition.end,
+ proxyListener
+ );
+ }
+ },
+
+ handleOpen: function () {
+ if (this.options.onopened) {
+ this.options.onopened.call(this);
+ }
+ },
+
+ initWidget: function () {
+ var that = this,
+ openHandler = function (event) {
+ if (event.target === that.container[0]) {
+ that.container.off(
+ that.support.transition.end,
+ openHandler
+ );
+ that.handleOpen();
+ }
+ };
+ this.container = $(this.options.container);
+ if (!this.container.length) {
+ this.console.log(
+ 'blueimp Gallery: Widget container not found.',
+ this.options.container
+ );
+ return false;
+ }
+ this.slidesContainer = this.container.find(
+ this.options.slidesContainer
+ ).first();
+ if (!this.slidesContainer.length) {
+ this.console.log(
+ 'blueimp Gallery: Slides container not found.',
+ this.options.slidesContainer
+ );
+ return false;
+ }
+ this.titleElement = this.container.find(
+ this.options.titleElement
+ ).first();
+ if (this.num === 1) {
+ this.container.addClass(this.options.singleClass);
+ }
+ if (this.options.onopen) {
+ this.options.onopen.call(this);
+ }
+ if (this.support.transition && this.options.displayTransition) {
+ this.container.on(
+ this.support.transition.end,
+ openHandler
+ );
+ } else {
+ this.handleOpen();
+ }
+ if (this.options.hidePageScrollbars) {
+ // Hide the page scrollbars:
+ this.bodyOverflowStyle = document.body.style.overflow;
+ document.body.style.overflow = 'hidden';
+ }
+ this.container[0].style.display = 'block';
+ this.initSlides();
+ this.container.addClass(this.options.displayClass);
+ },
+
+ initOptions: function (options) {
+ // Create a copy of the prototype options:
+ this.options = $.extend({}, this.options);
+ // Check if carousel mode is enabled:
+ if ((options && options.carousel) ||
+ (this.options.carousel && (!options || options.carousel !== false))) {
+ $.extend(this.options, this.carouselOptions);
+ }
+ // Override any given options:
+ $.extend(this.options, options);
+ if (this.num < 3) {
+ // 1 or 2 slides cannot be displayed continuous,
+ // remember the original option by setting to null instead of false:
+ this.options.continuous = this.options.continuous ? null : false;
+ }
+ if (!this.support.transition) {
+ this.options.emulateTouchEvents = false;
+ }
+ if (this.options.event) {
+ this.preventDefault(this.options.event);
+ }
+ }
+
+ });
+
+ return Gallery;
+}));
diff --git a/phpgwapi/js/jquery/blueimp/js/blueimp-gallery.min.js b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery.min.js
new file mode 100644
index 0000000000..c28d4aa63b
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery.min.js
@@ -0,0 +1,2 @@
+!function(){"use strict";function a(a,b){var c;for(c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}function b(a){if(!this||this.find!==b.prototype.find)return new b(a);if(this.length=0,a)if("string"==typeof a&&(a=this.find(a)),a.nodeType||a===a.window)this.length=1,this[0]=a;else{var c=a.length;for(this.length=c;c;)c-=1,this[c]=a[c]}}b.extend=a,b.contains=function(a,b){do if(b=b.parentNode,b===a)return!0;while(b);return!1},b.parseJSON=function(a){return window.JSON&&JSON.parse(a)},a(b.prototype,{find:function(a){var c=this[0]||document;return"string"==typeof a&&(a=c.querySelectorAll?c.querySelectorAll(a):"#"===a.charAt(0)?c.getElementById(a.slice(1)):c.getElementsByTagName(a)),new b(a)},hasClass:function(a){return this[0]?new RegExp("(^|\\s+)"+a+"(\\s+|$)").test(this[0].className):!1},addClass:function(a){for(var b,c=this.length;c;){if(c-=1,b=this[c],!b.className)return b.className=a,this;if(this.hasClass(a))return this;b.className+=" "+a}return this},removeClass:function(a){for(var b,c=new RegExp("(^|\\s+)"+a+"(\\s+|$)"),d=this.length;d;)d-=1,b=this[d],b.className=b.className.replace(c," ");return this},on:function(a,b){for(var c,d,e=a.split(/\s+/);e.length;)for(a=e.shift(),c=this.length;c;)c-=1,d=this[c],d.addEventListener?d.addEventListener(a,b,!1):d.attachEvent&&d.attachEvent("on"+a,b);return this},off:function(a,b){for(var c,d,e=a.split(/\s+/);e.length;)for(a=e.shift(),c=this.length;c;)c-=1,d=this[c],d.removeEventListener?d.removeEventListener(a,b,!1):d.detachEvent&&d.detachEvent("on"+a,b);return this},empty:function(){for(var a,b=this.length;b;)for(b-=1,a=this[b];a.hasChildNodes();)a.removeChild(a.lastChild);return this},first:function(){return new b(this[0])}}),"function"==typeof define&&define.amd?define(function(){return b}):(window.blueimp=window.blueimp||{},window.blueimp.helper=b)}(),function(a){"use strict";"function"==typeof define&&define.amd?define(["./blueimp-helper"],a):(window.blueimp=window.blueimp||{},window.blueimp.Gallery=a(window.blueimp.helper||window.jQuery))}(function(a){"use strict";function b(a,c){return void 0===document.body.style.maxHeight?null:this&&this.options===b.prototype.options?a&&a.length?(this.list=a,this.num=a.length,this.initOptions(c),void this.initialize()):void this.console.log("blueimp Gallery: No or empty list provided as first argument.",a):new b(a,c)}return a.extend(b.prototype,{options:{container:"#blueimp-gallery",slidesContainer:"div",titleElement:"h3",displayClass:"blueimp-gallery-display",controlsClass:"blueimp-gallery-controls",singleClass:"blueimp-gallery-single",leftEdgeClass:"blueimp-gallery-left",rightEdgeClass:"blueimp-gallery-right",playingClass:"blueimp-gallery-playing",slideClass:"slide",slideLoadingClass:"slide-loading",slideErrorClass:"slide-error",slideContentClass:"slide-content",toggleClass:"toggle",prevClass:"prev",nextClass:"next",closeClass:"close",playPauseClass:"play-pause",typeProperty:"type",titleProperty:"title",urlProperty:"href",displayTransition:!0,clearSlides:!0,stretchImages:!1,toggleControlsOnReturn:!0,toggleSlideshowOnSpace:!0,enableKeyboardNavigation:!0,closeOnEscape:!0,closeOnSlideClick:!0,closeOnSwipeUpOrDown:!0,emulateTouchEvents:!0,stopTouchEventsPropagation:!1,hidePageScrollbars:!0,disableScroll:!0,carousel:!1,continuous:!0,unloadElements:!0,startSlideshow:!1,slideshowInterval:5e3,index:0,preloadRange:2,transitionSpeed:400,slideshowTransitionSpeed:void 0,event:void 0,onopen:void 0,onopened:void 0,onslide:void 0,onslideend:void 0,onslidecomplete:void 0,onclose:void 0,onclosed:void 0},carouselOptions:{hidePageScrollbars:!1,toggleControlsOnReturn:!1,toggleSlideshowOnSpace:!1,enableKeyboardNavigation:!1,closeOnEscape:!1,closeOnSlideClick:!1,closeOnSwipeUpOrDown:!1,disableScroll:!1,startSlideshow:!0},console:window.console&&"function"==typeof window.console.log?window.console:{log:function(){}},support:function(b){var c={touch:void 0!==window.ontouchstart||window.DocumentTouch&&document instanceof DocumentTouch},d={webkitTransition:{end:"webkitTransitionEnd",prefix:"-webkit-"},MozTransition:{end:"transitionend",prefix:"-moz-"},OTransition:{end:"otransitionend",prefix:"-o-"},transition:{end:"transitionend",prefix:""}},e=function(){var a,d,e=c.transition;document.body.appendChild(b),e&&(a=e.name.slice(0,-9)+"ransform",void 0!==b.style[a]&&(b.style[a]="translateZ(0)",d=window.getComputedStyle(b).getPropertyValue(e.prefix+"transform"),c.transform={prefix:e.prefix,name:a,translate:!0,translateZ:!!d&&"none"!==d})),void 0!==b.style.backgroundSize&&(c.backgroundSize={},b.style.backgroundSize="contain",c.backgroundSize.contain="contain"===window.getComputedStyle(b).getPropertyValue("background-size"),b.style.backgroundSize="cover",c.backgroundSize.cover="cover"===window.getComputedStyle(b).getPropertyValue("background-size")),document.body.removeChild(b)};return function(a,c){var d;for(d in c)if(c.hasOwnProperty(d)&&void 0!==b.style[d]){a.transition=c[d],a.transition.name=d;break}}(c,d),document.body?e():a(document).on("DOMContentLoaded",e),c}(document.createElement("div")),requestAnimationFrame:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame,initialize:function(){return this.initStartIndex(),this.initWidget()===!1?!1:(this.initEventListeners(),this.onslide(this.index),this.ontransitionend(),void(this.options.startSlideshow&&this.play()))},slide:function(a,b){window.clearTimeout(this.timeout);var c,d,e,f=this.index;if(f!==a&&1!==this.num){if(b||(b=this.options.transitionSpeed),this.support.transform){for(this.options.continuous||(a=this.circle(a)),c=Math.abs(f-a)/(f-a),this.options.continuous&&(d=c,c=-this.positions[this.circle(a)]/this.slideWidth,c!==d&&(a=-c*this.num+a)),e=Math.abs(f-a)-1;e;)e-=1,this.move(this.circle((a>f?a:f)-e-1),this.slideWidth*c,0);a=this.circle(a),this.move(f,this.slideWidth*c,b),this.move(a,0,b),this.options.continuous&&this.move(this.circle(a-c),-(this.slideWidth*c),0)}else a=this.circle(a),this.animate(f*-this.slideWidth,a*-this.slideWidth,b);this.onslide(a)}},getIndex:function(){return this.index},getNumber:function(){return this.num},prev:function(){(this.options.continuous||this.index)&&this.slide(this.index-1)},next:function(){(this.options.continuous||this.index
1&&(this.timeout=this.setTimeout(!this.requestAnimationFrame&&this.slide||function(a,c){b.animationFrameId=b.requestAnimationFrame.call(window,function(){b.slide(a,c)})},[this.index+1,this.options.slideshowTransitionSpeed],this.interval)),this.container.addClass(this.options.playingClass)},pause:function(){window.clearTimeout(this.timeout),this.interval=null,this.container.removeClass(this.options.playingClass)},add:function(a){var b;for(a.concat||(a=Array.prototype.slice.call(a)),this.list.concat||(this.list=Array.prototype.slice.call(this.list)),this.list=this.list.concat(a),this.num=this.list.length,this.num>2&&null===this.options.continuous&&(this.options.continuous=!0,this.container.removeClass(this.options.leftEdgeClass)),this.container.removeClass(this.options.rightEdgeClass).removeClass(this.options.singleClass),b=this.num-a.length;bc?(d.slidesContainer[0].style.left=b+"px",d.ontransitionend(),void window.clearInterval(f)):void(d.slidesContainer[0].style.left=(b-a)*(Math.floor(g/c*100)/100)+a+"px")},4)},preventDefault:function(a){a.preventDefault?a.preventDefault():a.returnValue=!1},stopPropagation:function(a){a.stopPropagation?a.stopPropagation():a.cancelBubble=!0},onresize:function(){this.initSlides(!0)},onmousedown:function(a){a.which&&1===a.which&&"VIDEO"!==a.target.nodeName&&(a.preventDefault(),(a.originalEvent||a).touches=[{pageX:a.pageX,pageY:a.pageY}],this.ontouchstart(a))},onmousemove:function(a){this.touchStart&&((a.originalEvent||a).touches=[{pageX:a.pageX,pageY:a.pageY}],this.ontouchmove(a))},onmouseup:function(a){this.touchStart&&(this.ontouchend(a),delete this.touchStart)},onmouseout:function(b){if(this.touchStart){var c=b.target,d=b.relatedTarget;(!d||d!==c&&!a.contains(c,d))&&this.onmouseup(b)}},ontouchstart:function(a){this.options.stopTouchEventsPropagation&&this.stopPropagation(a);var b=(a.originalEvent||a).touches[0];this.touchStart={x:b.pageX,y:b.pageY,time:Date.now()},this.isScrolling=void 0,this.touchDelta={}},ontouchmove:function(a){this.options.stopTouchEventsPropagation&&this.stopPropagation(a);var b,c,d=(a.originalEvent||a).touches[0],e=(a.originalEvent||a).scale,f=this.index;if(!(d.length>1||e&&1!==e))if(this.options.disableScroll&&a.preventDefault(),this.touchDelta={x:d.pageX-this.touchStart.x,y:d.pageY-this.touchStart.y},b=this.touchDelta.x,void 0===this.isScrolling&&(this.isScrolling=this.isScrolling||Math.abs(b)0||f===this.num-1&&0>b?Math.abs(b)/this.slideWidth+1:1,c=[f],f&&c.push(f-1),f20||Math.abs(this.touchDelta.x)>i/2,l=!g&&this.touchDelta.x>0||g===this.num-1&&this.touchDelta.x<0,m=!k&&this.options.closeOnSwipeUpOrDown&&(j&&Math.abs(this.touchDelta.y)>20||Math.abs(this.touchDelta.y)>this.slideHeight/2);this.options.continuous&&(l=!1),b=this.touchDelta.x<0?-1:1,this.isScrolling?m?this.close():this.translateY(g,0,h):k&&!l?(c=g+b,d=g-b,e=i*b,f=-i*b,this.options.continuous?(this.move(this.circle(c),e,0),this.move(this.circle(g-2*b),f,0)):c>=0&&cthis.container[0].clientHeight&&(d.style.maxHeight=this.container[0].clientHeight),this.interval&&this.slides[this.index]===e&&this.play(),this.setTimeout(this.options.onslidecomplete,[c,e]))},onload:function(a){this.oncomplete(a)},onerror:function(a){this.oncomplete(a)},onkeydown:function(a){switch(a.which||a.keyCode){case 13:this.options.toggleControlsOnReturn&&(this.preventDefault(a),this.toggleControls());break;case 27:this.options.closeOnEscape&&this.close();break;case 32:this.options.toggleSlideshowOnSpace&&(this.preventDefault(a),this.toggleSlideshow());break;case 37:this.options.enableKeyboardNavigation&&(this.preventDefault(a),this.prev());break;case 39:this.options.enableKeyboardNavigation&&(this.preventDefault(a),this.next())}},handleClick:function(b){var c=this.options,d=b.target||b.srcElement,e=d.parentNode,f=function(b){return a(d).hasClass(b)||a(e).hasClass(b)};f(c.toggleClass)?(this.preventDefault(b),this.toggleControls()):f(c.prevClass)?(this.preventDefault(b),this.prev()):f(c.nextClass)?(this.preventDefault(b),this.next()):f(c.closeClass)?(this.preventDefault(b),this.close()):f(c.playPauseClass)?(this.preventDefault(b),this.toggleSlideshow()):e===this.slidesContainer[0]?(this.preventDefault(b),c.closeOnSlideClick?this.close():this.toggleControls()):e.parentNode&&e.parentNode===this.slidesContainer[0]&&(this.preventDefault(b),this.toggleControls())},onclick:function(a){return this.options.emulateTouchEvents&&this.touchDelta&&(Math.abs(this.touchDelta.x)>20||Math.abs(this.touchDelta.y)>20)?void delete this.touchDelta:this.handleClick(a)},updateEdgeClasses:function(a){a?this.container.removeClass(this.options.leftEdgeClass):this.container.addClass(this.options.leftEdgeClass),a===this.num-1?this.container.addClass(this.options.rightEdgeClass):this.container.removeClass(this.options.rightEdgeClass)},handleSlide:function(a){this.options.continuous||this.updateEdgeClasses(a),this.loadElements(a),this.options.unloadElements&&this.unloadElements(a),this.setTitle(a)},onslide:function(a){this.index=a,this.handleSlide(a),this.setTimeout(this.options.onslide,[a,this.slides[a]])},setTitle:function(a){var b=this.slides[a].firstChild.title,c=this.titleElement;c.length&&(this.titleElement.empty(),b&&c[0].appendChild(document.createTextNode(b)))},setTimeout:function(a,b,c){var d=this;return a&&window.setTimeout(function(){a.apply(d,b||[])},c||0)},imageFactory:function(b,c){var d,e,f,g=this,h=this.imagePrototype.cloneNode(!1),i=b,j=this.options.stretchImages,k=function(b){if(!d){if(b={type:b.type,target:e},!e.parentNode)return g.setTimeout(k,[b]);d=!0,a(h).off("load error",k),j&&"load"===b.type&&(e.style.background='url("'+i+'") center no-repeat',e.style.backgroundSize=j),c(b)}};return"string"!=typeof i&&(i=this.getItemProperty(b,this.options.urlProperty),f=this.getItemProperty(b,this.options.titleProperty)),j===!0&&(j="contain"),j=this.support.backgroundSize&&this.support.backgroundSize[j]&&j,j?e=this.elementPrototype.cloneNode(!1):(e=h,h.draggable=!1),f&&(e.title=f),a(h).on("load error",k),h.src=i,e},createElement:function(b,c){var d=b&&this.getItemProperty(b,this.options.typeProperty),e=d&&this[d.split("/")[0]+"Factory"]||this.imageFactory,f=b&&e.call(this,b,c);return f||(f=this.elementPrototype.cloneNode(!1),this.setTimeout(c,[{type:"error",target:f}])),a(f).addClass(this.options.slideContentClass),f},loadElement:function(b){this.elements[b]||(this.slides[b].firstChild?this.elements[b]=a(this.slides[b]).hasClass(this.options.slideErrorClass)?3:2:(this.elements[b]=1,a(this.slides[b]).addClass(this.options.slideLoadingClass),this.slides[b].appendChild(this.createElement(this.list[b],this.proxyListener))))},loadElements:function(a){var b,c=Math.min(this.num,2*this.options.preloadRange+1),d=a;for(b=0;c>b;b+=1)d+=b*(b%2===0?-1:1),d=this.circle(d),this.loadElement(d)},unloadElements:function(a){var b,c,d;for(b in this.elements)this.elements.hasOwnProperty(b)&&(d=Math.abs(a-b),d>this.options.preloadRange&&d+this.options.preloadRangea?-this.slideWidth:this.index ')
+ .append($(' ').prop('src', baseUrl + '_s.jpg'))
+ .prop('href', baseUrl + '_b.jpg')
+ .prop('title', photo.title)
+ .attr('data-gallery', '')
+ .appendTo(linksContainer);
+ carouselLinks.push({
+ href: baseUrl + '_c.jpg',
+ title: photo.title
+ });
+ });
+ // Initialize the Gallery as image carousel:
+ blueimp.Gallery(carouselLinks, {
+ container: '#blueimp-image-carousel',
+ carousel: true
+ });
+ });
+
+ // Initialize the Gallery as video carousel:
+ blueimp.Gallery([
+ {
+ title: 'Sintel',
+ href: 'https://archive.org/download/Sintel/sintel-2048-surround_512kb.mp4',
+ type: 'video/mp4',
+ poster: 'https://i.imgur.com/MUSw4Zu.jpg'
+ },
+ {
+ title: 'Big Buck Bunny',
+ href: 'https://upload.wikimedia.org/wikipedia/commons/7/75/' +
+ 'Big_Buck_Bunny_Trailer_400p.ogg',
+ type: 'video/ogg',
+ poster: 'https://upload.wikimedia.org/wikipedia/commons/thumb/7/70/' +
+ 'Big.Buck.Bunny.-.Opening.Screen.png/' +
+ '800px-Big.Buck.Bunny.-.Opening.Screen.png'
+ },
+ {
+ title: 'Elephants Dream',
+ href: 'https://upload.wikimedia.org/wikipedia/commons/transcoded/8/83/' +
+ 'Elephants_Dream_%28high_quality%29.ogv/' +
+ 'Elephants_Dream_%28high_quality%29.ogv.360p.webm',
+ type: 'video/webm',
+ poster: 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/90/' +
+ 'Elephants_Dream_s1_proog.jpg/800px-Elephants_Dream_s1_proog.jpg'
+ },
+ {
+ title: 'LES TWINS - An Industry Ahead',
+ type: 'text/html',
+ youtube: 'zi4CIXpx7Bg'
+ },
+ {
+ title: 'KN1GHT - Last Moon',
+ type: 'text/html',
+ vimeo: '73686146',
+ poster: 'https://secure-a.vimeocdn.com/ts/448/835/448835699_960.jpg'
+ }
+ ], {
+ container: '#blueimp-video-carousel',
+ carousel: true
+ });
+
+});
diff --git a/phpgwapi/js/jquery/blueimp/js/jquery.blueimp-gallery.js b/phpgwapi/js/jquery/blueimp/js/jquery.blueimp-gallery.js
new file mode 100644
index 0000000000..e9e6d003b3
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/js/jquery.blueimp-gallery.js
@@ -0,0 +1,84 @@
+/*
+ * blueimp Gallery jQuery plugin 1.2.2
+ * https://github.com/blueimp/Gallery
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* global define, window, document */
+
+(function (factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ define([
+ 'jquery',
+ './blueimp-gallery'
+ ], factory);
+ } else {
+ factory(
+ window.jQuery,
+ window.blueimp.Gallery
+ );
+ }
+}(function ($, Gallery) {
+ 'use strict';
+
+ // Global click handler to open links with data-gallery attribute
+ // in the Gallery lightbox:
+ $(document).on('click', '[data-gallery]', function (event) {
+ // Get the container id from the data-gallery attribute:
+ var id = $(this).data('gallery'),
+ widget = $(id),
+ container = (widget.length && widget) ||
+ $(Gallery.prototype.options.container),
+ callbacks = {
+ onopen: function () {
+ container
+ .data('gallery', this)
+ .trigger('open');
+ },
+ onopened: function () {
+ container.trigger('opened');
+ },
+ onslide: function () {
+ container.trigger('slide', arguments);
+ },
+ onslideend: function () {
+ container.trigger('slideend', arguments);
+ },
+ onslidecomplete: function () {
+ container.trigger('slidecomplete', arguments);
+ },
+ onclose: function () {
+ container.trigger('close');
+ },
+ onclosed: function () {
+ container
+ .trigger('closed')
+ .removeData('gallery');
+ }
+ },
+ options = $.extend(
+ // Retrieve custom options from data-attributes
+ // on the Gallery widget:
+ container.data(),
+ {
+ container: container[0],
+ index: this,
+ event: event
+ },
+ callbacks
+ ),
+ // Select all links with the same data-gallery attribute:
+ links = $('[data-gallery="' + id + '"]');
+ if (options.filter) {
+ links = links.filter(options.filter);
+ }
+ return new Gallery(links, options);
+ });
+
+}));
diff --git a/phpgwapi/js/jquery/blueimp/js/jquery.blueimp-gallery.min.js b/phpgwapi/js/jquery/blueimp/js/jquery.blueimp-gallery.min.js
new file mode 100644
index 0000000000..161c1a1683
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/js/jquery.blueimp-gallery.min.js
@@ -0,0 +1,789 @@
+!
+function(a) {
+ "use strict";
+ "function" == typeof define && define.amd ? define(["./blueimp-helper"], a) : (window.blueimp = window.blueimp || {}, window.blueimp.Gallery = a(window.blueimp.helper || window.jQuery))
+}(function(a) {
+ "use strict";
+
+ function b(a, c) {
+ return void 0 === document.body.style.maxHeight ? null : this && this.options === b.prototype.options ? a && a.length ? (this.list = a, this.num = a.length, this.initOptions(c), void this.initialize()) : void this.console.log("blueimp Gallery: No or empty list provided as first argument.", a) : new b(a, c)
+ }
+ return a.extend(b.prototype, {
+ options: {
+ container: "#blueimp-gallery",
+ slidesContainer: "div",
+ titleElement: "h3",
+ displayClass: "blueimp-gallery-display",
+ controlsClass: "blueimp-gallery-controls",
+ singleClass: "blueimp-gallery-single",
+ leftEdgeClass: "blueimp-gallery-left",
+ rightEdgeClass: "blueimp-gallery-right",
+ playingClass: "blueimp-gallery-playing",
+ slideClass: "slide",
+ slideLoadingClass: "slide-loading",
+ slideErrorClass: "slide-error",
+ slideContentClass: "slide-content",
+ toggleClass: "toggle",
+ prevClass: "prev",
+ nextClass: "next",
+ closeClass: "close",
+ playPauseClass: "play-pause",
+ typeProperty: "type",
+ titleProperty: "title",
+ urlProperty: "href",
+ displayTransition: !0,
+ clearSlides: !0,
+ stretchImages: !1,
+ toggleControlsOnReturn: !0,
+ toggleSlideshowOnSpace: !0,
+ enableKeyboardNavigation: !0,
+ closeOnEscape: !0,
+ closeOnSlideClick: !0,
+ closeOnSwipeUpOrDown: !0,
+ emulateTouchEvents: !0,
+ stopTouchEventsPropagation: !1,
+ hidePageScrollbars: !0,
+ disableScroll: !0,
+ carousel: !1,
+ continuous: !0,
+ unloadElements: !0,
+ startSlideshow: !1,
+ slideshowInterval: 5e3,
+ index: 0,
+ preloadRange: 2,
+ transitionSpeed: 400,
+ slideshowTransitionSpeed: void 0,
+ event: void 0,
+ onopen: void 0,
+ onopened: void 0,
+ onslide: void 0,
+ onslideend: void 0,
+ onslidecomplete: void 0,
+ onclose: void 0,
+ onclosed: void 0
+ },
+ carouselOptions: {
+ hidePageScrollbars: !1,
+ toggleControlsOnReturn: !1,
+ toggleSlideshowOnSpace: !1,
+ enableKeyboardNavigation: !1,
+ closeOnEscape: !1,
+ closeOnSlideClick: !1,
+ closeOnSwipeUpOrDown: !1,
+ disableScroll: !1,
+ startSlideshow: !0
+ },
+ console: window.console && "function" == typeof window.console.log ? window.console : {
+ log: function() {}
+ },
+ support: function(b) {
+ var c = {
+ touch: void 0 !== window.ontouchstart || window.DocumentTouch && document instanceof DocumentTouch
+ },
+ d = {
+ webkitTransition: {
+ end: "webkitTransitionEnd",
+ prefix: "-webkit-"
+ },
+ MozTransition: {
+ end: "transitionend",
+ prefix: "-moz-"
+ },
+ OTransition: {
+ end: "otransitionend",
+ prefix: "-o-"
+ },
+ transition: {
+ end: "transitionend",
+ prefix: ""
+ }
+ },
+ e = function() {
+ var a, d, e = c.transition;
+ document.body.appendChild(b), e && (a = e.name.slice(0, -9) + "ransform", void 0 !== b.style[a] && (b.style[a] = "translateZ(0)", d = window.getComputedStyle(b).getPropertyValue(e.prefix + "transform"), c.transform = {
+ prefix: e.prefix,
+ name: a,
+ translate: !0,
+ translateZ: !! d && "none" !== d
+ })), void 0 !== b.style.backgroundSize && (c.backgroundSize = {}, b.style.backgroundSize = "contain", c.backgroundSize.contain = "contain" === window.getComputedStyle(b).getPropertyValue("background-size"), b.style.backgroundSize = "cover", c.backgroundSize.cover = "cover" === window.getComputedStyle(b).getPropertyValue("background-size")), document.body.removeChild(b)
+ };
+ return function(a, c) {
+ var d;
+ for (d in c) if (c.hasOwnProperty(d) && void 0 !== b.style[d]) {
+ a.transition = c[d], a.transition.name = d;
+ break
+ }
+ }(c, d), document.body ? e() : a(document).on("DOMContentLoaded", e), c
+ }(document.createElement("div")),
+ requestAnimationFrame: window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame,
+ initialize: function() {
+ return this.initStartIndex(), this.initWidget() === !1 ? !1 : (this.initEventListeners(), this.onslide(this.index), this.ontransitionend(), void(this.options.startSlideshow && this.play()))
+ },
+ slide: function(a, b) {
+ window.clearTimeout(this.timeout);
+ var c, d, e, f = this.index;
+ if (f !== a && 1 !== this.num) {
+ if (b || (b = this.options.transitionSpeed), this.support.transform) {
+ for (this.options.continuous || (a = this.circle(a)), c = Math.abs(f - a) / (f - a), this.options.continuous && (d = c, c = -this.positions[this.circle(a)] / this.slideWidth, c !== d && (a = -c * this.num + a)), e = Math.abs(f - a) - 1; e;) e -= 1, this.move(this.circle((a > f ? a : f) - e - 1), this.slideWidth * c, 0);
+ a = this.circle(a), this.move(f, this.slideWidth * c, b), this.move(a, 0, b), this.options.continuous && this.move(this.circle(a - c), -(this.slideWidth * c), 0)
+ } else a = this.circle(a), this.animate(f * -this.slideWidth, a * -this.slideWidth, b);
+ this.onslide(a)
+ }
+ },
+ getIndex: function() {
+ return this.index
+ },
+ getNumber: function() {
+ return this.num
+ },
+ prev: function() {
+ (this.options.continuous || this.index) && this.slide(this.index - 1)
+ },
+ next: function() {
+ (this.options.continuous || this.index < this.num - 1) && this.slide(this.index + 1)
+ },
+ play: function(a) {
+ var b = this;
+ window.clearTimeout(this.timeout), this.interval = a || this.options.slideshowInterval, this.elements[this.index] > 1 && (this.timeout = this.setTimeout(!this.requestAnimationFrame && this.slide ||
+ function(a, c) {
+ b.animationFrameId = b.requestAnimationFrame.call(window, function() {
+ b.slide(a, c)
+ })
+ }, [this.index + 1, this.options.slideshowTransitionSpeed], this.interval)), this.container.addClass(this.options.playingClass)
+ },
+ pause: function() {
+ window.clearTimeout(this.timeout), this.interval = null, this.container.removeClass(this.options.playingClass)
+ },
+ add: function(a) {
+ var b;
+ for (a.concat || (a = Array.prototype.slice.call(a)), this.list.concat || (this.list = Array.prototype.slice.call(this.list)), this.list = this.list.concat(a), this.num = this.list.length, this.num > 2 && null === this.options.continuous && (this.options.continuous = !0, this.container.removeClass(this.options.leftEdgeClass)), this.container.removeClass(this.options.rightEdgeClass).removeClass(this.options.singleClass), b = this.num - a.length; b < this.num; b += 1) this.addSlide(b), this.positionSlide(b);
+ this.positions.length = this.num, this.initSlides(!0)
+ },
+ resetSlides: function() {
+ this.slidesContainer.empty(), this.slides = []
+ },
+ handleClose: function() {
+ var a = this.options;
+ this.destroyEventListeners(), this.pause(), this.container[0].style.display = "none", this.container.removeClass(a.displayClass).removeClass(a.singleClass).removeClass(a.leftEdgeClass).removeClass(a.rightEdgeClass), a.hidePageScrollbars && (document.body.style.overflow = this.bodyOverflowStyle), this.options.clearSlides && this.resetSlides(), this.options.onclosed && this.options.onclosed.call(this)
+ },
+ close: function() {
+ var a = this,
+ b = function(c) {
+ c.target === a.container[0] && (a.container.off(a.support.transition.end, b), a.handleClose())
+ };
+ this.options.onclose && this.options.onclose.call(this), this.support.transition && this.options.displayTransition ? (this.container.on(this.support.transition.end, b), this.container.removeClass(this.options.displayClass)) : this.handleClose()
+ },
+ circle: function(a) {
+ return (this.num + a % this.num) % this.num
+ },
+ move: function(a, b, c) {
+ this.translateX(a, b, c), this.positions[a] = b
+ },
+ translate: function(a, b, c, d) {
+ var e = this.slides[a].style,
+ f = this.support.transition,
+ g = this.support.transform;
+ e[f.name + "Duration"] = d + "ms", e[g.name] = "translate(" + b + "px, " + c + "px)" + (g.translateZ ? " translateZ(0)" : "")
+ },
+ translateX: function(a, b, c) {
+ this.translate(a, b, 0, c)
+ },
+ translateY: function(a, b, c) {
+ this.translate(a, 0, b, c)
+ },
+ animate: function(a, b, c) {
+ if (!c) return void(this.slidesContainer[0].style.left = b + "px");
+ var d = this,
+ e = (new Date).getTime(),
+ f = window.setInterval(function() {
+ var g = (new Date).getTime() - e;
+ return g > c ? (d.slidesContainer[0].style.left = b + "px", d.ontransitionend(), void window.clearInterval(f)) : void(d.slidesContainer[0].style.left = (b - a) * (Math.floor(g / c * 100) / 100) + a + "px")
+ }, 4)
+ },
+ preventDefault: function(a) {
+ a.preventDefault ? a.preventDefault() : a.returnValue = !1
+ },
+ stopPropagation: function(a) {
+ a.stopPropagation ? a.stopPropagation() : a.cancelBubble = !0
+ },
+ onresize: function() {
+ this.initSlides(!0)
+ },
+ onmousedown: function(a) {
+ a.which && 1 === a.which && "VIDEO" !== a.target.nodeName && (a.preventDefault(), (a.originalEvent || a).touches = [{
+ pageX: a.pageX,
+ pageY: a.pageY
+ }], this.ontouchstart(a))
+ },
+ onmousemove: function(a) {
+ this.touchStart && ((a.originalEvent || a).touches = [{
+ pageX: a.pageX,
+ pageY: a.pageY
+ }], this.ontouchmove(a))
+ },
+ onmouseup: function(a) {
+ this.touchStart && (this.ontouchend(a), delete this.touchStart)
+ },
+ onmouseout: function(b) {
+ if (this.touchStart) {
+ var c = b.target,
+ d = b.relatedTarget;
+ (!d || d !== c && !a.contains(c, d)) && this.onmouseup(b)
+ }
+ },
+ ontouchstart: function(a) {
+ this.options.stopTouchEventsPropagation && this.stopPropagation(a);
+ var b = (a.originalEvent || a).touches[0];
+ this.touchStart = {
+ x: b.pageX,
+ y: b.pageY,
+ time: Date.now()
+ }, this.isScrolling = void 0, this.touchDelta = {}
+ },
+ ontouchmove: function(a) {
+ this.options.stopTouchEventsPropagation && this.stopPropagation(a);
+ var b, c, d = (a.originalEvent || a).touches[0],
+ e = (a.originalEvent || a).scale,
+ f = this.index;
+ if (!(d.length > 1 || e && 1 !== e)) if (this.options.disableScroll && a.preventDefault(), this.touchDelta = {
+ x: d.pageX - this.touchStart.x,
+ y: d.pageY - this.touchStart.y
+ }, b = this.touchDelta.x, void 0 === this.isScrolling && (this.isScrolling = this.isScrolling || Math.abs(b) < Math.abs(this.touchDelta.y)), this.isScrolling) this.options.closeOnSwipeUpOrDown && this.translateY(f, this.touchDelta.y + this.positions[f], 0);
+ else for (a.preventDefault(), window.clearTimeout(this.timeout), this.options.continuous ? c = [this.circle(f + 1), f, this.circle(f - 1)] : (this.touchDelta.x = b /= !f && b > 0 || f === this.num - 1 && 0 > b ? Math.abs(b) / this.slideWidth + 1 : 1, c = [f], f && c.push(f - 1), f < this.num - 1 && c.unshift(f + 1)); c.length;) f = c.pop(), this.translateX(f, b + this.positions[f], 0)
+ },
+ ontouchend: function(a) {
+ this.options.stopTouchEventsPropagation && this.stopPropagation(a);
+ var b, c, d, e, f, g = this.index,
+ h = this.options.transitionSpeed,
+ i = this.slideWidth,
+ j = Number(Date.now() - this.touchStart.time) < 250,
+ k = j && Math.abs(this.touchDelta.x) > 20 || Math.abs(this.touchDelta.x) > i / 2,
+ l = !g && this.touchDelta.x > 0 || g === this.num - 1 && this.touchDelta.x < 0,
+ m = !k && this.options.closeOnSwipeUpOrDown && (j && Math.abs(this.touchDelta.y) > 20 || Math.abs(this.touchDelta.y) > this.slideHeight / 2);
+ this.options.continuous && (l = !1), b = this.touchDelta.x < 0 ? -1 : 1, this.isScrolling ? m ? this.close() : this.translateY(g, 0, h) : k && !l ? (c = g + b, d = g - b, e = i * b, f = -i * b, this.options.continuous ? (this.move(this.circle(c), e, 0), this.move(this.circle(g - 2 * b), f, 0)) : c >= 0 && c < this.num && this.move(c, e, 0), this.move(g, this.positions[g] + e, h), this.move(this.circle(d), this.positions[this.circle(d)] + e, h), g = this.circle(d), this.onslide(g)) : this.options.continuous ? (this.move(this.circle(g - 1), -i, h), this.move(g, 0, h), this.move(this.circle(g + 1), i, h)) : (g && this.move(g - 1, -i, h), this.move(g, 0, h), g < this.num - 1 && this.move(g + 1, i, h))
+ },
+ ontouchcancel: function(a) {
+ this.touchStart && (this.ontouchend(a), delete this.touchStart)
+ },
+ ontransitionend: function(a) {
+ var b = this.slides[this.index];
+ a && b !== a.target || (this.interval && this.play(), this.setTimeout(this.options.onslideend, [this.index, b]))
+ },
+ oncomplete: function(b) {
+ var c, d = b.target || b.srcElement,
+ e = d && d.parentNode;
+ d && e && (c = this.getNodeIndex(e), a(e).removeClass(this.options.slideLoadingClass), "error" === b.type ? (a(e).addClass(this.options.slideErrorClass), this.elements[c] = 3) : this.elements[c] = 2, d.clientHeight > this.container[0].clientHeight && (d.style.maxHeight = this.container[0].clientHeight), this.interval && this.slides[this.index] === e && this.play(), this.setTimeout(this.options.onslidecomplete, [c, e]))
+ },
+ onload: function(a) {
+ this.oncomplete(a)
+ },
+ onerror: function(a) {
+ this.oncomplete(a)
+ },
+ onkeydown: function(a) {
+ switch (a.which || a.keyCode) {
+ case 13:
+ this.options.toggleControlsOnReturn && (this.preventDefault(a), this.toggleControls());
+ break;
+ case 27:
+ this.options.closeOnEscape && this.close();
+ break;
+ case 32:
+ this.options.toggleSlideshowOnSpace && (this.preventDefault(a), this.toggleSlideshow());
+ break;
+ case 37:
+ this.options.enableKeyboardNavigation && (this.preventDefault(a), this.prev());
+ break;
+ case 39:
+ this.options.enableKeyboardNavigation && (this.preventDefault(a), this.next())
+ }
+ },
+ handleClick: function(b) {
+ var c = this.options,
+ d = b.target || b.srcElement,
+ e = d.parentNode,
+ f = function(b) {
+ return a(d).hasClass(b) || a(e).hasClass(b)
+ };
+ f(c.toggleClass) ? (this.preventDefault(b), this.toggleControls()) : f(c.prevClass) ? (this.preventDefault(b), this.prev()) : f(c.nextClass) ? (this.preventDefault(b), this.next()) : f(c.closeClass) ? (this.preventDefault(b), this.close()) : f(c.playPauseClass) ? (this.preventDefault(b), this.toggleSlideshow()) : e === this.slidesContainer[0] ? (this.preventDefault(b), c.closeOnSlideClick ? this.close() : this.toggleControls()) : e.parentNode && e.parentNode === this.slidesContainer[0] && (this.preventDefault(b), this.toggleControls())
+ },
+ onclick: function(a) {
+ return this.options.emulateTouchEvents && this.touchDelta && (Math.abs(this.touchDelta.x) > 20 || Math.abs(this.touchDelta.y) > 20) ? void delete this.touchDelta : this.handleClick(a)
+ },
+ updateEdgeClasses: function(a) {
+ a ? this.container.removeClass(this.options.leftEdgeClass) : this.container.addClass(this.options.leftEdgeClass), a === this.num - 1 ? this.container.addClass(this.options.rightEdgeClass) : this.container.removeClass(this.options.rightEdgeClass)
+ },
+ handleSlide: function(a) {
+ this.options.continuous || this.updateEdgeClasses(a), this.loadElements(a), this.options.unloadElements && this.unloadElements(a), this.setTitle(a)
+ },
+ onslide: function(a) {
+ this.index = a, this.handleSlide(a), this.setTimeout(this.options.onslide, [a, this.slides[a]])
+ },
+ setTitle: function(a) {
+ var b = this.slides[a].firstChild.title,
+ c = this.titleElement;
+ c.length && (this.titleElement.empty(), b && c[0].appendChild(document.createTextNode(b)))
+ },
+ setTimeout: function(a, b, c) {
+ var d = this;
+ return a && window.setTimeout(function() {
+ a.apply(d, b || [])
+ }, c || 0)
+ },
+ imageFactory: function(b, c) {
+ var d, e, f, g = this,
+ h = this.imagePrototype.cloneNode(!1),
+ i = b,
+ j = this.options.stretchImages,
+ k = function(b) {
+ if (!d) {
+ if (b = {
+ type: b.type,
+ target: e
+ }, !e.parentNode) return g.setTimeout(k, [b]);
+ d = !0, a(h).off("load error", k), j && "load" === b.type && (e.style.background = 'url("' + i + '") center no-repeat', e.style.backgroundSize = j), c(b)
+ }
+ };
+ return "string" != typeof i && (i = this.getItemProperty(b, this.options.urlProperty), f = this.getItemProperty(b, this.options.titleProperty)), j === !0 && (j = "contain"), j = this.support.backgroundSize && this.support.backgroundSize[j] && j, j ? e = this.elementPrototype.cloneNode(!1) : (e = h, h.draggable = !1), f && (e.title = f), a(h).on("load error", k), h.src = i, e
+ },
+ createElement: function(b, c) {
+ var d = b && this.getItemProperty(b, this.options.typeProperty),
+ e = d && this[d.split("/")[0] + "Factory"] || this.imageFactory,
+ f = b && e.call(this, b, c);
+ return f || (f = this.elementPrototype.cloneNode(!1), this.setTimeout(c, [{
+ type: "error",
+ target: f
+ }])), a(f).addClass(this.options.slideContentClass), f
+ },
+ loadElement: function(b) {
+ this.elements[b] || (this.slides[b].firstChild ? this.elements[b] = a(this.slides[b]).hasClass(this.options.slideErrorClass) ? 3 : 2 : (this.elements[b] = 1, a(this.slides[b]).addClass(this.options.slideLoadingClass), this.slides[b].appendChild(this.createElement(this.list[b], this.proxyListener))))
+ },
+ loadElements: function(a) {
+ var b, c = Math.min(this.num, 2 * this.options.preloadRange + 1),
+ d = a;
+ for (b = 0; c > b; b += 1) d += b * (b % 2 === 0 ? -1 : 1), d = this.circle(d), this.loadElement(d)
+ },
+ unloadElements: function(a) {
+ var b, c, d;
+ for (b in this.elements) this.elements.hasOwnProperty(b) && (d = Math.abs(a - b), d > this.options.preloadRange && d + this.options.preloadRange < this.num && (c = this.slides[b], c.removeChild(c.firstChild), delete this.elements[b]))
+ },
+ addSlide: function(a) {
+ var b = this.slidePrototype.cloneNode(!1);
+ b.setAttribute("data-index", a), this.slidesContainer[0].appendChild(b), this.slides.push(b)
+ },
+ positionSlide: function(a) {
+ var b = this.slides[a];
+ b.style.width = this.slideWidth + "px", this.support.transform && (b.style.left = a * -this.slideWidth + "px", this.move(a, this.index > a ? -this.slideWidth : this.index < a ? this.slideWidth : 0, 0))
+ },
+ initSlides: function(b) {
+ var c, d;
+ for (b || (this.positions = [], this.positions.length = this.num, this.elements = {}, this.imagePrototype = document.createElement("img"), this.elementPrototype = document.createElement("div"), this.slidePrototype = document.createElement("div"), a(this.slidePrototype).addClass(this.options.slideClass), this.slides = this.slidesContainer[0].children, c = this.options.clearSlides || this.slides.length !== this.num), this.slideWidth = this.container[0].offsetWidth, this.slideHeight = this.container[0].offsetHeight, this.slidesContainer[0].style.width = this.num * this.slideWidth + "px", c && this.resetSlides(), d = 0; d < this.num; d += 1) c && this.addSlide(d), this.positionSlide(d);
+ this.options.continuous && this.support.transform && (this.move(this.circle(this.index - 1), -this.slideWidth, 0), this.move(this.circle(this.index + 1), this.slideWidth, 0)), this.support.transform || (this.slidesContainer[0].style.left = this.index * -this.slideWidth + "px")
+ },
+ toggleControls: function() {
+ var a = this.options.controlsClass;
+ this.container.hasClass(a) ? this.container.removeClass(a) : this.container.addClass(a)
+ },
+ toggleSlideshow: function() {
+ this.interval ? this.pause() : this.play()
+ },
+ getNodeIndex: function(a) {
+ return parseInt(a.getAttribute("data-index"), 10)
+ },
+ getNestedProperty: function(a, b) {
+ return b.replace(/\[(?:'([^']+)'|"([^"]+)"|(\d+))\]|(?:(?:^|\.)([^\.\[]+))/g, function(b, c, d, e, f) {
+ var g = f || c || d || e && parseInt(e, 10);
+ b && a && (a = a[g])
+ }), a
+ },
+ getDataProperty: function(b, c) {
+ if (b.getAttribute) {
+ var d = b.getAttribute("data-" + c.replace(/([A-Z])/g, "-$1").toLowerCase());
+ if ("string" == typeof d) {
+ if (/^(true|false|null|-?\d+(\.\d+)?|\{[\s\S]*\}|\[[\s\S]*\])$/.test(d)) try {
+ return a.parseJSON(d)
+ } catch (e) {}
+ return d
+ }
+ }
+ },
+ getItemProperty: function(a, b) {
+ var c = a[b];
+ return void 0 === c && (c = this.getDataProperty(a, b), void 0 === c && (c = this.getNestedProperty(a, b))), c
+ },
+ initStartIndex: function() {
+ var a, b = this.options.index,
+ c = this.options.urlProperty;
+ if (b && "number" != typeof b) for (a = 0; a < this.num; a += 1) if (this.list[a] === b || this.getItemProperty(this.list[a], c) === this.getItemProperty(b, c)) {
+ b = a;
+ break
+ }
+ this.index = this.circle(parseInt(b, 10) || 0)
+ },
+ initEventListeners: function() {
+ var b = this,
+ c = this.slidesContainer,
+ d = function(a) {
+ var c = b.support.transition && b.support.transition.end === a.type ? "transitionend" : a.type;
+ b["on" + c](a)
+ };
+ a(window).on("resize", d), a(document.body).on("keydown", d), this.container.on("click", d), this.support.touch ? c.on("touchstart touchmove touchend touchcancel", d) : this.options.emulateTouchEvents && this.support.transition && c.on("mousedown mousemove mouseup mouseout", d), this.support.transition && c.on(this.support.transition.end, d), this.proxyListener = d
+ },
+ destroyEventListeners: function() {
+ var b = this.slidesContainer,
+ c = this.proxyListener;
+ a(window).off("resize", c), a(document.body).off("keydown", c), this.container.off("click", c), this.support.touch ? b.off("touchstart touchmove touchend touchcancel", c) : this.options.emulateTouchEvents && this.support.transition && b.off("mousedown mousemove mouseup mouseout", c), this.support.transition && b.off(this.support.transition.end, c)
+ },
+ handleOpen: function() {
+ this.options.onopened && this.options.onopened.call(this)
+ },
+ initWidget: function() {
+ var b = this,
+ c = function(a) {
+ a.target === b.container[0] && (b.container.off(b.support.transition.end, c), b.handleOpen())
+ };
+ return this.container = a(this.options.container), this.container.length ? (this.slidesContainer = this.container.find(this.options.slidesContainer).first(), this.slidesContainer.length ? (this.titleElement = this.container.find(this.options.titleElement).first(), 1 === this.num && this.container.addClass(this.options.singleClass), this.options.onopen && this.options.onopen.call(this), this.support.transition && this.options.displayTransition ? this.container.on(this.support.transition.end, c) : this.handleOpen(), this.options.hidePageScrollbars && (this.bodyOverflowStyle = document.body.style.overflow, document.body.style.overflow = "hidden"), this.container[0].style.display = "block", this.initSlides(), void this.container.addClass(this.options.displayClass)) : (this.console.log("blueimp Gallery: Slides container not found.", this.options.slidesContainer), !1)) : (this.console.log("blueimp Gallery: Widget container not found.", this.options.container), !1)
+ },
+ initOptions: function(b) {
+ this.options = a.extend({}, this.options), (b && b.carousel || this.options.carousel && (!b || b.carousel !== !1)) && a.extend(this.options, this.carouselOptions), a.extend(this.options, b), this.num < 3 && (this.options.continuous = this.options.continuous ? null : !1), this.support.transition || (this.options.emulateTouchEvents = !1), this.options.event && this.preventDefault(this.options.event)
+ }
+ }), b
+}), function(a) {
+ "use strict";
+ "function" == typeof define && define.amd ? define(["./blueimp-helper", "./blueimp-gallery"], a) : a(window.blueimp.helper || window.jQuery, window.blueimp.Gallery)
+}(function(a, b) {
+ "use strict";
+ a.extend(b.prototype.options, {
+ fullScreen: !1
+ });
+ var c = b.prototype.initialize,
+ d = b.prototype.close;
+ return a.extend(b.prototype, {
+ getFullScreenElement: function() {
+ return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement
+ },
+ requestFullScreen: function(a) {
+ a.requestFullscreen ? a.requestFullscreen() : a.webkitRequestFullscreen ? a.webkitRequestFullscreen() : a.mozRequestFullScreen ? a.mozRequestFullScreen() : a.msRequestFullscreen && a.msRequestFullscreen()
+ },
+ exitFullScreen: function() {
+ document.exitFullscreen ? document.exitFullscreen() : document.webkitCancelFullScreen ? document.webkitCancelFullScreen() : document.mozCancelFullScreen ? document.mozCancelFullScreen() : document.msExitFullscreen && document.msExitFullscreen()
+ },
+ initialize: function() {
+ c.call(this), this.options.fullScreen && !this.getFullScreenElement() && this.requestFullScreen(this.container[0])
+ },
+ close: function() {
+ this.getFullScreenElement() === this.container[0] && this.exitFullScreen(), d.call(this)
+ }
+ }), b
+}), function(a) {
+ "use strict";
+ "function" == typeof define && define.amd ? define(["./blueimp-helper", "./blueimp-gallery"], a) : a(window.blueimp.helper || window.jQuery, window.blueimp.Gallery)
+}(function(a, b) {
+ "use strict";
+ a.extend(b.prototype.options, {
+ indicatorContainer: "ol",
+ activeIndicatorClass: "active",
+ thumbnailProperty: "thumbnail",
+ thumbnailIndicators: !0
+ });
+ var c = b.prototype.initSlides,
+ d = b.prototype.addSlide,
+ e = b.prototype.resetSlides,
+ f = b.prototype.handleClick,
+ g = b.prototype.handleSlide,
+ h = b.prototype.handleClose;
+ return a.extend(b.prototype, {
+ createIndicator: function(b) {
+ var c, d, e = this.indicatorPrototype.cloneNode(!1),
+ f = this.getItemProperty(b, this.options.titleProperty),
+ g = this.options.thumbnailProperty;
+ return this.options.thumbnailIndicators && (d = b.getElementsByTagName && a(b).find("img")[0], d ? c = d.src : g && (c = this.getItemProperty(b, g)), c && (e.style.backgroundImage = 'url("' + c + '")')), f && (e.title = f), e
+ },
+ addIndicator: function(a) {
+ if (this.indicatorContainer.length) {
+ var b = this.createIndicator(this.list[a]);
+ b.setAttribute("data-index", a), this.indicatorContainer[0].appendChild(b), this.indicators.push(b)
+ }
+ },
+ setActiveIndicator: function(b) {
+ this.indicators && (this.activeIndicator && this.activeIndicator.removeClass(this.options.activeIndicatorClass), this.activeIndicator = a(this.indicators[b]), this.activeIndicator.addClass(this.options.activeIndicatorClass))
+ },
+ initSlides: function(a) {
+ a || (this.indicatorContainer = this.container.find(this.options.indicatorContainer), this.indicatorContainer.length && (this.indicatorPrototype = document.createElement("li"), this.indicators = this.indicatorContainer[0].children)), c.call(this, a)
+ },
+ addSlide: function(a) {
+ d.call(this, a), this.addIndicator(a)
+ },
+ resetSlides: function() {
+ e.call(this), this.indicatorContainer.empty(), this.indicators = []
+ },
+ handleClick: function(a) {
+ var b = a.target || a.srcElement,
+ c = b.parentNode;
+ if (c === this.indicatorContainer[0]) this.preventDefault(a), this.slide(this.getNodeIndex(b));
+ else {
+ if (c.parentNode !== this.indicatorContainer[0]) return f.call(this, a);
+ this.preventDefault(a), this.slide(this.getNodeIndex(c))
+ }
+ },
+ handleSlide: function(a) {
+ g.call(this, a), this.setActiveIndicator(a)
+ },
+ handleClose: function() {
+ this.activeIndicator && this.activeIndicator.removeClass(this.options.activeIndicatorClass), h.call(this)
+ }
+ }), b
+}), function(a) {
+ "use strict";
+ "function" == typeof define && define.amd ? define(["./blueimp-helper", "./blueimp-gallery"], a) : a(window.blueimp.helper || window.jQuery, window.blueimp.Gallery)
+}(function(a, b) {
+ "use strict";
+ a.extend(b.prototype.options, {
+ videoContentClass: "video-content",
+ videoLoadingClass: "video-loading",
+ videoPlayingClass: "video-playing",
+ videoPosterProperty: "poster",
+ videoSourcesProperty: "sources"
+ });
+ var c = b.prototype.handleSlide;
+ return a.extend(b.prototype, {
+ handleSlide: function(a) {
+ c.call(this, a), this.playingVideo && this.playingVideo.pause()
+ },
+ videoFactory: function(b, c, d) {
+ var e, f, g, h, i, j = this,
+ k = this.options,
+ l = this.elementPrototype.cloneNode(!1),
+ m = a(l),
+ n = [{
+ type: "error",
+ target: l
+ }],
+ o = d || document.createElement("video"),
+ p = this.getItemProperty(b, k.urlProperty),
+ q = this.getItemProperty(b, k.typeProperty),
+ r = this.getItemProperty(b, k.titleProperty),
+ s = this.getItemProperty(b, k.videoPosterProperty),
+ t = this.getItemProperty(b, k.videoSourcesProperty);
+ if (m.addClass(k.videoContentClass), r && (l.title = r), o.canPlayType) if (p && q && o.canPlayType(q)) o.src = p;
+ else for (; t && t.length;) if (f = t.shift(), p = this.getItemProperty(f, k.urlProperty), q = this.getItemProperty(f, k.typeProperty), p && q && o.canPlayType(q)) {
+ o.src = p;
+ break
+ }
+ return s && (o.poster = s, e = this.imagePrototype.cloneNode(!1), a(e).addClass(k.toggleClass), e.src = s, e.draggable = !1, l.appendChild(e)), g = document.createElement("a"), g.setAttribute("target", "_blank"), d || g.setAttribute("download", r), g.href = p, o.src && (o.controls = !0, (d || a(o)).on("error", function() {
+ j.setTimeout(c, n)
+ }).on("pause", function() {
+ h = !1, m.removeClass(j.options.videoLoadingClass).removeClass(j.options.videoPlayingClass), i && j.container.addClass(j.options.controlsClass), delete j.playingVideo, j.interval && j.play()
+ }).on("playing", function() {
+ h = !1, m.removeClass(j.options.videoLoadingClass).addClass(j.options.videoPlayingClass), j.container.hasClass(j.options.controlsClass) ? (i = !0, j.container.removeClass(j.options.controlsClass)) : i = !1
+ }).on("play", function() {
+ window.clearTimeout(j.timeout), h = !0, m.addClass(j.options.videoLoadingClass), j.playingVideo = o
+ }), a(g).on("click", function(a) {
+ j.preventDefault(a), h ? o.pause() : o.play()
+ }), l.appendChild(d && d.element || o)), l.appendChild(g), this.setTimeout(c, [{
+ type: "load",
+ target: l
+ }]), l
+ }
+ }), b
+}), function(a) {
+ "use strict";
+ "function" == typeof define && define.amd ? define(["./blueimp-helper", "./blueimp-gallery-video"], a) : a(window.blueimp.helper || window.jQuery, window.blueimp.Gallery)
+}(function(a, b) {
+ "use strict";
+ if (!window.postMessage) return b;
+ a.extend(b.prototype.options, {
+ vimeoVideoIdProperty: "vimeo",
+ vimeoPlayerUrl: "//player.vimeo.com/video/VIDEO_ID?api=1&player_id=PLAYER_ID",
+ vimeoPlayerIdPrefix: "vimeo-player-",
+ vimeoClickToPlay: !0
+ });
+ var c = b.prototype.textFactory || b.prototype.imageFactory,
+ d = function(a, b, c, d) {
+ this.url = a, this.videoId = b, this.playerId = c, this.clickToPlay = d, this.element = document.createElement("div"), this.listeners = {}
+ },
+ e = 0;
+ return a.extend(d.prototype, {
+ canPlayType: function() {
+ return !0
+ },
+ on: function(a, b) {
+ return this.listeners[a] = b, this
+ },
+ loadAPI: function() {
+ for (var b, c, d = this, e = "//" + ("https" === location.protocol ? "secure-" : "") + "a.vimeocdn.com/js/froogaloop2.min.js", f = document.getElementsByTagName("script"), g = f.length, h = function() {
+ !c && d.playOnReady && d.play(), c = !0
+ }; g;) if (g -= 1, f[g].src === e) {
+ b = f[g];
+ break
+ }
+ b || (b = document.createElement("script"), b.src = e), a(b).on("load", h), f[0].parentNode.insertBefore(b, f[0]), /loaded|complete/.test(b.readyState) && h()
+ },
+ onReady: function() {
+ var a = this;
+ this.ready = !0, this.player.addEvent("play", function() {
+ a.hasPlayed = !0, a.onPlaying()
+ }), this.player.addEvent("pause", function() {
+ a.onPause()
+ }), this.player.addEvent("finish", function() {
+ a.onPause()
+ }), this.playOnReady && this.play()
+ },
+ onPlaying: function() {
+ this.playStatus < 2 && (this.listeners.playing(), this.playStatus = 2)
+ },
+ onPause: function() {
+ this.listeners.pause(), delete this.playStatus
+ },
+ insertIframe: function() {
+ var a = document.createElement("iframe");
+ a.src = this.url.replace("VIDEO_ID", this.videoId).replace("PLAYER_ID", this.playerId), a.id = this.playerId, this.element.parentNode.replaceChild(a, this.element), this.element = a
+ },
+ play: function() {
+ var a = this;
+ this.playStatus || (this.listeners.play(), this.playStatus = 1), this.ready ? !this.hasPlayed && (this.clickToPlay || window.navigator && /iP(hone|od|ad)/.test(window.navigator.platform)) ? this.onPlaying() : this.player.api("play") : (this.playOnReady = !0, window.$f ? this.player || (this.insertIframe(), this.player = $f(this.element), this.player.addEvent("ready", function() {
+ a.onReady()
+ })) : this.loadAPI())
+ },
+ pause: function() {
+ this.ready ? this.player.api("pause") : this.playStatus && (delete this.playOnReady, this.listeners.pause(), delete this.playStatus)
+ }
+ }), a.extend(b.prototype, {
+ VimeoPlayer: d,
+ textFactory: function(a, b) {
+ var f = this.options,
+ g = this.getItemProperty(a, f.vimeoVideoIdProperty);
+ return g ? (void 0 === this.getItemProperty(a, f.urlProperty) && (a[f.urlProperty] = "//vimeo.com/" + g), e += 1, this.videoFactory(a, b, new d(f.vimeoPlayerUrl, g, f.vimeoPlayerIdPrefix + e, f.vimeoClickToPlay))) : c.call(this, a, b)
+ }
+ }), b
+}), function(a) {
+ "use strict";
+ "function" == typeof define && define.amd ? define(["./blueimp-helper", "./blueimp-gallery-video"], a) : a(window.blueimp.helper || window.jQuery, window.blueimp.Gallery)
+}(function(a, b) {
+ "use strict";
+ if (!window.postMessage) return b;
+ a.extend(b.prototype.options, {
+ youTubeVideoIdProperty: "youtube",
+ youTubePlayerVars: {
+ wmode: "transparent"
+ },
+ youTubeClickToPlay: !0
+ });
+ var c = b.prototype.textFactory || b.prototype.imageFactory,
+ d = function(a, b, c) {
+ this.videoId = a, this.playerVars = b, this.clickToPlay = c, this.element = document.createElement("div"), this.listeners = {}
+ };
+ return a.extend(d.prototype, {
+ canPlayType: function() {
+ return !0
+ },
+ on: function(a, b) {
+ return this.listeners[a] = b, this
+ },
+ loadAPI: function() {
+ var a, b = this,
+ c = window.onYouTubeIframeAPIReady,
+ d = "//www.youtube.com/iframe_api",
+ e = document.getElementsByTagName("script"),
+ f = e.length;
+ for (window.onYouTubeIframeAPIReady = function() {
+ c && c.apply(this), b.playOnReady && b.play()
+ }; f;) if (f -= 1, e[f].src === d) return;
+ a = document.createElement("script"), a.src = d, e[0].parentNode.insertBefore(a, e[0])
+ },
+ onReady: function() {
+ this.ready = !0, this.playOnReady && this.play()
+ },
+ onPlaying: function() {
+ this.playStatus < 2 && (this.listeners.playing(), this.playStatus = 2)
+ },
+ onPause: function() {
+ b.prototype.setTimeout.call(this, this.checkSeek, null, 2e3)
+ },
+ checkSeek: function() {
+ (this.stateChange === YT.PlayerState.PAUSED || this.stateChange === YT.PlayerState.ENDED) && (this.listeners.pause(), delete this.playStatus)
+ },
+ onStateChange: function(a) {
+ switch (a.data) {
+ case YT.PlayerState.PLAYING:
+ this.hasPlayed = !0, this.onPlaying();
+ break;
+ case YT.PlayerState.PAUSED:
+ case YT.PlayerState.ENDED:
+ this.onPause()
+ }
+ this.stateChange = a.data
+ },
+ onError: function(a) {
+ this.listeners.error(a)
+ },
+ play: function() {
+ var a = this;
+ this.playStatus || (this.listeners.play(), this.playStatus = 1), this.ready ? !this.hasPlayed && (this.clickToPlay || window.navigator && /iP(hone|od|ad)/.test(window.navigator.platform)) ? this.onPlaying() : this.player.playVideo() : (this.playOnReady = !0, window.YT && YT.Player ? this.player || (this.player = new YT.Player(this.element, {
+ videoId: this.videoId,
+ playerVars: this.playerVars,
+ events: {
+ onReady: function() {
+ a.onReady()
+ },
+ onStateChange: function(b) {
+ a.onStateChange(b)
+ },
+ onError: function(b) {
+ a.onError(b)
+ }
+ }
+ })) : this.loadAPI())
+ },
+ pause: function() {
+ this.ready ? this.player.pauseVideo() : this.playStatus && (delete this.playOnReady, this.listeners.pause(), delete this.playStatus)
+ }
+ }), a.extend(b.prototype, {
+ YouTubePlayer: d,
+ textFactory: function(a, b) {
+ var e = this.options,
+ f = this.getItemProperty(a, e.youTubeVideoIdProperty);
+ return f ? (void 0 === this.getItemProperty(a, e.urlProperty) && (a[e.urlProperty] = "//www.youtube.com/watch?v=" + f), void 0 === this.getItemProperty(a, e.videoPosterProperty) && (a[e.videoPosterProperty] = "//img.youtube.com/vi/" + f + "/maxresdefault.jpg"), this.videoFactory(a, b, new d(f, e.youTubePlayerVars, e.youTubeClickToPlay))) : c.call(this, a, b)
+ }
+ }), b
+}), function(a) {
+ "use strict";
+ "function" == typeof define && define.amd ? define(["jquery", "./blueimp-gallery"], a) : a(window.jQuery, window.blueimp.Gallery)
+}(function(a, b) {
+ "use strict";
+ a(document).on("click", "[data-gallery]", function(c) {
+ var d = a(this).data("gallery"),
+ e = a(d),
+ f = e.length && e || a(b.prototype.options.container),
+ g = {
+ onopen: function() {
+ f.data("gallery", this).trigger("open")
+ },
+ onopened: function() {
+ f.trigger("opened")
+ },
+ onslide: function() {
+ f.trigger("slide", arguments)
+ },
+ onslideend: function() {
+ f.trigger("slideend", arguments)
+ },
+ onslidecomplete: function() {
+ f.trigger("slidecomplete", arguments)
+ },
+ onclose: function() {
+ f.trigger("close")
+ },
+ onclosed: function() {
+ f.trigger("closed").removeData("gallery")
+ }
+ },
+ h = a.extend(f.data(), {
+ container: f[0],
+ index: this,
+ event: c
+ }, g),
+ i = a('[data-gallery="' + d + '"]');
+ return h.filter && (i = i.filter(h.filter)), new b(i, h)
+ })
+});
\ No newline at end of file
diff --git a/phpgwapi/js/jquery/blueimp/package.json b/phpgwapi/js/jquery/blueimp/package.json
new file mode 100644
index 0000000000..8141676e97
--- /dev/null
+++ b/phpgwapi/js/jquery/blueimp/package.json
@@ -0,0 +1,54 @@
+{
+ "name": "blueimp-gallery",
+ "version": "2.15.2",
+ "title": "blueimp Gallery",
+ "description": "blueimp Gallery is a touch-enabled, responsive and customizable image and video gallery, carousel and lightbox, optimized for both mobile and desktop web browsers. It features swipe, mouse and keyboard navigation, transition effects, slideshow functionality, fullscreen support and on-demand content loading and can be extended to display additional content types.",
+ "keywords": [
+ "image",
+ "video",
+ "gallery",
+ "carousel",
+ "lightbox",
+ "mobile",
+ "desktop",
+ "touch",
+ "responsive",
+ "swipe",
+ "mouse",
+ "keyboard",
+ "navigation",
+ "transition",
+ "effects",
+ "slideshow",
+ "fullscreen"
+ ],
+ "homepage": "https://github.com/blueimp/Gallery",
+ "author": {
+ "name": "Sebastian Tschan",
+ "url": "https://blueimp.net"
+ },
+ "maintainers": [
+ {
+ "name": "Sebastian Tschan",
+ "url": "https://blueimp.net"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/blueimp/Gallery.git"
+ },
+ "bugs": "https://github.com/blueimp/Gallery/issues",
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "http://www.opensource.org/licenses/MIT"
+ }
+ ],
+ "devDependencies": {
+ "grunt": "~0.4.5",
+ "grunt-contrib-less": "~0.11.3",
+ "grunt-contrib-uglify": "~0.5.0",
+ "grunt-contrib-jshint": "~0.10.0",
+ "grunt-bump-build-git": "~1.1.1"
+ }
+}