mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-08-09 08:25:03 +02:00
Merge branch 'master' into web-components
This commit is contained in:
@ -17,6 +17,7 @@
|
||||
|
||||
import {egw} from "../jsapi/egw_global";
|
||||
import {et2_IDOMNode} from "./et2_core_interfaces";
|
||||
import {et2_form_name} from "./et2_core_common";
|
||||
|
||||
export function et2_compileLegacyJS(_code, _widget, _context)
|
||||
{
|
||||
@ -144,7 +145,8 @@ function js_pseudo_funcs(_val,widget)
|
||||
{
|
||||
// et2_form_name doesn't care about ][, just [
|
||||
var _cname = widget.getPath() ? widget.getPath().join("[") : false;
|
||||
_val = _val.replace(/form::name\(/g, "'"+widget.getRoot()._inst.uniqueId+"_'+"+(_cname ? "et2_form_name('"+_cname+"'," : '('));
|
||||
document.et2_form_name = et2_form_name;
|
||||
_val = _val.replace(/form::name\(/g, "'"+widget.getRoot()._inst.uniqueId+"_'+"+(_cname ? "document.et2_form_name('"+_cname+"'," : '('));
|
||||
}
|
||||
|
||||
if (_val.indexOf('egw::lang(') != -1)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -519,9 +519,9 @@ export class et2_grid extends et2_DOMWidget implements et2_IDetachedDOM, et2_IAl
|
||||
cell.nm_id = node.getAttribute('id');
|
||||
}
|
||||
// Apply widget's class to td, for backward compatability
|
||||
if(node.getAttribute("class"))
|
||||
if (node.getAttribute("class"))
|
||||
{
|
||||
cell.class += (cell.class ? " " : "") + node.getAttribute("class");
|
||||
cell.class += (cell.class ? " " : "") + this.getArrayMgr("content").expandName(node.getAttribute("class"));
|
||||
}
|
||||
|
||||
// Create the element
|
||||
|
@ -641,7 +641,7 @@ export class et2_avatar extends et2_image
|
||||
}
|
||||
).sendRequest(true);
|
||||
}
|
||||
if (this.options.crop)
|
||||
if (this.options.crop && !this.options.readonly)
|
||||
{
|
||||
jQuery(this.image).cropper({
|
||||
aspectRatio: 1/1,
|
||||
|
@ -639,7 +639,6 @@ export class et2_link_entry extends et2_inputWidget
|
||||
protected search: JQuery;
|
||||
protected clear: JQuery;
|
||||
protected link_button: JQuery;
|
||||
private response: any;
|
||||
private request: any;
|
||||
private last_search: string;
|
||||
processing: boolean = false;
|
||||
@ -1041,6 +1040,13 @@ export class et2_link_entry extends et2_inputWidget
|
||||
};
|
||||
}
|
||||
}
|
||||
// display a search query, not a selected entry
|
||||
else if (_value !== null && typeof _value === 'object' && typeof _value.query === 'string')
|
||||
{
|
||||
this.options.value = { app: _value.app || this.options.only_app, id: null };
|
||||
this.search.val(_value.query);
|
||||
return;
|
||||
}
|
||||
this._oldValue = this.options.value;
|
||||
if (!_value || _value.length == 0 || _value == null || jQuery.isEmptyObject(_value))
|
||||
{
|
||||
@ -1183,18 +1189,29 @@ export class et2_link_entry extends et2_inputWidget
|
||||
return response(this.cache[request.term]);
|
||||
}
|
||||
|
||||
// Remember callback
|
||||
this.response = response;
|
||||
|
||||
this.search.addClass("loading");
|
||||
// Remove specific display and revert to CSS file
|
||||
// show() would use inline, should be inline-block
|
||||
this.clear.css('display', '');
|
||||
this.request = egw.json("EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_link_search",
|
||||
[this.app_select.val(), '', request.term, request.options],
|
||||
this._results,
|
||||
this, true, this
|
||||
).sendRequest();
|
||||
|
||||
this.request = egw.request("EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_link_search",
|
||||
[this.app_select.val(), '', request.term, request.options]);
|
||||
|
||||
this.request.then((data) =>
|
||||
{
|
||||
if (this.request)
|
||||
{
|
||||
this.request = null;
|
||||
}
|
||||
this.search.removeClass("loading");
|
||||
let result = [];
|
||||
for (var id in data)
|
||||
{
|
||||
result.push({"value": id, "label": data[id]});
|
||||
}
|
||||
this.cache[this.search.val()] = result;
|
||||
response(result);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1239,27 +1256,6 @@ export class et2_link_entry extends et2_inputWidget
|
||||
}, event.data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Server found some results
|
||||
*
|
||||
* @param {Array} data
|
||||
*/
|
||||
_results(data)
|
||||
{
|
||||
if (this.request)
|
||||
{
|
||||
this.request = null;
|
||||
}
|
||||
this.search.removeClass("loading");
|
||||
var result = [];
|
||||
for (var id in data)
|
||||
{
|
||||
result.push({"value": id, "label": data[id]});
|
||||
}
|
||||
this.cache[this.search.val()] = result;
|
||||
this.response(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a link using the current internal values
|
||||
*
|
||||
|
@ -19,7 +19,7 @@ import {ClassWithAttributes} from "./et2_core_inheritance";
|
||||
import {et2_no_init} from "./et2_core_common";
|
||||
import {egw} from "../jsapi/egw_global";
|
||||
import {et2_IInput} from "./et2_core_interfaces";
|
||||
|
||||
import {date} from "./lib/date.js";
|
||||
/**
|
||||
* Class which implements the "button-timestamper" XET-Tag
|
||||
*
|
||||
|
@ -96,6 +96,18 @@ export class et2_video extends et2_baseWidget implements et2_IDOMNode
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Defines if the video should be played repeatedly"
|
||||
},
|
||||
"volume": {
|
||||
"name": "Video volume",
|
||||
"type": "float",
|
||||
"default": 0,
|
||||
"description": "Set video's volume"
|
||||
},
|
||||
"playbackrate": {
|
||||
"name": "Video playBackRate",
|
||||
"type": "float",
|
||||
"default": 1,
|
||||
"description": "Set video's playBackRate"
|
||||
}
|
||||
};
|
||||
|
||||
@ -264,6 +276,105 @@ export class et2_video extends et2_baseWidget implements et2_IDOMNode
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to set volume
|
||||
* @param _value
|
||||
*/
|
||||
set_volume(_value: number)
|
||||
{
|
||||
let value = _value>100?100:_value;
|
||||
if (value>= 0)
|
||||
{
|
||||
if (this._isYoutube() && this.youtube)
|
||||
{
|
||||
this.youtube.setVolume(value);
|
||||
}
|
||||
else if(!this._isYoutube())
|
||||
{
|
||||
this.video[0].volume = value/100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get volume
|
||||
*/
|
||||
get_volume()
|
||||
{
|
||||
if (this._isYoutube() && this.youtube)
|
||||
{
|
||||
return this.youtube.getVolume();
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.video[0].volume * 100;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* method to set playBackRate
|
||||
* @param _value
|
||||
*/
|
||||
set_playBackRate(_value: number)
|
||||
{
|
||||
let value = _value>16?16:_value;
|
||||
if (value>= 0)
|
||||
{
|
||||
if (this._isYoutube() && this.youtube)
|
||||
{
|
||||
this.youtube.setPlaybackRate(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.video[0].playbackRate = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get playBackRate
|
||||
*/
|
||||
get_playBackRate()
|
||||
{
|
||||
if (this._isYoutube() && this.youtube)
|
||||
{
|
||||
return this.youtube.getPlaybackRate();
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.video[0].playbackRate;
|
||||
}
|
||||
}
|
||||
|
||||
set_mute(_value)
|
||||
{
|
||||
if (this._isYoutube() && this.youtube) {
|
||||
if (_value)
|
||||
{
|
||||
this.youtube.mute();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.youtube.unMute();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.video[0].muted = _value;
|
||||
}
|
||||
}
|
||||
|
||||
get_mute()
|
||||
{
|
||||
if (this._isYoutube() && this.youtube)
|
||||
{
|
||||
return this.youtube.isMuted();
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.video[0].muted;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set poster attribute in order to specify
|
||||
* an image to be shown while video is loading or before user play it
|
||||
|
@ -782,18 +782,21 @@ export abstract class EgwApp
|
||||
})
|
||||
.addClass("ui-helper-clearfix");
|
||||
|
||||
let el = document.getElementById('favorite_sidebox_'+this.appname).getElementsByTagName('ul')[0];
|
||||
let sortablejs = Sortable.create(el, {
|
||||
ghostClass: 'ui-fav-sortable-placeholder',
|
||||
draggable: 'li:not([data-id$="add"])',
|
||||
delay: 25,
|
||||
dataIdAttr:'data-id',
|
||||
onSort: function(event){
|
||||
let favSortedList = sortablejs.toArray();
|
||||
self.egw.set_preference(self.appname,'fav_sort_pref',favSortedList);
|
||||
self._refresh_fav_nm();
|
||||
}
|
||||
});
|
||||
let el = document.getElementById('favorite_sidebox_'+this.appname)?.getElementsByTagName('ul')[0];
|
||||
if (el && el instanceof HTMLElement)
|
||||
{
|
||||
let sortablejs = Sortable.create(el, {
|
||||
ghostClass: 'ui-fav-sortable-placeholder',
|
||||
draggable: 'li:not([data-id$="add"])',
|
||||
delay: 25,
|
||||
dataIdAttr:'data-id',
|
||||
onSort: function(event){
|
||||
let favSortedList = sortablejs.toArray();
|
||||
self.egw.set_preference(self.appname,'fav_sort_pref',favSortedList);
|
||||
self._refresh_fav_nm();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Bind favorite de-select
|
||||
var egw_fw = egw_getFramework();
|
||||
|
@ -127,16 +127,16 @@ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd)
|
||||
}.bind(this);
|
||||
|
||||
this.websocket = new WebSocket(url);
|
||||
this.websocket.onopen = jQuery.proxy(function(e)
|
||||
this.websocket.onopen = (e) =>
|
||||
{
|
||||
check_timer = window.setTimeout(check, check_interval);
|
||||
this.websocket.send(JSON.stringify({
|
||||
subscribe: tokens,
|
||||
account_id: parseInt(account_id)
|
||||
}));
|
||||
}, this);
|
||||
};
|
||||
|
||||
this.websocket.onmessage = jQuery.proxy(function(event)
|
||||
this.websocket.onmessage = (event) =>
|
||||
{
|
||||
reconnect_time = min_reconnect_time;
|
||||
console.log(event);
|
||||
@ -148,18 +148,18 @@ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd)
|
||||
{
|
||||
this.handleResponse({ response: [data]});
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
this.websocket.onerror = jQuery.proxy(function(error)
|
||||
this.websocket.onerror = (error) =>
|
||||
{
|
||||
reconnect_time *= 2;
|
||||
if (reconnect_time > max_reconnect_time) reconnect_time = max_reconnect_time;
|
||||
|
||||
console.log(error);
|
||||
(error||this.handleError({}, error));
|
||||
}, this);
|
||||
};
|
||||
|
||||
this.websocket.onclose = jQuery.proxy(function(event)
|
||||
this.websocket.onclose = (event) =>
|
||||
{
|
||||
if (event.wasClean)
|
||||
{
|
||||
@ -176,9 +176,9 @@ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd)
|
||||
console.log('[close] Connection died --> reconnect in '+reconnect_time+'ms');
|
||||
if (check_timer) window.clearTimeout(check_timer);
|
||||
check_timer = null;
|
||||
window.setTimeout(jQuery.proxy(this.openWebSocket, this, url, tokens, account_id, error, reconnect_time), reconnect_time);
|
||||
window.setTimeout(() => this.openWebSocket(url, tokens, account_id, error, reconnect_time), reconnect_time);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
@ -189,6 +189,7 @@ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd)
|
||||
* @param {function} error option error callback(_xmlhttp, _err) used instead our default this.error
|
||||
*
|
||||
* @return {Promise|boolean} Promise or for async==="keepalive" boolean is returned
|
||||
* Promise.abort() allows to abort the pending request
|
||||
*/
|
||||
json_request.prototype.sendRequest = function(async, method, error)
|
||||
{
|
||||
@ -231,7 +232,9 @@ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd)
|
||||
let promise;
|
||||
if (this.async)
|
||||
{
|
||||
promise = (this.egw.window?this.egw.window:window).fetch(url, init)
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
promise = (this.egw.window?this.egw.window:window).fetch(url, {...init, ...signal})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw response;
|
||||
@ -242,6 +245,9 @@ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd)
|
||||
.catch((_err) => {
|
||||
(error || this.handleError).call(this, _err)
|
||||
});
|
||||
|
||||
// offering a simple abort mechanism and compatibility with jQuery.ajax
|
||||
promise.abort = () => controller.abort();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -496,45 +502,40 @@ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd)
|
||||
* @param {string} _menuaction
|
||||
* @param {any[]} _parameters
|
||||
*
|
||||
* @return Promise
|
||||
* @return Promise resolving to data part (not full response, which can contain other parts)
|
||||
* Promise.abort() allows to abort the pending request
|
||||
*/
|
||||
request: function(_menuaction, _parameters)
|
||||
{
|
||||
let request = new json_request(_menuaction, _parameters, null, this, true, this, this);
|
||||
let ajax_promise = request.sendRequest();
|
||||
|
||||
// This happens first, immediately
|
||||
let resolvePromise = function(resolve, reject) {
|
||||
// Bind to ajax response - this is called _after_ any other handling
|
||||
ajax_promise.always(function(response, status, p) {
|
||||
if(status !== "success") reject();
|
||||
|
||||
// The ajax request has completed, get just the data & pass it on
|
||||
if(response && response.response)
|
||||
const request = new json_request(_menuaction, _parameters, null, this, true, this, this);
|
||||
const response = request.sendRequest();
|
||||
let promise = response.then(function(response)
|
||||
{
|
||||
// The ajax request has completed, get just the data & pass it on
|
||||
if(response && response.response)
|
||||
{
|
||||
for(let value of response.response)
|
||||
{
|
||||
for(let value of response.response)
|
||||
if(value.type && value.type === "data" && typeof value.data !== "undefined")
|
||||
{
|
||||
if(value.type && value.type === "data" && typeof value.data !== "undefined")
|
||||
{
|
||||
// Data was packed in response
|
||||
resolve(value.data);
|
||||
}
|
||||
else if (value && typeof value.type === "undefined" && typeof value.data === "undefined")
|
||||
{
|
||||
// Just raw data
|
||||
resolve(value);
|
||||
}
|
||||
// Data was packed in response
|
||||
return value.data;
|
||||
}
|
||||
else if (value && typeof value.type === "undefined" && typeof value.data === "undefined")
|
||||
{
|
||||
// Just raw data
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// No data? Resolve the promise with nothing
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
const myPromise = new Promise(resolvePromise);
|
||||
|
||||
return myPromise;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
// pass abort method to returned response
|
||||
if (typeof response.abort === 'function')
|
||||
{
|
||||
promise.abort = response.abort;
|
||||
}
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user