remove old expose view / blueimp gallery and all widgets which have been using them:
- new web-components are unconditionally replacing them via the preprocessor - some widgets (eg. et2_description) could not be removed as other legacy widgets are extending them, but they are no longer registered - as there are many places using et2_createWidget('description', ...), et2_description returns now web-components for no longer existing widgets
@ -13,13 +13,13 @@
|
||||
use EGroupware\Api;
|
||||
|
||||
// add et2- prefix to following widgets/tags, if NO <overlay legacy="true"
|
||||
const ADD_ET2_PREFIX_REGEXP = '#<((/?)([vh]?box|date(-time[^\s]*|-duration|-since)?|textbox|textarea|button|colorpicker|description|image|url(-email|-phone|-fax)?))(/?|\s[^>]*)>#m';
|
||||
const ADD_ET2_PREFIX_REGEXP = '#<((/?)([vh]?box|date(-time[^\s]*|-duration|-since)?|textbox|textarea|button|colorpicker|url(-email|-phone|-fax)?))(/?|\s[^>]*)>#m';
|
||||
const ADD_ET2_PREFIX_LAST_GROUP = 6;
|
||||
|
||||
// unconditional of legacy add et2- prefix to this widgets
|
||||
const ADD_ET2_PREFIX_LEGACY_REGEXP = '#<(vfs-mime|link|link-string|link-list)\s([^/>]+)/>#m';
|
||||
const ADD_ET2_PREFIX_LEGACY_REGEXP = '#<(description|label|image|vfs-mime|vfs-uid|vfs-gid|link|link-string|link-list)\s([^>]+)/>#m';
|
||||
|
||||
// switch evtl. set output-compression off, as we cant calculate a Content-Length header with transparent compression
|
||||
// switch evtl. set output-compression off, as we can't calculate a Content-Length header with transparent compression
|
||||
ini_set('zlib.output_compression', 0);
|
||||
|
||||
$GLOBALS['egw_info'] = array(
|
||||
@ -97,6 +97,9 @@ function send_template()
|
||||
// modify <(vfs-mime|link-string|link-list) --> <et2-*
|
||||
$str = preg_replace(ADD_ET2_PREFIX_LEGACY_REGEXP, '<et2-$1 $2></et2-$1>', $str);
|
||||
|
||||
// remove (unnecessary) empty widgets (were required with first eTemplate)
|
||||
$str = preg_replace('#^\s*<(description|label)/>\n#m', '', $str);
|
||||
|
||||
// ^^^^^^^^^^^^^^^^ above widgets get transformed independent of legacy="true" set in overlay ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
// eTemplate marked as legacy --> replace only some widgets (eg. requiring jQueryUI) with web-components
|
||||
|
@ -247,6 +247,9 @@ export class Et2Description extends Et2Widget(LitElement) implements et2_IDetach
|
||||
// nope
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore TypeScript is not recognizing that this widget is a LitElement
|
||||
customElements.define("et2-description", Et2Description);
|
||||
|
||||
export class Et2Label extends Et2Description {}
|
||||
// @ts-ignore TypeScript is not recognizing that this widget is a LitElement
|
||||
customElements.define("et2-label", Et2Label);
|
@ -52,7 +52,7 @@ li {
|
||||
}
|
||||
|
||||
private __select_options : SelectOption[];
|
||||
private __value : string[];
|
||||
private __value : string|string[];
|
||||
|
||||
constructor()
|
||||
{
|
||||
|
33
api/js/etemplate/Et2Vfs/Et2VfsUid.ts
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* EGroupware eTemplate2 - Readonly select WebComponent
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package api
|
||||
* @link https://www.egroupware.org
|
||||
* @author Ralf Becker <rb@egroupware.org>
|
||||
*/
|
||||
|
||||
import {Et2SelectAccountReadonly} from "../Et2Select/Et2SelectReadonly";
|
||||
|
||||
export class Et2VfsUid extends Et2SelectAccountReadonly
|
||||
{
|
||||
set value(_val)
|
||||
{
|
||||
if (!_val || _val === '0')
|
||||
{
|
||||
this.select_options = [{value: '0', label: 'root'}];
|
||||
}
|
||||
super.value = _val;
|
||||
}
|
||||
|
||||
get value()
|
||||
{
|
||||
return super.value;
|
||||
}
|
||||
}
|
||||
// @ts-ignore TypeScript is not recognizing that this widget is a LitElement
|
||||
customElements.define("et2-vfs-uid", Et2VfsUid);
|
||||
|
||||
export class Et2VfsGid extends Et2VfsUid {}
|
||||
// @ts-ignore TypeScript is not recognizing that this widget is a LitElement
|
||||
customElements.define("et2-vfs-gid", Et2VfsGid);
|
@ -1281,7 +1281,8 @@ export function loadWebComponent(_nodeName : string, _template_node : Element|{[
|
||||
if(!widget_class)
|
||||
{
|
||||
// Given node has no registered class. Try some of our special things (remove type, fallback to actual node)
|
||||
let tries = [_nodeName.split('-')[0], _template_node.nodeName.toLowerCase()];
|
||||
let tries = [_nodeName.split('-')[0]];
|
||||
if (_template_node.nodeName) tries = tries.concat(_template_node.nodeName.toLowerCase());
|
||||
for(let i = 0; i < tries.length && !window.customElements.get(_nodeName); i++)
|
||||
{
|
||||
_nodeName = tries[i];
|
||||
|
@ -77,7 +77,7 @@ export function et2_register_widget(_constructor, _types)
|
||||
* is not passed, it will default to null. Then you have to attach the element
|
||||
* to a parent using the addChild or insertChild method.
|
||||
*/
|
||||
export function et2_createWidget(_name: string, _attrs: object, _parent?: any): et2_widget
|
||||
export function et2_createWidget(_name: string, _attrs: object, _parent?: any): et2_widget|HTMLElement
|
||||
{
|
||||
"use strict";
|
||||
|
||||
@ -96,6 +96,12 @@ export function et2_createWidget(_name: string, _attrs: object, _parent?: any):
|
||||
_parent = null;
|
||||
}
|
||||
|
||||
// check and return web-components in case widget is no longer available as legacy widget
|
||||
if (typeof et2_registry[_name] === "undefined" && window.customElements.get('et2-'+_name))
|
||||
{
|
||||
return loadWebComponent('et2-'+_name, _attrs, _parent);
|
||||
}
|
||||
|
||||
// Parse the "readonly" and "type" flag for this element here, as they
|
||||
// determine which constructor is used
|
||||
var nodeName = _attrs["type"] = _name;
|
||||
|
@ -11,22 +11,23 @@
|
||||
/*egw:uses
|
||||
/vendor/bower-asset/jquery/dist/jquery.js;
|
||||
et2_core_baseWidget;
|
||||
expose;
|
||||
*/
|
||||
|
||||
import {et2_activateLinks, et2_csvSplit, et2_insertLinkText, et2_no_init} from "./et2_core_common";
|
||||
import {ClassWithAttributes} from "./et2_core_inheritance";
|
||||
import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
|
||||
import {WidgetConfig} from "./et2_core_widget";
|
||||
import {et2_baseWidget} from './et2_core_baseWidget'
|
||||
import {et2_inputWidget} from "./et2_core_inputWidget";
|
||||
import {expose} from "./expose";
|
||||
import {et2_IDetachedDOM, et2_IExposable, et2_IInputNode} from "./et2_core_interfaces";
|
||||
import {et2_IDetachedDOM, et2_IInputNode} from "./et2_core_interfaces";
|
||||
import {egw} from "../jsapi/egw_global";
|
||||
|
||||
/**
|
||||
* Class which implements the "description" XET-Tag
|
||||
*
|
||||
* @deprecated use Et2Description
|
||||
* @todo remove after extending widgets are ported to web-components
|
||||
*/
|
||||
export class et2_description extends expose(class et2_description extends et2_baseWidget implements et2_IDetachedDOM, et2_IExposable
|
||||
export class et2_description extends et2_baseWidget implements et2_IDetachedDOM
|
||||
{
|
||||
static readonly _attributes : any = {
|
||||
"label": {
|
||||
@ -443,5 +444,4 @@ export class et2_description extends expose(class et2_description extends et2_ba
|
||||
}
|
||||
});
|
||||
}
|
||||
}){};
|
||||
et2_register_widget(et2_description, ["description", "label"]);
|
||||
}
|
@ -29,7 +29,8 @@ import {Et2Dialog} from "./Et2Dialog/Et2Dialog";
|
||||
/**
|
||||
* Class which implements the "image" XET-Tag
|
||||
*
|
||||
* @augments et2_baseWidget
|
||||
* @deprecated use Et2Image
|
||||
* @ToDo remove when all extending classes are ported to web-components
|
||||
*/
|
||||
export class et2_image extends et2_baseWidget implements et2_IDetachedDOM
|
||||
{
|
||||
@ -297,7 +298,6 @@ export class et2_image extends et2_baseWidget implements et2_IDetachedDOM
|
||||
}
|
||||
}
|
||||
}
|
||||
et2_register_widget(et2_image, ["image"]);
|
||||
|
||||
/**
|
||||
* Widget displaying an application icon
|
||||
|
@ -15,7 +15,6 @@
|
||||
et2_core_inputWidget;
|
||||
et2_core_valueWidget;
|
||||
et2_widget_selectbox;
|
||||
expose;
|
||||
|
||||
// Include menu system for list context menu
|
||||
egw_action.egw_menu_dhtmlx;
|
||||
@ -29,15 +28,14 @@ import {et2_selectbox} from "./et2_widget_selectbox";
|
||||
import {et2_button} from "./et2_widget_button";
|
||||
import {et2_file} from "./et2_widget_file";
|
||||
import {et2_vfsSelect} from "./et2_widget_vfs";
|
||||
import {egw, egw_get_file_editor_prefered_mimes} from "../jsapi/egw_global";
|
||||
import {egw} from "../jsapi/egw_global";
|
||||
import {et2_tabbox} from "./et2_widget_tabs";
|
||||
import {et2_csvSplit, et2_no_init} from "./et2_core_common";
|
||||
import {et2_IDetachedDOM, et2_IExposable} from "./et2_core_interfaces";
|
||||
import {expose} from "./expose";
|
||||
import {egwMenu} from "../egw_action/egw_menu.js";
|
||||
import {Et2Dialog} from "./Et2Dialog/Et2Dialog";
|
||||
import {et2_DOMWidget} from "./et2_core_DOMWidget";
|
||||
import {Et2LinkList} from "./Et2Link/Et2LinkList";
|
||||
import type {Et2LinkString} from "./Et2Link/Et2LinkString";
|
||||
import {Et2Link} from "./Et2Link/Et2Link";
|
||||
|
||||
/**
|
||||
* UI widgets for Egroupware linking system
|
||||
@ -1324,11 +1322,16 @@ export class et2_link_entry extends et2_inputWidget
|
||||
|
||||
et2_register_widget(et2_link_entry, ["link-entry"]);
|
||||
|
||||
/**
|
||||
* @deprecated use Et2Link
|
||||
*/
|
||||
export type et2_link = Et2Link;
|
||||
|
||||
/**
|
||||
* UI widget for a single (read-only) link
|
||||
*
|
||||
*/
|
||||
export class et2_link extends et2_valueWidget implements et2_IDetachedDOM
|
||||
export class et2_link_entry_ro extends et2_valueWidget implements et2_IDetachedDOM
|
||||
{
|
||||
static readonly _attributes: any = {
|
||||
"only_app": {
|
||||
@ -1381,7 +1384,7 @@ export class et2_link extends et2_valueWidget implements et2_IDetachedDOM
|
||||
*/
|
||||
constructor(_parent: et2_widget, _attrs?: WidgetConfig, _child?: object)
|
||||
{
|
||||
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_link._attributes, _child || {}));
|
||||
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_valueWidget._attributes, _child || {}));
|
||||
|
||||
|
||||
this.label_span = jQuery(document.createElement("label"))
|
||||
@ -1552,897 +1555,23 @@ export class et2_link extends et2_valueWidget implements et2_IDetachedDOM
|
||||
|
||||
}
|
||||
|
||||
et2_register_widget(et2_link, ["link", "link-entry_ro"]);
|
||||
et2_register_widget(et2_link_entry_ro, ["link-entry_ro"]);
|
||||
|
||||
/**
|
||||
* UI widget for one or more links, comma separated
|
||||
*
|
||||
* TODO: This one used to have expose
|
||||
* @deprecated use Et2LinkString
|
||||
*/
|
||||
export class et2_link_string extends expose(class et2_link_string extends et2_valueWidget implements et2_IDetachedDOM, et2_IExposable
|
||||
{
|
||||
static readonly _attributes: any = {
|
||||
"application": {
|
||||
"name": "Application",
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Use the given application, so you can pass just the ID for value"
|
||||
},
|
||||
"value": {
|
||||
"description": "Either an array of link information (see egw_link::link()) or array with keys to_app and to_id",
|
||||
"type": "any"
|
||||
},
|
||||
"only_app": {
|
||||
"name": "Application filter",
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Appname, eg. 'projectmananager' to list only linked projects"
|
||||
},
|
||||
"link_type": {
|
||||
"name": "Type filter",
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Sub-type key to list only entries of that type"
|
||||
},
|
||||
"expose_view": {
|
||||
name: "Expose view",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
description: "Clicking on description with href value would popup an expose view, and will show content referenced by href."
|
||||
}
|
||||
};
|
||||
protected list: JQuery;
|
||||
protected value: any;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @memberOf et2_link_string
|
||||
*/
|
||||
constructor(_parent: et2_widget, _attrs?: WidgetConfig, _child?: object)
|
||||
{
|
||||
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_link_string._attributes, _child || {}));
|
||||
|
||||
|
||||
this.list = jQuery(document.createElement("ul"))
|
||||
.addClass("et2_link_string");
|
||||
|
||||
if (this.options['class']) this.list.addClass(this.options['class']);
|
||||
this.setDOMNode(this.list[0]);
|
||||
}
|
||||
|
||||
destroy()
|
||||
{
|
||||
super.destroy.apply(this, arguments);
|
||||
if (this.node != null)
|
||||
{
|
||||
jQuery(this.node).children().unbind();
|
||||
}
|
||||
}
|
||||
|
||||
set_value(_value)
|
||||
{
|
||||
// Get data
|
||||
if (!_value || _value == null || !this.list)
|
||||
{
|
||||
// List can be missing if the AJAX call returns after the form is destroyed
|
||||
if (this.list)
|
||||
{
|
||||
this.list.empty();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (typeof _value == "string" && _value.indexOf(',') > 0)
|
||||
{
|
||||
_value = _value.split(',');
|
||||
}
|
||||
if (!_value.to_app && typeof _value == "object" && this.options.application)
|
||||
{
|
||||
_value.to_app = this.options.application;
|
||||
}
|
||||
|
||||
if (typeof _value == 'object' && _value.to_app && _value.to_id)
|
||||
{
|
||||
this.value = _value;
|
||||
this._get_links();
|
||||
return;
|
||||
}
|
||||
this.list.empty();
|
||||
if (typeof _value == 'object' && _value.length > 0)
|
||||
{
|
||||
// Have full info
|
||||
// Don't store new value, just update display
|
||||
|
||||
|
||||
// Make new links
|
||||
for (var i = 0; i < _value.length; i++)
|
||||
{
|
||||
if (!this.options.only_app || this.options.only_app && _value[i].app == this.options.only_app)
|
||||
{
|
||||
this._add_link(_value[i].id ? _value[i] : {id: _value[i], app: _value.to_app});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (this.options.application)
|
||||
{
|
||||
this._add_link({id: _value, app: this.options.application});
|
||||
}
|
||||
}
|
||||
|
||||
_get_links()
|
||||
{
|
||||
var _value = this.value;
|
||||
// Just IDs - get from server
|
||||
if (this.options.only_app)
|
||||
{
|
||||
_value.only_app = this.options.only_app;
|
||||
}
|
||||
this.egw().jsonq('EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_link_list', [_value]).then(_value =>
|
||||
{
|
||||
this.set_value(_value);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to get media content to feed the expose
|
||||
* @param {type} _value
|
||||
* @returns {Array|Array.getMedia.mediaContent}
|
||||
*/
|
||||
getMedia(_value)
|
||||
{
|
||||
let base_url = egw.webserverUrl.match(/^\//, 'ig') ? egw(window).window.location.origin + egw.webserverUrl : egw.webserverUrl;
|
||||
let mediaContent = [];
|
||||
if (_value && typeof _value.type != 'undefined' && _value.type.match(/video\/|audio\//, 'ig'))
|
||||
{
|
||||
mediaContent = [{
|
||||
title: _value.id,
|
||||
type: _value.type,
|
||||
poster: '', // TODO: Should be changed by correct video thumbnail later
|
||||
href: base_url + egw().mime_open(_value),
|
||||
download_href: base_url + egw().mime_open(_value) + '?download'
|
||||
}];
|
||||
}
|
||||
else if (_value)
|
||||
{
|
||||
mediaContent = [{
|
||||
title: _value.id,
|
||||
href: base_url + egw().mime_open(_value).url,
|
||||
download_href: base_url + egw().mime_open(_value).url + '?download',
|
||||
type: _value.type
|
||||
}];
|
||||
}
|
||||
if (mediaContent[0].href && mediaContent[0].href.match(/\/webdav.php/, 'ig')) mediaContent[0]["download_href"] = mediaContent[0].href + '?download';
|
||||
return mediaContent;
|
||||
}
|
||||
|
||||
_add_link(_link_data)
|
||||
{
|
||||
var self = this;
|
||||
var link = jQuery(document.createElement("li"))
|
||||
.appendTo(this.list)
|
||||
.addClass("et2_link loading")
|
||||
.click(function (e)
|
||||
{
|
||||
var fe = egw_get_file_editor_prefered_mimes(_link_data.type);
|
||||
if (self.options.expose_view && typeof _link_data.type != 'undefined'
|
||||
&& _link_data.type.match(self.mime_regexp, 'ig') && !_link_data.type.match(self.mime_audio_regexp, 'ig'))
|
||||
{
|
||||
self._init_blueimp_gallery(e, _link_data);
|
||||
}
|
||||
else if (_link_data.type && _link_data.type.match(self.mime_audio_regexp, 'ig'))
|
||||
{
|
||||
self._audio_player(_link_data);
|
||||
}
|
||||
else if (typeof _link_data.type != 'undefined' && fe && fe.mime && fe.mime[_link_data.type])
|
||||
{
|
||||
egw.open_link(egw.link('/index.php', {
|
||||
menuaction: fe.edit.menuaction,
|
||||
path: egw().mime_open(_link_data).url.replace('/webdav.php', '')
|
||||
}), '', fe.edit_popup);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.egw().open(_link_data, "", "view", null, _link_data.app, _link_data.app);
|
||||
}
|
||||
e.stopImmediatePropagation();
|
||||
});
|
||||
|
||||
if (_link_data.title)
|
||||
{
|
||||
link.text(_link_data.title)
|
||||
.removeClass("loading");
|
||||
}
|
||||
// Now that link is created, get title from server & update
|
||||
else
|
||||
{
|
||||
this.egw().link_title(_link_data.app, _link_data.id, true).then(title =>
|
||||
{
|
||||
if (title)
|
||||
link.removeClass("loading").text(title);
|
||||
else
|
||||
link.remove(); // no rights or not found
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a list of attributes which can be set when working in the
|
||||
* "detached" mode. The result is stored in the _attrs array which is provided
|
||||
* by the calling code.
|
||||
*
|
||||
* @param {Array} _attrs an array of attributes
|
||||
*/
|
||||
getDetachedAttributes(_attrs)
|
||||
{
|
||||
// Create the label container if it didn't exist yet
|
||||
if (this._labelContainer == null)
|
||||
{
|
||||
this._labelContainer = jQuery(document.createElement("label"))
|
||||
.addClass("et2_label");
|
||||
this.getSurroundings().insertDOMNode(this._labelContainer[0]);
|
||||
this.getSurroundings().update();
|
||||
}
|
||||
_attrs.push("value", "label");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of DOM nodes. The (relatively) same DOM-Nodes have to be
|
||||
* passed to the "setDetachedAttributes" function in the same order.
|
||||
*/
|
||||
getDetachedNodes()
|
||||
{
|
||||
// Create the label container if it didn't exist yet
|
||||
if (this._labelContainer == null)
|
||||
{
|
||||
this._labelContainer = jQuery(document.createElement("label"))
|
||||
.addClass("et2_label");
|
||||
this.getSurroundings().insertDOMNode(this._labelContainer[0]);
|
||||
}
|
||||
return [this.list[0], this._labelContainer[0]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given associative attribute->value array and applies the
|
||||
* attributes to the given DOM-Node.
|
||||
*
|
||||
* @param _nodes is an array of nodes which have to be in the same order as
|
||||
* the nodes returned by "getDetachedNodes"
|
||||
* @param _values is an associative array which contains a subset of attributes
|
||||
* returned by the "getDetachedAttributes" function and sets them to the
|
||||
* given values.
|
||||
*/
|
||||
setDetachedAttributes(_nodes, _values)
|
||||
{
|
||||
this.list = jQuery(_nodes[0]);
|
||||
|
||||
this.set_value(_values["value"]);
|
||||
|
||||
// Special detached, to prevent DOM node modification of the normal method
|
||||
this._labelContainer = _nodes.length > 1 ? jQuery(_nodes[1]) : null;
|
||||
if (_values['label'])
|
||||
{
|
||||
this.set_label(_values['label']);
|
||||
}
|
||||
else if (this._labelContainer)
|
||||
{
|
||||
this._labelContainer.contents().not(this.list).remove();
|
||||
}
|
||||
}
|
||||
})
|
||||
{
|
||||
};
|
||||
et2_register_widget(et2_link_string, ["link-string"]);
|
||||
export type et2_link_string = Et2LinkString;
|
||||
|
||||
/**
|
||||
* UI widget for one or more links in a list (table)
|
||||
*
|
||||
* @deprecated use Et2LinkList
|
||||
*/
|
||||
export class et2_link_list extends et2_link_string
|
||||
{
|
||||
static readonly _attributes: any = {
|
||||
"show_deleted": {
|
||||
"name": "Show deleted",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Show links that are marked as deleted, being held for purge"
|
||||
},
|
||||
"onchange": {
|
||||
"name": "onchange",
|
||||
"type": "js",
|
||||
"default": et2_no_init,
|
||||
"description": "JS code which is executed when the links change."
|
||||
},
|
||||
readonly: {
|
||||
name: "readonly",
|
||||
type: "boolean",
|
||||
"default": false,
|
||||
description: "Does NOT allow user to enter data, just displays existing data"
|
||||
},
|
||||
"target_app": {
|
||||
"name": "Target application",
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Optional parameter to be passed to egw().open in order to open links in specified application "
|
||||
}
|
||||
};
|
||||
private context: egwMenu;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
*/
|
||||
constructor(_parent: et2_widget, _attrs?: WidgetConfig, _child?: object)
|
||||
{
|
||||
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_link_list._attributes, _child || {}));
|
||||
|
||||
|
||||
this.list = jQuery(document.createElement("table"))
|
||||
.addClass("et2_link_list");
|
||||
if (this.options['class']) this.list.addClass(this.options['class']);
|
||||
this.setDOMNode(this.list[0]);
|
||||
|
||||
// Set up context menu
|
||||
var self = this;
|
||||
this.context = new egwMenu();
|
||||
this.context.addItem("comment", this.egw().lang("Comment"), "", function ()
|
||||
{
|
||||
var link_id = typeof self.context.data.link_id == 'number' ? self.context.data.link_id : self.context.data.link_id.replace(/[:\.]/g, '_');
|
||||
|
||||
Et2Dialog.show_prompt(
|
||||
function(button, comment)
|
||||
{
|
||||
if(button != Et2Dialog.OK_BUTTON)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var remark = jQuery('#link_' + (self.context.data.dom_id ? self.context.data.dom_id : link_id), self.list).children('.remark');
|
||||
if(isNaN(self.context.data.link_id)) // new entry, not yet stored
|
||||
{
|
||||
remark.text(comment);
|
||||
// Look for a link-to with the same ID, refresh it
|
||||
if(self.context.data.link_id)
|
||||
{
|
||||
var _widget = link_id.widget || null;
|
||||
self.getRoot().iterateOver(
|
||||
function (widget)
|
||||
{
|
||||
if (widget.id == self.id)
|
||||
{
|
||||
_widget = widget;
|
||||
}
|
||||
},
|
||||
self, et2_link_to
|
||||
);
|
||||
var value = _widget != null ? _widget.getValue() : false;
|
||||
if (_widget && value && value.to_id)
|
||||
{
|
||||
value.to_id[self.context.data.link_id].remark = comment;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
remark.addClass("loading");
|
||||
var request = egw.json("EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_link_comment",
|
||||
[link_id, comment],
|
||||
function ()
|
||||
{
|
||||
if (remark)
|
||||
{
|
||||
// Append "" to make sure it's a string, not undefined
|
||||
remark.removeClass("loading").text(comment + "");
|
||||
// Update internal data
|
||||
self.context.data.remark = comment + "";
|
||||
}
|
||||
},
|
||||
this, true
|
||||
).sendRequest();
|
||||
},
|
||||
'', self.egw().lang("Comment"), self.context.data.remark || ''
|
||||
);
|
||||
|
||||
});
|
||||
this.context.addItem("file_info", this.egw().lang("File information"), this.egw().image("edit"), function (menu_item)
|
||||
{
|
||||
var link_data = self.context.data;
|
||||
if (link_data.app == 'file')
|
||||
{
|
||||
// File info is always the same
|
||||
var url = '/apps/' + link_data.app2 + '/' + link_data.id2 + '/' + decodeURIComponent(link_data.id);
|
||||
if (typeof url == 'string' && url.indexOf('webdav.php'))
|
||||
{
|
||||
// URL is url to file in webdav, so get rid of that part
|
||||
url = url.replace('/webdav.php', '');
|
||||
}
|
||||
self.egw().open(url, "filemanager", "edit");
|
||||
}
|
||||
});
|
||||
this.context.addItem("-", "-");
|
||||
this.context.addItem("save", this.egw().lang("Save as"), this.egw().image('save'), function (menu_item)
|
||||
{
|
||||
var link_data = self.context.data;
|
||||
// Download file
|
||||
if (link_data.download_url)
|
||||
{
|
||||
var url = link_data.download_url;
|
||||
if (url[0] == '/') url = egw.link(url);
|
||||
|
||||
let a = document.createElement('a');
|
||||
if (typeof a.download == "undefined")
|
||||
{
|
||||
window.location.href = url + "?download";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Multiple file download for those that support it
|
||||
a = jQuery(a)
|
||||
.prop('href', url)
|
||||
.prop('download', link_data.title || "")
|
||||
.appendTo(self.getInstanceManager().DOMContainer);
|
||||
|
||||
var evt = document.createEvent('MouseEvent');
|
||||
evt.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
a[0].dispatchEvent(evt);
|
||||
a.remove();
|
||||
return false;
|
||||
}
|
||||
|
||||
self.egw().open(link_data, "", "view", 'download', link_data.target ? link_data.target : link_data.app, link_data.app);
|
||||
});
|
||||
this.context.addItem("zip", this.egw().lang("Save as Zip"), this.egw().image('save_zip'), function (menu_item)
|
||||
{
|
||||
// Highlight files for nice UI indicating what will be in the zip.
|
||||
// Files have negative IDs.
|
||||
jQuery('[id^="link_-"]', this.list).effect('highlight', {}, 2000);
|
||||
|
||||
// Download ZIP
|
||||
window.location = self.egw().link('/index.php', {
|
||||
menuaction: 'api.EGroupware\\Api\\Etemplate\\Widget\\Link.download_zip',
|
||||
app: self.value.to_app,
|
||||
id: self.value.to_id
|
||||
});
|
||||
});
|
||||
|
||||
// Only allow this option if the entry has been saved, and has a real ID
|
||||
if (self.options.value && self.options.value.to_id && typeof self.options.value.to_id != 'object')
|
||||
{
|
||||
this.context.addItem("copy_to", this.egw().lang("Copy to"), this.egw().image('copy'), function (menu_item)
|
||||
{
|
||||
// Highlight files for nice UI indicating what will be copied
|
||||
jQuery('[id="link_' + self.context.data.link_id + ']', this.list).effect('highlight', {}, 2000);
|
||||
|
||||
// Get target
|
||||
var select_attrs: any = {
|
||||
mode: "select-dir",
|
||||
button_caption: '',
|
||||
button_icon: 'copy',
|
||||
button_label: egw.lang("copy"),
|
||||
//extra_buttons: [{text: egw.lang("link"), id:"link", image: "link"}],
|
||||
dialog_title: egw.lang('Copy to'),
|
||||
method: "EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_copy_to",
|
||||
method_id: self.context.data
|
||||
};
|
||||
let vfs_select = <et2_vfsSelect>et2_createWidget("vfs-select", select_attrs, self);
|
||||
|
||||
// No button, just open it
|
||||
vfs_select.button.hide();
|
||||
vfs_select.click(null);
|
||||
});
|
||||
}
|
||||
this.context.addItem("-", "-");
|
||||
this.context.addItem("delete", this.egw().lang("Delete link"), this.egw().image("delete"), function (menu_item)
|
||||
{
|
||||
var link_id = isNaN(self.context.data.link_id) ? self.context.data : self.context.data.link_id;
|
||||
var row = jQuery('#link_' + (self.context.data.dom_id ? self.context.data.dom_id : self.context.data.link_id), self.list);
|
||||
Et2Dialog.show_dialog(
|
||||
function(button)
|
||||
{
|
||||
if(button == Et2Dialog.YES_BUTTON)
|
||||
{
|
||||
self._delete_link(link_id, row);
|
||||
}
|
||||
},
|
||||
egw.lang('Delete link?')
|
||||
);
|
||||
});
|
||||
|
||||
// Native DnD - Doesn't play nice with jQueryUI Sortable
|
||||
// Tell jQuery to include this property
|
||||
jQuery.event.props.push('dataTransfer');
|
||||
|
||||
}
|
||||
|
||||
destroy()
|
||||
{
|
||||
super.destroy.apply(this, arguments);
|
||||
if (this.context)
|
||||
{
|
||||
this.context.clear();
|
||||
delete this.context;
|
||||
}
|
||||
}
|
||||
|
||||
set_value(_value)
|
||||
{
|
||||
this.list.empty();
|
||||
// Handle server passed a list of links that aren't ready yet
|
||||
if (_value && typeof _value == "object")
|
||||
{
|
||||
var list = [];
|
||||
if (_value.to_id && typeof _value.to_id == "object")
|
||||
{
|
||||
list = _value.to_id;
|
||||
}
|
||||
else if (_value.length)
|
||||
{
|
||||
list = _value;
|
||||
}
|
||||
if (list.length > 0)
|
||||
{
|
||||
for (var id in list)
|
||||
{
|
||||
var link = list[id];
|
||||
if (link.app)
|
||||
{
|
||||
// Temp IDs can cause problems since the ID includes the file name or :
|
||||
if (link.link_id && typeof link.link_id != 'number')
|
||||
{
|
||||
link.dom_id = 'temp_' + egw.uid();
|
||||
}
|
||||
// Icon should be in registry
|
||||
if (!link.icon)
|
||||
{
|
||||
link.icon = egw.link_get_registry(link.app, 'icon');
|
||||
// No icon, try by mime type - different place for un-saved entries
|
||||
if (link.icon == false && link.id.type)
|
||||
{
|
||||
// Triggers icon by mime type, not thumbnail or app
|
||||
link.type = link.id.type;
|
||||
link.icon = true;
|
||||
}
|
||||
}
|
||||
// Special handling for file - if not existing, we can't ask for title
|
||||
if (typeof link.id == 'object' && !link.title)
|
||||
{
|
||||
link.title = link.id.name || '';
|
||||
}
|
||||
this._add_link(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
super.set_value(_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_add_link(_link_data)
|
||||
{
|
||||
var row = jQuery(document.createElement("tr"))
|
||||
.attr("id", "link_" + (_link_data.dom_id ? _link_data.dom_id : (typeof _link_data.link_id == "string" ? _link_data.link_id.replace(/[:\.]/g, '_') : _link_data.link_id || _link_data.id)))
|
||||
.attr("draggable", _link_data.app == 'file' ? "true" : "")
|
||||
.appendTo(this.list);
|
||||
if (!_link_data.link_id)
|
||||
{
|
||||
for (var k in _link_data)
|
||||
{
|
||||
row[0].dataset[k] = _link_data[k];
|
||||
}
|
||||
}
|
||||
|
||||
// Icon
|
||||
var icon = jQuery(document.createElement("td"))
|
||||
.appendTo(row)
|
||||
.addClass("icon");
|
||||
if (_link_data.icon)
|
||||
{
|
||||
var icon_widget = et2_createWidget("image", {});
|
||||
var src = '';
|
||||
// Creat a mime widget if the link has type
|
||||
if (_link_data.type)
|
||||
{
|
||||
// VFS - file
|
||||
var vfs_widget = et2_createWidget('vfs-mime', {});
|
||||
vfs_widget.set_value({
|
||||
download_url: _link_data.download_url,
|
||||
name: _link_data.title,
|
||||
mime: _link_data.type,
|
||||
path: _link_data.icon,
|
||||
mode: _link_data.mode
|
||||
});
|
||||
icon.append(vfs_widget.getDOMNode());
|
||||
}
|
||||
else
|
||||
{
|
||||
src = this.egw().image(_link_data.icon);
|
||||
if (src) icon_widget.set_src(src);
|
||||
icon.append(icon_widget.getDOMNode());
|
||||
}
|
||||
}
|
||||
|
||||
var columns = ['title', 'remark'];
|
||||
|
||||
var self = this;
|
||||
for (var i = 0; i < columns.length; i++)
|
||||
{
|
||||
var $td = jQuery(document.createElement("td"))
|
||||
.appendTo(row)
|
||||
.addClass(columns[i])
|
||||
.text(_link_data[columns[i]] ? _link_data[columns[i]] + "" : "");
|
||||
|
||||
var dirs = _link_data[columns[i]] ? _link_data[columns[i]].split('/') : [];
|
||||
if (columns[i] == 'title' && _link_data.type && dirs.length > 1)
|
||||
{
|
||||
this._format_vfs($td, dirs, _link_data);
|
||||
}
|
||||
//Bind the click handler if there is download_url
|
||||
if (_link_data && (typeof _link_data.download_url != 'undefined' || _link_data.app != 'egw-data'))
|
||||
{
|
||||
$td.click(function ()
|
||||
{
|
||||
var fe_mime = egw_get_file_editor_prefered_mimes(_link_data.type);
|
||||
// Check if the link entry is mime with media type, in order to open it in expose view
|
||||
if (typeof _link_data.type != 'undefined' &&
|
||||
(_link_data.type.match(self.mime_regexp, 'ig') || (fe_mime && fe_mime.mime[_link_data.type])))
|
||||
{
|
||||
var $vfs_img_node = jQuery(this).parent().find('.vfsMimeIcon');
|
||||
if ($vfs_img_node.length > 0) $vfs_img_node.click();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!self.options.target_app)
|
||||
{
|
||||
self.options.target_app = _link_data.app;
|
||||
}
|
||||
self.egw().open(_link_data, "", "view", null, _link_data.target ? _link_data.target : _link_data.app, self.options.target_app);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof _link_data.title == 'undefined')
|
||||
{
|
||||
// Title will be fetched from server and then set
|
||||
jQuery('td.title', row).addClass("loading");
|
||||
this.egw().link_title(_link_data.app, _link_data.id, true).then(title =>
|
||||
{
|
||||
jQuery('td.title', row).removeClass("loading").text(title + "");
|
||||
});
|
||||
}
|
||||
// Date
|
||||
/*
|
||||
var date_row = jQuery(document.createElement("td"))
|
||||
.appendTo(row);
|
||||
if(_link_data.lastmod)
|
||||
{
|
||||
var date_widget = et2_createWidget("date-since");
|
||||
date_widget.set_value(_link_data.lastmod);
|
||||
date_row.append(date_widget.getDOMNode());
|
||||
}
|
||||
*/
|
||||
|
||||
// Delete
|
||||
// build delete button if the link is not readonly
|
||||
if (!this.options.readonly)
|
||||
{
|
||||
var delete_button = jQuery(document.createElement("td"))
|
||||
.appendTo(row);
|
||||
jQuery("<div />")
|
||||
.appendTo(delete_button)
|
||||
// We don't use ui-icon because it assigns a bg image
|
||||
.addClass("delete icon")
|
||||
.bind('click', function ()
|
||||
{
|
||||
Et2Dialog.show_dialog(
|
||||
function(button)
|
||||
{
|
||||
if(button == Et2Dialog.YES_BUTTON)
|
||||
{
|
||||
self._delete_link(
|
||||
self.value && typeof self.value.to_id != 'object' && _link_data.link_id ? _link_data.link_id : _link_data,
|
||||
row
|
||||
);
|
||||
}
|
||||
},
|
||||
egw.lang('Delete link?')
|
||||
);
|
||||
});
|
||||
}
|
||||
// Context menu
|
||||
row.bind("contextmenu", function (e)
|
||||
{
|
||||
// Comment only available if link_id is there and not readonly
|
||||
self.context.getItem("comment").set_enabled(typeof _link_data.link_id != 'undefined' && !self.options.readonly);
|
||||
// File info only available for existing files
|
||||
self.context.getItem("file_info").set_enabled(typeof _link_data.id != 'object' && _link_data.app == 'file');
|
||||
self.context.getItem("save").set_enabled(typeof _link_data.id != 'object' && _link_data.app == 'file');
|
||||
// Zip download only offered if there are at least 2 files
|
||||
self.context.getItem("zip").set_enabled(jQuery('[id^="link_-"]', this.list).length >= 2);
|
||||
// Show delete item only if the widget is not readonly
|
||||
self.context.getItem("delete").set_enabled(!self.options.readonly);
|
||||
|
||||
self.context.data = _link_data;
|
||||
self.context.showAt(e.pageX, e.pageY, true);
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
|
||||
// Drag - adapted from egw_action_dragdrop, sidestepping action system
|
||||
// so all linked files get it
|
||||
// // Unfortunately, dragging files is currently only supported by Chrome
|
||||
if (navigator && navigator.userAgent.indexOf('Chrome') >= 0)
|
||||
{
|
||||
row.on("dragstart", _link_data, function (event)
|
||||
{
|
||||
if (event.dataTransfer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var data = event.data || {};
|
||||
if (data && data.type && data.download_url)
|
||||
{
|
||||
event.dataTransfer.dropEffect = "copy";
|
||||
event.dataTransfer.effectAllowed = "copy";
|
||||
|
||||
var url = data.download_url;
|
||||
|
||||
// NEED an absolute URL
|
||||
if (url[0] == '/') url = egw.link(url);
|
||||
// egw.link adds the webserver, but that might not be an absolute URL - try again
|
||||
if (url[0] == '/') url = window.location.origin + url;
|
||||
|
||||
// Unfortunately, dragging files is currently only supported by Chrome
|
||||
if (navigator && navigator.userAgent.indexOf('Chrome'))
|
||||
{
|
||||
event.dataTransfer.setData("DownloadURL", data.type + ':' + data.title + ':' + url);
|
||||
}
|
||||
|
||||
// Include URL as a fallback
|
||||
event.dataTransfer.setData("text/uri-list", url);
|
||||
}
|
||||
|
||||
if (event.dataTransfer.types.length == 0)
|
||||
{
|
||||
// No file data? Abort: drag does nothing
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
//event.dataTransfer.setDragImage(event.delegate.target,0,0);
|
||||
var div = jQuery(document.createElement("div"))
|
||||
.attr('id', 'drag_helper')
|
||||
.css({
|
||||
position: 'absolute',
|
||||
top: '0px',
|
||||
left: '0px',
|
||||
width: '300px'
|
||||
});
|
||||
div.append(event.target.cloneNode(true));
|
||||
|
||||
self.list.append(div);
|
||||
|
||||
event.dataTransfer.setDragImage(div.get(0), 0, 0);
|
||||
})
|
||||
.on('drag', function ()
|
||||
{
|
||||
jQuery('#drag_helper', self.list).remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_delete_link(link_id, row)
|
||||
{
|
||||
if (row)
|
||||
{
|
||||
var delete_button = jQuery('.delete', row);
|
||||
delete_button.removeClass("delete").addClass("loading");
|
||||
row.off();
|
||||
}
|
||||
if (this.onchange)
|
||||
{
|
||||
this.onchange(this, link_id, row);
|
||||
}
|
||||
if (typeof link_id != "object")
|
||||
{
|
||||
egw.json("EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_delete", [link_id],
|
||||
function (data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
row.slideUp(row.remove);
|
||||
}
|
||||
}
|
||||
).sendRequest();
|
||||
}
|
||||
else if (row)
|
||||
{
|
||||
// No link ID means a link on an unsaved entry.
|
||||
// Just remove the row, but need to adjust the link_to value also
|
||||
row.slideUp(row.remove);
|
||||
|
||||
// Look for a link-to with the same ID, refresh it
|
||||
if (link_id.link_id)
|
||||
{
|
||||
var self = this;
|
||||
var _widget = link_id.widget || null;
|
||||
this.getRoot().iterateOver(
|
||||
function (widget)
|
||||
{
|
||||
if (widget.id == self.id)
|
||||
{
|
||||
_widget = widget;
|
||||
}
|
||||
},
|
||||
this, et2_link_to
|
||||
);
|
||||
var value = _widget != null ? _widget.getValue() : false;
|
||||
if (_widget && value && value.to_id)
|
||||
{
|
||||
delete value.to_id[link_id.link_id];
|
||||
_widget.set_value(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the link is to a VFS file, we do some special formatting.
|
||||
*
|
||||
* Instead of listing the full path, we use
|
||||
* Path: - filename
|
||||
* When multiple files from the same directory are linked, we exclude
|
||||
* the directory name from all but the first link to that directory
|
||||
*
|
||||
* @param {JQuery} $td Current table data cell for the title
|
||||
* @param {String[]} dirs List of directories in the linked file's path
|
||||
* @param {String[]} _link_data Data for the egw_link
|
||||
* @returns {undefined}
|
||||
*/
|
||||
_format_vfs($td, dirs, _link_data)
|
||||
{
|
||||
// Keep it here for matching next row
|
||||
$td.attr('data-title', _link_data['title']);
|
||||
|
||||
// VFS link - check for same dir as above, and hide dir
|
||||
var reformat = false;
|
||||
var span_size = 1;
|
||||
var prev = jQuery('td.title', $td.parent().prev('tr'));
|
||||
if (prev.length === 1)
|
||||
{
|
||||
var prev_dirs = (prev.attr('data-title') || '').split('/');
|
||||
if (prev_dirs.length > 1 && prev_dirs.length == dirs.length)
|
||||
{
|
||||
for (var i = 0; i < dirs.length; i++)
|
||||
{
|
||||
// Current is same as prev, blank it
|
||||
if (dirs[i] === prev_dirs[i])
|
||||
{
|
||||
reformat = true;
|
||||
span_size += dirs[i].length + 1;
|
||||
dirs[i] = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var filename = dirs.pop();
|
||||
if (reformat && (dirs.length - i) === 0)
|
||||
{
|
||||
$td.html('<span style="display: inline-block; width:' + span_size + 'ex;"></span> - ' + filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Different format for directory
|
||||
span_size += dirs.join('/').length + 1;
|
||||
$td.html('<span style="display: inline-block; text-align: right; width:' + span_size + 'ex;">' + dirs.join('/') + ':</span> - ' + filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
et2_register_widget(et2_link_list, ["link-list"]);
|
||||
|
||||
// can't just define as type, as tracker/app.ts uses it with iterateOver()!
|
||||
// export type et2_link_list = Et2LinkList;
|
||||
export class et2_link_list extends Et2LinkList {}
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -22,6 +22,7 @@ import {ClassWithAttributes} from "./et2_core_inheritance";
|
||||
import {et2_link_entry, et2_link_string} from "./et2_widget_link";
|
||||
import {et2_dialog} from "./et2_widget_dialog";
|
||||
import {egw} from "../jsapi/egw_global";
|
||||
import type {Et2SelectAccountReadonly} from "./Et2Select/Et2SelectReadonly";
|
||||
|
||||
/**
|
||||
* Account selection widget
|
||||
@ -748,160 +749,6 @@ export class et2_selectAccount extends et2_selectbox
|
||||
et2_register_widget(et2_selectAccount, ["select-account"]);
|
||||
|
||||
/**
|
||||
* et2_selectAccount_ro is the readonly implementation of select account
|
||||
* It extends et2_link to avoid needing the whole user list on the client.
|
||||
* Instead, it just asks for the names of the ones needed, as needed.
|
||||
*
|
||||
* @augments et2_link_string
|
||||
* @deprecated use Et2SelectAccountReadonly
|
||||
*/
|
||||
export class et2_selectAccount_ro extends et2_link_string
|
||||
{
|
||||
static readonly _attributes : any = {
|
||||
"empty_label": {
|
||||
"name": "Empty label",
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Textual label for first row, eg: 'All' or 'None'. ID will be ''",
|
||||
translate:true
|
||||
}
|
||||
};
|
||||
|
||||
public static readonly legacyOptions : string[] = ["empty_label"];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor(_parent : et2_widget, _attrs? : WidgetConfig, _child? : object)
|
||||
{
|
||||
/**
|
||||
Resolve some circular dependency problems here
|
||||
selectAccount extends link, link is in a file that needs select,
|
||||
select has menulist wrapper, which needs to know about selectAccount before it allows it
|
||||
*/
|
||||
if(_parent.supportedWidgetClasses.indexOf(et2_selectAccount_ro) < 0)
|
||||
{
|
||||
_parent.supportedWidgetClasses.push(et2_selectAccount_ro);
|
||||
}
|
||||
|
||||
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_selectAccount_ro._attributes, _child || {}));
|
||||
|
||||
|
||||
if(_parent.supportedWidgetClasses.indexOf(et2_selectAccount_ro) > 0)
|
||||
{
|
||||
_parent.addChild(this);
|
||||
}
|
||||
|
||||
// Legacy options could have row count or empty label in first slot
|
||||
if(typeof this.options.empty_label == "string")
|
||||
{
|
||||
if(isNaN(<number><unknown>this.options.empty_label))
|
||||
{
|
||||
this.options.empty_label = this.egw().lang(this.options.empty_label);
|
||||
}
|
||||
}
|
||||
|
||||
this.options.application = 'api-accounts';
|
||||
|
||||
// Editable version allows app to set options that aren't accounts, so allow for them
|
||||
let options = et2_selectbox.find_select_options(this,_attrs['select_options'], this.options);
|
||||
if(!jQuery.isEmptyObject(options))
|
||||
{
|
||||
this.options.select_options = options;
|
||||
}
|
||||
|
||||
// Don't make it look like a link though
|
||||
this.list.removeClass("et2_link_string").addClass("et2_selectbox");
|
||||
}
|
||||
|
||||
transformAttributes( _attrs)
|
||||
{
|
||||
et2_selectbox.prototype.transformAttributes.apply(this, arguments);
|
||||
}
|
||||
|
||||
set_value( _value)
|
||||
{
|
||||
// Explode csv
|
||||
if(typeof _value == 'string' && _value.indexOf(',') > 0)
|
||||
{
|
||||
_value = _value.split(',');
|
||||
}
|
||||
|
||||
// pass objects to link widget right away, as following code can't deal with objects
|
||||
if (typeof _value == 'object')
|
||||
{
|
||||
super.set_value(_value);
|
||||
// Don't make it look like a link though
|
||||
jQuery('li',this.list).removeClass("et2_link et2_link_string")
|
||||
// No clicks either
|
||||
.off();
|
||||
return;
|
||||
}
|
||||
|
||||
// Empty it before we fill it
|
||||
jQuery('li',this.list).remove();
|
||||
|
||||
let found = false;
|
||||
if(this.options.select_options && !jQuery.isEmptyObject(this.options.select_options) || this.options.empty_label)
|
||||
{
|
||||
if(!_value)
|
||||
{
|
||||
// Empty label from selectbox
|
||||
this.list.append("<li>"+this.options.empty_label+"</li>");
|
||||
found = true;
|
||||
}
|
||||
else if (typeof _value == 'object')
|
||||
{
|
||||
// An array with 0 / empty in it?
|
||||
for(let i = 0; i < _value.length; i++)
|
||||
{
|
||||
if(!_value[i] || !parseInt(_value[i]))
|
||||
{
|
||||
this.list.append("<li>"+this.options.empty_label+"</li>");
|
||||
return;
|
||||
}
|
||||
else if (this.options.select_options[_value])
|
||||
{
|
||||
this.list.append("<li>"+this.options.select_options[_value]+"</li>");
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Options are not indexed, so we must look
|
||||
var search = _value;
|
||||
if (!jQuery.isArray(search))
|
||||
{
|
||||
search = [_value];
|
||||
}
|
||||
for(let j = 0; j < search.length; j++)
|
||||
{
|
||||
// Not having a value to look up causes an infinite loop
|
||||
if(!search[j]) continue;
|
||||
|
||||
for(let i in this.options.select_options)
|
||||
{
|
||||
if(this.options.select_options[i].value == search[j])
|
||||
{
|
||||
this.list.append("<li>"+this.options.select_options[i].label+"</li>");
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
// if nothing found in select-options let link widget try
|
||||
if(!found && !isNaN(_value))
|
||||
{
|
||||
super.set_value(_value);
|
||||
// Don't make it look like a link though
|
||||
jQuery('li',this.list).removeClass("et2_link et2_link_string")
|
||||
// No clicks either
|
||||
.off();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
et2_register_widget(et2_selectAccount_ro, ["select-account_ro"]);
|
||||
export type et2_selectAccount_ro = Et2SelectAccountReadonly;
|
@ -16,7 +16,6 @@
|
||||
et2_core_valueWidget;
|
||||
et2_widget_description;
|
||||
et2_widget_file;
|
||||
expose;
|
||||
*/
|
||||
|
||||
import {et2_valueWidget} from "./et2_core_valueWidget";
|
||||
@ -24,17 +23,17 @@ import {et2_createWidget, et2_register_widget, WidgetConfig} from "./et2_core_wi
|
||||
import {ClassWithAttributes} from "./et2_core_inheritance";
|
||||
import {et2_textbox, et2_textbox_ro} from "./et2_widget_textbox";
|
||||
import {et2_description} from "./et2_widget_description";
|
||||
import {et2_selectAccount_ro} from "./et2_widget_selectAccount";
|
||||
import {et2_file} from "./et2_widget_file";
|
||||
import {et2_inputWidget} from "./et2_core_inputWidget";
|
||||
import {et2_IDetachedDOM, et2_IExposable} from "./et2_core_interfaces";
|
||||
import {et2_IDetachedDOM} from "./et2_core_interfaces";
|
||||
import {et2_no_init} from "./et2_core_common";
|
||||
import {egw, egw_get_file_editor_prefered_mimes} from "../jsapi/egw_global";
|
||||
import {expose} from "./expose";
|
||||
import {egw_getAppObjectManager, egwActionObject} from "../egw_action/egw_action.js";
|
||||
import {egw_keyHandler} from '../egw_action/egw_keymanager.js';
|
||||
import {EGW_KEY_ENTER} from '../egw_action/egw_action_constants.js';
|
||||
import {Et2Dialog} from "./Et2Dialog/Et2Dialog";
|
||||
import type {Et2VfsMime} from "./Vfs/Et2VfsMime";
|
||||
import type {Et2VfsUid, Et2VfsGid} from "./Et2Vfs/Et2VfsUid";
|
||||
|
||||
/**
|
||||
* Class which implements the "vfs" XET-Tag
|
||||
@ -483,230 +482,22 @@ export class et2_vfsName_ro extends et2_textbox_ro
|
||||
et2_register_widget(et2_vfsName_ro, ["vfs-name_ro"]);
|
||||
|
||||
/**
|
||||
* vfs-mime: icon for mimetype of file, or thumbnail
|
||||
* incl. optional link overlay icon, if file is a symlink
|
||||
*
|
||||
* Creates following structure
|
||||
* <span class="iconOverlayContainer">
|
||||
* <img class="et2_vfs vfsMimeIcon" src="..."/>
|
||||
* <span class="overlayContainer">
|
||||
* <img class="overlay" src="etemplate/templates/default/images/link.png"/>
|
||||
* </span>
|
||||
* </span>
|
||||
*
|
||||
* span.overlayContainer is optional and only generated for symlinks
|
||||
* @augments et2_valueWidget
|
||||
* vfs-mime: icon for mimetype of file, or thumbnail
|
||||
* incl. optional link overlay icon, if file is a symlink
|
||||
*
|
||||
* Creates following structure
|
||||
* <span class="iconOverlayContainer">
|
||||
* <img class="et2_vfs vfsMimeIcon" src="..."/>
|
||||
* <span class="overlayContainer">
|
||||
* <img class="overlay" src="etemplate/templates/default/images/link.png"/>
|
||||
* </span>
|
||||
* </span>
|
||||
*
|
||||
* span.overlayContainer is optional and only generated for symlinks
|
||||
* @augments et2_valueWidget
|
||||
* @deprecated use Et2VfsMime
|
||||
*/
|
||||
export class et2_vfsMime extends expose(class et2_vfsMime extends et2_valueWidget implements et2_IDetachedDOM, et2_IExposable
|
||||
{
|
||||
static readonly _attributes : any = {
|
||||
"value": {
|
||||
"type": "any", // Object
|
||||
"description": "Array of (stat) information about the file"
|
||||
},
|
||||
"size": {
|
||||
"name": "Icon size",
|
||||
"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."
|
||||
},
|
||||
expose_view: {
|
||||
name: "Expose view",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
description: "Clicking on an image would popup an expose view"
|
||||
},
|
||||
thumb_mime_size:{
|
||||
name: "Image thumbnail size",
|
||||
type: "string",
|
||||
default:"",
|
||||
description:" Size of thumbnail in pixel for specified mime type with syntax of: mime_type(s),size (eg. image,video,128)"
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public static readonly legacyOptions : string[] = ["size"];
|
||||
iconOverlayContainer : JQuery = null;
|
||||
overlayContainer : JQuery;
|
||||
image : JQuery = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @memberOf et2_vfsMime
|
||||
*/
|
||||
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
|
||||
{
|
||||
// Call the inherited constructor
|
||||
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_vfsMime._attributes, _child || {}));
|
||||
this.iconOverlayContainer = jQuery(document.createElement('span')).addClass('iconOverlayContainer');
|
||||
this.image = jQuery(document.createElement("img"));
|
||||
this.image.addClass("et2_vfs vfsMimeIcon");
|
||||
this.iconOverlayContainer.append(this.image);
|
||||
this.setDOMNode(this.iconOverlayContainer[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for expose slide action, from expose
|
||||
* Returns data needed for the given index, or false to let expose handle it
|
||||
*
|
||||
* @param {Gallery} gallery
|
||||
* @param {integer} index
|
||||
* @param {DOMNode} slide
|
||||
* @return {Array} array of objects consist of media contnet
|
||||
*/
|
||||
expose_onslide(gallery, index, slide)
|
||||
{
|
||||
var content = false;
|
||||
if (this.options.expose_callback && typeof this.options.expose_callback == 'function')
|
||||
{
|
||||
//Call the callback to load more items
|
||||
content = this.options.expose_callback.call(this,[gallery, index]);
|
||||
if (content) this.add(content);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to get media content to feed the expose
|
||||
*
|
||||
* @param {type} _value
|
||||
* @returns {Array} return an array of object consists of media content
|
||||
*/
|
||||
getMedia(_value)
|
||||
{
|
||||
let base_url = egw.webserverUrl.match(/^\/ig/)?egw(window).window.location.origin + egw.webserverUrl:egw.webserverUrl;
|
||||
let mediaContent = [{
|
||||
title: _value.name,
|
||||
type: _value.mime,
|
||||
href: _value.download_url
|
||||
}];
|
||||
// check if download_url is not already an url (some stream-wrappers allow to specify that!)
|
||||
if (_value.download_url && (_value.download_url[0] == '/' || _value.download_url.substr(0, 4) != 'http'))
|
||||
{
|
||||
mediaContent[0].href = base_url + _value.download_url;
|
||||
|
||||
if (mediaContent[0].href && mediaContent[0].href.match(/\/webdav.php/,'ig'))
|
||||
{
|
||||
mediaContent[0]["download_href"] = mediaContent[0].href + '?download';
|
||||
}
|
||||
}
|
||||
if (_value && _value.mime && _value.mime.match(/video\//,'ig'))
|
||||
{
|
||||
mediaContent[0]["thumbnail"] = this.egw().mime_icon(_value.mime, _value.path, undefined, _value.mtime);
|
||||
}
|
||||
else
|
||||
{
|
||||
mediaContent[0]["thumbnail"] = _value.path && _value.mime ?
|
||||
this.egw().mime_icon(_value.mime, _value.path, undefined, _value.mtime) :
|
||||
this.image.attr('src')+ '&thheight=128';
|
||||
}
|
||||
return mediaContent;
|
||||
}
|
||||
|
||||
set_value(_value)
|
||||
{
|
||||
if (typeof _value !== 'object')
|
||||
{
|
||||
this.egw().debug("warn", "%s only has path, needs array with path & mime", this.id, _value);
|
||||
// Keep going, will be 'unknown type'
|
||||
}
|
||||
let src = this.egw().mime_icon(_value.mime, _value.path, undefined, _value.mtime);
|
||||
if(src)
|
||||
{
|
||||
// Set size of thumbnail
|
||||
if(src.indexOf("thumbnail.php") > -1)
|
||||
{
|
||||
if(this.options.size)
|
||||
{
|
||||
src += "&thsize="+this.options.size;
|
||||
}
|
||||
else if (this.options.thumb_mime_size)
|
||||
{
|
||||
let mime_size = this.options.thumb_mime_size.split(',');
|
||||
let mime_regex = RegExp(_value.mime.split('/')[0]);
|
||||
if (typeof mime_size != 'undefined' && jQuery.isArray(mime_size)
|
||||
&& !isNaN(mime_size[mime_size.length-1]) && isNaN(mime_size[0]) && this.options.thumb_mime_size.match(mime_regex[0], 'ig'))
|
||||
{
|
||||
src += "&thsize=" + mime_size[mime_size.length-1];
|
||||
}
|
||||
}
|
||||
this.image.css("max-width", "100%");
|
||||
}
|
||||
this.image.attr("src", src);
|
||||
// tooltip for mimetypes with available detailed thumbnail
|
||||
if (_value.mime && _value.mime.match(/application\/vnd\.oasis\.opendocument\.(text|presentation|spreadsheet|chart)/))
|
||||
{
|
||||
let tooltip_target = this.image.parent().parent().parent().length > 0 ?
|
||||
// Nextmatch row
|
||||
this.image.parent().parent().parent() :
|
||||
// Not in nextmatch
|
||||
this.image.parent();
|
||||
tooltip_target.tooltip({
|
||||
items:"img",
|
||||
position: {my:"right top", at:"left top", collision:"flipfit"},
|
||||
content: function(){
|
||||
return '<img src="'+this.src+'&thsize=512"/>';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// add/remove link icon, if file is (not) a symlink
|
||||
if ((_value.mode & et2_vfsMode.types.l) == et2_vfsMode.types.l)
|
||||
{
|
||||
if (typeof this.overlayContainer == 'undefined')
|
||||
{
|
||||
|
||||
this.overlayContainer = jQuery(document.createElement('span')).addClass('overlayContainer');
|
||||
this.overlayContainer.append(jQuery(document.createElement('img'))
|
||||
.addClass('overlay').attr('src', this.egw().image('link', 'etemplate')));
|
||||
this.iconOverlayContainer.append(this.overlayContainer);
|
||||
}
|
||||
}
|
||||
else if (typeof this.overlayContainer != 'undefined')
|
||||
{
|
||||
this.overlayContainer.remove();
|
||||
delete this.overlayContainer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of "et2_IDetachedDOM" for fast viewing in gridview
|
||||
* Override to add needed attributes
|
||||
*
|
||||
* @param {array} _attrs array of attribute-names to push further names onto
|
||||
*/
|
||||
getDetachedAttributes(_attrs)
|
||||
{
|
||||
_attrs.push("value", "class");
|
||||
}
|
||||
|
||||
getDetachedNodes()
|
||||
{
|
||||
return [this.node, this.iconOverlayContainer[0], this.image[0]];
|
||||
}
|
||||
|
||||
setDetachedAttributes(_nodes, _values)
|
||||
{
|
||||
this.iconOverlayContainer = jQuery(_nodes[1]);
|
||||
this.image = jQuery(_nodes[2]);
|
||||
this.node = _nodes[0];
|
||||
this.overlayContainer = _nodes[0].children[1];
|
||||
if(typeof _values['class'] != "undefined") {
|
||||
this.image.addClass(_values['class']);
|
||||
}
|
||||
if(typeof _values['value'] != "undefined") {
|
||||
this.set_value(_values['value']);
|
||||
}
|
||||
}
|
||||
}){};
|
||||
et2_register_widget(et2_vfsMime, ["vfs-mime"]);
|
||||
export type et2_vfsMime = Et2VfsMime;
|
||||
|
||||
/**
|
||||
* vfs-size
|
||||
@ -901,26 +692,13 @@ et2_register_widget(et2_vfsMode, ["vfs-mode"]);
|
||||
* vfs-uid / vfs-gid: Displays the name for an ID.
|
||||
* Same as read-only selectAccount, except if there's no user it shows "root"
|
||||
*
|
||||
* @augments et2_selectAccount_ro
|
||||
* @deprecated use Et2VfsUid
|
||||
*/
|
||||
export class et2_vfsUid extends et2_selectAccount_ro
|
||||
{
|
||||
/**
|
||||
* @memberOf et2_vfsUid
|
||||
* @param _node
|
||||
* @param _value
|
||||
*/
|
||||
set_title(_node, _value)
|
||||
{
|
||||
if(_value == "")
|
||||
{
|
||||
arguments[1] = "root";
|
||||
}
|
||||
super.set_title(_node, _value);
|
||||
}
|
||||
}
|
||||
et2_register_widget(et2_vfsUid, ["vfs-uid","vfs-gid"]);
|
||||
|
||||
export type et2_vfsUid = Et2VfsUid;
|
||||
/**
|
||||
* @deprecated use Et2VfsGid
|
||||
*/
|
||||
export type et2_vfsGid = Et2VfsGid;
|
||||
|
||||
/* vfs-upload aka VFS file: displays either download and delete (x) links or a file upload
|
||||
* + ID is either a vfs path or colon separated $app:$id:$relative_path, eg: infolog:123:special/offer
|
||||
|
@ -63,7 +63,8 @@ import './Et2Url/Et2UrlPhoneReadonly';
|
||||
import './Et2Url/Et2UrlFax';
|
||||
import './Et2Url/Et2UrlFaxReadonly';
|
||||
import "./Layout/Et2Split/Et2Split";
|
||||
import "./Vfs/Et2VfsMime";
|
||||
import "./Et2Vfs/Et2VfsMime";
|
||||
import "./Et2Vfs/Et2VfsUid";
|
||||
|
||||
/* Include all widget classes here, we only care about them registering, not importing anything*/
|
||||
import './et2_widget_vfs'; // Vfs must be first (before et2_widget_file) due to import cycle
|
||||
@ -74,7 +75,6 @@ import './et2_widget_hbox';
|
||||
import './et2_widget_groupbox';
|
||||
import './et2_widget_button';
|
||||
import './et2_widget_color';
|
||||
import './et2_widget_description';
|
||||
import './et2_widget_entry';
|
||||
import './et2_widget_textbox';
|
||||
import './et2_widget_number';
|
||||
|
@ -1,657 +0,0 @@
|
||||
/**
|
||||
* EGroupware eTemplate2 - JS object implementing expose view of media and a gallery view
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package etemplate
|
||||
* @subpackage api
|
||||
* @link https://www.egroupware.org
|
||||
* @author Hadi Nategh <hn[at]stylite.de>
|
||||
*/
|
||||
|
||||
/*egw:uses
|
||||
/vendor/bower-asset/jquery/dist/jquery.js;
|
||||
/api/js/jquery/blueimp/js/blueimp-gallery.min.js;
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
|
||||
import {egw, egw_get_file_editor_prefered_mimes} from "../jsapi/egw_global";
|
||||
import {et2_nextmatch} from "./et2_extension_nextmatch";
|
||||
import {ET2_DATAVIEW_STEPSIZE} from "./et2_dataview_controller";
|
||||
import "../jquery/blueimp/js/blueimp-gallery.min.js";
|
||||
import "../../../vendor/bower-asset/jquery-touchswipe/jquery.touchSwipe.js";
|
||||
import {Et2Dialog} from "./Et2Dialog/Et2Dialog";
|
||||
|
||||
type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
|
||||
/**
|
||||
* This function extends the given widget with blueimp gallery plugin
|
||||
*
|
||||
* @param {type} widget
|
||||
* @returns {widget}
|
||||
*/
|
||||
export function expose<TBase extends Constructor>(Base: TBase)
|
||||
{
|
||||
"use strict";
|
||||
|
||||
// Minimum data to qualify as an image and not cause errors
|
||||
const IMAGE_DEFAULT = {
|
||||
title: egw.lang('loading'),
|
||||
href: '',
|
||||
type: 'image/png',
|
||||
thumbnail: '',
|
||||
loading: true
|
||||
};
|
||||
|
||||
// For filtering to only show things we can handle
|
||||
const MIME_REGEX = (navigator.userAgent.match(/(MSIE|Trident)/)) ?
|
||||
// IE only supports video/mp4 mime type
|
||||
new RegExp(/(video\/mp4)|(image\/:*(?!tif|x-xcf|pdf))|(audio\/:*)/) :
|
||||
new RegExp(/(video\/(mp4|ogg|webm))|(image\/:*(?!tif|x-xcf|pdf))|(audio\/:*)/);
|
||||
|
||||
const MIME_AUDIO_REGEX = new RegExp(/(audio\/:*)/);
|
||||
// open office document mime type currently supported by webodf editor
|
||||
const MIME_ODF_REGEX = new RegExp(/application\/vnd\.oasis\.opendocument\.text/);
|
||||
|
||||
// Only one gallery
|
||||
var gallery = null;
|
||||
|
||||
/**
|
||||
* See if the current widget is in a nextmatch, as this allows us to display
|
||||
* thumbnails underneath
|
||||
*
|
||||
* @param {et2_IExposable} widget
|
||||
* @returns {et2_nextmatch | null}
|
||||
*/
|
||||
var find_nextmatch = function (widget) {
|
||||
let current = widget;
|
||||
let nextmatch = null;
|
||||
while (nextmatch == null && current) {
|
||||
current = current.getParent();
|
||||
if (typeof current != 'undefined' && current.instanceOf(et2_nextmatch)) {
|
||||
nextmatch = current;
|
||||
}
|
||||
}
|
||||
// No nextmatch, or nextmatch not quite ready
|
||||
// At the moment only filemanger nm would work
|
||||
// as gallery, thus we disable other nestmatches
|
||||
// to build up gallery but filemanager
|
||||
if (nextmatch == null || nextmatch.controller == null || !nextmatch.dom_id.match(/filemanager/, 'ig')) return null;
|
||||
|
||||
return nextmatch;
|
||||
};
|
||||
|
||||
/**
|
||||
* Read images out of the data for the nextmatch
|
||||
*
|
||||
* @param {et2_nextmatch} nm
|
||||
* @param {Object[]} images
|
||||
* @param {number} start_at
|
||||
* @returns {undefined}
|
||||
*/
|
||||
var read_from_nextmatch = function (nm, images, start_at) {
|
||||
if (!start_at) start_at = 0;
|
||||
let image_index = start_at;
|
||||
let stop = Math.max.apply(null, Object.keys(nm.controller._indexMap));
|
||||
|
||||
for (let i = start_at; i <= stop; i++) {
|
||||
if (!nm.controller._indexMap[i] || !nm.controller._indexMap[i].uid) {
|
||||
// Returning instead of using IMAGE_DEFAULT means we stop as
|
||||
// soon as a hole is found, instead of getting everything that is
|
||||
// available. The gallery can't fill in the holes.
|
||||
images[image_index++] = IMAGE_DEFAULT;
|
||||
continue;
|
||||
}
|
||||
let uid = nm.controller._indexMap[i].uid;
|
||||
if (!uid) continue;
|
||||
let data = egw.dataGetUIDdata(uid);
|
||||
if (data && data.data && data.data.mime && MIME_REGEX.test(data.data.mime) && !MIME_AUDIO_REGEX.test(data.data.mime)) {
|
||||
let media = this.getMedia(data.data);
|
||||
images[image_index++] = jQuery.extend({}, data.data, media[0]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a particular index/image in the gallery instead of just appending
|
||||
* it to the end
|
||||
*
|
||||
* @param {integer} index
|
||||
* @param {Object} image
|
||||
* @returns {undefined}
|
||||
*/
|
||||
var set_slide = function (index, image) {
|
||||
let active = (index == gallery.index);
|
||||
|
||||
// Pad with blanks until length is right
|
||||
while (index > gallery.getNumber()) {
|
||||
gallery.add([jQuery.extend({}, IMAGE_DEFAULT)]);
|
||||
}
|
||||
|
||||
// Don't bother with adding a default, we just did that
|
||||
if (image.loading) {
|
||||
//Add load class if it's really a slide with error
|
||||
if (gallery.slidesContainer.find('[data-index="' + index + '"]').hasClass(gallery.options.slideErrorClass))
|
||||
jQuery(gallery.slides[index])
|
||||
.addClass(gallery.options.slideLoadingClass)
|
||||
.removeClass(gallery.options.slideErrorClass);
|
||||
return;
|
||||
}
|
||||
// Remove the loading class if the slide is loaded
|
||||
else {
|
||||
jQuery(gallery.slides[index]).removeClass(gallery.options.slideLoadingClass);
|
||||
}
|
||||
|
||||
// Just use add to let gallery create everything it needs
|
||||
let new_index = gallery.num;
|
||||
gallery.add([image]);
|
||||
|
||||
// Move it to where we want it.
|
||||
// Gallery uses arrays and indexes and has several internal variables
|
||||
// that need to be updated.
|
||||
//
|
||||
// list
|
||||
gallery.list[index] = gallery.list[new_index];
|
||||
gallery.list.splice(new_index, 1);
|
||||
|
||||
// indicators & slides
|
||||
let dom_nodes = ['indicators', 'slides'];
|
||||
for (let i in dom_nodes) {
|
||||
let var_name = dom_nodes[i];
|
||||
// Remove old one from DOM
|
||||
jQuery(gallery[var_name][index]).remove();
|
||||
// Move new one into it's place in gallery
|
||||
gallery[var_name][index] = gallery[var_name][new_index];
|
||||
// Move into place in DOM
|
||||
let node = jQuery(gallery[var_name][index]);
|
||||
node.attr('data-index', index)
|
||||
.insertAfter(jQuery("[data-index='" + (index - 1) + "']", node.parent()));
|
||||
if (active) node.addClass(gallery.options.activeIndicatorClass);
|
||||
gallery[var_name].splice(new_index, 1);
|
||||
}
|
||||
if (active) {
|
||||
gallery.activeIndicator = jQuery(gallery.indicators[index]);
|
||||
}
|
||||
|
||||
// positions
|
||||
gallery.positions[index] = active ? 0 : (index > gallery.index ? gallery.slideWidth : -gallery.slideWidth);
|
||||
gallery.positions.splice(new_index, 1);
|
||||
|
||||
// elements - removing will allow to re-do the slide
|
||||
if (gallery.elements[index]) {
|
||||
delete gallery.elements[index];
|
||||
gallery.loadElement(index);
|
||||
}
|
||||
|
||||
// Remove the one we just added
|
||||
gallery.num -= 1;
|
||||
};
|
||||
|
||||
return class exposable extends Base
|
||||
{
|
||||
public mime_regexp: RegExp;
|
||||
private mime_odf_regex: RegExp;
|
||||
private expose_options: {
|
||||
container: string; toggleControlsOnReturn: boolean; closeOnSwipeUpOrDown: boolean; clearSlides: boolean; onopen: any; emulateTouchEvents: boolean; onslideend: (index, slide) => void; rightEdgeClass: string; thumbnailWithImgTag: boolean; onslidecomplete: (index, slide) => void; continuous: boolean; startSlideshow: boolean; fullscreenClass: string; onslide: (index, slide) => void; playingClass: string; slideClass: string; urlProperty: string; closeOnEscape: boolean; singleClass: string; slideErrorClass: string; index: number; preloadRange: number; slideContentClass: string; onclosed: any; hidePageScrollbars: boolean; displayTransition: boolean; indicatorContainer: string; disableScroll: boolean; unloadElements: boolean; nextClass: string; stopTouchEventsPropagation: boolean; transitionSpeed: number; carousel: boolean; titleProperty: string; prevClass: string; typeProperty: string; enableKeyboardNavigation: boolean; slidesContainer: string; closeOnSlideClick: boolean; stretchImages: boolean; onclose: any; onopened: any; playPauseClass: string; thumbnailProperty: string; titleElement: string; slideLoadingClass: string; toggleSlideshowOnSpace: boolean; thumbnailIndicators: boolean; activeIndicatorClass: string; displayClass: string; closeClass: string; slideshowInterval: number; toggleClass: string; hideControlsOnSlideshow: boolean; controlsClass: string; toggleFullscreenOnSlideShow: boolean; leftEdgeClass: string; slideshowTransitionSpeed: undefined
|
||||
};
|
||||
|
||||
constructor(...args: any[])
|
||||
{
|
||||
// Call the inherited constructor
|
||||
super(...args);
|
||||
this.mime_regexp = MIME_REGEX;
|
||||
this.mime_audio_regexp = MIME_AUDIO_REGEX;
|
||||
this.mime_odf_regex = MIME_ODF_REGEX;
|
||||
let self = this;
|
||||
this.expose_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: '',
|
||||
// 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 class to add for fullscreen button option
|
||||
fullscreenClass: 'fullscreen',
|
||||
// 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: true,
|
||||
// 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: false,
|
||||
// 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: true,
|
||||
// Allow continuous navigation, moving from last to first
|
||||
// and from first to last slide:
|
||||
continuous: false,
|
||||
// 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: 3000,
|
||||
// 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,
|
||||
//Hide controls when the slideshow is playing
|
||||
hideControlsOnSlideshow: true,
|
||||
//Request fullscreen on slide show
|
||||
toggleFullscreenOnSlideShow: true,
|
||||
// The transition speed for automatic slide changes, set to an integer
|
||||
// greater 0 to override the default transition speed:
|
||||
slideshowTransitionSpeed: undefined,
|
||||
// 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,
|
||||
//thumbnail with image tag
|
||||
thumbnailWithImgTag: true,
|
||||
// Callback function executed when the Gallery is initialized.
|
||||
// Is called with the gallery instance as "this" object:
|
||||
onopen: jQuery.proxy(this.expose_onopen, this),
|
||||
// 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: jQuery.proxy(this.expose_onopened, this),
|
||||
// Callback function executed on slide change.
|
||||
// Is called with the gallery instance as "this" object and the
|
||||
// current index and slide as arguments:
|
||||
onslide: function (index, slide) {
|
||||
// Call our onslide method, and include gallery as an attribute
|
||||
self.expose_onslide.apply(self, [this, index, slide]);
|
||||
},
|
||||
// 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: function (index, slide) {
|
||||
// Call our onslide method, and include gallery as an attribute
|
||||
self.expose_onslideend.apply(self, [this, index, slide]);
|
||||
},
|
||||
//// 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: function (index, slide) {
|
||||
// Call our onslide method, and include gallery as an attribute
|
||||
self.expose_onslidecomplete.apply(self, [this, index, slide]);
|
||||
},
|
||||
//// Callback function executed when the Gallery is about to be closed.
|
||||
// Is called with the gallery instance as "this" object:
|
||||
onclose: jQuery.proxy(this.expose_onclose, this),
|
||||
// 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: jQuery.proxy(this.expose_onclosed, this)
|
||||
};
|
||||
let $body = jQuery('body');
|
||||
if ($body.find('#blueimp-gallery').length == 0) {
|
||||
// Gallery Main DIV container
|
||||
let $expose_node = jQuery(document.createElement('div')).attr({
|
||||
id: "blueimp-gallery",
|
||||
class: "blueimp-gallery"
|
||||
});
|
||||
// Create Gallery DOM NODE
|
||||
$expose_node.append('<div class="slides"></div><h3 class="title"></h3><a class="prev">‹</a><a class="next">›</a><a title="' + egw().lang('Close') + '" class="close">×</a><a title="' + egw().lang('Play/Pause') + '" class="play-pause"></a><a title="' + egw().lang('Fullscreen') + '" class="fullscreen"></a><a title="' + egw().lang('Save') + '" class="download"></a><ol class="indicator"></ol>');
|
||||
// Append the gallery Node to DOM
|
||||
$body.append($expose_node);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
set_value(_value) {
|
||||
//todo: not sure if we need that with the new construction
|
||||
//if (typeof this._super == 'undefined') return;
|
||||
|
||||
// @ts-ignore
|
||||
super.set_value(_value);
|
||||
// Do not run set value of expose if expose_view is not set
|
||||
// it causes a wired error on nested image widgets which
|
||||
// seems the expose is not its child widget
|
||||
// @ts-ignore
|
||||
if (!this.options.expose_view) {
|
||||
return;
|
||||
}
|
||||
|
||||
let fe = egw_get_file_editor_prefered_mimes();
|
||||
let self = this;
|
||||
// If the media type is not supported do not bind the click handler
|
||||
if (!_value || typeof _value.mime != 'string' || (!_value.mime.match(MIME_REGEX, 'ig')
|
||||
&& (!fe || fe.mime && !fe.mime[_value.mime])) || typeof _value.download_url == 'undefined') {
|
||||
return;
|
||||
}
|
||||
// @ts-ignore
|
||||
if (typeof this.options.expose_view != 'undefined' && this.options.expose_view) {
|
||||
// @ts-ignore
|
||||
jQuery(this.node).on('click', function (event) {
|
||||
// Do not trigger expose view if one of the operator keys are held
|
||||
if (!event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey) {
|
||||
if (_value.mime.match(MIME_REGEX, 'ig') && !_value.mime.match(MIME_AUDIO_REGEX,'ig')) {
|
||||
self._init_blueimp_gallery(event, _value);
|
||||
}
|
||||
else if (_value.mime.match(MIME_AUDIO_REGEX,'ig'))
|
||||
{
|
||||
self._audio_player(_value);
|
||||
}
|
||||
else if (fe && fe.mime && fe.edit && fe.mime[_value.mime]) {
|
||||
egw.open_link(egw.link('/index.php', {
|
||||
menuaction: fe.edit.menuaction,
|
||||
path: _value.path,
|
||||
cd: 'no' // needed to not reload framework in sharing
|
||||
}), '', fe.edit_popup);
|
||||
}
|
||||
}
|
||||
event.stopImmediatePropagation();
|
||||
}).addClass('et2_clickable');
|
||||
}
|
||||
}
|
||||
|
||||
private _init_blueimp_gallery(event, _value) {
|
||||
let mediaContent = [];
|
||||
let nm = find_nextmatch(this);
|
||||
let current_index = 0;
|
||||
if (nm && !this._is_target_indepth(nm, event.target)) {
|
||||
// Get the row that was clicked, find its index in the list
|
||||
let current_entry = nm.controller.getRowByNode(event.target);
|
||||
|
||||
// But before it goes, we'll pull everything we can
|
||||
read_from_nextmatch.call(this, nm, mediaContent);
|
||||
// find current_entry in array and set it's array-index
|
||||
for (let i = 0; i < mediaContent.length; i++) {
|
||||
if ('filemanager::' + mediaContent[i].path == current_entry.uid) {
|
||||
current_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// This will trigger nm to refresh and get just the ones we can handle
|
||||
// but it might take a while, so do it later - make sure our current
|
||||
// one is loaded first.
|
||||
window.setTimeout(function () {
|
||||
nm.applyFilters({col_filter: {mime: '/' + MIME_REGEX.source + '/'}});
|
||||
}, 1);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
mediaContent = this.getMedia(_value);
|
||||
// Do not show thumbnail indicator on single expose view
|
||||
this.expose_options.thumbnailIndicators = false;
|
||||
}
|
||||
this.expose_options.index = current_index;
|
||||
// @ts-ignore
|
||||
gallery = blueimp.Gallery(mediaContent, this.expose_options);
|
||||
}
|
||||
|
||||
/**
|
||||
* audio player expose
|
||||
* @param _value
|
||||
* @private
|
||||
*/
|
||||
private _audio_player(_value)
|
||||
{
|
||||
let button = [
|
||||
{"button_id": 1, "text": egw.lang('close'), id: '1', image: 'cancel', default: true}
|
||||
];
|
||||
|
||||
// @ts-ignore
|
||||
let mediaContent = this.getMedia(_value)[0];
|
||||
let dialog = new Et2Dialog();
|
||||
dialog.transformAttributes({
|
||||
callback: function(_btn, value)
|
||||
{
|
||||
if(_btn == Et2Dialog.OK_BUTTON)
|
||||
{
|
||||
|
||||
}
|
||||
},
|
||||
beforeClose: function()
|
||||
{
|
||||
|
||||
},
|
||||
title: mediaContent.title,
|
||||
buttons: button,
|
||||
minWidth: 350,
|
||||
minHeight: 200,
|
||||
modal: false,
|
||||
position: "right bottom,right-50 bottom-10",
|
||||
value: {
|
||||
content: {
|
||||
src: mediaContent.download_href
|
||||
}
|
||||
},
|
||||
resizable: false,
|
||||
template: egw.webserverUrl + '/api/templates/default/audio_player.xet',
|
||||
dialogClass: "audio_player"
|
||||
});
|
||||
document.body.appendChild(dialog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if clicked target from nm is in depth
|
||||
*
|
||||
* @param nm nextmatch widget
|
||||
* @param target selected target dom node
|
||||
*
|
||||
* @return {boolean} returns false if target is not in depth otherwise True
|
||||
*/
|
||||
private _is_target_indepth(nm, target?)
|
||||
{
|
||||
let res = false;
|
||||
if (nm) {
|
||||
if (!target) {
|
||||
// @ts-ignore
|
||||
let target = this.getDOMNode();
|
||||
}
|
||||
let entry = nm.controller.getRowByNode(target);
|
||||
if (entry && entry.controller.getDepth() > 0) {
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
expose_onopen(event) {}
|
||||
|
||||
expose_onopened()
|
||||
{
|
||||
// Check to see if we're in a nextmatch, do magic
|
||||
let nm = find_nextmatch(this);
|
||||
let self = this;
|
||||
if (nm) {
|
||||
// Add scrolling to the indicator list
|
||||
let total_count = nm.controller._grid.getTotalCount();
|
||||
if (total_count >= gallery.num) {
|
||||
let $indicator = gallery.container.find('.indicator');
|
||||
$indicator.off()
|
||||
.addClass('paginating')
|
||||
.swipe(function (event, direction, distance) {
|
||||
// @ts-ignore
|
||||
if (direction == jQuery.fn.swipe.directions.LEFT)
|
||||
{
|
||||
distance *= -1;
|
||||
}
|
||||
// @ts-ignore
|
||||
else if (direction == jQuery.fn.swipe.directions.RIGHT)
|
||||
{
|
||||
// OK.
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
jQuery(this).css('left', Math.min(0, parseInt(jQuery(this).css('left')) - (distance * 30)) + 'px');
|
||||
});
|
||||
// Bind the mousewheel handler for FF (DOMMousewheel), and other browsers (mousewheel)
|
||||
$indicator.bind('mousewheel DOMMousewheel', function (event, _delta) {
|
||||
var delta = _delta || event.originalEvent.wheelDelta / 120;
|
||||
if (delta > 0 && parseInt(jQuery(this).css('left')) > gallery.container.width() / 2) return;
|
||||
|
||||
//Reload next pictures into the gallery by scrolling on thumbnails
|
||||
if (delta < 0 && jQuery(this).width() + parseInt(jQuery(this).css('left')) < gallery.container.width()) {
|
||||
var nextIndex = gallery.indicatorContainer.find('[title="loading"]')[0];
|
||||
if (nextIndex) self.expose_onslideend(gallery, nextIndex.dataset.index - 1);
|
||||
return;
|
||||
}
|
||||
// Move it about 5 indicators
|
||||
jQuery(this).css('left', parseInt(jQuery(this).css('left')) - (-delta * gallery.activeIndicator.width() * 5) + 'px');
|
||||
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger on slide left/right
|
||||
*/
|
||||
expose_onslide(gallery, index, slide)
|
||||
{
|
||||
//todo
|
||||
//if (typeof this._super == 'undefined') return;
|
||||
// First let parent try
|
||||
super.expose_onslide(gallery, index, slide);
|
||||
let nm = find_nextmatch(this);
|
||||
if (nm) {
|
||||
// See if we need to move the indicator
|
||||
let indicator = gallery.container.find('.indicator');
|
||||
let current = jQuery('.active', indicator).position();
|
||||
|
||||
if (current) {
|
||||
indicator.animate({left: (gallery.container.width() / 2) - current.left}, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expose_onslideend(gallery, index)
|
||||
{
|
||||
// Check to see if we're in a nextmatch, do magic
|
||||
let nm = find_nextmatch(this);
|
||||
if (nm) {
|
||||
// Check to see if we're near the end, or maybe some pagination
|
||||
// would be good.
|
||||
let total_count = nm.controller._grid.getTotalCount();
|
||||
|
||||
// Already at the end, don't bother
|
||||
if (index == total_count - 1 || index == 0) return;
|
||||
|
||||
// Try to determine direction from state of next & previous slides
|
||||
let direction = 1;
|
||||
for (let i in gallery.elements) {
|
||||
// Loading or error
|
||||
if (gallery.elements[i] == 1 || gallery.elements[i] == 3 || gallery.list[i].loading) {
|
||||
direction = i >= index ? 1 : -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gallery.list[index + direction] || gallery.list[index + direction].loading ||
|
||||
total_count > gallery.getNumber() && index + ET2_DATAVIEW_STEPSIZE > gallery.getNumber()) {
|
||||
// This will get the next batch of rows
|
||||
let start = Math.max(0, direction > 0 ? index : index - ET2_DATAVIEW_STEPSIZE);
|
||||
let end = Math.min(total_count - 1, start + ET2_DATAVIEW_STEPSIZE);
|
||||
nm.controller._gridCallback(start, end);
|
||||
let images = [];
|
||||
read_from_nextmatch.call(this, nm, images, start);
|
||||
|
||||
// Gallery always adds to the end, causing problems with pagination
|
||||
for (let i in images) {
|
||||
//if(i == index || i < gallery.num) continue;
|
||||
set_slide(i, images[i]);
|
||||
//gallery.add([images[i]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expose_onslidecomplete() {}
|
||||
|
||||
expose_onclose()
|
||||
{
|
||||
// Check to see if we're in a nextmatch, remove magic
|
||||
let nm = find_nextmatch(this);
|
||||
if (nm && !this._is_target_indepth(nm))
|
||||
{
|
||||
// Remove scrolling from thumbnails
|
||||
gallery.container.find('.indicator')
|
||||
.removeClass('paginating')
|
||||
.off('mousewheel')
|
||||
.off('swipe');
|
||||
|
||||
// Remove applied mime filter
|
||||
nm.applyFilters({col_filter: {mime: ''}});
|
||||
}
|
||||
}
|
||||
|
||||
expose_onclosed() {}
|
||||
}
|
||||
}
|
||||
|
84
api/js/jquery/blueimp/Gruntfile.js
vendored
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* 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']);
|
||||
|
||||
};
|
@ -1,984 +0,0 @@
|
||||
# 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
|
||||
<link rel="stylesheet" href="css/blueimp-gallery.min.css">
|
||||
```
|
||||
|
||||
Add the following HTML snippet with the Gallery widget to the body of your webpage:
|
||||
|
||||
```html
|
||||
<!-- The Gallery as lightbox dialog, should be a child element of the document body -->
|
||||
<div id="blueimp-gallery" class="blueimp-gallery">
|
||||
<div class="slides"></div>
|
||||
<h3 class="title"></h3>
|
||||
<a class="prev">‹</a>
|
||||
<a class="next">›</a>
|
||||
<a class="close">×</a>
|
||||
<a class="play-pause"></a>
|
||||
<ol class="indicator"></ol>
|
||||
</div>
|
||||
```
|
||||
|
||||
Include the Gallery script at the bottom of the body of your webpage:
|
||||
|
||||
```html
|
||||
<script src="js/blueimp-gallery.min.js"></script>
|
||||
```
|
||||
|
||||
Create a list of links to image files, optionally with enclosed thumbnails and add them to the body of your webpage, before including the Gallery script:
|
||||
|
||||
```html
|
||||
<div id="links">
|
||||
<a href="images/banana.jpg" title="Banana">
|
||||
<img src="images/thumbnails/banana.jpg" alt="Banana">
|
||||
</a>
|
||||
<a href="images/apple.jpg" title="Apple">
|
||||
<img src="images/thumbnails/apple.jpg" alt="Apple">
|
||||
</a>
|
||||
<a href="images/orange.jpg" title="Orange">
|
||||
<img src="images/thumbnails/orange.jpg" alt="Orange">
|
||||
</a>
|
||||
</div>
|
||||
```
|
||||
|
||||
Add the following JavaScript code after including the Gallery script, to display the images in the Gallery lightbox on click of the links:
|
||||
|
||||
```html
|
||||
<script>
|
||||
document.getElementById('links').onclick = function (event) {
|
||||
event = event || window.event;
|
||||
var target = event.target || event.srcElement,
|
||||
link = target.src ? target.parentNode : target,
|
||||
options = {index: link, event: event},
|
||||
links = this.getElementsByTagName('a');
|
||||
blueimp.Gallery(links, options);
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
### Controls
|
||||
To initialize the Gallery with visible controls, add the CSS class **blueimp-gallery-controls** to the Gallery widget:
|
||||
|
||||
```html
|
||||
<div id="blueimp-gallery" class="blueimp-gallery blueimp-gallery-controls">
|
||||
<div class="slides"></div>
|
||||
<h3 class="title"></h3>
|
||||
<a class="prev">‹</a>
|
||||
<a class="next">›</a>
|
||||
<a class="close">×</a>
|
||||
<a class="play-pause"></a>
|
||||
<ol class="indicator"></ol>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Carousel setup
|
||||
To display the images in an inline carousel instead of a lightbox, follow the [lightbox setup](#lightbox-setup) and add the CSS class **blueimp-gallery-carousel** to the Gallery widget and remove the child element with the **close** class, or add a new Gallery widget with a different **id** to your webpage:
|
||||
|
||||
```html
|
||||
<!-- The Gallery as inline carousel, can be positioned anywhere on the page -->
|
||||
<div id="blueimp-gallery-carousel" class="blueimp-gallery blueimp-gallery-carousel">
|
||||
<div class="slides"></div>
|
||||
<h3 class="title"></h3>
|
||||
<a class="prev">‹</a>
|
||||
<a class="next">›</a>
|
||||
<a class="play-pause"></a>
|
||||
<ol class="indicator"></ol>
|
||||
</div>
|
||||
```
|
||||
|
||||
Add the following JavaScript code after including the Gallery script to initialize the carousel:
|
||||
|
||||
```html
|
||||
<script>
|
||||
blueimp.Gallery(
|
||||
document.getElementById('links').getElementsByTagName('a'),
|
||||
{
|
||||
container: '#blueimp-gallery-carousel',
|
||||
carousel: true
|
||||
}
|
||||
);
|
||||
</script>
|
||||
```
|
||||
|
||||
## Keyboard shortcuts
|
||||
The Gallery can be controlled with the following keyboard shortcuts:
|
||||
|
||||
* **Return**: Toggle controls visibility.
|
||||
* **Esc**: Close the Gallery lightbox.
|
||||
* **Space**: Toggle the slideshow (play/pause).
|
||||
* **Left**: Move to the previous slide.
|
||||
* **Right**: Move to the next slide.
|
||||
|
||||
Please note that setting the **carousel** option to **true** disables the keyboard shortcuts by default.
|
||||
|
||||
## Options
|
||||
|
||||
### Default options
|
||||
The following are the default options set by the core Gallery library:
|
||||
|
||||
```js
|
||||
var 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
|
||||
};
|
||||
```
|
||||
|
||||
### Event callbacks
|
||||
Event callbacks can be set as function properties of the options object passed to the Gallery initialization function:
|
||||
|
||||
```js
|
||||
var gallery = blueimp.Gallery(
|
||||
linkList,
|
||||
{
|
||||
onopen: function () {
|
||||
// Callback function executed when the Gallery is initialized.
|
||||
},
|
||||
onopened: function () {
|
||||
// Callback function executed when the Gallery has been initialized
|
||||
// and the initialization transition has been completed.
|
||||
},
|
||||
onslide: function (index, slide) {
|
||||
// Callback function executed on slide change.
|
||||
},
|
||||
onslideend: function (index, slide) {
|
||||
// Callback function executed after the slide change transition.
|
||||
},
|
||||
onslidecomplete: function (index, slide) {
|
||||
// Callback function executed on slide content load.
|
||||
},
|
||||
onclose: function () {
|
||||
// Callback function executed when the Gallery is about to be closed.
|
||||
},
|
||||
onclosed: function () {
|
||||
// Callback function executed when the Gallery has been closed
|
||||
// and the closing transition has been completed.
|
||||
}
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Carousel options
|
||||
If the **carousel** option is **true**, the following options are set to different default values:
|
||||
|
||||
```js
|
||||
var carouselOptions = {
|
||||
hidePageScrollbars: false,
|
||||
toggleControlsOnReturn: false,
|
||||
toggleSlideshowOnSpace: false,
|
||||
enableKeyboardNavigation: false,
|
||||
closeOnEscape: false,
|
||||
closeOnSlideClick: false,
|
||||
closeOnSwipeUpOrDown: false,
|
||||
disableScroll: false,
|
||||
startSlideshow: true
|
||||
};
|
||||
```
|
||||
|
||||
The options object passed to the Gallery function extends the default options and also those options set via **carousel** mode.
|
||||
|
||||
### Indicator options
|
||||
The following are the additional default options set for the slide position indicator:
|
||||
|
||||
```js
|
||||
var indicatorOptions = {
|
||||
// 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
|
||||
};
|
||||
```
|
||||
|
||||
### Fullscreen options
|
||||
The following are the additional default options set for the fullscreen mode:
|
||||
|
||||
```js
|
||||
var fullscreenOptions = {
|
||||
// Defines if the gallery should open in fullscreen mode:
|
||||
fullScreen: false
|
||||
};
|
||||
```
|
||||
|
||||
### Video options
|
||||
|
||||
#### Video factory options
|
||||
The following are the additional default options set for the video factory:
|
||||
|
||||
```js
|
||||
var videoFactoryOptions = {
|
||||
// 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'
|
||||
};
|
||||
```
|
||||
#### YouTube options
|
||||
Options for [YouTube](https://www.youtube.com/) videos:
|
||||
|
||||
```js
|
||||
var youTubeOptions = {
|
||||
// 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: undefined,
|
||||
// Require a click on the native YouTube player for the initial playback:
|
||||
youTubeClickToPlay: true
|
||||
};
|
||||
```
|
||||
|
||||
#### Vimeo options
|
||||
Options for [Vimeo](https://vimeo.com/) videos:
|
||||
|
||||
```js
|
||||
var youTubeOptions = {
|
||||
// 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
|
||||
};
|
||||
```
|
||||
|
||||
### Container and element options
|
||||
The widget **container** option can be set as id string (with "#" as prefix) or element node, so the following are equivalent:
|
||||
|
||||
```js
|
||||
var options = {
|
||||
container: '#blueimp-gallery'
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
var options = {
|
||||
container: document.getElementById('blueimp-gallery')
|
||||
};
|
||||
```
|
||||
|
||||
The **slidesContainer**, **titleElement** and **indicatorContainer** options can also be defined using a tag name, which selects the first tag of this kind found inside of the widget container:
|
||||
|
||||
```js
|
||||
var options = {
|
||||
slidesContainer: 'div',
|
||||
titleElement: 'h3',
|
||||
indicatorContainer: 'ol'
|
||||
};
|
||||
```
|
||||
|
||||
It is also possible to define the container and element options with a more complex [querySelector](https://developer.mozilla.org/en-US/docs/Web/API/document.querySelector), which is supported by IE8+ and all modern web browsers.
|
||||
|
||||
If the helper script is replaced with [jQuery](https://jquery.com/), the container and element options can be any valid jQuery selector.
|
||||
|
||||
### Property options
|
||||
The options ending with "Property" define how the properties of each link element are accessed.
|
||||
For example, the **urlProperty** is by default set to **href**. This allows to define link elements with **href** or **data-href** attributes:
|
||||
|
||||
```html
|
||||
<div id="links">
|
||||
<a href="images/banana.jpg">Banana</a>
|
||||
<a data-href="images/apple.jpg">Apple</a>
|
||||
</div>
|
||||
```
|
||||
|
||||
If the links are passed as JavaScript array, it is also possible to define nested property names, by using the native JavaScript accessor syntax for the property string:
|
||||
|
||||
```js
|
||||
blueimp.Gallery(
|
||||
[
|
||||
{
|
||||
data: {urls: ['https://example.org/images/banana.jpg']}
|
||||
},
|
||||
{
|
||||
data: {urls: ['https://example.org/images/apple.jpg']}
|
||||
}
|
||||
],
|
||||
{
|
||||
urlProperty: 'data.urls[0]'
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Initialization
|
||||
The blueimp Gallery can be initialized by simply calling it as a function with an array of links as first argument and an optional options object as second argument:
|
||||
|
||||
```js
|
||||
var gallery = blueimp.Gallery(links, options);
|
||||
```
|
||||
|
||||
The links array can be a list of URL strings or a list of objects with URL properties:
|
||||
|
||||
```js
|
||||
var gallery = blueimp.Gallery([
|
||||
'https://example.org/images/banana.jpg',
|
||||
'https://example.org/images/apple.jpg',
|
||||
'https://example.org/images/orange.jpg'
|
||||
]);
|
||||
```
|
||||
|
||||
```js
|
||||
var gallery = blueimp.Gallery([
|
||||
{
|
||||
title: 'Banana',
|
||||
href: 'https://example.org/images/banana.jpg',
|
||||
type: 'image/jpeg',
|
||||
thumbnail: 'https://example.org/thumbnails/banana.jpg'
|
||||
},
|
||||
{
|
||||
title: 'Apple',
|
||||
href: 'https://example.org/images/apple.jpg',
|
||||
type: 'image/jpeg',
|
||||
thumbnail: 'https://example.org/thumbnails/apple.jpg'
|
||||
}
|
||||
]);
|
||||
```
|
||||
|
||||
The URL property name defined by each list object can be configured via the **urlProperty** option. By default, it is set to **href**, which allows to pass a list of HTML link elements as first argument.
|
||||
|
||||
For images, the **thumbnail** property defines the URL of the image thumbnail, which is used for the indicator navigation displayed at the bottom of the Gallery, if the controls are visible.
|
||||
|
||||
The object returned by executing the Gallery function (the **gallery** variable in the example code above) is a new instance of the Gallery and allows to access the public [API methods](#api-methods) provided by the Gallery.
|
||||
The Gallery initialization function returns **false** if the given list was empty, the Gallery widget is missing, or the browser doesn't pass the functionality test.
|
||||
|
||||
### API methods
|
||||
The Gallery object returned by executing the Gallery function provides the following public API methods:
|
||||
|
||||
```js
|
||||
// Return the current slide index position:
|
||||
var pos = gallery.getIndex();
|
||||
|
||||
// Return the total number of slides:
|
||||
var count = gallery.getNumber();
|
||||
|
||||
// Move to the previous slide:
|
||||
gallery.prev();
|
||||
|
||||
// Move to the next slide:
|
||||
gallery.next();
|
||||
|
||||
// Move to the given slide index with the (optional) given duraction speed in milliseconds:
|
||||
gallery.slide(index, duration);
|
||||
|
||||
// Start an automatic slideshow with the given interval in milliseconds (optional):
|
||||
gallery.play(interval);
|
||||
|
||||
// Stop the automatic slideshow:
|
||||
gallery.pause();
|
||||
|
||||
// Add additional slides after Gallery initialization:
|
||||
gallery.add(list);
|
||||
|
||||
// Close and deinitialize the Gallery:
|
||||
gallery.close();
|
||||
```
|
||||
|
||||
### Videos
|
||||
|
||||
#### HTML5 video player
|
||||
|
||||
The Gallery can be initialized with a list of videos instead of images, or a combination of both:
|
||||
|
||||
```js
|
||||
blueimp.Gallery([
|
||||
{
|
||||
title: 'Fruits',
|
||||
href: 'https://example.org/videos/fruits.mp4',
|
||||
type: 'video/mp4',
|
||||
poster: 'https://example.org/images/fruits.jpg'
|
||||
},
|
||||
{
|
||||
title: 'Banana',
|
||||
href: 'https://example.org/images/banana.jpg',
|
||||
type: 'image/jpeg',
|
||||
thumbnail: 'https://example.org/thumbnails/banana.jpg'
|
||||
}
|
||||
]);
|
||||
```
|
||||
|
||||
The Gallery uses the **type** property to determine the content type of the object to display.
|
||||
If the type property is empty or doesn't exist, the default type **image** is assumed.
|
||||
Objects with a video type will be displayed in a [HTML5 video element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video) if the browser supports the video content type.
|
||||
|
||||
For videos, the **poster** property defines the URL of the poster image to display, before the video is started.
|
||||
|
||||
#### Multiple video sources
|
||||
To provide multiple video formats, the **sources** property of a list object can be set to an array of objects with **href** and **type** properties for each video source. The first video format in the list that the browser can play will be displayed:
|
||||
|
||||
```js
|
||||
blueimp.Gallery([
|
||||
{
|
||||
title: 'Fruits',
|
||||
type: 'video/*',
|
||||
poster: 'https://example.org/images/fruits.jpg',
|
||||
sources: [
|
||||
{
|
||||
href: 'https://example.org/videos/fruits.mp4',
|
||||
type: 'video/mp4'
|
||||
},
|
||||
{
|
||||
href: 'https://example.org/videos/fruits.ogg',
|
||||
type: 'video/ogg'
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
```
|
||||
|
||||
It is also possible to define the video sources as data-attribute on a link element in [JSON](https://developer.mozilla.org/en-US/docs/JSON) array format:
|
||||
|
||||
```html
|
||||
<div id="links">
|
||||
<a
|
||||
href="https://example.org/videos/fruits.mp4"
|
||||
title="Fruits"
|
||||
type="video/mp4"
|
||||
data-poster="https://example.org/images/fruits.jpg"
|
||||
data-sources='[{"href": "https://example.org/videos/fruits.mp4", "type": "video/mp4"}, {"href": "https://example.org/videos/fruits.ogg", "type": "video/ogg"}]'
|
||||
>Fruits</a>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### YouTube
|
||||
The Gallery can display [YouTube](https://www.youtube.com/) videos for Gallery items with a **type** of **text/html** and a **youtube** property (configurable via [YouTube options](#youtube-options)) with the YouTube video-ID:
|
||||
|
||||
```js
|
||||
blueimp.Gallery([
|
||||
{
|
||||
title: 'A YouYube video',
|
||||
href: 'https://www.youtube.com/watch?v=VIDEO_ID',
|
||||
type: 'text/html',
|
||||
youtube: 'VIDEO_ID',
|
||||
poster: 'https://img.youtube.com/vi/VIDEO_ID/maxresdefault.jpg'
|
||||
},
|
||||
{
|
||||
title: 'Banana',
|
||||
href: 'https://example.org/images/banana.jpg',
|
||||
type: 'image/jpeg',
|
||||
thumbnail: 'https://example.org/thumbnails/banana.jpg'
|
||||
}
|
||||
]);
|
||||
```
|
||||
|
||||
If the `href` and `poster` properties are undefined, they are set automatically based on the video ID.
|
||||
|
||||
Please note that the Gallery YouTube integration requires a browser with [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage) support, which excludes IE7.
|
||||
|
||||
#### Vimeo
|
||||
The Gallery can display [Vimeo](https://vimeo.com/) videos for Gallery items with a **type** of **text/html** and a **vimeo** property (configurable via [Vimeo options](#vimeo-options)) with the Vimeo video-ID:
|
||||
|
||||
```js
|
||||
blueimp.Gallery([
|
||||
{
|
||||
title: 'A Vimeo video',
|
||||
href: 'https://vimeo.com/VIDEO_ID',
|
||||
type: 'text/html',
|
||||
vimeo: 'VIDEO_ID',
|
||||
poster: 'https://secure-b.vimeocdn.com/ts/POSTER_ID.jpg'
|
||||
},
|
||||
{
|
||||
title: 'Banana',
|
||||
href: 'https://example.org/images/banana.jpg',
|
||||
type: 'image/jpeg',
|
||||
thumbnail: 'https://example.org/thumbnails/banana.jpg'
|
||||
}
|
||||
]);
|
||||
```
|
||||
|
||||
If the `href` property is undefined, it is set automatically based on the video ID.
|
||||
|
||||
Please note that the Gallery Vimeo integration requires a browser with [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage) support, which excludes IE7.
|
||||
|
||||
### Additional Gallery elements
|
||||
It is possible to add additional elements to the Gallery widget, e.g. a description label.
|
||||
|
||||
First, add the desired HTML element to the Gallery widget:
|
||||
|
||||
```html
|
||||
<div id="blueimp-gallery" class="blueimp-gallery">
|
||||
<div class="slides"></div>
|
||||
<h3 class="title"></h3>
|
||||
<!-- The placeholder for the description label: -->
|
||||
<p class="description"></p>
|
||||
<a class="prev">‹</a>
|
||||
<a class="next">›</a>
|
||||
<a class="close">×</a>
|
||||
<a class="play-pause"></a>
|
||||
<ol class="indicator"></ol>
|
||||
</div>
|
||||
```
|
||||
|
||||
Next, add the desired element styles to your CSS file:
|
||||
|
||||
```css
|
||||
.blueimp-gallery > .description {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 15px;
|
||||
color: #fff;
|
||||
display: none;
|
||||
}
|
||||
.blueimp-gallery-controls > .description {
|
||||
display: block;
|
||||
}
|
||||
```
|
||||
|
||||
Then, add the additional element information to each of your links:
|
||||
|
||||
```html
|
||||
<div id="links">
|
||||
<a href="images/banana.jpg" title="Banana" data-description="This is a banana.">Banana</a>
|
||||
<a href="images/apple.jpg" title="Apple" data-description="This is an apple.">Apple</a>
|
||||
</div>
|
||||
```
|
||||
|
||||
Finally, initialize the Gallery with an onslide callback option, to set the element content based on the information from the current link:
|
||||
|
||||
```js
|
||||
blueimp.Gallery(
|
||||
document.getElementById('links'),
|
||||
{
|
||||
onslide: function (index, slide) {
|
||||
var text = this.list[index].getAttribute('data-description'),
|
||||
node = this.container.find('.description');
|
||||
node.empty();
|
||||
if (text) {
|
||||
node[0].appendChild(document.createTextNode(text));
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Additional content types
|
||||
By extending the Gallery prototype with new factory methods, additional content types can be displayed. By default, blueimp Gallery provides the **imageFactory** and **videoFactory** methods for **image** and **video** content types respectively.
|
||||
|
||||
The Gallery uses the **type** property of each content object to determine which factory method to use. The **type** defines the [Internet media type](https://en.wikipedia.org/wiki/Internet_media_type) of the content object and is composed of two or more parts: A type, a subtype, and zero or more optional parameters, e.g. **text/html; charset=UTF-8** for an HTML document with UTF-8 encoding.
|
||||
The main type (the string in front of the slash, **text** in the example above) is concatenated with the string **Factory** to create the factory method name, e.g. **textFactory**.
|
||||
|
||||
#### Example HTML text factory implementation
|
||||
Please note that the textFactory script has to be included after the core Gallery script, but before including the [YouTube](#youtube) and [Vimeo](#vimeo) integration plugins, which extend the textFactory implementation to handle YouTube and Vimeo video links.
|
||||
|
||||
Please also note that although blueimp Gallery doesn't require [jQuery](https://jquery.com/), the following example uses it for convenience.
|
||||
|
||||
Extend the Gallery prototype with the **textFactory** method:
|
||||
|
||||
```js
|
||||
blueimp.Gallery.prototype.textFactory = function (obj, callback) {
|
||||
var $element = $('<div>')
|
||||
.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
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
|
||||
<script src="js/jquery.blueimp-gallery.min.js"></script>
|
||||
```
|
||||
|
||||
Next, add the attribute **data-gallery** to your Gallery links:
|
||||
|
||||
```html
|
||||
<div id="links">
|
||||
<a href="images/banana.jpg" title="Banana" data-gallery>
|
||||
<img src="images/thumbnails/banana.jpg" alt="Banana">
|
||||
</a>
|
||||
<a href="images/apple.jpg" title="Apple" data-gallery>
|
||||
<img src="images/thumbnails/apple.jpg" alt="Apple">
|
||||
</a>
|
||||
<a href="images/orange.jpg" title="Orange" data-gallery>
|
||||
<img src="images/thumbnails/orange.jpg" alt="Orange">
|
||||
</a>
|
||||
</div>
|
||||
```
|
||||
|
||||
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
|
||||
<div id="blueimp-gallery" class="blueimp-gallery" data-start-slideshow="true" data-filter=":even">
|
||||
<div class="slides"></div>
|
||||
<h3 class="title"></h3>
|
||||
<a class="prev">‹</a>
|
||||
<a class="next">›</a>
|
||||
<a class="close">×</a>
|
||||
<a class="play-pause"></a>
|
||||
<ol class="indicator"></ol>
|
||||
</div>
|
||||
```
|
||||
|
||||
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
|
||||
<div id="fruits">
|
||||
<a href="images/banana.jpg" title="Banana" data-gallery="#blueimp-gallery-fruits">
|
||||
<img src="images/thumbnails/banana.jpg" alt="Banana">
|
||||
</a>
|
||||
<a href="images/apple.jpg" title="Apple" data-gallery="#blueimp-gallery-fruits">
|
||||
<img src="images/thumbnails/apple.jpg" alt="Apple">
|
||||
</a>
|
||||
</div>
|
||||
<div id="vegetables">
|
||||
<a href="images/tomato.jpg" title="Tomato" data-gallery="#blueimp-gallery-vegetables">
|
||||
<img src="images/thumbnails/tomato.jpg" alt="Tomato">
|
||||
</a>
|
||||
<a href="images/onion.jpg" title="Onion" data-gallery="#blueimp-gallery-vegetables">
|
||||
<img src="images/thumbnails/onion.jpg" alt="Onion">
|
||||
</a>
|
||||
</div>
|
||||
```
|
||||
|
||||
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
|
||||
<link rel="stylesheet" href="css/blueimp-gallery.css">
|
||||
<link rel="stylesheet" href="css/blueimp-gallery-indicator.css">
|
||||
<link rel="stylesheet" href="css/blueimp-gallery-video.css">
|
||||
<!-- ... -->
|
||||
<script src="js/blueimp-helper.js"></script>
|
||||
<script src="js/blueimp-gallery.js"></script>
|
||||
<script src="js/blueimp-gallery-fullscreen.js"></script>
|
||||
<script src="js/blueimp-gallery-indicator.js"></script>
|
||||
<script src="js/blueimp-gallery-video.js"></script>
|
||||
<script src="js/blueimp-gallery-youtube.js"></script>
|
||||
<script src="js/blueimp-gallery-vimeo.js"></script>
|
||||
```
|
||||
|
||||
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
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
|
||||
<script src="js/blueimp-gallery.js"></script>
|
||||
<script src="js/blueimp-gallery-fullscreen.js"></script>
|
||||
<script src="js/blueimp-gallery-indicator.js"></script>
|
||||
<script src="js/blueimp-gallery-video.js"></script>
|
||||
<script src="js/blueimp-gallery-youtube.js"></script>
|
||||
<script src="js/blueimp-gallery-vimeo.js"></script>
|
||||
<script src="js/jquery.blueimp-gallery.js"></script>
|
||||
```
|
||||
|
||||
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.
|
@ -1,48 +0,0 @@
|
||||
{
|
||||
"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/"
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
@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;
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
@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;
|
||||
}
|
@ -1,254 +0,0 @@
|
||||
@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 > .fullscreen {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
bottom: 15px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
background: url(../img/fullscreen.png) 0 0 no-repeat;
|
||||
cursor: pointer;
|
||||
opacity: 0.5;
|
||||
display:none;
|
||||
}
|
||||
.blueimp-gallery > .download {
|
||||
position: absolute;
|
||||
right: 45px;
|
||||
bottom: 15px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
background: url(../img/download.png) 0 0 no-repeat;
|
||||
cursor: pointer;
|
||||
opacity: 0.5;
|
||||
display:none;
|
||||
}
|
||||
.blueimp-gallery > .prev:hover,
|
||||
.blueimp-gallery > .next:hover,
|
||||
.blueimp-gallery > .close:hover,
|
||||
.blueimp-gallery > .title:hover,
|
||||
.blueimp-gallery > .play-pause:hover,
|
||||
.blueimp-gallery > .fullscreen:hover,
|
||||
.blueimp-gallery > .download: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,
|
||||
.blueimp-gallery-controls > .fullscreen,
|
||||
.blueimp-gallery-controls > .download{
|
||||
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,
|
||||
.blueimp-gallery > .fullscreen,
|
||||
.blueimp-gallery > .download{
|
||||
-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;
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* 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: '| ';
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 217 B |
Before Width: | Height: | Size: 2.2 KiB |
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="64" height="64">
|
||||
<circle cx="32" cy="32" r="25" stroke="red" stroke-width="7" fill="black" fill-opacity="0.2"/>
|
||||
<rect x="28" y="7" width="8" height="50" fill="red" transform="rotate(45, 32, 32)"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 306 B |
Before Width: | Height: | Size: 196 B |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 606 B |
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="30" height="15">
|
||||
<polygon points="2,1 2,14 13,7" stroke="black" stroke-width="1" fill="white"/>
|
||||
<rect x="17" y="2" width="4" height="11" stroke="black" stroke-width="1" fill="white"/>
|
||||
<rect x="24" y="2" width="4" height="11" stroke="black" stroke-width="1" fill="white"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 382 B |
Before Width: | Height: | Size: 2.7 KiB |
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="64" height="64">
|
||||
<circle cx="32" cy="32" r="25" stroke="white" stroke-width="7" fill="black" fill-opacity="0.2"/>
|
||||
<polygon points="26,22 26,42 43,32" fill="white"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 274 B |
@ -1,95 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!--
|
||||
/*
|
||||
* blueimp Gallery Demo 2.11.0
|
||||
* https://github.com/blueimp/Gallery
|
||||
*
|
||||
* Copyright 2013, Sebastian Tschan
|
||||
* https://blueimp.net
|
||||
*
|
||||
* Licensed under the MIT license:
|
||||
* http://www.opensource.org/licenses/MIT
|
||||
*/
|
||||
-->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!--[if IE]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<![endif]-->
|
||||
<meta charset="utf-8">
|
||||
<title>blueimp Gallery</title>
|
||||
<meta name="description" content="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.">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="css/blueimp-gallery.css">
|
||||
<link rel="stylesheet" href="css/blueimp-gallery-indicator.css">
|
||||
<link rel="stylesheet" href="css/blueimp-gallery-video.css">
|
||||
<link rel="stylesheet" href="css/demo.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>blueimp Gallery</h1>
|
||||
<p><a href="https://github.com/blueimp/Gallery">blueimp Gallery</a> is a touch-enabled, responsive and customizable image & video gallery, carousel and lightbox, optimized for both mobile and desktop web browsers.<br>
|
||||
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.</p>
|
||||
<ul class="navigation">
|
||||
<li><a href="https://github.com/blueimp/Gallery/tags">Download</a></li>
|
||||
<li><a href="https://github.com/blueimp/Gallery">Source Code</a></li>
|
||||
<li><a href="https://github.com/blueimp/Gallery/blob/master/README.md">Documentation</a></li>
|
||||
<li><a href="https://blueimp.net">© Sebastian Tschan</a></li>
|
||||
</ul>
|
||||
<p><a href="https://github.com/blueimp/Gallery">blueimp Gallery</a> is based on <a href="http://swipejs.com/">Swipe</a>.</p>
|
||||
<br>
|
||||
<h2>Carousel image gallery</h2>
|
||||
<!-- The Gallery as inline carousel, can be positioned anywhere on the page -->
|
||||
<div id="blueimp-image-carousel" class="blueimp-gallery blueimp-gallery-carousel">
|
||||
<div class="slides"></div>
|
||||
<h3 class="title"></h3>
|
||||
<a class="prev">‹</a>
|
||||
<a class="next">›</a>
|
||||
<a class="play-pause"></a>
|
||||
</div>
|
||||
<br>
|
||||
<h2>Carousel video gallery</h2>
|
||||
<!-- The Gallery as inline carousel, can be positioned anywhere on the page -->
|
||||
<div id="blueimp-video-carousel" class="blueimp-gallery blueimp-gallery-controls blueimp-gallery-carousel">
|
||||
<div class="slides"></div>
|
||||
<h3 class="title"></h3>
|
||||
<a class="prev">‹</a>
|
||||
<a class="next">›</a>
|
||||
<a class="play-pause"></a>
|
||||
</div>
|
||||
<br>
|
||||
<h2>Lightbox image gallery</h2>
|
||||
<!-- The container for the list of example images -->
|
||||
<div id="links" class="links"></div>
|
||||
<!-- The Gallery as lightbox dialog, should be a child element of the document body -->
|
||||
<div id="blueimp-gallery" class="blueimp-gallery">
|
||||
<div class="slides"></div>
|
||||
<h3 class="title"></h3>
|
||||
<a class="prev">‹</a>
|
||||
<a class="next">›</a>
|
||||
<a class="close">×</a>
|
||||
<a class="play-pause"></a>
|
||||
<ol class="indicator"></ol>
|
||||
</div>
|
||||
<script src="js/blueimp-helper.js"></script>
|
||||
<script src="js/blueimp-gallery.js"></script>
|
||||
<script src="js/blueimp-gallery-fullscreen.js"></script>
|
||||
<script src="js/blueimp-gallery-indicator.js"></script>
|
||||
<script src="js/blueimp-gallery-video.js"></script>
|
||||
<script src="js/blueimp-gallery-vimeo.js"></script>
|
||||
<script src="js/blueimp-gallery-youtube.js"></script>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
|
||||
<script>
|
||||
/*jslint evil: true */
|
||||
/*global window, document*/
|
||||
// Including jQuery via the protocol relative url above works for both http and https.
|
||||
// Explicitly including jQuery via https allows running the Gallery demo as local file:
|
||||
if (!window.jQuery) {
|
||||
document.write(
|
||||
'<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"><\/script>'
|
||||
);
|
||||
}
|
||||
</script>
|
||||
<script src="js/jquery.blueimp-gallery.js"></script>
|
||||
<script src="js/demo.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,90 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}));
|
@ -1,169 +0,0 @@
|
||||
/*
|
||||
* 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,
|
||||
// The tag name of thumbnails indicators
|
||||
thumbnailsTagIndicators: 'li',
|
||||
//thumbnail with image tag
|
||||
thumbnailWithImgTag: false
|
||||
});
|
||||
|
||||
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(this.options.thumbnailWithImgTag),
|
||||
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) {
|
||||
if (this.options.thumbnailsTagIndicators == 'img')
|
||||
{
|
||||
indicator.src = thumbnailUrl;
|
||||
}
|
||||
else if (this.options.thumbnailWithImgTag)
|
||||
{
|
||||
indicator.children[0].src = thumbnailUrl;
|
||||
}
|
||||
else
|
||||
{
|
||||
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(this.options.thumbnailsTagIndicators);
|
||||
if (this.options.thumbnailWithImgTag) this.indicatorPrototype.appendChild(document.createElement('img'));
|
||||
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;
|
||||
}));
|
171
api/js/jquery/blueimp/js/blueimp-gallery-video.js
vendored
@ -1,171 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}));
|
215
api/js/jquery/blueimp/js/blueimp-gallery-vimeo.js
vendored
@ -1,215 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}));
|
229
api/js/jquery/blueimp/js/blueimp-gallery-youtube.js
vendored
@ -1,229 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}));
|
1381
api/js/jquery/blueimp/js/blueimp-gallery.js
vendored
191
api/js/jquery/blueimp/js/blueimp-helper.js
vendored
@ -1,191 +0,0 @@
|
||||
/*
|
||||
* blueimp helper 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 () {
|
||||
'use strict';
|
||||
|
||||
function extend(obj1, obj2) {
|
||||
var prop;
|
||||
for (prop in obj2) {
|
||||
if (obj2.hasOwnProperty(prop)) {
|
||||
obj1[prop] = obj2[prop];
|
||||
}
|
||||
}
|
||||
return obj1;
|
||||
}
|
||||
|
||||
function Helper(query) {
|
||||
if (!this || this.find !== Helper.prototype.find) {
|
||||
// Called as function instead of as constructor,
|
||||
// so we simply return a new instance:
|
||||
return new Helper(query);
|
||||
}
|
||||
this.length = 0;
|
||||
if (query) {
|
||||
if (typeof query === 'string') {
|
||||
query = this.find(query);
|
||||
}
|
||||
if (query.nodeType || query === query.window) {
|
||||
// Single HTML element
|
||||
this.length = 1;
|
||||
this[0] = query;
|
||||
} else {
|
||||
// HTML element collection
|
||||
var i = query.length;
|
||||
this.length = i;
|
||||
while (i) {
|
||||
i -= 1;
|
||||
this[i] = query[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Helper.extend = extend;
|
||||
|
||||
Helper.contains = function (container, element) {
|
||||
do {
|
||||
element = element.parentNode;
|
||||
if (element === container) {
|
||||
return true;
|
||||
}
|
||||
} while (element);
|
||||
return false;
|
||||
};
|
||||
|
||||
Helper.parseJSON = function (string) {
|
||||
return window.JSON && JSON.parse(string);
|
||||
};
|
||||
|
||||
extend(Helper.prototype, {
|
||||
|
||||
find: function (query) {
|
||||
var container = this[0] || document;
|
||||
if (typeof query === 'string') {
|
||||
if (container.querySelectorAll) {
|
||||
query = container.querySelectorAll(query);
|
||||
} else if (query.charAt(0) === '#') {
|
||||
query = container.getElementById(query.slice(1));
|
||||
} else {
|
||||
query = container.getElementsByTagName(query);
|
||||
}
|
||||
}
|
||||
return new Helper(query);
|
||||
},
|
||||
|
||||
hasClass: function (className) {
|
||||
if (!this[0]) {
|
||||
return false;
|
||||
}
|
||||
return new RegExp('(^|\\s+)' + className +
|
||||
'(\\s+|$)').test(this[0].className);
|
||||
},
|
||||
|
||||
addClass: function (className) {
|
||||
var i = this.length,
|
||||
element;
|
||||
while (i) {
|
||||
i -= 1;
|
||||
element = this[i];
|
||||
if (!element.className) {
|
||||
element.className = className;
|
||||
return this;
|
||||
}
|
||||
if (this.hasClass(className)) {
|
||||
return this;
|
||||
}
|
||||
element.className += ' ' + className;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
removeClass: function (className) {
|
||||
var regexp = new RegExp('(^|\\s+)' + className + '(\\s+|$)'),
|
||||
i = this.length,
|
||||
element;
|
||||
while (i) {
|
||||
i -= 1;
|
||||
element = this[i];
|
||||
element.className = element.className.replace(regexp, ' ');
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
on: function (eventName, handler) {
|
||||
var eventNames = eventName.split(/\s+/),
|
||||
i,
|
||||
element;
|
||||
while (eventNames.length) {
|
||||
eventName = eventNames.shift();
|
||||
i = this.length;
|
||||
while (i) {
|
||||
i -= 1;
|
||||
element = this[i];
|
||||
if (element.addEventListener) {
|
||||
element.addEventListener(eventName, handler, false);
|
||||
} else if (element.attachEvent) {
|
||||
element.attachEvent('on' + eventName, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
off: function (eventName, handler) {
|
||||
var eventNames = eventName.split(/\s+/),
|
||||
i,
|
||||
element;
|
||||
while (eventNames.length) {
|
||||
eventName = eventNames.shift();
|
||||
i = this.length;
|
||||
while (i) {
|
||||
i -= 1;
|
||||
element = this[i];
|
||||
if (element.removeEventListener) {
|
||||
element.removeEventListener(eventName, handler, false);
|
||||
} else if (element.detachEvent) {
|
||||
element.detachEvent('on' + eventName, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
empty: function () {
|
||||
var i = this.length,
|
||||
element;
|
||||
while (i) {
|
||||
i -= 1;
|
||||
element = this[i];
|
||||
while (element.hasChildNodes()) {
|
||||
element.removeChild(element.lastChild);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
first: function () {
|
||||
return new Helper(this[0]);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(function () {
|
||||
return Helper;
|
||||
});
|
||||
} else {
|
||||
window.blueimp = window.blueimp || {};
|
||||
window.blueimp.helper = Helper;
|
||||
}
|
||||
}());
|
97
api/js/jquery/blueimp/js/demo.js
vendored
@ -1,97 +0,0 @@
|
||||
/*
|
||||
* blueimp Gallery Demo JS 2.12.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 blueimp, $ */
|
||||
|
||||
$(function () {
|
||||
'use strict';
|
||||
|
||||
// Load demo images from flickr:
|
||||
$.ajax({
|
||||
// Flickr API is SSL only:
|
||||
// https://code.flickr.net/2014/04/30/flickr-api-going-ssl-only-on-june-27th-2014/
|
||||
url: 'https://api.flickr.com/services/rest/',
|
||||
data: {
|
||||
format: 'json',
|
||||
method: 'flickr.interestingness.getList',
|
||||
api_key: '7617adae70159d09ba78cfec73c13be3' // jshint ignore:line
|
||||
},
|
||||
dataType: 'jsonp',
|
||||
jsonp: 'jsoncallback'
|
||||
}).done(function (result) {
|
||||
var carouselLinks = [],
|
||||
linksContainer = $('#links'),
|
||||
baseUrl;
|
||||
// Add the demo images as links with thumbnails to the page:
|
||||
$.each(result.photos.photo, function (index, photo) {
|
||||
baseUrl = 'https://farm' + photo.farm + '.static.flickr.com/' +
|
||||
photo.server + '/' + photo.id + '_' + photo.secret;
|
||||
$('<a/>')
|
||||
.append($('<img>').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
|
||||
});
|
||||
|
||||
});
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
});
|
||||
|
||||
}));
|
@ -1,791 +0,0 @@
|
||||
!
|
||||
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,
|
||||
hideControlsOnSlideshow:false,
|
||||
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;
|
||||
if (this.options.hideControlsOnSlideshow) this.container.removeClass(this.options.controlsClass);
|
||||
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)
|
||||
})
|
||||
});
|
@ -1,54 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|