Convert expose to TS and apply it to all exposable widgets

This commit is contained in:
Hadi Nategh 2020-02-19 17:14:44 +01:00
parent 2d5012d208
commit 9a4f2d1e69
13 changed files with 1722 additions and 1178 deletions

View File

@ -428,6 +428,7 @@ function et2_activateLinks(_content) {
* Inserts the structure generated by et2_activateLinks into the given DOM-Node * Inserts the structure generated by et2_activateLinks into the given DOM-Node
*/ */
function et2_insertLinkText(_text, _node, _target) { function et2_insertLinkText(_text, _node, _target) {
var _a;
if (!_node) { if (!_node) {
egw.debug("warn", "et2_insertLinkText called without node", _text, _node, _target); egw.debug("warn", "et2_insertLinkText called without node", _text, _node, _target);
return; return;
@ -449,8 +450,7 @@ function et2_insertLinkText(_text, _node, _target) {
} }
} }
} }
else if (s.text) // no need to generate a link, if there is no content in it else if ((_a = s) === null || _a === void 0 ? void 0 : _a.text) {
{
if (!s.href) { if (!s.href) {
egw.debug("warn", "et2_activateLinks gave bad data", s, _node, _target); egw.debug("warn", "et2_activateLinks gave bad data", s, _node, _target);
s.href = ""; s.href = "";

View File

@ -606,7 +606,7 @@ function et2_insertLinkText(_text, _node, _target)
} }
} }
} }
else if(s.text) // no need to generate a link, if there is no content in it else if(s?.text) // no need to generate a link, if there is no content in it
{ {
if(!s.href) if(!s.href)
{ {

View File

@ -49,4 +49,8 @@ var et2_IPrint = "et2_IPrint";
function implements_et2_IPrint(obj) { function implements_et2_IPrint(obj) {
return implements_methods(obj, ["beforePrint", "afterPrint"]); return implements_methods(obj, ["beforePrint", "afterPrint"]);
} }
var et2_IExposable = "et2_IExposable";
function implements_et2_IExposable(obj) {
return implements_methods(obj, ["getMedia"]);
}
//# sourceMappingURL=et2_core_interfaces.js.map //# sourceMappingURL=et2_core_interfaces.js.map

View File

@ -211,3 +211,20 @@ function implements_et2_IPrint(obj : et2_widget)
{ {
return implements_methods(obj, ["beforePrint", "afterPrint"]); return implements_methods(obj, ["beforePrint", "afterPrint"]);
} }
/**
* Interface all exposed widget must support in order to getMedia for the blueimp Gallery.
*/
interface et2_IExposable
{
/**
* get media an array of media objects to pass to blueimp Gallery
* @param {array} _attrs
*/
getMedia(_attrs) : void;
}
var et2_IExposable = "et2_IExposable";
function implements_et2_IExposable(obj : et2_widget)
{
return implements_methods(obj, ["getMedia"]);
}

View File

@ -167,7 +167,6 @@ declare var et2_vfsUid : any;
declare var et2_vfsUpload : any; declare var et2_vfsUpload : any;
declare var et2_vfsSelect : any; declare var et2_vfsSelect : any;
declare var et2_video : any; declare var et2_video : any;
declare var et2_IExposable : any;
declare var tinymce : any; declare var tinymce : any;
declare var date : any; declare var date : any;
declare var tinyMCE : any; declare var tinyMCE : any;
@ -200,3 +199,4 @@ declare class Resumable {
declare class dhtmlXTreeObject { declare class dhtmlXTreeObject {
constructor(options : any); constructor(options : any);
} }
declare function expose(widget:any) : any;

View File

@ -21,6 +21,7 @@ var __extends = (this && this.__extends) || (function () {
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}; };
})(); })();
var _a;
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
/*egw:uses /*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js; /vendor/bower-asset/jquery/dist/jquery.js;
@ -35,7 +36,7 @@ require("./et2_types");
/** /**
* Class which implements the "description" XET-Tag * Class which implements the "description" XET-Tag
*/ */
var et2_description = /** @class */ (function (_super) { exports.et2_description = expose((_a = /** @class */ (function (_super) {
__extends(et2_description, _super); __extends(et2_description, _super);
/** /**
* Constructor * Constructor
@ -167,7 +168,7 @@ var et2_description = /** @class */ (function (_super) {
var $span = this.options.mime_data ? jQuery(this.span) : jQuery('a', this.span); var $span = this.options.mime_data ? jQuery(this.span) : jQuery('a', this.span);
$span.click(function (e) { $span.click(function (e) {
if (self.options.expose_view && typeof self.options.mime != 'undefined' && self.options.mime.match(self.mime_regexp, 'ig')) { if (self.options.expose_view && typeof self.options.mime != 'undefined' && self.options.mime.match(self.mime_regexp, 'ig')) {
// ToDoExpose: self._init_blueimp_gallery(e, href); self._init_blueimp_gallery(e, href);
} }
else { else {
egw(window).open_link(mime_data || href, self.options.extra_link_target, self.options.extra_link_popup, null, null, self.options.mime); egw(window).open_link(mime_data || href, self.options.extra_link_target, self.options.extra_link_popup, null, null, self.options.mime);
@ -267,7 +268,9 @@ var et2_description = /** @class */ (function (_super) {
.tooltip("open"); .tooltip("open");
}, { widget: this, span: this.span })); }, { widget: this, span: this.span }));
}; };
et2_description._attributes = { return et2_description;
}(et2_core_baseWidget_1.et2_baseWidget)),
_a._attributes = {
"label": { "label": {
"name": "Label", "name": "Label",
"default": "", "default": "",
@ -349,9 +352,7 @@ var et2_description = /** @class */ (function (_super) {
"default": "Edit", "default": "Edit",
"description": "Text to show as tooltip of defined action" "description": "Text to show as tooltip of defined action"
} }
}; },
return et2_description; _a));
}(et2_core_baseWidget_1.et2_baseWidget)); et2_core_widget_1.et2_register_widget(exports.et2_description, ["description", "label"]);
exports.et2_description = et2_description;
et2_core_widget_1.et2_register_widget(et2_description, ["description", "label"]);
//# sourceMappingURL=et2_widget_description.js.map //# sourceMappingURL=et2_widget_description.js.map

View File

@ -23,7 +23,7 @@ import './et2_types';
/** /**
* Class which implements the "description" XET-Tag * Class which implements the "description" XET-Tag
*/ */
export class et2_description extends et2_baseWidget implements et2_IDetachedDOM export const et2_description = expose(class et2_description extends et2_baseWidget implements et2_IDetachedDOM, et2_IExposable
{ {
static readonly _attributes : any = { static readonly _attributes : any = {
"label": { "label": {
@ -292,7 +292,7 @@ export class et2_description extends et2_baseWidget implements et2_IDetachedDOM
$span.click(function(e) { $span.click(function(e) {
if (self.options.expose_view && typeof self.options.mime !='undefined' && self.options.mime.match(self.mime_regexp,'ig')) if (self.options.expose_view && typeof self.options.mime !='undefined' && self.options.mime.match(self.mime_regexp,'ig'))
{ {
// ToDoExpose: self._init_blueimp_gallery(e, href); self._init_blueimp_gallery(e, href);
} }
else else
{ {
@ -423,6 +423,5 @@ export class et2_description extends et2_baseWidget implements et2_IDetachedDOM
.tooltip("open"); .tooltip("open");
}, {widget: this, span: this.span})); }, {widget: this, span: this.span}));
} }
} });
et2_register_widget(et2_description, ["description", "label"]); et2_register_widget(et2_description, ["description", "label"]);

View File

@ -22,6 +22,7 @@ var __extends = (this && this.__extends) || (function () {
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}; };
})(); })();
var _a;
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
/*egw:uses /*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js; /vendor/bower-asset/jquery/dist/jquery.js;
@ -528,7 +529,7 @@ var et2_link_entry = /** @class */ (function (_super) {
buttonItem.css('background-image', 'url(' + url + ')'); buttonItem.css('background-image', 'url(' + url + ')');
}, },
_renderItem: function (ul, item) { _renderItem: function (ul, item) {
var li = jQuery("<li>", { "class": "et2_link_entry_app_option" }), wrapper = jQuery("<div>", { text: item.label }); var li = jQuery("<li>", { class: "et2_link_entry_app_option" }), wrapper = jQuery("<div>", { text: item.label });
if (item.disabled) { if (item.disabled) {
li.addClass("ui-state-disabled"); li.addClass("ui-state-disabled");
} }
@ -1229,7 +1230,7 @@ et2_core_widget_1.et2_register_widget(et2_link, ["link", "link-entry_ro"]);
* *
* TODO: This one used to have expose * TODO: This one used to have expose
*/ */
var et2_link_string = /** @class */ (function (_super) { exports.et2_link_string = expose((_a = /** @class */ (function (_super) {
__extends(et2_link_string, _super); __extends(et2_link_string, _super);
/** /**
* Constructor * Constructor
@ -1410,7 +1411,9 @@ var et2_link_string = /** @class */ (function (_super) {
this._labelContainer.contents().not(this.list).remove(); this._labelContainer.contents().not(this.list).remove();
} }
}; };
et2_link_string._attributes = { return et2_link_string;
}(et2_core_valueWidget_1.et2_valueWidget)),
_a._attributes = {
"application": { "application": {
"name": "Application", "name": "Application",
"type": "string", "type": "string",
@ -1436,14 +1439,12 @@ var et2_link_string = /** @class */ (function (_super) {
"expose_view": { "expose_view": {
name: "Expose view", name: "Expose view",
type: "boolean", type: "boolean",
"default": true, default: true,
description: "Clicking on description with href value would popup an expose view, and will show content referenced by href." description: "Clicking on description with href value would popup an expose view, and will show content referenced by href."
} }
}; },
return et2_link_string; _a));
}(et2_core_valueWidget_1.et2_valueWidget)); et2_core_widget_1.et2_register_widget(exports.et2_link_string, ["link-string"]);
exports.et2_link_string = et2_link_string;
et2_core_widget_1.et2_register_widget(et2_link_string, ["link-string"]);
/** /**
* UI widget for one or more links in a list (table) * UI widget for one or more links in a list (table)
*/ */
@ -1882,7 +1883,7 @@ var et2_link_list = /** @class */ (function (_super) {
} }
}; };
return et2_link_list; return et2_link_list;
}(et2_link_string)); }(exports.et2_link_string));
exports.et2_link_list = et2_link_list; exports.et2_link_list = et2_link_list;
et2_core_widget_1.et2_register_widget(et2_link_list, ["link-list"]); et2_core_widget_1.et2_register_widget(et2_link_list, ["link-list"]);
/** /**
@ -1948,3 +1949,4 @@ var et2_link_add = /** @class */ (function (_super) {
}(et2_core_inputWidget_1.et2_inputWidget)); }(et2_core_inputWidget_1.et2_inputWidget));
exports.et2_link_add = et2_link_add; exports.et2_link_add = et2_link_add;
et2_core_widget_1.et2_register_widget(et2_link_add, ["link-add"]); et2_core_widget_1.et2_register_widget(et2_link_add, ["link-add"]);
//# sourceMappingURL=et2_widget_link.js.map

View File

@ -1496,7 +1496,7 @@ et2_register_widget(et2_link, ["link", "link-entry_ro"]);
* *
* TODO: This one used to have expose * TODO: This one used to have expose
*/ */
export class et2_link_string extends et2_valueWidget implements et2_IDetachedDOM export const et2_link_string = expose(class et2_link_string extends et2_valueWidget implements et2_IDetachedDOM, et2_IExposable
{ {
static readonly _attributes : any = { static readonly _attributes : any = {
"application": { "application": {
@ -1751,7 +1751,7 @@ export class et2_link_string extends et2_valueWidget implements et2_IDetachedDOM
this._labelContainer.contents().not(this.list).remove(); this._labelContainer.contents().not(this.list).remove();
} }
} }
} });
et2_register_widget(et2_link_string, ["link-string"]); et2_register_widget(et2_link_string, ["link-string"]);
/** /**

View File

@ -22,6 +22,7 @@ var __extends = (this && this.__extends) || (function () {
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}; };
})(); })();
var _a;
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
/*egw:uses /*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js; /vendor/bower-asset/jquery/dist/jquery.js;
@ -429,10 +430,9 @@ et2_core_widget_1.et2_register_widget(et2_vfsName_ro, ["vfs-name_ro"]);
* </span> * </span>
* *
* span.overlayContainer is optional and only generated for symlinks * span.overlayContainer is optional and only generated for symlinks
* @todo implement mixin Expose class
* @augments et2_valueWidget * @augments et2_valueWidget
*/ */
var et2_vfsMime = /** @class */ (function (_super) { exports.et2_vfsMime = expose((_a = /** @class */ (function (_super) {
__extends(et2_vfsMime, _super); __extends(et2_vfsMime, _super);
/** /**
* Constructor * Constructor
@ -579,7 +579,9 @@ var et2_vfsMime = /** @class */ (function (_super) {
this.set_value(_values['value']); this.set_value(_values['value']);
} }
}; };
et2_vfsMime._attributes = { return et2_vfsMime;
}(et2_core_valueWidget_1.et2_valueWidget)),
_a._attributes = {
"value": { "value": {
"type": "any", "type": "any",
"description": "Array of (stat) information about the file" "description": "Array of (stat) information about the file"
@ -608,10 +610,9 @@ var et2_vfsMime = /** @class */ (function (_super) {
default: "", default: "",
description: " Size of thumbnail in pixel for specified mime type with syntax of: mime_type(s),size (eg. image,video,128)" description: " Size of thumbnail in pixel for specified mime type with syntax of: mime_type(s),size (eg. image,video,128)"
} }
}; },
return et2_vfsMime; _a));
}(et2_core_valueWidget_1.et2_valueWidget)); et2_core_widget_1.et2_register_widget(exports.et2_vfsMime, ["vfs-mime"]);
et2_core_widget_1.et2_register_widget(et2_vfsMime, ["vfs-mime"]);
/** /**
* vfs-size * vfs-size
* Human readable file sizes * Human readable file sizes

View File

@ -483,10 +483,9 @@ et2_register_widget(et2_vfsName_ro, ["vfs-name_ro"]);
* </span> * </span>
* *
* span.overlayContainer is optional and only generated for symlinks * span.overlayContainer is optional and only generated for symlinks
* @todo implement mixin Expose class
* @augments et2_valueWidget * @augments et2_valueWidget
*/ */
class et2_vfsMime extends et2_valueWidget implements et2_IDetachedDOM export const et2_vfsMime = expose(class et2_vfsMime extends et2_valueWidget implements et2_IDetachedDOM, et2_IExposable
{ {
static readonly _attributes : any = { static readonly _attributes : any = {
"value": { "value": {
@ -694,7 +693,7 @@ class et2_vfsMime extends et2_valueWidget implements et2_IDetachedDOM
this.set_value(_values['value']); this.set_value(_values['value']);
} }
} }
} });
et2_register_widget(et2_vfsMime, ["vfs-mime"]); et2_register_widget(et2_vfsMime, ["vfs-mime"]);
/** /**

View File

@ -1,3 +1,4 @@
"use strict";
/** /**
* EGroupware eTemplate2 - JS object implementing expose view of media and a gallery view * EGroupware eTemplate2 - JS object implementing expose view of media and a gallery view
* *
@ -9,37 +10,28 @@
* @copyright Stylite AG * @copyright Stylite AG
* @version $Id$ * @version $Id$
*/ */
var __extends = (this && this.__extends) || (function () {
/*egw:uses var extendStatics = function (d, b) {
/vendor/bower-asset/jquery/dist/jquery.js; extendStatics = Object.setPrototypeOf ||
/api/js/jquery/blueimp/js/blueimp-gallery.min.js; ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
*/ function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
/** };
* Interface all exposed widget must support in order to getMedia for the blueimp Gallery. return function (d, b) {
*/ extendStatics(d, b);
var et2_IExposable = new Interface( function __() { this.constructor = d; }
{ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
/** };
* get media an array of media objects to pass to blueimp Gallery })();
* @param {array} _attrs Object.defineProperty(exports, "__esModule", { value: true });
*/
getMedia: function(_attrs) {}
});
/** /**
* This function extends the given widget with blueimp gallery plugin * This function extends the given widget with blueimp gallery plugin
* *
* @param {type} widget * @param {type} widget
* @returns {widget} * @returns {widget}
*/ */
function expose (widget) function expose(Base) {
{
"use strict"; "use strict";
// Common expose functions
var THUMBNAIL_MAX = 100;
// Minimum data to qualify as an image and not cause errors // Minimum data to qualify as an image and not cause errors
var IMAGE_DEFAULT = { var IMAGE_DEFAULT = {
title: egw.lang('loading'), title: egw.lang('loading'),
@ -48,19 +40,15 @@ function expose (widget)
thumbnail: '', thumbnail: '',
loading: true loading: true
}; };
// For filtering to only show things we can handle // For filtering to only show things we can handle
var mime_regex = new RegExp(/(video\/(mp4|ogg|webm))|(image\/:*(?!tif|x-xcf|pdf))/); var MIME_REGEX = (navigator.userAgent.match(/(MSIE|Trident)/)) ?
// open office document mime type currently supported by webodf editor
var mime_odf_regex = new RegExp(/application\/vnd\.oasis\.opendocument\.text/);
// IE only supports video/mp4 mime type // IE only supports video/mp4 mime type
if (navigator.userAgent.match(/(MSIE|Trident)/)) mime_regex.compile(/(video\/mp4)|(image\/:*(?!tif|x-xcf|pdf))/); new RegExp(/(video\/mp4)|(image\/:*(?!tif|x-xcf|pdf))/) :
new RegExp(/(video\/(mp4|ogg|webm))|(image\/:*(?!tif|x-xcf|pdf))/);
// open office document mime type currently supported by webodf editor
var MIME_ODF_REGEX = new RegExp(/application\/vnd\.oasis\.opendocument\.text/);
// Only one gallery // Only one gallery
var gallery = null; var gallery = null;
/** /**
* See if the current widget is in a nextmatch, as this allows us to display * See if the current widget is in a nextmatch, as this allows us to display
* thumbnails underneath * thumbnails underneath
@ -68,15 +56,12 @@ function expose (widget)
* @param {et2_IExposable} widget * @param {et2_IExposable} widget
* @returns {et2_nextmatch | null} * @returns {et2_nextmatch | null}
*/ */
var find_nextmatch = function(widget) var find_nextmatch = function (widget) {
{
var current = widget; var current = widget;
var nextmatch = null; var nextmatch = null;
while(nextmatch == null && current) while (nextmatch == null && current) {
{
current = current.getParent(); current = current.getParent();
if(typeof current !='undefined' && current.instanceOf(et2_nextmatch)) if (typeof current != 'undefined' && current.instanceOf(et2_nextmatch)) {
{
nextmatch = current; nextmatch = current;
} }
} }
@ -84,11 +69,10 @@ function expose (widget)
// At the moment only filemanger nm would work // At the moment only filemanger nm would work
// as gallery, thus we disable other nestmatches // as gallery, thus we disable other nestmatches
// to build up gallery but filemanager // to build up gallery but filemanager
if(nextmatch == null || nextmatch.controller == null || !nextmatch.dom_id.match(/filemanager/,'ig')) return null; if (nextmatch == null || nextmatch.controller == null || !nextmatch.dom_id.match(/filemanager/, 'ig'))
return null;
return nextmatch; return nextmatch;
}; };
/** /**
* Read images out of the data for the nextmatch * Read images out of the data for the nextmatch
* *
@ -97,16 +81,13 @@ function expose (widget)
* @param {number} start_at * @param {number} start_at
* @returns {undefined} * @returns {undefined}
*/ */
var read_from_nextmatch = function(nm, images, start_at) var read_from_nextmatch = function (nm, images, start_at) {
{ if (!start_at)
if(!start_at) start_at = 0; start_at = 0;
var image_index = start_at; var image_index = start_at;
var stop = Math.max.apply(null, Object.keys(nm.controller._indexMap)); var stop = Math.max.apply(null, Object.keys(nm.controller._indexMap));
for (var i = start_at; i <= stop; i++) {
for(var i = start_at; i <= stop; i++) if (!nm.controller._indexMap[i] || !nm.controller._indexMap[i].uid) {
{
if(!nm.controller._indexMap[i] || !nm.controller._indexMap[i].uid)
{
// Returning instead of using IMAGE_DEFAULT means we stop as // Returning instead of using IMAGE_DEFAULT means we stop as
// soon as a hole is found, instead of getting everything that is // soon as a hole is found, instead of getting everything that is
// available. The gallery can't fill in the holes. // available. The gallery can't fill in the holes.
@ -114,16 +95,15 @@ function expose (widget)
continue; continue;
} }
var uid = nm.controller._indexMap[i].uid; var uid = nm.controller._indexMap[i].uid;
if(!uid) continue; if (!uid)
continue;
var data = egw.dataGetUIDdata(uid); var data = egw.dataGetUIDdata(uid);
if(data && data.data && data.data.mime && mime_regex.test(data.data.mime)) if (data && data.data && data.data.mime && MIME_REGEX.test(data.data.mime)) {
{
var media = this.getMedia(data.data); var media = this.getMedia(data.data);
images[image_index++] = jQuery.extend({}, data.data, media[0]); images[image_index++] = jQuery.extend({}, data.data, media[0]);
} }
} }
}; };
/** /**
* Set a particular index/image in the gallery instead of just appending * Set a particular index/image in the gallery instead of just appending
* it to the end * it to the end
@ -132,19 +112,14 @@ function expose (widget)
* @param {Object} image * @param {Object} image
* @returns {undefined} * @returns {undefined}
*/ */
var set_slide = function(index, image) var set_slide = function (index, image) {
{
var active = (index == gallery.index); var active = (index == gallery.index);
// Pad with blanks until length is right // Pad with blanks until length is right
while(index > gallery.getNumber()) while (index > gallery.getNumber()) {
{
gallery.add([jQuery.extend({}, IMAGE_DEFAULT)]); gallery.add([jQuery.extend({}, IMAGE_DEFAULT)]);
} }
// Don't bother with adding a default, we just did that // Don't bother with adding a default, we just did that
if(image.loading) if (image.loading) {
{
//Add load class if it's really a slide with error //Add load class if it's really a slide with error
if (gallery.slidesContainer.find('[data-index="' + index + '"]').hasClass(gallery.options.slideErrorClass)) if (gallery.slidesContainer.find('[data-index="' + index + '"]').hasClass(gallery.options.slideErrorClass))
jQuery(gallery.slides[index]) jQuery(gallery.slides[index])
@ -153,15 +128,12 @@ function expose (widget)
return; return;
} }
// Remove the loading class if the slide is loaded // Remove the loading class if the slide is loaded
else else {
{
jQuery(gallery.slides[index]).removeClass(gallery.options.slideLoadingClass); jQuery(gallery.slides[index]).removeClass(gallery.options.slideLoadingClass);
} }
// Just use add to let gallery create everything it needs // Just use add to let gallery create everything it needs
var new_index = gallery.num; var new_index = gallery.num;
gallery.add([image]); gallery.add([image]);
// Move it to where we want it. // Move it to where we want it.
// Gallery uses arrays and indexes and has several internal variables // Gallery uses arrays and indexes and has several internal variables
// that need to be updated. // that need to be updated.
@ -169,11 +141,9 @@ function expose (widget)
// list // list
gallery.list[index] = gallery.list[new_index]; gallery.list[index] = gallery.list[new_index];
gallery.list.splice(new_index, 1); gallery.list.splice(new_index, 1);
// indicators & slides // indicators & slides
var dom_nodes = ['indicators', 'slides']; var dom_nodes = ['indicators', 'slides'];
for(var i in dom_nodes) for (var i in dom_nodes) {
{
var var_name = dom_nodes[i]; var var_name = dom_nodes[i];
// Remove old one from DOM // Remove old one from DOM
jQuery(gallery[var_name][index]).remove(); jQuery(gallery[var_name][index]).remove();
@ -183,41 +153,36 @@ function expose (widget)
var node = jQuery(gallery[var_name][index]); var node = jQuery(gallery[var_name][index]);
node.attr('data-index', index) node.attr('data-index', index)
.insertAfter(jQuery("[data-index='" + (index - 1) + "']", node.parent())); .insertAfter(jQuery("[data-index='" + (index - 1) + "']", node.parent()));
if(active) node.addClass(gallery.options.activeIndicatorClass); if (active)
node.addClass(gallery.options.activeIndicatorClass);
gallery[var_name].splice(new_index, 1); gallery[var_name].splice(new_index, 1);
} }
if(active) if (active) {
{
gallery.activeIndicator = jQuery(gallery.indicators[index]); gallery.activeIndicator = jQuery(gallery.indicators[index]);
} }
// positions // positions
gallery.positions[index] = active ? 0 : (index > gallery.index ? gallery.slideWidth : -gallery.slideWidth); gallery.positions[index] = active ? 0 : (index > gallery.index ? gallery.slideWidth : -gallery.slideWidth);
gallery.positions.splice(new_index, 1); gallery.positions.splice(new_index, 1);
// elements - removing will allow to re-do the slide // elements - removing will allow to re-do the slide
if(gallery.elements[index]) if (gallery.elements[index]) {
{
delete gallery.elements[index]; delete gallery.elements[index];
gallery.loadElement(index); gallery.loadElement(index);
} }
// Remove the one we just added // Remove the one we just added
gallery.num -= 1; gallery.num -= 1;
}; };
return /** @class */ (function (_super) {
return widget.extend([et2_IExposable],{ __extends(exposable, _super);
function exposable() {
/** var args = [];
* Initialize the expose media gallery for (var _i = 0; _i < arguments.length; _i++) {
*/ args[_i] = arguments[_i];
init: function() }
{ var _this = _super.apply(this, args) || this;
this._super.apply(this, arguments); _this.mime_regexp = MIME_REGEX;
this.mime_regexp = mime_regex; _this.mime_odf_regex = MIME_ODF_REGEX;
this.mime_odf_regex = mime_odf_regex; var self = _this;
var self=this; _this.expose_options = {
this.expose_options = {
// The Id, element or querySelector of the gallery widget: // The Id, element or querySelector of the gallery widget:
container: '#blueimp-gallery', container: '#blueimp-gallery',
// The tag name, Id, element or querySelector of the slides container: // The tag name, Id, element or querySelector of the slides container:
@ -333,11 +298,11 @@ function expose (widget)
thumbnailWithImgTag: true, thumbnailWithImgTag: true,
// Callback function executed when the Gallery is initialized. // Callback function executed when the Gallery is initialized.
// Is called with the gallery instance as "this" object: // Is called with the gallery instance as "this" object:
onopen: jQuery.proxy(this.expose_onopen,this), onopen: jQuery.proxy(_this.expose_onopen, _this),
// Callback function executed when the Gallery has been initialized // Callback function executed when the Gallery has been initialized
// and the initialization transition has been completed. // and the initialization transition has been completed.
// Is called with the gallery instance as "this" object: // Is called with the gallery instance as "this" object:
onopened: jQuery.proxy(this.expose_onopened,this), onopened: jQuery.proxy(_this.expose_onopened, _this),
// Callback function executed on slide change. // Callback function executed on slide change.
// Is called with the gallery instance as "this" object and the // Is called with the gallery instance as "this" object and the
// current index and slide as arguments: // current index and slide as arguments:
@ -361,58 +326,51 @@ function expose (widget)
}, },
//// Callback function executed when the Gallery is about to be closed. //// Callback function executed when the Gallery is about to be closed.
// Is called with the gallery instance as "this" object: // Is called with the gallery instance as "this" object:
onclose:jQuery.proxy(this.expose_onclose,this), onclose: jQuery.proxy(_this.expose_onclose, _this),
// Callback function executed when the Gallery has been closed // Callback function executed when the Gallery has been closed
// and the closing transition has been completed. // and the closing transition has been completed.
// Is called with the gallery instance as "this" object: // Is called with the gallery instance as "this" object:
onclosed: jQuery.proxy(this.expose_onclosed,this) onclosed: jQuery.proxy(_this.expose_onclosed, _this)
}; };
var $body = jQuery('body'); var $body = jQuery('body');
if ($body.find('#blueimp-gallery').length == 0) if ($body.find('#blueimp-gallery').length == 0) {
{
// Gallery Main DIV container // Gallery Main DIV container
var $expose_node = jQuery(document.createElement('div')).attr({id:"blueimp-gallery", class:"blueimp-gallery"}); var $expose_node = jQuery(document.createElement('div')).attr({
id: "blueimp-gallery",
class: "blueimp-gallery"
});
// Create Gallery DOM NODE // 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>'); $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 // Append the gallery Node to DOM
$body.append($expose_node); $body.append($expose_node);
} }
return _this;
}, }
exposable.prototype.set_value = function (_value) {
set_value:function (_value) //todo: not sure if we need that with the new construction
{ //if (typeof this._super == 'undefined') return;
if (typeof this._super == 'undefined') return; _super.prototype.set_value.call(this, _value);
this._super.apply(this,arguments);
// Do not run set value of expose if expose_view is not set // Do not run set value of expose if expose_view is not set
// it causes a wired error on nested image widgets which // it causes a wired error on nested image widgets which
// seems the expose is not its child widget // seems the expose is not its child widget
if (!this.options.expose_view ) if (!this.options.expose_view) {
{
return; return;
} }
var fe = egw_get_file_editor_prefered_mimes(); var fe = egw_get_file_editor_prefered_mimes();
var self = this; var self = this;
// If the media type is not supported do not bind the click handler // 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') if (!_value || typeof _value.mime != 'string' || (!_value.mime.match(MIME_REGEX, 'ig')
&& (!fe || fe.mime && !fe.mime[_value.mime])) || typeof _value.download_url == 'undefined') && (!fe || fe.mime && !fe.mime[_value.mime])) || typeof _value.download_url == 'undefined') {
{
return; return;
} }
if (typeof this.options.expose_view != 'undefined' && this.options.expose_view ) if (typeof this.options.expose_view != 'undefined' && this.options.expose_view) {
{
jQuery(this.node).on('click', function (event) { jQuery(this.node).on('click', function (event) {
// Do not trigger expose view if one of the operator keys are held // Do not trigger expose view if one of the operator keys are held
if (!event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey) if (!event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey) {
{ if (_value.mime.match(MIME_REGEX, 'ig')) {
if (_value.mime.match(mime_regex,'ig'))
{
self._init_blueimp_gallery(event, _value); self._init_blueimp_gallery(event, _value);
} }
else if(fe && fe.mime && fe.edit && fe.mime[_value.mime]) else if (fe && fe.mime && fe.edit && fe.mime[_value.mime]) {
{
egw.open_link(egw.link('/index.php', { egw.open_link(egw.link('/index.php', {
menuaction: fe.edit.menuaction, menuaction: fe.edit.menuaction,
path: _value.path, path: _value.path,
@ -423,47 +381,38 @@ function expose (widget)
event.stopImmediatePropagation(); event.stopImmediatePropagation();
}).addClass('et2_clickable'); }).addClass('et2_clickable');
} }
}, };
exposable.prototype._init_blueimp_gallery = function (event, _value) {
_init_blueimp_gallery: function (event, _value)
{
var mediaContent = []; var mediaContent = [];
var nm = find_nextmatch(this); var nm = find_nextmatch(this);
var current_index = 0; var current_index = 0;
if(nm && !this._is_target_indepth(nm,event.target)) if (nm && !this._is_target_indepth(nm, event.target)) {
{
// Get the row that was clicked, find its index in the list // Get the row that was clicked, find its index in the list
var current_entry = nm.controller.getRowByNode(event.target); var current_entry = nm.controller.getRowByNode(event.target);
// But before it goes, we'll pull everything we can // But before it goes, we'll pull everything we can
read_from_nextmatch.call(this, nm, mediaContent); read_from_nextmatch.call(this, nm, mediaContent);
// find current_entry in array and set it's array-index // find current_entry in array and set it's array-index
for(var i=0; i < mediaContent.length; i++) for (var i = 0; i < mediaContent.length; i++) {
{ if ('filemanager::' + mediaContent[i].path == current_entry.uid) {
if ('filemanager::'+mediaContent[i].path == current_entry.uid)
{
current_index = i; current_index = i;
break; break;
} }
} }
// This will trigger nm to refresh and get just the ones we can handle // 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 // but it might take a while, so do it later - make sure our current
// one is loaded first. // one is loaded first.
window.setTimeout(function () { window.setTimeout(function () {
nm.applyFilters({col_filter: {mime: '/'+mime_regex.source+'/'}}); nm.applyFilters({ col_filter: { mime: '/' + MIME_REGEX.source + '/' } });
}, 1); }, 1);
} }
else else {
{
mediaContent = this.getMedia(_value); mediaContent = this.getMedia(_value);
// Do not show thumbnail indicator on single expose view // Do not show thumbnail indicator on single expose view
this.expose_options.thumbnailIndicators = false; this.expose_options.thumbnailIndicators = false;
} }
this.expose_options.index = current_index; this.expose_options.index = current_index;
gallery = blueimp.Gallery(mediaContent, this.expose_options); gallery = blueimp.Gallery(mediaContent, this.expose_options);
}, };
/** /**
* Check if clicked target from nm is in depth * Check if clicked target from nm is in depth
* *
@ -472,47 +421,39 @@ function expose (widget)
* *
* @return {boolean} returns false if target is not in depth otherwise True * @return {boolean} returns false if target is not in depth otherwise True
*/ */
_is_target_indepth: function (nm, target){ exposable.prototype._is_target_indepth = function (nm, target) {
var res = false; var res = false;
if (nm) if (nm) {
{ if (!target) {
if (!target) var target_1 = this.getDOMNode();
{
var target = this.getDOMNode();
} }
var entry = nm.controller.getRowByNode(target); var entry = nm.controller.getRowByNode(target);
if (entry && entry.controller.getDepth()>0) if (entry && entry.controller.getDepth() > 0) {
{
res = true; res = true;
} }
} }
return res; return res;
}, };
expose_onopen: function (event){}, exposable.prototype.expose_onopen = function (event) { };
expose_onopened: function (event){ exposable.prototype.expose_onopened = function (event) {
// Check to see if we're in a nextmatch, do magic // Check to see if we're in a nextmatch, do magic
var nm = find_nextmatch(this); var nm = find_nextmatch(this);
var self = this; var self = this;
if(nm) if (nm) {
{
// Add scrolling to the indicator list // Add scrolling to the indicator list
var total_count = nm.controller._grid.getTotalCount(); var total_count = nm.controller._grid.getTotalCount();
if(total_count >= gallery.num) if (total_count >= gallery.num) {
{
var $indicator = gallery.container.find('.indicator'); var $indicator = gallery.container.find('.indicator');
$indicator.off() $indicator.off()
.addClass('paginating') .addClass('paginating')
.swipe(function (event, direction, distance) { .swipe(function (event, direction, distance) {
if(direction == jQuery.fn.swipe.directions.LEFT) if (direction == jQuery.fn.swipe.directions.LEFT) {
{
distance *= -1; distance *= -1;
} }
else if(direction == jQuery.fn.swipe.directions.RIGHT) else if (direction == jQuery.fn.swipe.directions.RIGHT) {
{
// OK. // OK.
} }
else else {
{
return; return;
} }
jQuery(this).css('left', min(0, parseInt(jQuery(this).css('left')) - (distance * 30)) + 'px'); jQuery(this).css('left', min(0, parseInt(jQuery(this).css('left')) - (distance * 30)) + 'px');
@ -520,106 +461,97 @@ function expose (widget)
// Bind the mousewheel handler for FF (DOMMousewheel), and other browsers (mousewheel) // Bind the mousewheel handler for FF (DOMMousewheel), and other browsers (mousewheel)
$indicator.bind('mousewheel DOMMousewheel', function (event, _delta) { $indicator.bind('mousewheel DOMMousewheel', function (event, _delta) {
var delta = _delta || event.originalEvent.wheelDelta / 120; var delta = _delta || event.originalEvent.wheelDelta / 120;
if(delta > 0 && parseInt(jQuery(this).css('left')) > gallery.container.width() / 2) return; if (delta > 0 && parseInt(jQuery(this).css('left')) > gallery.container.width() / 2)
return;
//Reload next pictures into the gallery by scrolling on thumbnails //Reload next pictures into the gallery by scrolling on thumbnails
if (delta<0 && jQuery(this).width() + parseInt(jQuery(this).css('left')) < gallery.container.width()) if (delta < 0 && jQuery(this).width() + parseInt(jQuery(this).css('left')) < gallery.container.width()) {
{
var nextIndex = gallery.indicatorContainer.find('[title="loading"]')[0]; var nextIndex = gallery.indicatorContainer.find('[title="loading"]')[0];
if (nextIndex) self.expose_onslideend(gallery,nextIndex.dataset.index -1); if (nextIndex)
self.expose_onslideend(gallery, nextIndex.dataset.index - 1);
return; return;
} }
// Move it about 5 indicators // Move it about 5 indicators
jQuery(this).css('left', parseInt(jQuery(this).css('left')) - (-delta * gallery.activeIndicator.width() * 5) + 'px'); jQuery(this).css('left', parseInt(jQuery(this).css('left')) - (-delta * gallery.activeIndicator.width() * 5) + 'px');
event.preventDefault(); event.preventDefault();
}); });
} }
} }
}, };
/** /**
* Trigger on slide left/right * Trigger on slide left/right
* @param {Gallery} gallery * @param {Gallery} gallery
* @param {integer} index * @param {integer} index
* @param {DOMNode} slide * @param {DOMNode} slide
*/ */
expose_onslide: function (gallery, index, slide){ exposable.prototype.expose_onslide = function (gallery, index, slide) {
if (typeof this._super == 'undefined') return; //todo
//if (typeof this._super == 'undefined') return;
// First let parent try // First let parent try
this._super.apply(this, arguments); _this = _super.call(this, gallery, index, slide) || this;
var nm = find_nextmatch(this); var nm = find_nextmatch(this);
if(nm) if (nm) {
{
// See if we need to move the indicator // See if we need to move the indicator
var indicator = gallery.container.find('.indicator'); var indicator = gallery.container.find('.indicator');
var current = jQuery('.active', indicator).position(); var current = jQuery('.active', indicator).position();
if (current) {
if(current)
{
indicator.animate({ left: (gallery.container.width() / 2) - current.left }, 10); indicator.animate({ left: (gallery.container.width() / 2) - current.left }, 10);
} }
} }
}, };
expose_onslideend: function (gallery, index, slide){ exposable.prototype.expose_onslideend = function (gallery, index, slide) {
// Check to see if we're in a nextmatch, do magic // Check to see if we're in a nextmatch, do magic
var nm = find_nextmatch(this); var nm = find_nextmatch(this);
if(nm) if (nm) {
{
// Check to see if we're near the end, or maybe some pagination // Check to see if we're near the end, or maybe some pagination
// would be good. // would be good.
var total_count = nm.controller._grid.getTotalCount(); var total_count = nm.controller._grid.getTotalCount();
// Already at the end, don't bother // Already at the end, don't bother
if(index == total_count -1 || index == 0) return; if (index == total_count - 1 || index == 0)
return;
// Try to determine direction from state of next & previous slides // Try to determine direction from state of next & previous slides
var direction = 1; var direction = 1;
for(var i in gallery.elements) for (var i in gallery.elements) {
{
// Loading or error // Loading or error
if(gallery.elements[i] == 1 || gallery.elements[i] == 3 || gallery.list[i].loading) if (gallery.elements[i] == 1 || gallery.elements[i] == 3 || gallery.list[i].loading) {
{
direction = i >= index ? 1 : -1; direction = i >= index ? 1 : -1;
break; break;
} }
} }
if (!gallery.list[index + direction] || gallery.list[index + direction].loading || if (!gallery.list[index + direction] || gallery.list[index + direction].loading ||
total_count > gallery.getNumber() && index + ET2_DATAVIEW_STEPSIZE > gallery.getNumber()) total_count > gallery.getNumber() && index + ET2_DATAVIEW_STEPSIZE > gallery.getNumber()) {
{
// This will get the next batch of rows // This will get the next batch of rows
var start = Math.max(0, direction > 0 ? index : index - ET2_DATAVIEW_STEPSIZE); var start = Math.max(0, direction > 0 ? index : index - ET2_DATAVIEW_STEPSIZE);
var end = Math.min(total_count - 1, start + ET2_DATAVIEW_STEPSIZE); var end = Math.min(total_count - 1, start + ET2_DATAVIEW_STEPSIZE);
nm.controller._gridCallback(start, end); nm.controller._gridCallback(start, end);
var images = []; var images = [];
read_from_nextmatch.call(this, nm, images, start); read_from_nextmatch.call(this, nm, images, start);
// Gallery always adds to the end, causing problems with pagination // Gallery always adds to the end, causing problems with pagination
for(var i in images) for (var i in images) {
{
//if(i == index || i < gallery.num) continue; //if(i == index || i < gallery.num) continue;
set_slide(i, images[i]); set_slide(i, images[i]);
//gallery.add([images[i]]); //gallery.add([images[i]]);
} }
} }
} }
}, };
expose_onslidecomplete:function (gallery, index, slide){}, exposable.prototype.expose_onslidecomplete = function (gallery, index, slide) {
expose_onclose: function(event){ };
exposable.prototype.expose_onclose = function (event) {
// Check to see if we're in a nextmatch, remove magic // Check to see if we're in a nextmatch, remove magic
var nm = find_nextmatch(this); var nm = find_nextmatch(this);
if(nm && !this._is_target_indepth(nm)) if (nm && !this._is_target_indepth(nm)) {
{
// Remove scrolling from thumbnails // Remove scrolling from thumbnails
gallery.container.find('.indicator') gallery.container.find('.indicator')
.removeClass('paginating') .removeClass('paginating')
.off('mousewheel') .off('mousewheel')
.off('swipe'); .off('swipe');
// Remove applied mime filter // Remove applied mime filter
nm.applyFilters({ col_filter: { mime: '' } }); nm.applyFilters({ col_filter: { mime: '' } });
} }
}, };
expose_onclosed: function (event){}, exposable.prototype.expose_onclosed = function (event) {
}); };
return exposable;
}(Base));
} }
//# sourceMappingURL=expose.js.map

589
api/js/etemplate/expose.ts Normal file
View File

@ -0,0 +1,589 @@
/**
* 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 http://www.egroupware.org
* @author Hadi Nategh <hn[at]stylite.de>
* @copyright Stylite AG
* @version $Id$
*/
/*egw:uses
/vendor/bower-asset/jquery/dist/jquery.js;
/api/js/jquery/blueimp/js/blueimp-gallery.min.js;
*/
import {WidgetConfig} from "./et2_core_widget";
import {ClassWithAttributes} from "./et2_core_inheritance";
type Constructor<T = {}> = new (...args: any[]) => T;
/**
* This function extends the given widget with blueimp gallery plugin
*
* @param {type} widget
* @returns {widget}
*/
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))/) :
new RegExp(/(video\/(mp4|ogg|webm))|(image\/:*(?!tif|x-xcf|pdf))/);
// 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) {
var current = widget;
var 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;
var image_index = start_at;
var stop = Math.max.apply(null, Object.keys(nm.controller._indexMap));
for (var 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;
}
var uid = nm.controller._indexMap[i].uid;
if (!uid) continue;
var data = egw.dataGetUIDdata(uid);
if (data && data.data && data.data.mime && MIME_REGEX.test(data.data.mime)) {
var 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) {
var 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
var 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
var dom_nodes = ['indicators', 'slides'];
for (var i in dom_nodes) {
var 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
var 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 {
private 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_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;
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
if (!this.options.expose_view) {
return;
}
var fe = egw_get_file_editor_prefered_mimes();
var 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;
}
if (typeof this.options.expose_view != 'undefined' && this.options.expose_view) {
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')) {
self._init_blueimp_gallery(event, _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 {
mediaContent = this.getMedia(_value);
// Do not show thumbnail indicator on single expose view
this.expose_options.thumbnailIndicators = false;
}
this.expose_options.index = current_index;
gallery = blueimp.Gallery(mediaContent, this.expose_options);
}
/**
* 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) {
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(event)
{
// 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) {
if (direction == jQuery.fn.swipe.directions.LEFT) {
distance *= -1;
} else if (direction == jQuery.fn.swipe.directions.RIGHT) {
// OK.
} else {
return;
}
jQuery(this).css('left', 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
* @param {Gallery} gallery
* @param {integer} index
* @param {DOMNode} slide
*/
expose_onslide(gallery, index, slide)
{
//todo
//if (typeof this._super == 'undefined') return;
// First let parent try
super(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, slide?) {
// 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(gallery, index, slide) {
}
expose_onclose(event) {
// 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(event) {
}
}
}