Files
resourcespace/js/global.js
2025-07-18 16:20:14 +07:00

2245 lines
68 KiB
JavaScript
Executable File

/* global.js : Functions to support features available globally throughout ResourceSpace */
var modalalign=false;
var modalfit=false;
var CentralSpaceLoading=false;
var ajaxinprogress=false;
const currentPageUrlParams = new URLSearchParams(window.location.search);
const external_access_key = currentPageUrlParams.get('k');
// prevent all caching of ajax requests by stupid browsers like IE
jQuery.ajaxSetup({ cache: false });
// function to help determine exceptions
function basename(path) {
return path.replace(/\\/g,'/').replace( /.*\//, '' );
}
// IE 8 does not support console.log unless developer dialog is open, so we need a failsafe here if we're going to use it for debugging
var alertFallback = false;
if (typeof console === "undefined" || typeof console.log === "undefined") {
console = {};
if (alertFallback) {
console.log = function(msg) {
alert(msg);
};
} else {
console.log = function() {};
}
}
function is_touch_device()
{
return 'ontouchstart' in window // works on most browsers
|| (navigator.maxTouchPoints > 0)
|| (navigator.msMaxTouchPoints > 0);
}
function SetCookie (cookieName,cookieValue,nDays)
{
var today = new Date();
var expire = new Date();
if (nDays==null || nDays==0) nDays=1;
expire.setTime(today.getTime() + 3600000*24*nDays);
path = ";path=" + baseurl_short;
// Expire first the old cookie which might not be on the desired path (path used to be set to empty string which made
// certain cookies be set at /pages) causing issues further down the process (e.g collection bar "thumbs" cookie which
// was causing a "Exceeding call stack" error)
var expired_date = new Date();
expired_date.setTime(today.getTime() + 3600000 * 24 * -1);
if (window.location.protocol === "https:")
{
document.cookie = cookieName + "=" + encodeURI(cookieValue) + ";expires=" + expired_date.toGMTString()+";secure";
}
else
{
document.cookie = cookieName + "=" + encodeURI(cookieValue) + ";expires=" + expired_date.toGMTString();
}
// Set cookie
if (window.location.protocol === "https:")
{
document.cookie = cookieName+"="+encodeURI(cookieValue)+";expires="+expire.toGMTString()+path+";secure";
}
else
{
document.cookie = cookieName+"="+encodeURI(cookieValue)+";expires="+expire.toGMTString()+path;
}
}
function getCookie(c_name)
{
var i,x,y,ARRcookies=document.cookie.split("; ");
for (i=0;i<ARRcookies.length;i++)
{
x=ARRcookies[i].substr(0,ARRcookies[i].indexOf("="));
y=ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1);
x=x.replace(/^\s+|\s+$/g,"");
if (x==c_name)
{
return decodeURI(y);
}
}
}
/* Keep a global array of timers */
var timers = new Array();
var loadingtimers = new Array();
function ClearTimers()
{
// Remove all existing page timers.
for (var i = 0; i < timers.length; i++)
{
clearTimeout(timers[i]);
}
}
function ClearLoadingTimers()
{
// Remove all existing page timers.
for (var i = 0; i < loadingtimers.length; i++)
{
clearTimeout(loadingtimers[i]);
}
}
/* AJAX loading of searchbar contents for search executed outside of searchbar */
function ReloadSearchBar()
{
var SearchBar = jQuery('#SearchBarContainer');
SearchBar.load(baseurl_short + "pages/ajax/reload_searchbar.php?ajax=true&pagename=" + pagename, function (response, status, xhr) {
if (status=="error") {
SearchBar.html(errorpageload + xhr.status + " " + xhr.statusText + "<br/>" + response);
} else if (typeof chosen_config !== 'undefined' && chosen_config['#SearchBox select']!=='undefined') {
// Load completed, add Chosen dropdowns if configured
jQuery('#SearchBox select').each(function() {
ChosenDropdownInit(this, '#SearchBox select');
});
}
});
return false;
}
/* Scroll to top if parameter set - used when changing pages */
function pageScrolltop(element)
{
jQuery(element).animate({scrollTop:0}, 'fast');
}
/* AJAX loading of central space contents given a link */
function CentralSpaceLoad (anchor,scrolltop,modal,keep_fragment = true)
{
ajaxinprogress=true;
var CentralSpace=jQuery('#CentralSpace');
if (typeof modal=='undefined') {modal=false;}
// Prohibit edit page in modal when already in centralspace because this combination does not work
if (basename(window.location.href).substr(0,8)=="edit.php" && typeof anchor.href!='undefined' && basename(anchor.href).substr(0,8)=="edit.php")
{
modal = false;
}
if (!modal)
{
// If what we're loading isn't a modal, close any modal. Ensures the modal closes if a link on it is clicked.
ModalClose();
}
else
{
// Targeting a modal. Set the target to be the modal, not CentralSpace.
CentralSpace=jQuery('#modal');
}
// Handle straight urls:
if (typeof(anchor)!=='object'){
var plainurl=anchor;
var anchor = document.createElement('a');
anchor.href=plainurl;
}
// Remove fragments when necessary
if(anchor.href.includes('#') && keep_fragment == false)
{
anchor.href = anchor.href.substr(0, anchor.href.lastIndexOf('#'));
}
/* Open as standard link in new tab (no AJAX) if URL is external */
if (anchor.hostname != "" && window.location.hostname != anchor.hostname)
{
var win=window.open(anchor.href,'_blank');win.focus();
return false;
}
/* Handle link normally (no AJAX) if the CentralSpace element does not exist */
if (!CentralSpace )
{
location.href=anchor.href;
return false;
}
/* more exceptions, going to or from pages without header */
var fromnoheader=false;
var tonoheader=false;
if (
basename(window.location.href).substr(0,11)=="preview.php"
||
basename(window.location.href).substr(0,9)=="index.php"
||
basename(window.location.href).substr(0,16)=="team_plugins.php"
||
basename(window.location.href).substr(0,19)=="search_advanced.php"
) {
fromnoheader=true;
}
if (
basename(anchor.href).substr(0,11)=="preview.php"
||
basename(anchor.href).substr(0,9)=="index.php"
||
basename(anchor.href).substr(0,19)=="search_advanced.php"
) {
tonoheader=true;
}
if (typeof fromnoheaderadd!=='undefined' && !modal)
{
for (var i = 0; i < fromnoheaderadd.length; i++)
{
if (basename(window.location.href).substr(0,fromnoheaderadd[i].charindex)==fromnoheaderadd[i].page) fromnoheader=true;
}
}
if (typeof tonoheaderadd!=='undefined' && !modal)
{
for (var i = 0; i < tonoheaderadd.length; i++)
{
if (basename(anchor.href).substr(0,tonoheaderadd[i].charindex)==tonoheaderadd[i].page) tonoheader=true;
}
}
// XOR to allow these pages to ajax with themselves
if((tonoheader || fromnoheader) && !(fromnoheader && tonoheader) && !modal)
{
location.href = anchor.href;
CentralSpace.trigger('CentralSpaceLoadedNoHeader', [{url: anchor.href}]);
return false;
}
var url = anchor.href;
pagename=basename(url);
pagename=pagename.substr(0, pagename.lastIndexOf('.'));
var end_url = url.substr(url.indexOf('#'));
// Drop # at end of url if present
if (end_url.substr(0,1) == "#") {
url = url.substr(0,url.length - end_url.length);
}
// Attach ajax parameter
if (url.indexOf("?")!=-1)
{
url += '&ajax=true';
}
else
{
url += '?ajax=true';
}
// Reinstate # at end of url if present
if (end_url.substr(0,1) == "#") {
url += end_url;
}
if (modal) {url+="&modal=true";}
// Fade out the link temporarily while loading. Helps to give the user feedback that their click is having an effect.
// if (!modal) {jQuery(anchor).fadeTo(0,0.6);}
// Start the timer for the loading box.
CentralSpaceShowProcessing();
CentralSpace.load(url, function (response, status, xhr)
{
if(xhr.status == 403)
{
CentralSpaceHideProcessing();
styledalert(errortext,xhr.responseText);
}
else if (xhr.status == 205)
{
console.debug("Full page reload required " + response);
CentralSpaceHideProcessing();
window.location.href = anchor.href;
}
else if (status=="error")
{
CentralSpaceHideProcessing();
CentralSpace.html(errorpageload + xhr.status + " " + xhr.statusText + "<br/>" + response);
jQuery(anchor).fadeTo(0,1);
}
else
{
// Load completed
CentralSpaceHideProcessing();
if (modal)
{
// Show the modal
ModalCentre();
jQuery('#modal_overlay').fadeIn('fast');
if (modalalign=='right')
{
// Right aligned "drop down" style modal used for My Account.
jQuery('#modal').slideDown('fast');
}
else
{
jQuery('#modal').show();
}
jQuery('#modal').focus();
}
// Activate or deactivate the large slideshow, if this function is enabled.
if (typeof ActivateSlideshow == 'function' && !modal)
{
if (basename(anchor.href).substr(0,8)=="home.php")
{
ActivateSlideshow();
}
else
{
DeactivateSlideshow();
}
}
// Only allow reordering when search results are collections
if(basename(anchor.href).substr(0, 10) == 'search.php')
{
var query_strings = get_query_strings(anchor.href);
if(!is_empty(query_strings))
{
if(query_strings.hasOwnProperty('search') && query_strings.search.substring(0, 11) !== '!collection')
{
allow_reorder = false;
}
}
CentralSpace.trigger('CentralSpaceSortable');
}
CentralSpace.trigger('CentralSpaceLoaded', [{url: url}]);
// Change the browser URL and save the CentralSpace HTML state in the browser's history record.
if(typeof(top.history.pushState)=='function' && !modal)
{
top.history.pushState(document.title+'&&&'+CentralSpace.html(), applicationname, anchor.href);
}
}
/* Scroll to top if parameter set - used when changing pages */
if (scrolltop==true)
{
if (modal)
{
pageScrolltop(scrolltopElementModal);
}
else if (jQuery(window).width() <= 1100)
{
pageScrolltop(scrolltopElementContainer);
}
else
{
pageScrolltop(scrolltopElementCentral);
}
}
// Add accessibility enhancement:
CentralSpace.append('<!-- Use aria-live assertive for high priority changes in the content: -->');
CentralSpace.append('<span role="status" aria-live="assertive" class="ui-helper-hidden-accessible"></span>');
// Add global trash bin:
CentralSpace.append(global_trash_html);
CentralSpace.trigger('prepareDragDrop');
// Add Chosen dropdowns, if configured
if (typeof chosen_config !== 'undefined' && chosen_config['#CentralSpace select']!=='undefined')
{
jQuery('#CentralSpace select').each(function()
{
ChosenDropdownInit(this, '#CentralSpace select');
});
}
if (typeof AdditionalJs == 'function') {
AdditionalJs();
}
ReloadLinks();
});
jQuery('#UICenter').show(0);
jQuery("#SearchBarContainer").removeClass("FullSearch");
ajaxinprogress=false;
return false;
}
/* When back button is clicked, reload AJAX content stored in browser history record */
top.window.onpopstate = function(event)
{
if (!event.state) {return true;} // No state
page=window.history.state;
mytitle=page.substr(0, page.indexOf('&&&'));
if (mytitle.substr(-1,1)!="'" && mytitle.length!=0) {
page=page.substr(mytitle.length+3);
document.title=mytitle;
// Calculate the name of the page the user is navigating to.
pagename=basename(document.URL);
pagename=pagename.substr(0, pagename.lastIndexOf('.'));
if (pagename=="home" || pagename=="view" || pagename=="preview" || pagename=="search" || pagename=="collection_manage"){
// Certain pages do not handle back navigation well as start-up scripts are not executed. Always reload in these cases.
// The ultimate fix is for this all to be unnecessary due
// to scripts initialising correctly, perhaps using an event or similar,
// or for all initialisation to have already completed in the header.php load ~DH
CentralSpaceShowProcessing();
window.location.reload(true);
return true;
}
ModalClose();
jQuery('#CentralSpace').html(page);
}
}
/* AJAX posting of a form, result are displayed in the CentralSpace area. */
function CentralSpacePost (form, scrolltop, modal, update_history, container_id)
{
update_history = (typeof update_history !== "undefined" ? update_history : true);
ajaxinprogress=true;
var url=form.action;
let CentralSpaceCtID = typeof container_id !== "undefined" ? container_id : 'CentralSpace';
var CentralSpace=jQuery('#' + CentralSpaceCtID);// for ajax targeting top div
formdata = jQuery(form).serialize();
if (typeof modal=='undefined') {modal=false;}
if (!modal)
{
// If what we're loading isn't a modal, close any modal. Ensures the modal closes if a link on it is clicked.
ModalClose();
}
else
{
// Targeting a modal. Set the target to be the modal, not CentralSpace.
CentralSpace=jQuery('#modal');
}
var end_url = url.substr(url.indexOf('#'));
// Drop # at end of url if present
if (end_url.substr(0,1) == "#") {
url = url.substr(0,url.length - end_url.length);
}
// Attach ajax parameter
if (url.indexOf("?")!=-1)
{
url += '&ajax=true';
}
else
{
url += '?ajax=true';
}
url += '&posting=true';
// Reinstate # at end of url if present
if (end_url.substr(0,1) == "#") {
url += end_url;
}
CentralSpaceShowProcessing();
pagename=basename(url);
pagename=pagename.substr(0, pagename.lastIndexOf('.'));
jQuery.post(url,formdata,function(data)
{
// Check for a redirect (use instead of HTTP redirect to avoid URL not updating)
if(isJson(data))
{
jsonresponse = JSON.parse(data);
if(typeof jsonresponse.redirecturl !== "undefined")
{
CentralSpaceLoad(jsonresponse.redirecturl);
}
return;
}
CentralSpaceHideProcessing();
CentralSpace.html(data);
jQuery('#UICenter').show(0);
jQuery("#SearchBarContainer").removeClass("FullSearch");
// Add global trash bin:
CentralSpace.append(global_trash_html);
CentralSpace.trigger('prepareDragDrop');
// Activate or deactivate the large slideshow, if this function is enabled.
if (typeof ActivateSlideshow == 'function' && !modal)
{
if (basename(form.action).substr(0,8)=="home.php")
{
ActivateSlideshow();
}
else
{
DeactivateSlideshow();
}
}
// Change the browser URL and save the CentralSpace HTML state in the browser's history record.
if(update_history && typeof(top.history.pushState)=='function' && !modal)
{
top.history.pushState(document.title+'&&&'+data, applicationname, form.action);
}
/* Scroll to top if parameter set - used when changing pages */
if (scrolltop==true && (typeof preventautoscroll == 'undefined' || !preventautoscroll)) {
if (modal)
{
pageScrolltop(scrolltopElementModal);
}
else
{
pageScrolltop(scrolltopElementCentral);
}
}
// Reset scroll prevention flag
preventautoscroll = false;
// Add Chosen dropdowns, if configured
if (typeof chosen_config !== 'undefined' && chosen_config['#CentralSpace select']!=='undefined')
{
jQuery('#CentralSpace select').each(function()
{
ChosenDropdownInit(this, '#CentralSpace select');
});
}
return false;
})
.fail(function(result,textStatus) {
if (result.status>0)
{
CentralSpaceHideProcessing();
if(result.status == 409)
{
// This is used for edit conflicts - show the response
CentralSpace.append(result.responseText);
}
else if(result.status == 403)
{
styledalert(errortext,result.responseText);
}
else
{
CentralSpace.html(errorpageload + result.status + ' ' + result.statusText + '<br/>URL: ' + url + '<br/>POST data: ' + jQuery(form).serialize());
}
}
});
ajaxinprogress=false;
// Reload Browse bar item if required
if (typeof browsereload !== "undefined")
{
toggleBrowseElements(browsereload, true);
}
return false;
}
/* AJAX loading of CollectionDiv contents given a link */
function CollectionDivLoad (anchor,scrolltop)
{
// Handle straight urls:
if (typeof(anchor)!=='object'){
var plainurl=anchor;
var anchor = document.createElement('a');
anchor.href=plainurl;
}
/* Handle link normally if the CollectionDiv element does not exist */
if (jQuery('#CollectionDiv').length==0 && top.collections!==undefined)
{
top.collections.location.href=anchor.href;
return false;
}
/* Scroll to top if parameter set - used when changing pages */
if (scrolltop==true) {pageScrolltop(scrolltopElementCollection);}
var url = anchor.href;
if (url.indexOf("?")!=-1)
{
url += '&ajax=true';
}
else
{
url += '?ajax=true';
}
// Load the collection bar and execute a callback after the load has completed
jQuery('#CollectionDiv').load(url, function ()
{
jQuery("#CollectionDiv").trigger("CollectionDiv_loaded");
// For each resource in centralspace, assume its not in the collection, so show + icon and hide - icon
jQuery('div.ResourcePanel a.addToCollection').removeClass('DisplayNone');
jQuery('div.ResourcePanel a.removeFromCollection').addClass('DisplayNone');
// For each resource in the collection bar, set all matching items in central space to hide + icon and show - icon
collection_resources.forEach(function(value) {
var resource_in_centralspace=jQuery('#ResourceShell' + value +'.ResourcePanel');
if(resource_in_centralspace.length == 0)
{
resource_in_centralspace=jQuery('#CentralSpace [data-identifier="'+jQuery(this).attr('data-identifier')+'"]');
}
resource_in_centralspace.find('div.ResourcePanelIcons > a.addToCollection').addClass('DisplayNone');
resource_in_centralspace.find('div.ResourcePanelIcons > a.removeFromCollection').removeClass('DisplayNone');
});
});
return false;
}
function directDownload(url, element = undefined)
{
console.debug('directDownload(url = %o, element = %o)', url, element);
dlIFrma = document.getElementById('dlIFrm');
if(typeof dlIFrma != "undefined")
{
dlIFrma.src = url;
if (element != undefined){
// Lock width of download options column to prevent it resizeing due to smaller download button
let options_td = jQuery(element).parents('tbody').children('tr:first').children('td:last');
options_td.attr('style', 'width: ' + options_td.width() + 'px;');
let el = jQuery(element);
let el_txt = el.text();
el.html('<i id="download_spinner" class="fa fa-circle-o-notch fa-spin fa-3x fa-fw" style="font-size: 100%"></i>');
jQuery(dlIFrma).one('load', () => el.text(el_txt));
}
}
else
{
window.open(url, '_blank').focus();
}
}
/* AJAX loading of navigation link */
function ReloadLinks()
{
if(!linkreload)
{
return false;
}
var nav2=jQuery('#HeaderNav2');
if(!nav2.has("#HeaderLinksContainer").length)
{
return false;
}
nav2.load(baseurl_short+"pages/ajax/reload_links.php?ajax=true", function (response, status, xhr)
{
// 403 is returned when user is logged out and ajax request! @see revision #12655
if(xhr.status == 403)
{
window.location = baseurl_short + "login.php";
}
else if(status=="error")
{
var SearchBar=jQuery('#SearchBarContainer');
SearchBar.html(errorpageload + xhr.status + " " + xhr.statusText + "<br/>" + response);
}
else
{
// Load completed
ActivateHeaderLink(document.location.href);
}
});
headerLinksDropdown();
return false;
}
function relateresources(ref,related,action, ctx)
{
var add_param='1';
if (action=="remove") { add_param='0';}
var post_data = {
'ref': ref,
'related': related,
'add': add_param
};
api("update_related_resource", post_data, function(response) {
if(response!= false)
{
jQuery('#relatedresource' + related).remove();
return true;
}
else
{
alert ("ERROR");
return false;
}
}, ctx);
}
/*
When an object of class "CollapsibleSectionHead" is clicked then next element is collapsed or expanded.
*/
function registerCollapsibleSections(use_cookies)
{
// Default params
use_cookies = (typeof use_cookies !== 'undefined') ? use_cookies : true;
jQuery(document).ready(function()
{
jQuery('.CollapsibleSectionHead').click(function()
{
cur = jQuery(this).next();
cur_id = cur.attr("id");
var cur_state = null;
if (cur.is(':visible'))
{
if(use_cookies)
{
SetCookie(cur_id, 'collapsed');
}
cur_state = "collapsed";
jQuery(this).removeClass('expanded');
jQuery(this).addClass('collapsed');
}
else
{
if(use_cookies)
{
SetCookie(cur_id, 'expanded');
}
cur_state = "expanded";
jQuery(this).addClass('expanded');
jQuery(this).removeClass('collapsed');
}
cur.stop(); // Stop existing animation if any
cur.slideToggle();
jQuery("#" + cur_id).trigger("ToggleCollapsibleSection", [{section_id: cur_id, state: cur_state}]);
return false;
}).each(function()
{
cur_id = jQuery(this).next().attr("id");
if ((use_cookies && getCookie(cur_id) == 'collapsed') || jQuery(this).hasClass('collapsed'))
{
jQuery(this).next().hide();
jQuery(this).addClass('collapsed');
}
else
{
jQuery(this).addClass('expanded');
}
});
});
}
function getQueryStrings()
{
var assoc = {};
var decode = function(s) { return decodeURIComponent(s.replace(/\+/g, " ")); };
var queryString = location.search.substring(1);
var keyValues = queryString.split('&');
for(var i in keyValues) {
if (typeof keyValues[i].split == 'function'){
var key = keyValues[i].split('=');
if (key.length > 1) {
assoc[decode(key[0])] = decode(key[1]);
}
}
}
return assoc;
}
// Take the current query string and attach it to a form action
// Useful when avoiding moving away from the page you are on
function passQueryStrings(params, formID)
{
var form_action = '';
var query_string = '';
var qs = getQueryStrings();
if (params.constructor !== Array) {
return false;
}
// Pass only specified params to the query string
for (var i = 0; i < params.length; i++) {
if (qs.hasOwnProperty(params[i]) && query_string === '') {
query_string = params[i] + '=' + qs[params[i]];
} else if(qs.hasOwnProperty(params[i]) && query_string !== '') {
query_string += '&' + params[i] + '=' + qs[params[i]];
}
}
form_action = document.getElementById(formID).action + '?' + query_string;
if (document.getElementById(formID).action !== form_action) {
document.getElementById(formID).action = form_action;
}
return true;
}
// Use this function to confirm special searches
// e.g: is_special_search('!collection', 11)
function is_special_search(special_search, string_length)
{
var query_strings = getQueryStrings();
if (is_empty(query_strings)) {
return false;
}
return query_strings.search.substring(0, string_length) === special_search;
}
// Check if object is empty or not
// Note: safer solution compared to using keys()
function is_empty(object)
{
for(var property in object) {
if(object.hasOwnProperty(property)) {
return false;
}
}
return true;
}
// Returns object with all query strings found
// Note: should be used when the location does not have the correct URL
function get_query_strings(url)
{
if (url.trim() === '') {
return {};
}
var query_strings = {};
var url_split = url.split('?');
if (url_split.length === 1) {
return query_strings;
}
url_split = url_split[1];
url_split = url_split.split('&');
for (var i = 0; i < url_split.length; i++) {
var var_value_pair = url_split[i].split('=');
query_strings[var_value_pair[0]] = decodeURI(var_value_pair[1]);
}
return query_strings;
}
function ModalLoad(url,jump,fittosize,align)
{
// Load to a modal rather than CentralSpace. "url" can be an anchor object.
// No modal? Don't launch a modal. Go to CentralSpaceLoad
if (!jQuery('#modal')) {return CentralSpaceLoad(url,jump);}
// Prohibit edit page in modal when already in centralspace because this combination does not work
if (basename(window.location.href).substr(0,8)=="edit.php" && typeof url.href!='undefined' && basename(url.href).substr(0,8)=="edit.php")
{
return CentralSpaceLoad(url,jump);
}
// Window smaller than the modal? No point showing a modal as it wouldn't appear over the background.
if (jQuery(window).width()<=jQuery('#modal').width()) {return CentralSpaceLoad(url,jump);}
jQuery('#modal').draggable({ handle: ".RecordHeader", opacity: 0.7 });
// Set modalfit so that resizing does not change the size
modalfit = false;
if ((typeof fittosize != 'undefined') && fittosize) {
modalfit = true;
}
// Set modalalign to store the alignment of the current modal (needs to be global so that resizing the window still correctly aligns the modal)
modalalign = false;
if (typeof align != 'undefined') {
modalalign = align;
}
// To help with calling of a popup modal from full modal, can return to previous modal location
if (typeof modalurl != 'undefined') {
modalbackurl = modalurl;
}
url=SetContext(url);
modalurl=url;
return CentralSpaceLoad(url,jump,true);
}
function ModalPost(form,jump,fittosize)
// Post to a modal rather than CentralSpace. "url" can be an anchor object.
{
var url=form.action;
jQuery('#modal_overlay').show();
jQuery('#modal').show();
jQuery('#modal').draggable({ handle: ".RecordHeader" });
// Set modalfit so that resizing does not change the size
modalfit=false;
if ((typeof fittosize != 'undefined') && fittosize) {
modalfit = true;
}
ModalCentre();
if (typeof modalurl != 'undefined') {
modalbackurl = modalurl;
}
modalurl=url;
return CentralSpacePost(form,jump,true);
}
function ModalCentre()
{
// Centre the modal and overlay on the screen. Called automatically when opened and also when the browser is resized, but can also be called manually.
// If modalfit is not specified default to the full modal dimensions
if (modalalign == 'right') {
modalmaxheight = Math.max(jQuery(window).height() - 100);
modalwidth = 550;
if (!TileNav) {
// Smaller menu if tile navigation disabled.
modalwidth = 250;
}
} else if ((typeof modalfit != 'undefined') && modalfit) {
modalmaxheight = 'auto';
modalwidth = 'auto';
} else {
modalmaxheight = jQuery('.ui-layout-container').height() - 60;
modalwidth = 1235;
}
jQuery('#modal').css({
maxHeight: modalmaxheight,
width: modalwidth
});
// Support alignment of modal (e.g. for 'My Account')
topmargin = 30;
if (modalalign == 'right') {
left = Math.max(jQuery(window).width() - jQuery('#modal').outerWidth(), 0) - 20;
topmargin = 50;
} else {
left = Math.max(jQuery(window).width() - jQuery('#modal').outerWidth(), 0) / 2;
}
jQuery('#modal').css({
top: topmargin + jQuery(window).scrollTop(),
left: left + jQuery(window).scrollLeft()
});
// Resize knowledge base iframe to correct size if present
if (jQuery('#knowledge_base').length) {
jQuery('#knowledge_base').css('height', modalmaxheight);
jQuery('#modal').css('overflow', 'hidden');
} else {
jQuery('#modal').css('overflow', 'auto');
}
}
function ModalClose()
{
jQuery('#modal_overlay').fadeOut('fast');
jQuery('#modal').hide();
jQuery('#modal').html('');
// Trigger an event so we can detect when a modal is closed
if (typeof modalurl != 'undefined') {
jQuery('#CentralSpace').trigger('ModalClosed', [{url: modalurl}]);
delete modalurl;
}
}
// For modals, set what context the modal was called from and insert if missing
function SetContext(url)
{
if(typeof url != 'string'){url = url.href;}
var query_strings = get_query_strings(url);
var context = query_strings.context;
if(context==undefined){
if(jQuery('#modal').is(":visible")){
context='Modal';
} else {
context='Body';
}
if (url.indexOf("?")!=-1){
url = url + '&context=' + context;
} else {
url = url + '?context=' + context;
}
}
return url;
}
// Update header links to add a class that indicates current location
function ActivateHeaderLink(activeurl)
{
var matchedstring=0;
activelink='';
// Was the 'recent' link clicked?
var recentlink=(decodeURIComponent(activeurl).indexOf('%21last')>-1 ||
decodeURIComponent(activeurl).indexOf('!last')>-1);
jQuery('#HeaderNav2 li a').each(function() {
// Remove current class from all header links
jQuery(this).removeClass('current');
if(decodeURIComponent(activeurl).indexOf(this.href)>-1
// Set "recent" rather than "search results" when the search is for recent items.
|| (recentlink && this.href.indexOf('%21last')>-1))
{
if (this.href.length>matchedstring) {
// Find longest matched URL
matchedstring=this.href.length;
activelink=jQuery(this);
}
}
});
jQuery(activelink).addClass('current').attr('aria-current', 'page');
}
function ReplaceUrlParameter(url, parameter, value){
var parameterstring = new RegExp('\\b(' + parameter + '=).*?(&|$)')
if(url.search(parameterstring)>=0){
return url.replace(parameterstring,'$1' + value + '$2');
}
return url + (url.indexOf('?')>0 ? '&' : '?') + parameter + '=' + value;
}
function nl2br(text) {
return (text).replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + '<br />' + '$2');
}
// Check if given JSON can be parsed or not
function isJson(str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
function styledalert(title,text,minwidth){
if(typeof minwidth === 'undefined') {
minwidth = 300;
}
jQuery("#modal_dialog").html(text);
jQuery("#modal_dialog").dialog({
title: title,
resizable: false,
minWidth: minwidth,
dialogClass: 'no-close',
modal: true,
maxHeight:500,
buttons: [{
text: oktext,
click: function() {
jQuery( this ).dialog( "close" );
}
}],
create: function(event, ui)
{
if (title == '')
{
jQuery('.ui-dialog-title').html('<i class=\'fa fa-info-circle\' ></i>');
}
}
});
jQuery("#modal_dialog").bind('clickoutside',function(){
jQuery( this ).dialog( "close" );
});
}
// Restrict all defined numeric fields
jQuery(document).ready(function()
{
jQuery('.numericinput').keypress(function(e)
{
var key = e.which || e.keyCode;
if (!(key >= 48 && key <= 57) && // Interval of values (0-9)
(key !== 8)) // Backspace
{
e.preventDefault();
return false;
}
})
});
function HideCollectionBar() {
if (typeof collection_bar_hidden === "undefined" || collection_bar_hidden==false){
colbarresizeon=false;
myLayout.options.south.spacing_open = 0;
myLayout.options.south.spacing_closed = 0;
myLayout.options.south.minSize = 0;
myLayout.sizePane("south",1);
jQuery('#CollectionDiv').hide();
collection_bar_hidden=true;
}
}
function ShowCollectionBar() {
if (typeof collection_bar_hidden === "undefined" || collection_bar_hidden==true){
colbarresizeon=true;
myLayout.options.south.spacing_open = 6;
myLayout.options.south.spacing_closed = 6;
myLayout.options.south.minSize = 40;
collection_bar_hidden=false;
jQuery('#CollectionDiv').show();
InitThumbs();
}
}
function ChosenDropdownInit(elem, selector)
{
if(typeof chosen_config!=='undefined')
{
css_width = jQuery(elem).css("width").replace("px","");
css_boxsixing = jQuery(elem).css("box-sizing");
if(css_boxsixing=='border-box')
{
css_padding_left = jQuery(elem).css("padding-left").replace("px","");
css_padding_right = jQuery(elem).css("padding-right").replace("px","");
css_width_total = (parseInt(css_width)+parseInt(css_padding_left)+parseInt(css_padding_right))+"px";
}
else
{
css_width_total = css_width;
}
chosen_config[selector]['width']=css_width_total;
jQuery(elem).chosen(chosen_config[selector]);
}
}
function removeSearchTagInputPills(search_input)
{
var tags = search_input.tagEditor('getTags')[0].tags;
for(i = 0; i < tags.length; i++)
{
search_input.tagEditor('removeTag', tags[i]);
}
return true;
}
function array_diff(array_1, array_2)
{
var a = []
var diff = [];
for(var i = 0; i < array_1.length; i++)
{
a[array_1[i]] = true;
}
for(var i = 0; i < array_2.length; i++)
{
if(a[array_2[i]])
{
delete a[array_2[i]];
}
else
{
a[array_2[i]] = true;
}
}
for(var k in a)
{
diff.push(k);
}
return diff;
}
function StripResizeResults(targetImageHeight)
{
var screenWidth = jQuery('#CentralSpaceResources').width()-15;
var images = jQuery('.ImageStrip');
var accumulatedWidth = 0;
var imageGap = 15;
var processArray = [];
for (var i = 0; i < images.length; i++) {
// Take a copy to get the unscaled image size
var imageCopy = new Image();imageCopy.src=images[i].src;
var ratio = imageCopy.width/imageCopy.height;
var presentationWidth = (targetImageHeight * ratio);
accumulatedWidth += presentationWidth+imageGap; // add to our calculation of the row width
if (accumulatedWidth > screenWidth) {
// Work out a height for the row (excluding the current image which will fall to the next row)
var factor = screenWidth / (accumulatedWidth-presentationWidth - imageGap);
// Rescale images on current row
for (var n=0; n< processArray.length; n++) {
jQuery(processArray[n]).css("height", Math.floor(factor * targetImageHeight) + "px");
}
// Empty process array
processArray = [];
accumulatedWidth = images[i].width + imageGap;
}
processArray.push(images[i]);
}
}
/**
* Load unified dropdown actions
*
* @param {string} pagename
* @param {string|object} id The ID of the element where the actions should be loaded. Can also be a jQuery object.
* @param {string} type The action type (@see ajax/load_actions.php)
* @param {int} ref The associated (type) ref (e.g for type "collection", the collection ref)
* @param {object} extra_data Any other extra data the associated type might need (e.g for type "search", the searchparams)
*
* @return {Promise} Returns a promise of whether the actions were loaded or not (true/false)
*/
function LoadActions(pagename,id,type,ref, extra_data)
{
console.debug('LoadActions(pagename = %o, id = %o, type = %o, ref = %o, extra_data = %o)', pagename, id, type, ref, extra_data);
var actionspace = typeof id === "object" ? id : jQuery('#' + id);
console.debug('[LoadActions] actionspace = %o', actionspace);
if(actionspace.attr('data-actions-loaded') != 0)
{
return jQuery.when(false);
}
CentralSpaceShowProcessing();
url = baseurl_short+"pages/ajax/load_actions.php";
var data = {
actiontype: type,
ref: ref,
page: pagename
};
var data_obj = Object.assign({}, data, extra_data);
var request = jQuery.ajax({
type:'GET',
url: url,
data: data_obj,
async: false
});
return jQuery.when(request)
.then(function(response, status, xhr)
{
console.debug('[LoadActions] xhr = %o', xhr);
if(response.includes('pagename="login"'))
{
styledalert(errortext,errornotloggedin + ' <a href="' + baseurl_short + '" target="_new">' + login + '</a>');
}
else if(!response.includes('<option'))
{
console.error('[LoadActions] Unexpected response, no options');
}
else
{
console.debug('[LoadActions] actionspace.html(response)');
actionspace.html(response);
// If type is selection collection, set attribute to 1 to prevent further reloading (not needed as selection
// collection actions won't change unless selection is cleared which is handled in ajax_collections.js)
if(type == "selection_collection")
{
actionspace.attr('data-actions-loaded','1');
}
else
{
actionspace.attr('data-actions-loaded','0');
}
// The following hack is required for Chrome (tested in version 111). When rendering dropdowns in the
// lower part of the screen that slide upwards, the new options fail to show until the dropdown is reopened.
setTimeout(() => actionspace.css('height', 'auto'), 100);
return true;
}
return false;
})
.always(() => CentralSpaceHideProcessing());
}
/**
* Get extension of a file
*
*/
function getFilePathExtension(path)
{
var filename = path.split('\\').pop().split('/').pop();
return filename.substring(filename.lastIndexOf('.') + 1, filename.length) || filename;
}
/*
*
* Toggles the locking of an edit field
*
*/
function toggleFieldLock(field)
{
if (typeof lockedfields === 'undefined') {
lockedfields = new Array();
}
if (lockedfields.indexOf(field.toString())!=-1) {
jQuery('#lock_icon_' + field + '> i').removeClass('fa-lock');
jQuery('#lock_icon_' + field + '> i').addClass('fa-unlock');
jQuery('#lock_icon_' + field).parent().closest('div').removeClass('lockedQuestion');
lockedfields = jQuery.grep(lockedfields, function(value) {return value != field.toString();});
SetCookie('lockedfields',lockedfields.map(String));
} else {
jQuery('#lock_icon_' + field + '> i').removeClass('fa-unlock');
jQuery('#lock_icon_' + field + '> i').addClass('fa-lock');
jQuery('#lock_icon_' + field).parent().closest('div').addClass('lockedQuestion');
// Remove checksum as this will break things
jQuery('#field_' + field + '_checksum').val("");
jQuery('#' + field + '_checksum').val("");
lockedfields.push(field.toString());
SetCookie('lockedfields', lockedfields);
}
if (lockedfields.length > 0) {
jQuery(".save_auto_next").show();
} else {
jQuery(".save_auto_next").hide();
}
return true;
}
/*
*
* Places overflowing header links into a dropdown
*
*/
function headerLinksDropdown() {
if (jQuery(window).width() < 1200) {
return;
}
var menuBox = jQuery("#HeaderNav2");
var mb_offset = menuBox.offset();
var mb_width = menuBox.outerWidth(true);
var mb_rightedge = mb_offset.left + mb_width;
var uploadBox = jQuery("#HeaderNav1");
var ub_offset = uploadBox.offset();
var ub_leftedge = ub_offset.left;
var containerTarget = jQuery("#HeaderLinksContainer");
var containerWidth = containerTarget.innerWidth();
var containerOverlap = mb_rightedge - ub_leftedge;
var links = jQuery("#HeaderLinksContainer").find(".HeaderLink"); // get elements that are links in the header bar
var linksWidth = 0;
var caretCreated = false;
jQuery("#OverFlowLinks").remove(); // remove the drop-down menu div
if (jQuery('#OverflowListElement').is(':visible'))
{
caretCreated = true;
}
for (var i = 0; i < links.length; i++)
{
linksWidth += jQuery(links[i]).outerWidth();
if (linksWidth > containerWidth-containerOverlap)
{
if (!caretCreated)
{
jQuery(links[i- 1]).after('<li id="OverflowListElement"><a href="#" id="DropdownCaret" onclick="showHideLinks();"><span class="fa fa-caret-down"></span></a></li>');
// append a div to the document.body element that will contain the drop-down menu items
jQuery(document.body).append('<div id="OverFlowLinks"><ul id="HiddenLinks"></ul></div>');
caretCreated = true;
}
// remove the li element from header links and append it to drop-down link list
jQuery(links[i]).remove();
jQuery(links[i]).appendTo('#HiddenLinks');
}
}
}
/*
*
* Show or hide the header overflow drop down links
*
*/
function showHideLinks()
{
jQuery('div#OverFlowLinks').toggle();
jQuery('div#OverFlowLinks').css('right', 290);
jQuery('div#OverFlowLinks').css('z-index', 1000);
jQuery('div#OverFlowLinks').css('top', 64);
}
/*
*
* Toggles the edit_multi_checkbox question on edit page when in batch edit mode (ie. multiple == true)
*
*/
function batch_edit_toggle_edit_multi_checkbox_question(question_ref)
{
var question = document.getElementById('question_' + question_ref);
var modeselect = document.getElementById('modeselect_' + question_ref);
var findreplace = document.getElementById('findreplace_' + question_ref);
var revert = document.getElementById('revert_' + question_ref);
var remove = document.getElementById('remove_from_field_' + question_ref);
// The copy_from_field is rendered with an id of the resource_type_field ref, not the question_ref (seq number) passed in here
// The resource_type_field ref is however the last digits of the checkbox name attribute, so use that to get the copyfrom element
var this_checkbox=document.getElementById('editthis_' + question_ref);
var copyfrom = document.getElementById('copy_from_field_' + this_checkbox.name.substring(15));
var displayexisting = document.getElementById('displayexisting_' + question_ref);
if(jQuery("#editthis_" + question_ref).prop("checked"))
{
question.style.display = 'block';
modeselect.style.display = 'block';
if(displayexisting)
{
displayexisting.style.display = 'block';
getCurrentFixedListFieldVals(question_ref);
setActionPromptText(question_ref);
}
}
else
{
question.style.display = 'none';
modeselect.style.display = 'none';
findreplace.style.display = 'none';
// revert is not always present, but if it is then hide it
if(revert)
{
revert.style.display = 'none';
}
// copyfrom is not always present, but if it is then hide it
if(copyfrom)
{
copyfrom.style.display = 'none';
}
// remove is not always present, but if it is then hide it
if(remove)
{
remove.style.display = 'none';
}
// Only present for fixed list fields.
if(displayexisting)
{
displayexisting.style.display = 'none';
jQuery('div .currentmultiquestion' + question_ref).hide();
}
document.getElementById('modeselectinput_' + question_ref).selectedIndex = 0;
}
}
/*
*
* Redirects to a target URL after a specified delay time expressed in milliseconds
*
*/
function redirectAfterDelay(targetUrl,delayTime)
{
setTimeout(function()
{
window.location.href = targetUrl;
}, delayTime);
}
function UICenterScrollBottom()
{
// Smoothly croll to the bottom of the central container. Useful to show content after expanding a section at the bottom of the page (e.g. the uploader)
window.setTimeout('jQuery(\'#UICenter\').animate({scrollTop: document.getElementById(\'UICenter\').scrollHeight },"slow");',300);
}
/**
* Detect the users' local time zone using the Internationalisation API
*
* @returns {string}
*/
function detect_local_timezone()
{
return Intl.DateTimeFormat().resolvedOptions().timeZone;
}
/**
*
* @summary Escape characters in a string that may cause the string to be interpreted as HTML in the browser. If regex pattern parameter use this to escape characters, otherwise use default regex pattern provided.
*
* @param {string} string - string potentially containing characters to escape - required
* @param {string} pattern - regex pattern containing HTML characters to escape - not required
*
* @var {array} entityMap - array of HTML entities with their escaped versions
* @var {string} default_pattern - default regex pattern to use if no pattern passed
*
* @returns {string} escaped_string - string with characters escaped
*
* @see upload_plupload.php plupload.addFileFilter()
*
*/
function escape_HTML(string, pattern)
{
var entityMap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'/': '&#x2F;',
'`': '&#x60;',
'=': '&#x3D;'
};
var default_pattern = "[&<>\"'`=]";
// if regex pattern is not passed as argument use default pattern
pattern = (pattern === undefined) ? default_pattern : pattern;
pattern = new RegExp(pattern, "g");
var escaped_string = string.replace(pattern, function (s)
{
return entityMap[s];
});
return escaped_string;
}
/* Manage .TabBar clicks so the show/hide the correct .TabbedPanel within .TabBar */
function SelectMetaTab(resourceid,tabnum,modal)
{
if (modal)
{
jQuery('#Modaltabswitch'+tabnum+'-'+resourceid).siblings().removeClass('TabSelected');
jQuery('#Modaltabswitch'+tabnum+'-'+resourceid).addClass('TabSelected');
jQuery('div.MetaTabIsModal-'+resourceid).hide();
jQuery('#Modaltab'+tabnum+'-'+resourceid).show();
}
else
{
jQuery('#tabswitch'+tabnum+'-'+resourceid).siblings().removeClass('TabSelected');
jQuery('#tabswitch'+tabnum+'-'+resourceid).addClass('TabSelected');
jQuery('div.MetaTabIsNotModal-'+resourceid).hide();
jQuery('#tab'+tabnum+'-'+resourceid).show();
}
}
/**
* Handle clicks on the .TabBar in the resource downloads area
*
* @param {string} tabName - name of the tab clicked
* @param {boolean} modal - whether the resource is opened in a modal or not
*/
function selectDownloadTab(tabName, modal)
{
if (modal)
{
jQuery('#modal #RecordDownloadTabButtons .Tab').removeClass('TabSelected');
jQuery('#modal #'+tabName+'Button').addClass('TabSelected');
jQuery('#modal #RecordDownloadTabContainer .RecordDownloadSpace').hide();
jQuery('#modal #'+tabName).show();
}
else
{
jQuery('#RecordDownloadTabButtons .Tab').removeClass('TabSelected');
jQuery('#'+tabName+'Button').addClass('TabSelected');
jQuery('#RecordDownloadTabContainer .RecordDownloadSpace').hide();
jQuery('#'+tabName).show();
}
}
/**
* Handle clicks on the tab bar in the search area
*
* @param {string} tabName - name of the tab clicked
*/
function selectSearchBarTab(tabName)
{
jQuery('#SearchBarTabsContainer .SearchBarTab').removeClass('SearchBarTabSelected');
switch(tabName)
{
case 'search':
jQuery('#SearchBarTabsContainer .SearchTab').addClass('SearchBarTabSelected');
jQuery('#SearchBoxPanel').show();
jQuery('#BrowseBarContainer').hide();
SetCookie('selected_search_tab', 'search');
break;
case 'browse':
jQuery('#SearchBarTabsContainer .BrowseTab').addClass('SearchBarTabSelected');
jQuery('#SearchBoxPanel').hide();
jQuery('#BrowseBarContainer').show();
SetCookie('selected_search_tab', 'browse');
break;
}
}
// unset cookie
function unsetCookie(cookieName, cpath)
{
document.cookie = cookieName + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;path=' + cpath;
}
/**
* Check if system upgrade is in progress. Reloads the page if it is to show user current status.
*
* @return void
*/
function check_upgrade_in_progress()
{
jQuery.ajax({
type: 'GET',
url: baseurl + "/pages/ajax/message.php",
data: {
ajax: true,
check_upgrade_in_progress: true
},
dataType: "json"
}).done(function(response) {
if (response.status != "success") {
return;
}
if (response.data.upgrade_in_progress) {
window.location.reload(true);
}
});
}
/**
* Add hidden input dynamically to help further requests maintain modals.
*
* @param {string} form The form ID to which to append the hidden input
* @param {boolean} decision_factor TRUE to add it, FALSE otherwise
*
* @return boolean
*/
function add_hidden_modal_input(form, decision_factor)
{
if(!decision_factor)
{
return false;
}
jQuery('<input type="hidden" name="modal" value="true">').appendTo("#" + form);
return true;
}
function api(name, params, callback, post_data_extra = {})
{
query = {};
query["function"] = name;
for (var key in params) {
query[key] = params[key];
}
console.debug("API Query",query);
postobj = Object.assign({}, post_data_extra);
postobj['query'] = jQuery.param(query);
postobj['authmode'] = "native";
// Below is used to get access to responseURL
var xhr = new XMLHttpRequest();
jQuery.ajax({
method: 'POST',
url: baseurl_short + 'api/?',
data: postobj,
xhr: function() {
return xhr;
}
})
.done(function(data, textStatus, jqXHR )
{
response = jqXHR.responseText;
if(response.includes('pagename="login"'))
{
styledalert(errortext,errornotloggedin + ' <a href="' + baseurl_short + '" target="_new">' + login + '</a>');
return false;
}
if(isJson(response))
{
response = JSON.parse(response)
}
if(typeof callback === "function")
{
callback(response);
}
})
.fail(function(jqXHR, textStatus, errorThrown)
{
let response = typeof jqXHR.responseJSON.data.message !== 'undefined'
? jqXHR.responseJSON.data.message
: textStatus;
console.error("API Error: %s - %s", errorThrown, response);
});
}
/**
* Deselect the children and all associated descendents of a given node in a category tree
* Children which are themselves parents will be opened if necessary so that their descendents can be deselected
*
* @param {jsTree} theJstree The category tree component
* @param {number} nodeId The node to be processed
*
* @return void
*/
function deselect_children_of_jstree_node(theJstree, nodeId)
{
// Node is open by the time we get here so that its children can also be deselected if necessary
var children_of_this = theJstree.jstree('get_children_dom', nodeId);
for (var i = 0; i < children_of_this.length; i++) {
// Trigger deselection of each child if necessary
if (theJstree.jstree('is_selected', children_of_this[i].id)) {
theJstree.jstree('deselect_node', children_of_this[i].id);
} else if (theJstree.jstree('is_parent', children_of_this[i].id)) {
// Child is not selected; but continue to process the descendents to cater for tier gaps
if (theJstree.jstree('is_closed', children_of_this[i].id)) {
theJstree.jstree('open_node', children_of_this[i].id, function(e, data) {
deselect_children_of_jstree_node(theJstree, e.id);
});
} else {
// Child is already open
deselect_children_of_jstree_node(theJstree, children_of_this[i].id);
}
}
}
}
/**
* Show the help box if available.
*
* @param {string} id The identifier (NOT the id attribute) for the div.FormHelp element.
*
* @return void
*/
function ShowHelp(id)
{
var el_id = 'help_' + id;
if (document.getElementById(el_id))
{
jQuery('#' + el_id).fadeIn();
}
}
/**
* Hide the help box if available.
*
* @param {string} id The identifier (NOT the id attribute) for the div.FormHelp element.
*
* @return void
*/
function HideHelp(id)
{
var el_id = 'help_' + id;
if (document.getElementById(el_id))
{
document.getElementById(el_id).style.display = 'none';
}
}
var ProcessingTimersActive=false;
var ProcessingFirstTimer=0;
var ProcessingSecondTimer=0;
var ProcessingDisplayTimer=0;
var ProcessingAPITimer=0;
var ProcessingMessages;
var ProcessingCount=-1;
function CentralSpaceShowProcessing(delay = 2000, defaultmessage = '')
{
if (ProcessingTimersActive) { return; }
if (b_progressmsgs && (external_access_key == null || external_access_key.length == 0)) {
jQuery('#ProcessingStatus').html('');
ProcessingMessages=[];
ProcessingDisplayTimer = setInterval(CentralSpace_ProcessingDisplayTimer, 350);
ProcessingFirstTimer=setTimeout(CentralSpace_ProcessingAPITimer, 1000);
ProcessingAPITimer = setInterval(CentralSpace_ProcessingAPITimer, 3000);
jQuery('#ProcessingStatus').html(DOMPurify.sanitize(defaultmessage));
}
ProcessingSecondTimer=setTimeout(
function (){jQuery('#ProcessingBox').fadeIn('fast');},
delay
);
ProcessingTimersActive=true;
}
function CentralSpaceHideProcessing()
{
if (!ProcessingTimersActive) { return; }
jQuery('#ProcessingBox').fadeOut('fast');
clearInterval(ProcessingDisplayTimer); ProcessingDisplayTimer=0;
clearTimeout(ProcessingFirstTimer); ProcessingFirstTimer=0;
clearInterval(ProcessingAPITimer); ProcessingAPITimer=0;
clearTimeout(ProcessingSecondTimer); ProcessingSecondTimer=0;
ProcessingTimersActive=false;
}
function CentralSpace_ProcessingDisplayTimer()
{
var NewMessage=ProcessingMessages.shift();
if (NewMessage!==undefined)
{
jQuery('#ProcessingStatus').html(DOMPurify.sanitize(NewMessage));
}
}
function CentralSpace_ProcessingAPITimer()
{
// Use the API to fetch any new processing messages.
api("get_processing_message", null, function(response) {
console.log ("API execution");
console.log(response);
if(response!= false && Array.isArray(response))
{
ProcessingMessages=ProcessingMessages.concat(response);
}
}, ProcessingCSRF);
}
/**
* Generate ResourceSpace URL with context aware params (from an elements' data set).
*
* @param {HTML element} el The element from which we want to generate a URL for
* @param {string} path URL path
* @param {string} data_key The data set key to retrieve params from. Note: the data value is an object
*
* @return string
*/
function GenerateRsUrlFromElement(el, path, data_key)
{
params = jQuery(el).data(data_key);
url = baseurl_short + path + '?' + new URLSearchParams(params).toString();
console.debug('[GenerateRsUrlFromElement] url = %s', url);
return url;
}
/**
* Get client's date and time in friendly format
*
* @return string
*
*/
function rsGetLocalDatetime()
{
var dt = new Date();
var logtime = dt.getFullYear() + "-" +dt.getMonth() + "-" + dt.getDate() + " " + dt.getHours() + ":" + dt.getMinutes() + ":" + dt.getSeconds();
return logtime;
}
/**
* Log all ResourceSpace global client side variables
*/
function resourcespace_get_global_state() {
// global variables that exist in chrome browser by default
let stdChromeVars = [
"parent",
"opener",
"top",
"length",
"frames",
"closed",
"location",
"self",
"window",
"document",
"name",
"customElements",
"history",
"locationbar",
"menubar",
"personalbar",
"scrollbars",
"statusbar",
"toolbar",
"status",
"frameElement",
"navigator",
"origin",
"external",
"screen",
"innerWidth",
"innerHeight",
"scrollX",
"pageXOffset",
"scrollY",
"pageYOffset",
"visualViewport",
"screenX",
"screenY",
"outerWidth",
"outerHeight",
"devicePixelRatio",
"clientInformation",
"screenLeft",
"screenTop",
"defaultStatus",
"defaultstatus",
"styleMedia",
"onsearch",
"isSecureContext",
"onabort",
"onblur",
"oncancel",
"oncanplay",
"oncanplaythrough",
"onchange",
"onclick",
"onclose",
"oncontextmenu",
"oncuechange",
"ondblclick",
"ondrag",
"ondragend",
"ondragenter",
"ondragleave",
"ondragover",
"ondragstart",
"ondrop",
"ondurationchange",
"onemptied",
"onended",
"onerror",
"onfocus",
"onformdata",
"oninput",
"oninvalid",
"onkeydown",
"onkeypress",
"onkeyup",
"onload",
"onloadeddata",
"onloadedmetadata",
"onloadstart",
"onmousedown",
"onmouseenter",
"onmouseleave",
"onmousemove",
"onmouseout",
"onmouseover",
"onmouseup",
"onmousewheel",
"onpause",
"onplay",
"onplaying",
"onprogress",
"onratechange",
"onreset",
"onresize",
"onscroll",
"onseeked",
"onseeking",
"onselect",
"onstalled",
"onsubmit",
"onsuspend",
"ontimeupdate",
"ontoggle",
"onvolumechange",
"onwaiting",
"onwebkitanimationend",
"onwebkitanimationiteration",
"onwebkitanimationstart",
"onwebkittransitionend",
"onwheel",
"onauxclick",
"ongotpointercapture",
"onlostpointercapture",
"onpointerdown",
"onpointermove",
"onpointerup",
"onpointercancel",
"onpointerover",
"onpointerout",
"onpointerenter",
"onpointerleave",
"onselectstart",
"onselectionchange",
"onanimationend",
"onanimationiteration",
"onanimationstart",
"ontransitionend",
"onafterprint",
"onbeforeprint",
"onbeforeunload",
"onhashchange",
"onlanguagechange",
"onmessage",
"onmessageerror",
"onoffline",
"ononline",
"onpagehide",
"onpageshow",
"onpopstate",
"onrejectionhandled",
"onstorage",
"onunhandledrejection",
"onunload",
"performance",
"stop",
"open",
"alert",
"confirm",
"prompt",
"print",
"queueMicrotask",
"requestAnimationFrame",
"cancelAnimationFrame",
"captureEvents",
"releaseEvents",
"requestIdleCallback",
"cancelIdleCallback",
"getComputedStyle",
"matchMedia",
"moveTo",
"moveBy",
"resizeTo",
"resizeBy",
"scroll",
"scrollTo",
"scrollBy",
"getSelection",
"find",
"webkitRequestAnimationFrame",
"webkitCancelAnimationFrame",
"fetch",
"btoa",
"atob",
"setTimeout",
"clearTimeout",
"setInterval",
"clearInterval",
"createImageBitmap",
"close",
"focus",
"blur",
"postMessage",
"onappinstalled",
"onbeforeinstallprompt",
"crypto",
"indexedDB",
"webkitStorageInfo",
"sessionStorage",
"localStorage",
"chrome",
"applicationCache",
"onpointerrawupdate",
"trustedTypes",
"speechSynthesis",
"webkitRequestFileSystem",
"webkitResolveLocalFileSystemURL",
"openDatabase",
"caches",
"ondevicemotion",
"ondeviceorientation",
"ondeviceorientationabsolute"
];
let T = Object.keys(window)
.filter(prop => stdChromeVars.indexOf(prop) < 0)
.filter(prop => typeof window[prop] !== 'function')
.sort()
;
for (let i in T) {
let prop = T[i];
console.debug('%s: %s = %o', prop, typeof window[prop], window[prop]);
}
}
function enforceSharePassword(error_text)
{
let passwordInput = document.getElementById('sharepassword');
if (passwordInput.hasAttribute('required') && passwordInput.value === '') {
alert(error_text);
return false;
}
return true;
}
/**
* Determine if we have minimum date parts in order to allow calling AutoSave (on edit page - see edit_fields/4.php)
* @param {String} fieldidentifier
* @returns {bool}
*/
function sufficientDateParts(fieldidentifier)
{
year = jQuery('#' + fieldidentifier + "-y").val() ?? "";
month = jQuery('#' + fieldidentifier + "-m").val() ?? "";
day = jQuery('#' + fieldidentifier + "-d").val() ?? "";
hour = jQuery('#' + fieldidentifier + '-h').val() ?? "";
minute = jQuery('#' + fieldidentifier + '-i').val() ?? "";
console.debug("Day: (#" + fieldidentifier + "-d) : " + day);
console.debug("Month: (#" + fieldidentifier + "-m) : " + month);
console.debug("Year: (#" + fieldidentifier + "-y) : " + year);
console.debug("Hour: (#" + fieldidentifier + "-h) : " + hour);
console.debug("Minute: (#" + fieldidentifier + "-i) : " + minute);
const required_validator = (
year !== '' // Always need a year
&& (day === '' || month !== '') // If a day is set then must have month
&& (hour === '' || day !== '') // If an hour is set then must have day
&& (minute === '' || hour !== '') // If a minute is set then must have hour
);
const optional_validator = (
[year, month, day, hour, minute].filter(e => e === '').length === 5
|| required_validator
);
const valid = jQuery(`label[for="${fieldidentifier}"] > sup`).text() === '*'
? required_validator
: optional_validator;
if (!valid) {
console.debug("Insufficient date inputs selected for field " + fieldidentifier + ". Preventing autosave");
}
return valid;
}
function registerResourceSelectDeselectHandlers() {
jQuery('#CentralSpace').on('resourcesaddedtocollection', function(response,resource_list) {
resource_list.forEach(function (resource)
{
jQuery("#ResourceShell" + resource).addClass("Selected");
jQuery("#check" + resource).prop('checked','checked');
});
UpdateSelColSearchFilterBar();
CentralSpaceHideProcessing();
});
jQuery('#CentralSpace').on('resourcesremovedfromcollection', function(response,resource_list) {
resource_list.forEach(function (resource)
{
jQuery("#ResourceShell" + resource).removeClass("Selected");
jQuery("#check" + resource).prop('checked','');
});
CentralSpaceHideProcessing();
UpdateSelColSearchFilterBar();
});
}
// Set a cookie to store the user's prefers-color-scheme value
function setThemePreference() {
const darkMode = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
SetCookie('css_color_scheme', darkMode, 1000);
}
// Dynamically update header image using Ajax
function updateHeaderImage() {
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches ? "dark" : "light";
const ajax_url = baseurl_short + 'pages/ajax/header_image.php?force_appearance=' + encodeURIComponent(isDarkMode);
jQuery.ajax({
type: "GET",
url: ajax_url,
success: function(data) {
jQuery('#HeaderImg').attr("src", DOMPurify.sanitize(data, { SAFE_FOR_URL: true }));
}
});
}