860 lines
38 KiB
JavaScript
860 lines
38 KiB
JavaScript
/*
|
|
$Revision$
|
|
$Id$
|
|
|
|
File: FcltJquery.js
|
|
Description: Generieke functies die bij jquery wordt gebruikt.
|
|
Parameters:
|
|
Context: Bestand wordt gelijktijdig geinclude met jquery.js in Shared/header.inc (Requires plugin)
|
|
Note:
|
|
|
|
*/
|
|
|
|
// Let op: afterAction wordt alleen uitgevoerd als json.success gezet
|
|
function FcltCallbackAndThen(afterAction) {
|
|
return function(json, textStatus) {
|
|
if (json.message) FcltMgr.alert(json.message);
|
|
if (json.warning) FcltMgr.alert(json.warning);
|
|
if (json.toaster) FcltMgr.topmanager().window.$.toast({ text: json.toaster, icon: "success", position: 'top-center', loaderBg: '#fff' });
|
|
json.message = null;
|
|
json.warning = null;
|
|
json.toaster = null;
|
|
if (json.success) {
|
|
if (afterAction) afterAction(json);
|
|
}
|
|
}
|
|
};
|
|
|
|
function FcltCallbackAndThenAlways(afterAction) {
|
|
return function(json, textStatus) {
|
|
if (json.message) FcltMgr.alert(json.message);
|
|
if (json.warning) FcltMgr.alert(json.warning);
|
|
if (json.toaster) FcltMgr.topmanager().window.$.toast({ text: json.toaster, icon: "success", position: 'top-center', loaderBg: '#fff' });
|
|
json.message = null;
|
|
json.warning = null;
|
|
json.toaster = null;
|
|
if (afterAction) afterAction(json);
|
|
}
|
|
};
|
|
|
|
var FcltCallback = FcltCallbackAndThen(null);
|
|
|
|
|
|
var FcltCallbackRefresh = FcltCallbackAndThen(function() {
|
|
FcltMgr.reload();
|
|
});
|
|
|
|
var FcltCallbackClose = FcltCallbackAndThen(function(json) {
|
|
json.close = true;
|
|
FcltMgr.closeDetail(window, json);
|
|
});
|
|
|
|
var FcltCallbackDirtyLine = FcltCallbackAndThen(function(json) {
|
|
// Bestaat json.key uit 1 of meerdere keys?
|
|
if (json.key.indexOf(",") >= 0) {
|
|
var key = json.key.split(",");
|
|
for (var i = 0; i < key.length; i++) {
|
|
$("#row" + key[i]).addClass('dirty');
|
|
}
|
|
} else
|
|
$("#row" + json.key).addClass('dirty');
|
|
});
|
|
|
|
// Roep met Ajax syncroon(!) een asp bestand aan en lever de JSON data op.
|
|
function FcltSyncgetJSON(url, data) {
|
|
var globalData;
|
|
jQuery.ajax({
|
|
type: "GET",
|
|
url: url,
|
|
async: false,
|
|
data: data,
|
|
dataType: "json",
|
|
success: function(data, textStatus) { globalData = data;
|
|
globalData.textStatus = textStatus; }
|
|
});
|
|
if (globalData && globalData.warning)
|
|
FcltMgr.alert(globalData.warning);
|
|
if (globalData && globalData.error)
|
|
FcltMgr.alert(globalData.error);
|
|
if (globalData && globalData.toaster)
|
|
FcltMgr.topmanager().window.$.toast({ text: globalData.toaster, icon: "success", position: 'top-center', loaderBg: '#fff' });;
|
|
return globalData;
|
|
}
|
|
|
|
function FcltToast(msg, type, position, loaderBg) {
|
|
if (msg == null || msg == "")
|
|
return;
|
|
|
|
var icon;
|
|
switch (type) {
|
|
case 0:
|
|
icon = "success";
|
|
break;
|
|
case 1:
|
|
icon = "error";
|
|
break;
|
|
case 2:
|
|
icon = "info";
|
|
break;
|
|
case 3:
|
|
icon = "warning";
|
|
break;
|
|
default:
|
|
icon = "success";
|
|
}
|
|
position = position || "top-center";
|
|
loaderBg = loaderBg || "#fff";
|
|
|
|
FcltMgr.topmanager().window.$.toast({ text: msg, icon: icon, position: position, loaderBg: loaderBg });;
|
|
}
|
|
|
|
// Handig, komt heel veel voor
|
|
function gen_cancel() {
|
|
FcltMgr.closeDetail(window, { cancel: true });
|
|
}
|
|
|
|
var ajaxBusy = {};
|
|
|
|
// TODO Alleen als Logging aan?
|
|
$(document).on("ajaxError", function(event, jqXHR, ajaxSettings, thrownError) {
|
|
|
|
if (jqXHR.status == 0)
|
|
{
|
|
var errtxt = "Request not sent";
|
|
if ("statusText" in jqXHR && jqXHR.statusText !== "") // Only for HTTP 1.1
|
|
errtxt += " (status = " + jqXHR.statusText + ")";
|
|
console.error(errtxt);
|
|
}
|
|
else if (jqXHR.responseText.match(/^FCLTFriendly:/)) // Friendly tekst uit shared/500_error.asp
|
|
{
|
|
FcltMgr.alert(jqXHR.responseText.substring(13));
|
|
}
|
|
else if (jqXHR.responseText.match(/^FCLTExpired:/)) // Expired tekst uit common.inc
|
|
{
|
|
if (FcltMgr.getData('expired'))
|
|
FcltMgr.getData('expired')();
|
|
else
|
|
FcltMgr.alert(jqXHR.responseText.substring(12));
|
|
}
|
|
else
|
|
{
|
|
if (jqXHR.responseJSON && "error" in jqXHR.responseJSON)
|
|
var errtxt = jqXHR.responseJSON.error.message; // een api2.error(400, "Tekst");
|
|
else
|
|
errtxt = "JQuery Ajax Error: " +
|
|
"\n" + jqXHR.status + ": " + jqXHR.statusText +
|
|
(thrownError && thrownError != jqXHR.statusText ? "\n" + thrownError : "") +
|
|
"\n\n" + ajaxSettings.type + " " + ajaxSettings.url;
|
|
FcltMgr.alert(errtxt +
|
|
"\n\n" + new Date().toLocaleString());
|
|
}
|
|
});
|
|
|
|
var $wasfocus = null; // focus terugzetten als je busyloading weghaalt
|
|
$(document).on("ajaxSend", function(event, xhr, options) {
|
|
|
|
if (options.type.toUpperCase() === "POST" && !options.noSpinner) {
|
|
|
|
options.fcltindex = Math.random().toString(36).slice(2);
|
|
var $body = $("body");
|
|
var overlay = setTimeout(function() {
|
|
window.$wasfocus = $(':focus');
|
|
if (window.location.href.match(/empty\.asp|empty\.html/ig)) {
|
|
if (!$body.find("div.busyloading").length) {
|
|
$body.prepend("<div class='busyloading'>" + I("fa-fclt-spinner fa-4x") + "</div>");
|
|
}
|
|
} else if (!$body.find("div.busyoverlay").length) {
|
|
$body.prepend("<div class='busyoverlay'></div>");
|
|
}
|
|
}, 500);
|
|
|
|
var spinner = setTimeout(function() {
|
|
if (!$body.find("div.busyloading").length) {
|
|
$body.prepend("<div class='busyloading'>" + I("fa-fclt-spinner fa-4x") + "</div>");
|
|
}
|
|
}, 2000);
|
|
window.ajaxBusy[options.fcltindex] = [overlay, spinner];
|
|
}
|
|
|
|
}).on("ajaxComplete", function(event, xhr, options) {
|
|
|
|
if (options.type.toUpperCase() === "POST" && !options.noSpinner) {
|
|
if ("fcltindex" in options && options.fcltindex in window.ajaxBusy) {
|
|
for (var i = 0; i < window.ajaxBusy[options.fcltindex].length; i++) {
|
|
clearTimeout(window.ajaxBusy[options.fcltindex][i]);
|
|
}
|
|
delete window.ajaxBusy[options.fcltindex];
|
|
}
|
|
if (Object.keys(window.ajaxBusy).length === 0) { // Niets meer 'busy'
|
|
$(".busyoverlay").remove();
|
|
$(".busyloading").remove();
|
|
if (window.$wasfocus) {
|
|
window.$wasfocus.focus();
|
|
window.$wasfocus = null;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Experiment:
|
|
// Ctrl+W sluit onze eigen tabjes ipv browser tabjes?
|
|
// TODO: eigenlijk via FcltMgr._pageManager._closeTab(this)? Of juist niet?
|
|
// Je kunt nu ook je portal tabje sluiten. Is niet de bedoeling
|
|
//$(window).keydown(function(event)
|
|
//{
|
|
// if (FcltMgr && event.ctrlKey && event.keyCode == 87) // ctrl+W
|
|
// {
|
|
// FcltMgr.closeDetail();
|
|
// event.preventDefault(); // Niet de *browser* tab
|
|
// }
|
|
// });
|
|
|
|
// FIX FSN#22060 charset encoding probleem met $.getJSON en Ajax
|
|
// Mochten we ooit op utf8 overstappen dan kan dit er waarschijnlijk uit.
|
|
// http://forum.jquery.com/topic/serialize-problem-with-latin-1-iso-8859-1-and-solution
|
|
var myEncode = function(s) {
|
|
return unescape(encodeURIComponent(escape(s))).replace(/\+/g, "%2B");
|
|
}
|
|
|
|
// Obj to Url string
|
|
function serializeObj(obj) {
|
|
var arr = [];
|
|
for (var key in obj)
|
|
arr.push(key + "=" + myEncode(obj[key]));
|
|
return arr.join("&");
|
|
}
|
|
|
|
if (document.characterSet != 'UTF-8')
|
|
{
|
|
jQuery.extend({
|
|
param: function(a) {
|
|
var s = [];
|
|
// If an array was passed in, assume that it is an array
|
|
// of form elements
|
|
if (a.constructor == Array || a.jquery) {
|
|
// Serialize the form elements
|
|
jQuery.each(a, function() {
|
|
s.push(myEncode(this.name) + "=" + myEncode(this.value));
|
|
});
|
|
}
|
|
// Otherwise, assume that it's an object of key/value pairs
|
|
else {
|
|
// Serialize the key/values
|
|
for (var j in a)
|
|
// If the value is an array then the key names need to be repeated
|
|
if (a[j] && a[j].constructor == Array)
|
|
jQuery.each(a[j], function() {
|
|
s.push(myEncode(j) + "=" + myEncode(this));
|
|
});
|
|
else
|
|
s.push(myEncode(j) + "=" + myEncode(a[j]));
|
|
}
|
|
// Return the resulting serialization
|
|
return s.join("&").replace(/ /g, "+");
|
|
},
|
|
|
|
serialize: function() {
|
|
return this.param(this.serializeArray());
|
|
}
|
|
});
|
|
}
|
|
|
|
// Voormalig jquery.autogrow-textarea.js
|
|
(function($) {
|
|
|
|
/*
|
|
* Auto-growing textareas; technique ripped from Facebook
|
|
* JGL: modified shadow width ook goed doen als textarea (nog) invisible
|
|
* JGL: modified with delayedUpdate
|
|
* KRE: also used for mentions
|
|
*/
|
|
$.fn.autogrow = function(options) {
|
|
|
|
this.filter('textarea').each(function() {
|
|
|
|
var $this = $(this),
|
|
minHeight = parseInt($this.height(), 10),
|
|
self = this;
|
|
|
|
var shadow = $('<div></div>').css({
|
|
'position': 'absolute',
|
|
'top': -10000,
|
|
'left': -10000,
|
|
'width': Math.max(0, parseFloat($(this).css('width'))),
|
|
'fontSize': $this.css('fontSize'),
|
|
'fontFamily': $this.css('fontFamily'),
|
|
'lineHeight': $this.css('lineHeight'),
|
|
'resize': 'none',
|
|
'overflow-wrap': 'break-word'
|
|
}).appendTo(document.body);
|
|
|
|
var delayedUpdate = function() {
|
|
if (self.timerID) clearTimeout(self.timerID);
|
|
self.timerID = setTimeout(function() { update.apply(self) }, 250);
|
|
}
|
|
|
|
var update = function(init) {
|
|
|
|
if (self.timerID) clearTimeout(self.timerID);
|
|
var times = function(string, number) {
|
|
for (var i = 0, r = ''; i < number; i++) {
|
|
r += string;
|
|
}
|
|
return r;
|
|
};
|
|
|
|
var cleanValue = function(cursorIndex) {
|
|
return self.value.substr(0, cursorIndex || self.value.length)
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/&/g, '&')
|
|
.replace(/\n$/, '<br/> ')
|
|
.replace(/\n/g, '<br/>')
|
|
.replace(/ {2,}/g, function(space) { return times(' ', space.length - 1) + ' ' });
|
|
}
|
|
|
|
if ($this.is("textarea#note.mentioning") || (init && $this.is("textarea#note"))) // Y van de caret bijhouden voor een mention-suggest
|
|
{
|
|
var val = cleanValue(this.selectionStart);
|
|
shadow.html(val);
|
|
shadow.css("width", Math.max(0, $(this).width()));
|
|
$("#note").data("caret-bottom-y", parseFloat($("#note").css("padding-top")) + parseFloat(shadow.height()));
|
|
}
|
|
|
|
var val = cleanValue();
|
|
shadow.html(val);
|
|
shadow.css("width", Math.max(0, $(this).width()));
|
|
var oldheight = parseInt($(this).height());
|
|
var newheight = Math.max(parseInt(shadow.height()) + 20, minHeight);
|
|
if (oldheight != newheight && $(this).is(":visible")) {
|
|
$(this).height(newheight);
|
|
FcltMgr.resized(window);
|
|
}
|
|
|
|
/* Experimentele max-lengte indicatie onder label
|
|
var len = this.value.length;
|
|
var maxlen = parseInt(this.getAttribute("maxlength"), 10)||4000;
|
|
var $lbl = $("label[for="+ this.name +"]");
|
|
$lbl.find("div").remove();
|
|
if ($lbl.length && len > maxlen * (90/100))
|
|
{
|
|
if (len > maxlen)
|
|
$lbl.append("<div style='color:red;font-size:0.9em;'>Te lang.<br>Maximum is {0}, huidig is {1}</div>".format(maxlen, len));
|
|
else
|
|
$lbl.append("<div style='color:blue;font-size:0.9em'>{1}/{0} karakters</div>".format(maxlen, len));
|
|
}
|
|
*/
|
|
}
|
|
|
|
$(this).change(delayedUpdate).keyup(delayedUpdate).keydown(delayedUpdate).blur(update);
|
|
|
|
update.apply(this, [true]);
|
|
|
|
});
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
})(jQuery);
|
|
|
|
jQuery.fn.filterByText = function(textbox, selectSingleMatch) {
|
|
return this.each(function() {
|
|
var select = this;
|
|
var options = [];
|
|
$(select).find('option').each(function() {
|
|
options.push(this);
|
|
});
|
|
$(select).data('options', options);
|
|
$(textbox).bind('change keyup', function() {
|
|
var options = $(select).empty().scrollTop(0).data('options');
|
|
var search = $(this).val().trim().toUpperCase();
|
|
|
|
$.each(options, function(i) {
|
|
var option = options[i];
|
|
if ($(option).text().toUpperCase().indexOf(search) > -1) {
|
|
$(select).append(option);
|
|
}
|
|
});
|
|
if (selectSingleMatch === true &&
|
|
$(select).children().length === 1) {
|
|
$(select).children().get(0).selected = true;
|
|
$(select).children().trigger("change");
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
// Add a clickable sign after a select element that when clicked
|
|
// toggles the multiple attribute on the select box
|
|
$(function () {
|
|
$(document).on('click', '.multi_select_toggle', function(e) {
|
|
e.stopPropagation();
|
|
var $label = $(this);
|
|
var sel_id = $label.attr("fcltfor");
|
|
var $selector = $('select#' + sel_id);
|
|
if (($selector.length && $selector[0].className.match(/^ui-/)) || $label.hasClass("disabled"))
|
|
return; // Afblijven van jQuery calendar jaar listboxje
|
|
var old = $selector.attr('multiple');
|
|
$selector.attr('multiple', !old);
|
|
$selector.attr('size', old ? 1 : Math.min($selector.find("option").length, 8));
|
|
$label.html(old ? I("fa-ellipsis") : I("fa-angle-up"));
|
|
FcltMgr.resized();
|
|
});
|
|
|
|
$.fn.extend({
|
|
multiSelectToggle: function(options) {
|
|
// Settings list and the default values")
|
|
var defaults = {
|
|
label: I("fa-ellipsis")
|
|
};
|
|
|
|
var options = $.extend(defaults, options);
|
|
return this.each(function() {
|
|
if (!$(this).prev("span.multi_select_toggle").length) {
|
|
var startedMulti = $(this).prop("multiple");
|
|
var lbl = $("<span fcltfor='" + this.id + "' class='multi_select_toggle'></span>").html(startedMulti ? I("fa-angle-up") : options.label);
|
|
var div = $('<div></div>').addClass(options.advanced ? "advmultitoggle" : "multitoggle");
|
|
$(this).wrap(div);
|
|
$(this).before(lbl);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
$(function() {
|
|
$('select[fcltmulti=1]').multiSelectToggle();
|
|
});
|
|
});
|
|
|
|
// Usage: "Wilt U melding {0} afmelden?".format(12345)
|
|
String.prototype.format = function() {
|
|
var formatted = this;
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
if (typeof arguments[i] == "string")
|
|
arguments[i] = arguments[i].replace(/\$/g, '$$$$');
|
|
var regexp = new RegExp('\\{' + i + '\\}', 'gi');
|
|
formatted = formatted.replace(regexp, arguments[i]);
|
|
}
|
|
return formatted;
|
|
};
|
|
|
|
var safe =
|
|
{
|
|
_htmlDecode: function(txt)
|
|
{
|
|
return $("<textarea></textarea>").html(txt).text();
|
|
},
|
|
_htmlEncode: function(txt)
|
|
{
|
|
return $("<textarea></textarea>").text(txt).html();
|
|
},
|
|
html: function(txt)
|
|
{
|
|
return safe._htmlEncode(txt).replace(/\n/g, "<br>");
|
|
},
|
|
htmlattr: function(txt)
|
|
{
|
|
return safe._htmlEncode(txt).replace(/\'/g, "'").replace(/\"/g, """);
|
|
}
|
|
}
|
|
|
|
// Returns an icon to be displayed. icon can be an image (e.g. "notfound.png") or a fontawesome icon-name (e.g. "fa-user")
|
|
// trailing fa-qualifiers (e.g. "fa-2x fa-rotate-180" are preserved
|
|
// The optional params provides some options
|
|
// params.fastyle: overrule global fastyle
|
|
function I(icon, params) {
|
|
var params = params || {};
|
|
|
|
if (icon.match(/^fa-/)) {
|
|
// a fontawesome icon
|
|
|
|
// Temporary fallback for fontawesome4 icons, where the -o suffix was used for open variant
|
|
// to be removed if all these are gone
|
|
// could also (better) be done in ICONS[]
|
|
//if (icon.indexOf("-o") > -1) {
|
|
// icon = icon.replace("-o", ""); // sincs 'icon' can have additional qualifiers, the -o can reside in the middle as well
|
|
//}
|
|
|
|
var lstyle = params.fastyle == null ? S_fontawesomestyle : params.fastyle;
|
|
//var lstyle = params.fastyle;
|
|
return "<i class='{0} fa-fw {1}'></i>".format(lstyle, safe.htmlattr(icon));
|
|
} else {
|
|
// an image
|
|
return "<img alt='" + L("mgt_kenmerk_icon") + "' src='{0}/appl/pictures/{1}'>".format(rooturl, safe.htmlattr(icon));
|
|
}
|
|
}
|
|
|
|
async function do_fcltfilters() { // Filters van opgeslagen tabjes verwerken
|
|
if (window.fcltfilters && window.iface && window.fcltfilters != "null") {
|
|
var formObject = JSON.parse(window.fcltfilters);
|
|
if ("columns" in formObject) // Dit is het hidden veld wat normaal meegesubmit wordt
|
|
{ // Werk de interface checkboxjes ook bij
|
|
var colarr = formObject.columns.split(",");
|
|
var grparr = formObject.groupby.split(",");
|
|
$("#scfcolpicker tr").each(function() {
|
|
// Ignore disabled checkboxes ('Altijd')
|
|
if (!this.id || !$(this).find("input:enabled[type=checkbox]").length)
|
|
return;
|
|
|
|
var pos = $.inArray(this.id, colarr);
|
|
if (pos > -1) {
|
|
$(this).find("input[type=checkbox]").prop("checked", true);
|
|
var selectgroup = $(this).find("select");
|
|
selectgroup.val(grparr[pos]);
|
|
} else {
|
|
$(this).find("input[type=checkbox]").prop("checked", false);
|
|
}
|
|
|
|
});
|
|
// En nu de volgorde ook nog aanpassen
|
|
$("#scfcolpicker tbody tr").sort(function(a, b) {
|
|
var $a = $("#scfcolpicker tbody tr#" + a.id);
|
|
var $b = $("#scfcolpicker tbody tr#" + b.id);
|
|
var pos1 = (!$a.find("input[type=checkbox]").prop("checked") ? (1000 + $a.index()) : $.inArray(a.id, colarr));
|
|
var pos2 = (!$b.find("input[type=checkbox]").prop("checked") ? (1000 + $b.index()) : $.inArray(b.id, colarr));
|
|
return pos1 - pos2;
|
|
}).appendTo('#scfcolpicker tbody');
|
|
}
|
|
|
|
// Voeg ook de gepinde filters toe die initieel optioneel zijn
|
|
if (typeof addFilter == "function") {
|
|
$.each(formObject, function(key, val) {
|
|
|
|
// key kan iets zijn als "veldnaam" of "veldnaam.adhoc" (indien adhoc filter)
|
|
var key_split = key.split(".");
|
|
if (key_split.length > 1 && key_split[1] == "adhoc") {
|
|
$("div#search>div>select>option[value=" + key_split[0] + "]").eq(0).each(function() {
|
|
|
|
$(this).prop("selected", true);
|
|
addFilter($(this).parent("select"));
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
await iface.stringToForm(window.fcltfilters, $('form[name=u2]'));
|
|
|
|
if ("scf_pivot" in formObject) // Dit is het hidden veld wat normaal meegesubmit wordt
|
|
{
|
|
$("#chk_pivot").prop("checked", formObject.scf_pivot == 1)
|
|
}
|
|
}
|
|
if (window.advfilters && window.iface && window.advfilters != "null" && typeof myModal === "function") {
|
|
await myModal(JSON.parse(window.advfilters));
|
|
}
|
|
if ( FcltMgr._frmaccessible(parent) && FcltMgr._safe_parent() !== window
|
|
&& typeof parent.widgetPageLoading === "object" && parent.widgetPageLoading instanceof parent.Promise) {
|
|
await parent.widgetPageLoading;
|
|
}
|
|
window.afterfiltersready?.call();
|
|
}
|
|
|
|
var is_ie_or_edge = (document.documentMode || /Edge/.test(navigator.userAgent));
|
|
var elems = {
|
|
hiddenHeight: function() { return (this.$floatingHeaderRow.hasClass("float-alone") ? 0 : (this.$originalHeaderRow.length ? this.fcltHeight + this.histHeight : 0)); },
|
|
headFootRowHeight: function() { return (this.$originalHeaderRow.length ? this.$floatingHeaderRow.height() + this.footHeight : this.fcltHeight + this.histHeight); }
|
|
};
|
|
|
|
function preserveHeaderWidth() {
|
|
if (elems.$originalHeaderRow.length) { // for tables
|
|
// Copy cell widths from floating header cells
|
|
elems.$originalHeaderRowChildren = elems.$originalHeaderRow.find("TH, TD");
|
|
elems.$floatingHeaderRowChildren = elems.$floatingHeaderRow.find("TH, TD");
|
|
elems.$originalHeaderRowChildren.each(function(index) {
|
|
$(this).css("width", elems.$floatingHeaderRowChildren.eq(index).css("width"));
|
|
});
|
|
}
|
|
}
|
|
|
|
function masonry_float_test(isFloating)
|
|
{
|
|
// Bootstrap zet de frameheader bovenaan de pagina,
|
|
// Masonry stamp't de frameheader waardoor content eronder pas wordt gerenderd
|
|
// Als de frameheader begint met floaten moet Masonry de filler-div stampen
|
|
// zodat de content vervolgens daaronder gerender kan worden.
|
|
// Dit vergt ook een her-positionering van de filler/frameheader div binnen de bootstrap flexbox (mbv de order-first class)
|
|
//
|
|
// Deze functie wordt alleen aangeroepen als de floating-state van de frameheader (floating of niet-floating) veranderd.
|
|
if (typeof isFloating === "undefined" || typeof elems === "undefined")
|
|
return;
|
|
|
|
if (elems.$fcltFrameHeader.closest(".row.content-wrapper").length) { // Deze pagina gebruikt Bootstrap
|
|
elems.$fcltFiller.toggleClass("order-first", isFloating);
|
|
elems.$fcltFrameHeader.toggleClass("order-first", !isFloating);
|
|
if (window.$masonry?.data("masonry")?._isLayoutInited === true) { // Masonry is klaar
|
|
window.$masonry.masonry("unstamp", "*");
|
|
window.$masonry.masonry("stamp", (isFloating ? elems.$fcltFiller : elems.$fcltFrameHeader));
|
|
}
|
|
}
|
|
}
|
|
|
|
var timeout_timer = false;
|
|
|
|
function updateTableHeaders(event) {
|
|
function startTableHeaders(event) {
|
|
slowUpdateTableHeaders(event);
|
|
}
|
|
if (is_ie_or_edge) {
|
|
if (timeout_timer) { clearTimeout(timeout_timer); }
|
|
timeout_timer = setTimeout(startTableHeaders, 50);
|
|
} else {
|
|
startTableHeaders(event);
|
|
}
|
|
}
|
|
|
|
function slowUpdateTableHeaders(event) {
|
|
$("div.divrstable, div.subframe").not(".no-floating").each(function() {
|
|
if (is_ie_or_edge && $(".tableFloatingHeader", this).hasClass("float-alone")) { // Prevent floating headers and footer in case of Internet Explorer: legacy 2018.1
|
|
var $originalHeaderRow = $(".tableFloatingHeaderOriginal", this);
|
|
var $floatingHeaderRow = $(".tableFloatingHeader", this);
|
|
|
|
// Copy cell widths from original header cells
|
|
var $originalHeaderRowChildren = $originalHeaderRow.find("TH, TD");
|
|
|
|
$floatingHeaderRow.find("TH, TD").each(function(index) {
|
|
var cellWidth = $originalHeaderRowChildren.eq(index).css("width");
|
|
$(this).css("width", cellWidth);
|
|
});
|
|
|
|
var insideFrame = $(this).offset();
|
|
var outsideFrame = $(window.frameElement).offset();
|
|
|
|
var scrollTop = $(window.parent).scrollTop() + $(window).scrollTop();
|
|
|
|
var offset = 0;
|
|
if (insideFrame) offset += insideFrame.top;
|
|
if (outsideFrame && $(window.parent).scrollTop() != 0) offset += outsideFrame.top;
|
|
|
|
if ((scrollTop - offset > 1) && (scrollTop < offset + $(this).height())) {
|
|
$originalHeaderRow.css("visibility", "visible");
|
|
$floatingHeaderRow.css("top", (Math.min(scrollTop - offset, $(this).height() - $floatingHeaderRow.height())) + "px");
|
|
|
|
} else {
|
|
$originalHeaderRow.css("visibility", "hidden");
|
|
$floatingHeaderRow.css("top", "0px");
|
|
}
|
|
} else {
|
|
|
|
elems.$originalHeaderRow = $(".tableFloatingHeaderOriginal", this);
|
|
elems.$floatingHeaderRow = $(".tableFloatingHeader", this);
|
|
elems.$floatingFooterTd = $("td.rsfooter", this);
|
|
elems.$fixedFooterRow = $("tr.tr-footer-filler", this);
|
|
elems.$fixedFooterTd = $("td.td-footer-filler", this);
|
|
elems.isFloating;
|
|
|
|
function initElems() {
|
|
elems.$fcltFrameHeader = (elems.$fcltFrameHeader && elems.$fcltFrameHeader.length ? elems.$fcltFrameHeader : $("div.fcltframeheader"));
|
|
elems.$fcltFiller = (elems.$fcltFiller && elems.$fcltFiller.length ? elems.$fcltFiller : $("div.fcltfiller"));
|
|
elems.$maxHistoryB = (elems.$maxHistoryB && elems.$maxHistoryB.length ? elems.$maxHistoryB : $("#rsMaxHistoryB"));
|
|
elems.fcltHeight = (elems.$fcltFrameHeader.length ? parseFloat(getComputedStyle(elems.$fcltFrameHeader.get(0)).height) || 0 : 0);
|
|
elems.histHeight = (elems.$maxHistoryB.length ? parseFloat(getComputedStyle(elems.$maxHistoryB.get(0)).height) || 0 : 0);
|
|
elems.footHeight = (elems.$floatingFooterTd.length ? parseFloat(getComputedStyle(elems.$floatingFooterTd.get(0)).height) || 0 : 0);
|
|
elems.footColspan = (elems.footColspan || elems.$floatingFooterTd.css("colspan"));
|
|
}
|
|
initElems();
|
|
|
|
// set column widths at trigger definition or when a resize occurs
|
|
if (!event || event.type == "resize" || event.type == "scroll") {
|
|
elems.windowHeight = window.parent.innerHeight;
|
|
if (elems.$fcltFiller.length && elems.$fcltFiller.width() > 0) { // set the frameheader width to the original header width
|
|
elems.$fcltFrameHeader.get(0).style.width = elems.$fcltFiller.get(0).style.width;
|
|
} else if (elems.$fcltFrameHeader.is(":visible")) { // else set the frameheader width to 100%
|
|
elems.$fcltFrameHeader.get(0).style.width = "100%";
|
|
}
|
|
elems.horScrollbarHeight = 0;
|
|
if (elems.$fcltFrameHeader.outerWidth() > window.parent.innerWidth) {
|
|
elems.horScrollbarHeight = 15;
|
|
}
|
|
if (elems.$originalHeaderRow.width() > 250) {
|
|
elems.$maxHistoryB.css("width", elems.$originalHeaderRow.css("width")); // preserve the tablefooter width for use when the footer is positioned "absolute"
|
|
}
|
|
elems.$floatingFooterTd.css("width", elems.$originalHeaderRow.css("width"));
|
|
if (elems.$originalHeaderRow.length) {
|
|
if (!event || event.type == "resize") { // for tables
|
|
// Copy cell widths from original header cells
|
|
elems.$originalHeaderRowChildren = elems.$originalHeaderRow.find("TH, TD");
|
|
elems.$floatingHeaderRowChildren = elems.$floatingHeaderRow.find("TH, TD");
|
|
elems.thisHeight = $(this).height();
|
|
|
|
elems.$floatingHeaderRow.css("width", elems.$originalHeaderRow.css("width")); // set the floatingheader width to the original header width
|
|
elems.$floatingHeaderRowChildren.each(function(index) {
|
|
$(this).css("width", elems.$originalHeaderRowChildren.eq(index).css("width"));
|
|
});
|
|
}
|
|
} else { // for show and edit windows
|
|
if (elems.thisHeight && elems.thisHeight < $(this).closest("html").height()) {
|
|
FcltMgr.resized(window, { noMasonry: true });
|
|
}
|
|
elems.thisHeight = $(this).closest("html").height();
|
|
}
|
|
}
|
|
|
|
var isFrameheader = false;
|
|
if (elems.$originalHeaderRow.length) { // for tables
|
|
var insideFrame = $(this);
|
|
} else { // for show and edit windows
|
|
isFrameheader = true;
|
|
var insideFrame = $(this).closest("html");
|
|
}
|
|
|
|
var outsideFrame = $(window.frameElement);
|
|
var scrollTop = $(window).scrollTop();
|
|
var offset = 0;
|
|
if (insideFrame.length) offset += insideFrame.offset().top;
|
|
if ($(window.frameElement).length && $(window.parent).scrollTop() != 0 && !$(window.frameElement).hasClass("frametab"))
|
|
{
|
|
offset += outsideFrame.offset().top;
|
|
scrollTop += $(window.parent).scrollTop();
|
|
}
|
|
// calculate floating elements at top of screen
|
|
initElems();
|
|
if ((scrollTop + elems.hiddenHeight() - offset > 1) && (scrollTop - elems.hiddenHeight() < (offset + elems.thisHeight))) {
|
|
if (isFrameheader && !elems.isFloating)
|
|
{ // Switch from non-floating to floating
|
|
elems.isFloating = true;
|
|
masonry_float_test(elems.isFloating);
|
|
}
|
|
elems.$fcltFiller.posTop = (elems.$fcltFiller.length > 0 ? (elems.$fcltFiller.closest(".subframe").length ? 0 : elems.$fcltFiller.position().top) : 0);
|
|
if (elems.$fcltFrameHeader.length) // frameheader present?
|
|
elems.$fcltFrameHeader.css({
|
|
position: "absolute",
|
|
zIndex: "99",
|
|
overflowY: "visible",
|
|
top: (Math.min(scrollTop - offset + elems.hiddenHeight() + elems.$fcltFiller.posTop, elems.thisHeight - elems.headFootRowHeight())) + "px"
|
|
});
|
|
if (elems.$maxHistoryB.length) // maxrecords message present?
|
|
elems.$maxHistoryB.css({
|
|
position: "absolute",
|
|
zIndex: "98",
|
|
top: (Math.min(scrollTop - offset + elems.hiddenHeight() + elems.fcltHeight + elems.$fcltFiller.posTop, elems.thisHeight - elems.headFootRowHeight() + elems.fcltHeight)) + "px"
|
|
});
|
|
elems.$originalHeaderRow.css("visibility", "visible");
|
|
elems.$floatingHeaderRow.css("top", (Math.min(scrollTop - offset + elems.hiddenHeight(), elems.thisHeight - elems.headFootRowHeight())) + "px");
|
|
// Door deze regel trilt de floating header soms (FCLT#58242) omdat hij elke keer 1px hoger en dan weer lager wordt ge-set
|
|
if (elems.$fcltFiller.length !== 0 && elems.fcltHeight + elems.histHeight != elems.$fcltFiller.height()) {
|
|
elems.$fcltFiller.get(0).style.height = (elems.fcltHeight + elems.histHeight).toFixed(0) + "px";
|
|
}
|
|
} else {
|
|
if (isFrameheader && elems.isFloating)
|
|
{ // Switch from floating to non-floating
|
|
elems.isFloating = false;
|
|
masonry_float_test(elems.isFloating);
|
|
}
|
|
elems.$fcltFrameHeader.css({ position: "", zIndex: "", overflowY: "", top: "" });
|
|
elems.$maxHistoryB.css({ position: "", zIndex: "", top: "" });
|
|
elems.$originalHeaderRow.css("visibility", "hidden");
|
|
elems.$floatingHeaderRow.css("top", "0px");
|
|
elems.$fcltFiller.css({ height: 0 });
|
|
}
|
|
// calculate floating element at bottom of screen (if any)
|
|
if (elems.$fixedFooterRow.length) {
|
|
if ($(window.frameElement).length && (($(window.parent).scrollTop() != 0 && $(window.frameElement).hasClass("frametab")) || // if embedded
|
|
$(window.parent).scrollTop() == 0))
|
|
offset += outsideFrame.offset().top;
|
|
if ((scrollTop - offset + elems.windowHeight) <= (elems.$fixedFooterRow.offset().top)) {
|
|
elems.$floatingFooterTd.css({
|
|
position: "absolute",
|
|
zIndex: "99",
|
|
overflowY: "visible",
|
|
collspan: elems.footColspan,
|
|
top: (scrollTop - offset + elems.windowHeight - elems.footHeight - elems.horScrollbarHeight - 1) + "px"
|
|
});
|
|
elems.$fixedFooterTd.height(elems.footHeight);
|
|
$("input.multiselect:first", this).change();
|
|
} else {
|
|
elems.$floatingFooterTd.css({ position: "", zIndex: "", overflowY: "", top: "" });
|
|
elems.$fixedFooterTd.css({ display: "none" });
|
|
}
|
|
}
|
|
}
|
|
});
|
|
timeout_timer = false;
|
|
}
|
|
|
|
$(async function() {
|
|
if ($("table:not(.tab_cat_sched):not(#rscolumnstable) > thead").length > $("div.fcltframeheader").length) { $("table:not(.tab_cat_sched):not(#rscolumnstable) > thead").addClass("float-alone"); }
|
|
$("table:not(.tab_cat_sched) > thead").each(function() {
|
|
var nofloatingclass = ($(this).parent("table").hasClass("no-floating") ? " no-floating" : "");
|
|
$(this).parent("table").wrap("<div class=\"divrstable" + nofloatingclass + "\" style=\"position:relative\"></div>");
|
|
if ($(this).closest("body").hasClass("fclt-modal")) {
|
|
FcltMgr.resized();
|
|
}
|
|
if (nofloatingclass == "")
|
|
{
|
|
var $originalHeaderRow = $(this);
|
|
var $clonedHeaderRow = $originalHeaderRow.clone(true)
|
|
$originalHeaderRow.before($clonedHeaderRow);
|
|
$clonedHeaderRow.addClass("tableFloatingHeader");
|
|
$clonedHeaderRow.css("position", "absolute");
|
|
$clonedHeaderRow.css("top", "0px");
|
|
$clonedHeaderRow.css("left", "0px");
|
|
$clonedHeaderRow.css("opacity", "0.90");
|
|
|
|
$originalHeaderRow.addClass("tableFloatingHeaderOriginal");
|
|
}
|
|
});
|
|
var $elem = $("div.fcltframeheader");
|
|
$elem.css("width", "100%");
|
|
$elem.before("<div class='fcltfiller' style='height: 0; visibility: hidden; width: 100%;'></div>");
|
|
$elem = $("td.rsfooter", this);
|
|
$elem.parent().before("<tr class='tr-footer-filler'><td class='td-footer-filler' colspan='" + $elem.attr("colspan") + "' style='height: 0; background-color: " + $elem.css("backgroundColor") + ";'></td></tr>");
|
|
|
|
$("input.multiselect, input.master", this).on("change", function(e) {
|
|
elems.$floatingFooterTd.css({ zIndex: -1 });
|
|
if ($(e.target).hasClass("master")) {
|
|
if ($(this).prop('checked')) {
|
|
elems.$floatingFooterTd.css({ zIndex: "" });
|
|
}
|
|
} else if ($("input.multiselect:checked").length) {
|
|
elems.$floatingFooterTd.css({ zIndex: "" });
|
|
}
|
|
});
|
|
|
|
$(window).on("scroll", updateTableHeaders);
|
|
$(window).on("resize", updateTableHeaders);
|
|
$(window).on("load", updateTableHeaders);
|
|
try {
|
|
if (window.parent.name != "fclttop") {
|
|
$(window.parent).on("scroll", updateTableHeaders);
|
|
$(window.parent).on("resize", updateTableHeaders);
|
|
}
|
|
} catch (e) {
|
|
// in een frame?
|
|
}
|
|
$("div.collapsed").on("click", updateTableHeaders);
|
|
// Anders geheugenlek
|
|
$(window).on("unload", function() {
|
|
$(window).off("scroll", updateTableHeaders);
|
|
$(window).off("resize", updateTableHeaders);
|
|
$(window).off("load", updateTableHeaders);
|
|
$("div.collapsed").off("click", updateTableHeaders);
|
|
try {
|
|
if (window.parent.name != "fclttop") {
|
|
$(window.parent).off("scroll", updateTableHeaders);
|
|
$(window.parent).off("resize", updateTableHeaders);
|
|
}
|
|
} catch (e) {
|
|
// in een frame?
|
|
}
|
|
});
|
|
|
|
// Dit lijkt misschien een beetje getruukt, maar is een prima manier om code uit te voeren na de eerste 'jQuery.Ready' (anders zijn de suggests nog niet defined)
|
|
$(async _ => {
|
|
var allSuggests = [];
|
|
if (typeof Suggest === "function") {
|
|
for (var key in window) {
|
|
if ((key, ["InstallTrigger","onmozfullscreenchange","onmozfullscreenerror", "fullScreen"].includes(key)))
|
|
{
|
|
continue; // die geven anders op Firefox een verwarrende warning in de console als je window[key] opvraagt
|
|
}
|
|
if (window[key] instanceof Suggest) { // Er kunnen meerdere afhankelijke suggest velden zijn.
|
|
allSuggests.push(window[key].sgReadyState);
|
|
}
|
|
}
|
|
}
|
|
await Promise.all(allSuggests);
|
|
do_fcltfilters();
|
|
});
|
|
|
|
setTimeout(updateTableHeaders, 250); // First-time refresh after tabelheaders are set
|
|
}); |