You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4464 lines
163 KiB

/* global $ */
// $Id$
// JavaScript glue for jQuery in Tiki
//
// Tiki 6 - $ is now initialised in jquery.js
// but let's keep $jq available too for legacy custom code
var legacyLoad = jQuery.fn.load;
jQuery.fn.load = function (url, _data, _complete) {
var element = this;
element.show();
element.tikiModal(tr('Loading...'));
if (typeof _data === "function") {
_complete = _data;
_data = "";
}
var complete = function (responseText, textStatus, jqXHR) {
element.tikiModal();
if (textStatus === 'error') {
element.html('<div class="alert alert-danger alert-dismissible" role="alert">'
+ '<button type="button" class="close" data-bs-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>'
+ '<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>'
+ 'Error loading content.</div>');
return;
}
_complete.call(this, responseText, textStatus, jqXHR)
};
return legacyLoad.call(this, url, _data, complete);
}
var $jq = $,
$window = $(window),
$document = $(document);
// Check / Uncheck all Checkboxes
function switchCheckboxes (tform, elements_name, state, hiddenToo) {
// checkboxes need to have the same name elements_name
// e.g. <input type="checkbox" name="my_ename[]">, will arrive as Array in php.
if (hiddenToo == undefined) {
hiddenToo = false;
}
var closeTag;
if (hiddenToo) {
closeTag = '"]';
} else {
closeTag = '"]:visible';
}
$(tform).contents().find('input[name="' + jQuery.escapeSelector(elements_name) + closeTag).prop('checked', state).change();
}
// add id's of any elements that don't like being animated here
var jqNoAnimElements = ['help_sections', 'ajaxLoading'];
function show(foo, f, section) {
if ($.inArray(foo, jqNoAnimElements) > -1 || typeof jqueryTiki === 'undefined') { // exceptions that don't animate reliably
$("#" + foo).show();
} else if ($("#" + foo).hasClass("tabcontent")) { // different anim prefs for tabs
showJQ("#" + foo, jqueryTiki.effect_tabs, jqueryTiki.effect_tabs_speed, jqueryTiki.effect_tabs_direction);
} else {
showJQ("#" + foo, jqueryTiki.effect, jqueryTiki.effect_speed, jqueryTiki.effect_direction);
}
if (f) {setCookie(foo, "o", section);}
}
function hide(foo, f, section) {
if ($.inArray(foo, jqNoAnimElements) > -1 || typeof jqueryTiki === 'undefined') { // exceptions
$("#" + foo).hide();
} else if ($("#" + foo).hasClass("tabcontent")) {
hideJQ("#" + foo, jqueryTiki.effect_tabs, jqueryTiki.effect_tabs_speed, jqueryTiki.effect_tabs_direction);
} else {
hideJQ("#" + foo, jqueryTiki.effect, jqueryTiki.effect_speed, jqueryTiki.effect_direction);
}
if (f) {
// var wasnot = getCookie(foo, section, 'x') == 'x';
setCookie(foo, "c", section);
// if (wasnot) {
// history.go(0); // used to reload the page with all menu items closed - broken since 3.x
// }
}
}
// flip function... unfortunately didn't use show/hide (ay?)
function flip(foo, style) {
var $foo = $("#" + foo);
if (style && style !== 'block' || foo === 'help_sections' || foo === 'fgalexplorer' || typeof jqueryTiki === 'undefined') { // TODO find a better way?
$foo.toggle(); // inlines don't animate reliably (yet) (also help)
if ($foo.css('display') === 'none') {
setSessionVar('show_' + tiki_encodeURIComponent(foo), 'n');
} else {
setSessionVar('show_' + tiki_encodeURIComponent(foo), 'y');
}
} else {
if ($foo.css("display") === "none") {
setSessionVar('show_' + tiki_encodeURIComponent(foo), 'y');
show(foo);
}
else {
setSessionVar('show_' + tiki_encodeURIComponent(foo), 'n');
hide(foo);
}
}
}
// handle JQ effects
function showJQ(selector, effect, speed, dir) {
if (effect === 'none') {
$(selector).show();
} else if (effect === '' || effect === 'normal') {
$(selector).show(400); // jquery 1.4 no longer seems to understand 'nnormal' as a speed
} else if (effect == 'slide') {
$(selector).slideDown(speed);
} else if (effect === 'fade') {
$(selector).fadeIn(speed);
} else if (effect.match(/(.*)_ui$/).length > 1) {
$(selector).show(effect.match(/(.*)_ui$/)[1], {direction: dir}, speed);
} else {
$(selector).show();
}
}
function hideJQ(selector, effect, speed, dir) {
if (effect === 'none') {
$(selector).hide();
} else if (effect === '' || effect === 'normal') {
$(selector).hide(400); // jquery 1.4 no longer seems to understand 'nnormal' as a speed
} else if (effect === 'slide') {
$(selector).slideUp(speed);
} else if (effect === 'fade') {
$(selector).fadeOut(speed);
} else if (effect.match(/(.*)_ui$/).length > 1) {
$(selector).hide(effect.match(/(.*)_ui$/)[1], {direction: dir}, speed);
} else {
$(selector).hide();
}
}
// ajax loading indicator
function ajaxLoadingShow(destName) {
var $dest, $loading, pos, x, y, w, h;
if (typeof destName === 'string') {
$dest = $('#' + destName);
} else {
$dest = $(destName);
}
if ($dest.length === 0 || $dest.parents(":hidden").length > 0) {
return;
}
$loading = $('#ajaxLoading');
// find area of destination element
pos = $dest.offset();
// clip to page
if (pos.left + $dest.width() > $window.width()) {
w = $window.width() - pos.left;
} else {
w = $dest.width();
}
if (pos.top + $dest.height() > $window.height()) {
h = $window.height() - pos.top;
} else {
h = $dest.height();
}
x = pos.left + (w / 2) - ($loading.width() / 2);
y = pos.top + (h / 2) - ($loading.height() / 2);
// position loading div
$loading.css('left', x).css('top', y);
// now BG
x = pos.left + ccsValueToInteger($dest.css("margin-left"));
y = pos.top + ccsValueToInteger($dest.css("margin-top"));
w = ccsValueToInteger($dest.css("padding-left")) + $dest.width() + ccsValueToInteger($dest.css("padding-right"));
h = ccsValueToInteger($dest.css("padding-top")) + $dest.height() + ccsValueToInteger($dest.css("padding-bottom"));
$('#ajaxLoadingBG').css('left', pos.left).css('top', pos.top).width(w).height(h).fadeIn("fast");
show('ajaxLoading');
}
function ajaxLoadingHide() {
hide('ajaxLoading');
$('#ajaxLoadingBG').fadeOut("fast");
}
function ajaxSubmitEventHandler(successCallback, dataType) {
return function (e) {
e.preventDefault();
let form = this,
act = $(form).attr('action'),
modal = $(form).closest('.modal-dialog'),
formData = null;
if (! act) {
if (typeof url !== "undefined") {
act = url;
} else {
return false;
}
}
dataType = dataType || 'json';
if (typeof $(form).valid === "function") {
if (!$(form).valid()) {
return false;
} else if ($(form).validate().pendingRequest > 0) {
$(form).validate();
setTimeout(function() {$(form).submit();}, 500);
return false;
}
}
modal.tikiModal(tr('Loading...'));
// if there is a file is included in form, use FormData, otherwise, serialize the form input values.
// FormData still has issues in IE, though they've been fixed in Edge.
if ($(form).find("input[type=file]").length){
formData = new FormData(form);
} else {
formData = $(form).serialize();
}
let formSubmission = {
type: 'POST',
data: formData,
dataType: dataType,
success: function (data) {
successCallback.apply(form, [data]);
},
error: function (jqxhr) {
// Headers sent from Feedback class already handled through an ajaxComplete
if (! jqxhr.getResponseHeader('X-Tiki-Feedback')) {
modal.tikiModal();
$(form).showError(jqxhr);
}
},
complete: function () {
modal.tikiModal();
}
};
// if the encryption type on the form is set to 'multipart/form-data' or formData is a FormData object
// we must set contentType and processData to false on the ajax submission
if (form.enctype === "multipart/form-data" || formData.constructor === FormData) {
formSubmission.contentType = false;
formSubmission.processData = false;
}
$.ajax(act, formSubmission);
return false;
};
}
function checkDuplicateRows( button, columnSelector, rowSelector, parentSelector ) {
if (typeof columnSelector === 'undefined') {
columnSelector = "td";
}
if (typeof rowSelector === 'undefined') {
rowSelector = "tr:not(:first)";
}
if (typeof parentSelector === 'undefined') {
parentSelector = "table:first";
}
var $rows = $(button).parents(parentSelector).find(rowSelector);
$rows.each(function( ix, el ){
if ($("input:checked", el).length === 0) {
var $el = $(el);
var line = $el.find(columnSelector).text();
$rows.each(function( ix, el ){
if ($el[0] !== el && $("input:checked", el).length === 0) {
if (line === $(el).find(columnSelector).text()) {
$(":checkbox:first", el).prop("checked", true);
}
}
});
}
});
}
$.fn.tiki_popover = function () {
var list, $container = this;
// To allow table elements etc in tips and popovers
if ($.fn.tooltip.Constructor && typeof $.fn.tooltip.Constructor.Default.allowList === "object") {
var myDefaultAllowList = $.fn.tooltip.Constructor.Default.allowList;
myDefaultAllowList.table = [];
myDefaultAllowList.thead = [];
myDefaultAllowList.tbody = [];
myDefaultAllowList.tr = [];
myDefaultAllowList.th = [];
myDefaultAllowList.td = [];
myDefaultAllowList.form = ["action", "method"];
myDefaultAllowList.input = ["name", "value", "type"];
myDefaultAllowList.button = ["type", "disabled", "name", "value", "onclick"];
myDefaultAllowList.time = ["datetime"]; // for timeago
myDefaultAllowList.a = ["target", "href", "title", "rel", "data-bs-toggle", "data-bs-backdrop",
"data-bs-target", "data-bs-size", "onclick"]; // data items for smarty_function_bootstrap_modal
}
/*
* Prepare the data so all elements so the data is all in the right format for bootstrap popovers
*/
list = $container.find('.tips[title!=""], .tikihelp[title!=""]')
.each(function () {
var $element = $(this);
if ($element.attr('title') && ! $element.hasClass('tikihelp-prefs')) {
$.each(['|', ':', '<br/>', '<br>'], function (key, sep) {
var parts = $element.attr('title').split(sep);
if (parts.length > 1) {
$element.attr('title', parts.shift());
// note setting the jQuery data object doesn't update the DOM, which is what bs5 needs now
$element.attr('data-bs-content', parts.join(sep));
}
});
} else {
$element.attr('title', '');
}
if (! $element.data('bs-trigger')) {
$element.attr('data-bs-trigger', 'hover focus');
}
// default Tiki delay
if (! $element.data('bs-delay')) {
//$element.attr('data-bs-delay', {"show": 250, "hide": 500}); FIXME (bs5 data object bug)
$element.attr('data-bs-delay', 500);
}
});
$.merge(list, $container.find("a[data-bs-toggle=popover]:not(.tips[title!='']):not(.tikihelp[title!=''])"));
list.filter('.bottom').attr('data-bs-placement', 'bottom');
list.filter('.left').attr('data-bs-placement', 'left');
list.filter('.right').attr('data-bs-placement', 'right');
// FIXME temporary fix of https://github.com/twbs/bootstrap/issues/35020
//list.filter('.slow').attr('data-bs-delay', { "show": 1000, "hide": 500 });
list.filter('.slow').attr('data-bs-delay', 1000);
list.find('img').attr('title', ''); // Remove the img title to avoid browser tooltip
list.filter('[data-bs-trigger="click"]')
.click(function (e) {
e.preventDefault();
});
list.filter('[data-bs-delay^="{"]').each(function () {
// FIXME temporary fix of https://github.com/twbs/bootstrap/issues/35020 only single numeric values work for delay via data attributes
const $this = $(this);
$this.attr("data-bs-delay", $this.data('bs-delay').show);
// only use the show setting
});
// Handle common cases
list
.popover({
container: 'body',
html: true,
boundary: "window",
placement: $.tikiPopoverWhereToPlace
});
$container.find('.ajaxtips').each(function() {
var me = $(this),
trigger = me.data('bs-trigger') || 'hover focus';
$(this).popover({
trigger: trigger,
html: true,
//bsDelay: { "show": 500, "hide": 500 },
delay: 500,
placement: $.tikiPopoverWhereToPlace,
boundary: "window",
content: function () {
const link = this;
let content = $(this).data('bs-content');
if (! content) {
$.get($(this).data('ajaxtips'), function (data) {
content = data;
link.dataset.bsContent = content;
$(link).attr('data-bs-content', content);
bootstrap.Popover.getInstance(link).dispose();
bootstrap.Popover.getOrCreateInstance(link, {
trigger: trigger,
html: true,
delay: 500,
placement: $.tikiPopoverWhereToPlace,
boundary: "window"
});
bootstrap.Popover.getInstance(link).show();
});
// display a spinner while waiting for the ajax call to return the content
content = "<div class='text-center p-3'><img src='img/spinner.gif' alt='Loading...'></div>";
}
return content;
}
});
});
// only have one popover showing at a time
$document.on("show.bs.popover", function ( e ) {
var event = e;
$('.popover:visible:not(.tour-tour)').each(function () {
if (this.previousElementSibling !== event.target) {
$(this).popover('hide');
}
});
});
$document.on("hide.bs.popover", function ( e ) {
var $popover = $('.popover:visible:not(.tour-tour)');
// if mouse is over the popover
if ($popover.find(":hover").length) {
// change the leave event to be when leaving the popover
$popover.mouseleave(function () {
$(this).popover("hide");
});
// and cancel the hide event
e.preventDefault();
return false;
}
});
return $container;
};
$.tikiPopoverWhereToPlace = function (pop, el) {
var pxNum = function(str) {
return (str || '').replace('px', '') * 1;
},
$win = $(window),
$el = $(el),
width = $el.offsetParent().width(),
height = $el.offsetParent().height(),
$pop = $(pop),
allowedImgWidth = width * 0.60,
allowedImgHeight = height * 0.60,
manualImageWidth = $el.data('width'),
leftPos = $el.offset().left,
rightPos = leftPos + $el.outerWidth(),
bottomPos = $el.offset().top + $el.outerHeight() - $win.scrollTop(),
$img = $pop.find('div[style*="background-image"],img').first(),
$imgContainer = $img.parent(),
$imgPopover = $imgContainer.parent(),
imgWidth = pxNum($img.css('width')),
imgHeight = pxNum($img.css('height')),
newImgWidth,
newImgHeight,
widthBuffer,
heightBuffer;
if ($el.data("bs-placement")) {
return $el.data("bs-placement"); // element already has popover placement set
}
if (manualImageWidth) {
$img.css({
width: manualImageWidth + 'px'
});
$pop.css({
"max-width" : '100%'
});
imgWidth = manualImageWidth;
}
//lets check the size of the popover img
if (imgWidth > allowedImgWidth || imgHeight > allowedImgHeight) {
widthBuffer = (pxNum($imgContainer.css('padding-left')) + pxNum($imgContainer.css('margin-left')) + pxNum($imgContainer.css('border-left-width'))) * 2;
heightBuffer = (pxNum($imgContainer.css('padding-top')) + pxNum($imgContainer.css('margin-top')) + pxNum($imgContainer.css('border-top-width'))) * 2;
// proportionate the image relative to what is allowed
if(allowedImgWidth/imgWidth > allowedImgHeight/imgHeight){
newImgWidth = allowedImgWidth;
newImgHeight = imgHeight*(allowedImgWidth/imgWidth);
} else {
newImgWidth = imgWidth*(allowedImgHeight/imgHeight);
newImgHeight = allowedImgHeight;
}
$img.css({
backgroundSize: newImgWidth + 'px ' + newImgHeight + 'px',
width: newImgWidth + 'px',
height: newImgHeight + 'px'
});
$imgPopover.css({
maxWidth: (newImgWidth + widthBuffer) + 'px',
maxHeight: (newImgHeight + heightBuffer) +'px'
});
}
var $popTemp = $("<div class='popover temp'><div class='popover-body'>" + $el.data("bs-content") + "</div></div>");
$("body").append($popTemp);
var popWidth = $popTemp.outerWidth(),
popHeight = $popTemp.outerHeight();
$popTemp.remove();
if (width - leftPos < popWidth && width - rightPos < popWidth) {
if (bottomPos > popHeight ||
bottomPos + popHeight > $win.height()) {
return 'top';
} else {
return 'bottom';
}
} else if (width - leftPos > popWidth) {
return 'left';
} else if (width - rightPos > popWidth) {
return 'right';
}
if (imgWidth && width - leftPos + imgWidth > width) return 'bottom';
return 'auto';
};
$(function() { // JQuery's DOM is ready event - before onload
if (!window.jqueryTiki) window.jqueryTiki = {};
// Reflections
if (jqueryTiki.reflection) {
$("img.reflect").reflect({});
}
if (jqueryTiki.tooltips) {
$(document).tiki_popover();
}
// superfish setup (CSS menu effects)
if (jqueryTiki.superfish) {
$('ul.cssmenu_horiz').supersubs({
minWidth: 11, // minimum width of sub-menus in em units
maxWidth: 20, // maximum width of sub-menus in em units
extraWidth: 1 // extra width can ensure lines don't sometimes turn over
// due to slight rounding differences and font-family
});
$('ul.cssmenu_vert').supersubs({
minWidth: 11, // minimum width of sub-menus in em units
maxWidth: 20, // maximum width of sub-menus in em units
extraWidth: 1 // extra width can ensure lines don't sometimes turn over
// due to slight rounding differences and font-family
});
$('ul.cssmenu_horiz').superfish({
animation: {opacity:'show', height:'show'}, // fade-in and slide-down animation
speed: 'fast', // faster animation speed
onShow: function(){
if ($(this).data('active')) return;
$(this)
.data('active', true)
.moveToWithinWindow();
},
onHide: function(){
$(this).removeData('active');
}
});
$('ul.cssmenu_vert').superfish({
animation: {opacity:'show', height:'show'}, // fade-in and slide-down animation
speed: 'fast', // faster animation speed
onShow: function(){
if ($(this).data('active')) return;
$(this)
.data('active', true)
.moveToWithinWindow();
},
onHide: function(){
$(this).removeData('active');
}
});
}
$.fn.applyColorbox = function() {
$(this).find("a[data-box*='box']").colorbox({
rel: function(){
return $(this).attr('data-box');
},
transition: "elastic",
maxHeight:"95%",
maxWidth:"95%",
overlayClose: true,
current: jqueryTiki.cboxCurrent
});
// now, first let suppose that we want to display images in ColorBox by default:
// this matches data-box attributes containing type=img or no type= specified
$(this).find("a[data-box*='box'][data-box*='type=img'], a[data-box*='box'][data-box!='type=']:not([data-is-text])").colorbox({
photo: true
});
// data-box attributes containing slideshow (this one must be without #col1)
$(this).find("a[data-box*='box'][data-box*='slideshow']").colorbox({
photo: true,
slideshow: true,
slideshowSpeed: 3500,
preloading: false,
width: "100%",
height: "100%"
});
// this are the defaults matching all *box links which are not obviously links to images...
// (if we need to support more, add here... otherwise it is possible to override with type=iframe in data-box attribute of a link)
// (from here one to speed it up, matches any link in #col1 only - the main content column)
$(this).find("#col1 a[data-box*='box']:not([data-box*='type=img']):not([href*='display']):not([href*='preview']):not([href*='thumb']):not([data-box*='slideshow']):not([href*='image']):not([href$='\.jpg']):not([href$='\.jpeg']):not([href$='\.png']):not([href$='\.gif'])").colorbox({
iframe: true,
width: "95%",
height: "95%"
});
// hrefs starting with ftp(s)
$(this).find("#col1 a[data-box*='box'][href^='ftp://'], #col1 a[data-box*='box'][href^='ftps://']").colorbox({
iframe: true,
width: "95%",
height: "95%"
});
// data-box attributes containing type=flash
$(this).find("#col1 a[data-box*='box'][data-box*='type=flash']").colorbox({
inline: true,
width: "60%",
height: "60%",
href: function () {
var $el = $("#cb_swf_player");
if ($el.length === 0) {
$el = $("<div id='cb_swf_player' />");
$(document.body).append($("<div />").hide().append($el));
}
//$(this).media.swf(el, { width: 400, height: 300, autoplay: true, src: $(this).attr("href") });
swfobject.embedSWF($(this).attr("href"), "cb_swf_player", "100%", "90%", "9.0.0", "vendor_bundled/vendor/bower-asset/swfobject/swfobject/expressInstall.swf");
return $("#cb_swf_player");
}
});
// data-box attributes with type=iframe (if someone needs to override anything above)
$(this).find("#col1 a[data-box*='box'][data-box*='type=iframe']").colorbox({
iframe: true
});
// inline content: hrefs starting with #
$(this).find("#col1 a[data-box*='box'][href^='#']").colorbox({
inline: true,
width: "50%",
height: "50%",
href: function(){
return $(this).attr('href');
}
});
// titles (for captions):
// by default get title from the title attribute of the link (in all columns)
$(this).find("a[data-box*='box'][title]").colorbox({
title: function(){
return $(this).attr('title');
}
});
// but prefer the title from title attribute of a wrapped image if any (in all columns)
$(this).find("a[data-box*='box'] img[title]").colorbox({
title: function(){
return $(this).attr('title');
},
photo: true, // and if you take title from the image you need photo
href: function(){ // and href as well (for colobox 1.3.6 tiki 5.0)
return $(this).parent().attr("href");
}
});
/* Shadowbox params compatibility extracted using regexp functions */
var re, ret;
// data-box attributes containing title param overrides title attribute of the link (shadowbox compatible)
$(this).find("#col1 a[data-box*='box'][data-box*='title=']").colorbox({
title: function () {
re = /(title=([^;\"]+))/i;
ret = $(this).attr("data-box").match(re);
return ret[2];
}
});
// data-box attributes containing height param (shadowbox compatible)
$(this).find("#col1 a[data-box*='box'][data-box*='height=']").colorbox({
height: function () {
re = /(height=([^;\"]+))/i;
ret = $(this).attr("data-box").match(re);
return ret[2];
}
});
// data-box attributes containing width param (shadowbox compatible)
$(this).find("#col1 a[data-box*='box'][data-box*='width=']").colorbox({
width: function () {
re = /(width=([^;\"]+))/i;
ret = $(this).attr("data-box").match(re);
return ret[2];
}
});
// links generated by the {COLORBOX} plugin
if (jqueryTiki.colorbox) {
$(this).find("a[data-box^='shadowbox[colorbox']").each(function () {$(this).attr('savedTitle', $(this).attr('title'));});
$(this).find("a[data-box^='shadowbox[colorbox']").colorbox({
title: function() {
return $(this).attr('savedTitle'); // this fix not required is colorbox was disabled
}
});
}
};
$.applyColorbox = function() {
if (jqueryTiki.colorbox) {
$('body').applyColorbox();
}
};
// ColorBox setup (Shadowbox, actually "<any>box" replacement)
if (jqueryTiki.colorbox && !jqueryTiki.mobile) {
$().on('cbox_complete', function(){
$("#cboxTitle").wrapInner("<div></div>");
});
$.applyColorbox();
} // end if (jqueryTiki.colorbox)
if (jqueryTiki.zoom) {
$("a[data-box*=zoom]").each(function () {
$(this)
.wrap('<span class="img_zoom"></span>')
.parent()
.zoom({
url: $(this).attr("href")
});
});
}
if (jqueryTiki.smartmenus) {
// Init all menus
var $navbars = $('ul.navbar-nav');
var options = {
noMouseOver: jqueryTiki.smartmenus_open_close_click,
hideOnClick: jqueryTiki.smartmenus_open_close_click,
collapsibleBehavior: jqueryTiki.smartmenus_collapsible_behavior,
subIndicators: true,
collapsibleShowFunction: function($ul, complete) { $ul.slideDown(200, complete); },
collapsibleHideFunction: function($ul, complete) { $ul.slideUp(250, complete); }
};
$.SmartMenus.Bootstrap.init($navbars, options);
}
// Select2
$.fn.applySelect2 = function () {
if (jqueryTiki.select2) {
$("select:not(.allow_single_deselect):not(.noselect2)").tiki("select2");
$(document).on('select2:open', () => {
let allSelectSearchInputs = document.querySelectorAll('.select2-container--open .select2-search__field');
$(this).one('mouseup',()=>{
setTimeout(()=>{
allSelectSearchInputs[allSelectSearchInputs.length - 1].focus();
},0);
});
});
}
};
$.applySelect2 = function() {
return $('body').applySelect2();
};
if (jqueryTiki.select2) {
$.applySelect2();
}
$( function() {
$("#keepOpenCbx").click(function() {
if (this.checked) {
setCookie("fgalKeepOpen", "1");
} else {
setCookie("fgalKeepOpen", "");
}
});
var keepopen = getCookie("fgalKeepOpen");
$("#keepOpenCbx").prop("checked", !! keepopen);
});
// end fgal fns
$.paginationHelper();
// bind clickModal to links with or in click-modal class
$(document).on('click', 'a.click-modal, .click-modal a', $.clickModal({
size: 'modal-lg',
backdrop: 'static',
success: function (data) {
let redirect = $(this).data('modal-submit-redirect-url') || $(this).parent().data('modal-submit-redirect-url');
if (! redirect && data.url) {
redirect = data.url;
}
window.location.href = redirect || window.location.href.replace(/#.*$/, '');
}
}));
if (jqueryTiki.numericFieldScroll === "none" || jqueryTiki.numericFieldScroll === null){
// disable mousewheel on a input number field when in focus
// (to prevent browsers change the value when scrolling)
$(document).on('focus', 'input[type=number]', function (e) {
$(this).on('wheel.disableScroll', function (e) {
e.preventDefault()
})
});
$(document).on('blur', 'input[type=number]', function (e) {
$(this).off('wheel.disableScroll')
});
}
}); // end $document.ready
//For ajax/custom search
$document.on('pageSearchReady', function() {
$.paginationHelper();
});
// moved from tiki-list_file_gallery.tpl in tiki 6
function checkClose() {
if (!$("#keepOpenCbx").prop("checked")) {
window.close();
} else {
window.blur();
if (window.opener) {
window.opener.focus();
}
}
}
/*
* JS only textarea fullscreen function (for Tiki 5+)
*/
$(function() { // if in translation-diff-mode go fullscreen automatically
if ($("#diff_outer").length && !$.trim($(".wikipreview .wikitext").html()).length) { // but not if previewing (TODO better)
toggleFullScreen("editwiki");
}
});
function sideBySideDiff() {
if ($('.side-by-side-fullscreen').size()) {
$('.side-by-side-fullscreen').remove();
return;
}
var $diff = $('#diff_outer').remove(), $zone = $('.edit-zone');
$zone.after($diff.addClass('side-by-side-fullscreen'));
$diff.find('#diff_history').height('');
}
function toggleFullScreen(area_id) {
if ($("input[name=wysiwyg]").val() === "y") { // quick fix to disable side-by-side translation for wysiwyg
$("#diff_outer").css({
position: "inherit",
height: "400px",
overflowX: "auto"
});
return;
}
var textarea = $("#" + area_id);
//codemirror interation and preservation
var textareaEditor = syntaxHighlighter.get(textarea);
if (textareaEditor) {
syntaxHighlighter.fullscreen(textarea);
sideBySideDiff();
return;
}
var toolbar = $('#editwiki_toolbar'),
preview = $("#autosave_preview"),
comment = $("#comment").parents("fieldset:first"),
screen = $('.TextArea-fullscreen'),
zone = $('.edit-zone', screen);
screen.add(textarea).css('height', '');
//removes wiki command buttons (save, cancel, preview) from fullscreen view
$('.TextArea-fullscreen .actions').remove();
if (textarea.parent().hasClass("ui-wrapper")) {
textarea.resizable("destroy"); // if codemirror is off, jquery-ui resizable messes this up
}
var textareaParent = textarea.parents(".tab-content:first").toggleClass('TextArea-fullscreen');
if (textareaParent.hasClass('TextArea-fullscreen')) {
$('body').css('overflow', 'hidden');
$('.tabs,.rbox-title').toggle();
$('#fullscreenbutton').hide();
var win = $window
.data('cm-resize', true),
diff = $("#diff_outer"),
msg = $(".translation_message"),
actions = $('.actions', textarea.parents("form"));
//adds wiki command buttons (save, cancel, preview) to fullscreen view
actions.clone().appendTo('.TextArea-fullscreen');
actions = $('.actions', $('.TextArea-fullscreen'));
comment.css({ // fix comments fieldset to bottom and hide others (like contributions)
position: "absolute",
bottom: actions.outerHeight() + "px",
width: "100%"
}).nextAll("fieldset").hide();
preview.css({
position: "absolute",
top: 0,
left: 0
});
win.resize(function() {
screen = $('.TextArea-fullscreen');
actions = $('.actions', screen);
comment = $("#comment").parents("fieldset:first");
if (win.data('cm-resize') && screen) {
screen.css('height', win.height() + 'px');
var swidth = win.width() + "px";
var commentMargin = parseInt(comment.css("paddingTop").replace("px", "")) * 4;
commentMargin += parseInt(comment.css("borderBottomWidth").replace("px", "")) * 2;
var innerHeight = win.height() - comment.outerHeight() - commentMargin - actions.outerHeight();
// reducing innerHeight by 85px in prev line makes the "Describe the change you made:" and
// "Monitor this page:" edit fields visible and usable. Tested in all 22 themes in Tiki-12 r.48429
if (diff.length) {
swidth = (screen.width() / 2) + "px";
innerHeight -= msg.outerHeight();
msg.css("width", (screen.width() / 2 - msg.css("paddingLeft").replace("px", "") - msg.css("paddingRight").replace("px", "")) + "px");
diff.css({
width: swidth,
height: innerHeight + 'px'
});
$('#diff_history').height(innerHeight + "px");
}
textarea.css("width", swidth);
toolbar.css('width', swidth);
zone.css("width", swidth);
preview.css("width", swidth);
textarea.css('height', (innerHeight - toolbar.outerHeight()) + "px");
}
});
setTimeout(function () {$window.resize();}, 500); // some themes (coelesce) don't show scrollbars unless this is delayed a bit
} else {
textarea.css("width", "");
toolbar.css('width', "");
zone.css({ width: "", height: ""});
screen.css("width", "");
comment.css({ position: "", bottom: "", width: "" }).nextAll("fieldset").show();
preview.css({ position: "", top: "", left: "" });
$('body').css('overflow', '');
$('.tabs,.rbox-title').toggle();
$('#fullscreenbutton').show();
$window.removeData('cm-resize');
}
sideBySideDiff();
}
/* Simple tiki plugin for jQuery
* Helpers for autocomplete and sheet
*/
var xhrCache = {}, lastXhr; // for jq-ui autocomplete
$.fn.tiki = function(func, type, options, excludepage) {
var opts = {}, opt;
switch (func) {
case "autocomplete":
if (jqueryTiki.autocomplete && jqueryTiki.ui) {
if (typeof type === 'undefined') { // func and type given
// setup error - alert here?
return null;
}
options = options || {};
var requestData = {}, _renderItem = null;
var url = "";
switch (type) {
case "pagename":
url = "tiki-listpages.php?listonly&initial=" + (options.initial ? options.initial + "&nonamespace" : "")+"&exclude_page="+excludepage;
break;
case "groupname":
url = "tiki-ajax_services.php?listonly=groups";
break;
case "username":
url = "tiki-ajax_services.php?listonly=users";
break;
case "usersandcontacts":
url = "tiki-ajax_services.php?listonly=usersandcontacts";
break;
case "userrealname":
url = "tiki-ajax_services.php?listonly=userrealnames";
break;
case "tag":
url = "tiki-ajax_services.php?listonly=tags&separator=+";
break;
case "icon":
url = null;
opts.source = Object.keys(jqueryTiki.iconset.icons).concat(jqueryTiki.iconset.defaults);
_renderItem = function(ul, item) {
return $("<li>")
.attr("data-value", item.value )
.append($().getIcon(item.value))
.append(" ")
.append(item.label)
.appendTo(ul);
};
break;
case 'trackername':
url = "tiki-ajax_services.php?listonly=trackername";
break;
case 'trackervalue':
if (typeof options.fieldId === "undefined") {
// error
return null;
}
$.extend( requestData, options );
options = {};
url = "list-tracker_field_values_ajax.php";
break;
case "reference":
url = "tiki-ajax_services.php?listonly=references";
break;
}
var multiple = options.multiple && (type == 'usersandcontacts' || type == 'userrealname' || type == 'username' || type == 'reference');
opts = $.extend({ // default options for autocompletes in tiki
minLength: 2,
source: function( request, response ) {
if( multiple ) {
request.term = (''+request.term).split( /,\s*/ ).pop();
}
if (options.tiki_replace_term) {
request.term = options.tiki_replace_term.apply(null, [request.term]);
}
var cacheKey = "ac." + type + "." + request.term;
if ( cacheKey in xhrCache ) {
response( xhrCache[ cacheKey ] );
return;
}
request.q = request.term;
$.extend( request, requestData );
lastXhr = $.getJSON( url, request, function( data, status, xhr ) {
xhrCache[ cacheKey ] = data;
if ( xhr === lastXhr ) {
response( data, function (item) {
return item
})
}});
},
focus: function(ev) {
// Important for usability handling below to prevent non-valid selections
ev.preventDefault();
},
search: function() {
if( multiple ) {
// custom minLength
var term = (''+this.value).split( /,\s*/ ).pop();
if ( term.length < 2 ) {
return false;
}
}
},
select: function(e, ui) {
if( multiple ) {
var terms = ''+this.value;
terms = terms.replace(';', ',');
terms = terms.split( /,\s*/ );
// remove the current input
terms.pop();
// add the selected item
terms.push( ui.item.value );
// add placeholder to get the comma-and-space at the end
terms.push( "" );
this.value = terms.join( ", " );
return false;
} else {
$(this).data('selected', true);
}
}
}, opts);
$.extend(opts, options);
if(options.mustMatch && multiple) {
// Control editing of autocomplete to avoid messing with selection
this.on("keydown", function (e) {
if (e.which === 8 || e.which === 46) {
e.preventDefault();
var terms = ''+this.value;
terms = terms.replace(';', ',');
terms = terms.split( /,\s*/ );
// remove the current input and the last previous item
var lastterm = terms.pop();
if (lastterm === '') {
terms.pop();
}
// add placeholder to get the comma-and-space at the end
terms.push( "" );
this.value = terms.join( ", " );
} else if (e.which === 37 || e.which === 39) {
e.preventDefault();
}
});
this.on("focus click", function() {
var currentVal = $(this).val();
$(this).val('').val(currentVal);
});
} else if (options.mustMatch) {
if ($(this).val()) {
// if there is value to begin then consider as selected
$(this).data('selected', true);
}
$(this).on("blur", function() {
if (! $(this).data('selected')) {
$(this).val('');
}
});
$(this).on("keydown", function(e) {
if ($(this).data('selected') && (e.which === 8 || e.which === 46)) {
e.preventDefault();
$(this).val('');
$(this).data('selected', false);
} else if ($(this).data('selected') && (e.which > 47 || e.which === 32)) {
e.preventDefault();
} else if (e.which === 13 && !$(this).data('selected')) {
e.preventDefault();
}
});
}
return this.each(function() {
var $element = $(this).autocomplete(opts).blur( function() {
$(this).removeClass( "ui-autocomplete-loading").change();
});
if (_renderItem && $element.length) {
$element.autocomplete("instance")._renderItem = _renderItem;
}
});
}
break;
case "carousel":
if (jqueryTiki.carousel) {
opts = {
imagePath: "vendor_bundled/vendor/jquery-plugins/infinitecarousel/images/",
autoPilot: true
};
$.extend(opts, options);
return this.each(function() {
$(this).infiniteCarousel(opts);
});
}
break;
case "datepicker":
case "datetimepicker":
if (jqueryTiki.ui) {
switch (type) {
case "jscalendar": // replacements for jscalendar
// timestamp result goes in the options.altField
if (typeof options.altField === "undefined") {
alert("jQuery.ui datepicker jscalendar replacement setup error: options.altField not set for " + $(this).attr("id"));
debugger;
}
opts = {
showOn: "both",
buttonText: '',
changeMonth: jqueryTiki.changeMonth,
changeYear: jqueryTiki.changeYear,
dateFormat: jqueryTiki.shortDateFormat,
timeFormat: jqueryTiki.shortTimeFormat,
showButtonPanel: true,
altFormat: "@",
altFieldTimeOnly: false,
onClose: function(dateText, inst) {
$.datepickerAdjustAltField(func, inst);
}
};
break;
default:
opts = {
showOn: "both",
buttonText: '',
dateFormat: jqueryTiki.shortDateFormat,
showButtonPanel: true,
firstDay: jqueryTiki.firstDayofWeek
};
break;
}
$.extend(opts, options);
if (func === "datetimepicker") {
return this.each(function() {
$(this).datetimepicker(opts);
});
} else {
return this.each(function() {
$(this).datepicker(opts);
});
}
}
break;
case "accordion":
if (jqueryTiki.ui) {
opts = {
autoHeight: false,
collapsible: true,
navigation: true
// change: function(event, ui) {
// // sadly accordion active property is broken in 1.7, but fix is coming in 1.8 so TODO
// setCookie(ui, ui.options.active, "accordion");
// }
};
$.extend(opts, options);
return this.each(function() {
$(this).accordion(opts);
});
}
break;
case "select2":
if (jqueryTiki.select2) {
var selects = this;
opts = {
containerCssClass: 'select2-selection-tiki',
dropdownCssClass: 'select2-dropdown-tiki dropdown-animate',
theme: 'bootstrap-5',
dir: $('html').attr('dir') || 'ltr',
language: {
noResults: function () {
return tr('No results match');
}
},
templateSelection: function (obj) {
var $el = $(`<span class="select2-selection__choice__value" data-option-value="${obj.id}"></span>`);
$el.text(obj.text);
return $el;
},
};
// Adds and/or merge more options
$.extend(opts, options);
selects.each(function() {
let $select = $(this),
multiple = $select.prop('multiple'),
placeholder = $.trim($select.find("option[value='']").text());
if (! placeholder) {
placeholder = multiple ? tr('Select Some Options') : tr('Select an Option');
}
// <select data-placeholder="some placeholder">...</select> overrides option placeholder
opts.allowClear = ! multiple;
opts.closeOnSelect = true;
opts.placeholder = placeholder;
opts.width = $select.parent().hasClass('input-group') ? 'resolve' : '100%';
// Fix Boostrap4 Modal scrolling - move dropdown menu to modal-body
opts.dropdownParent = $select.closest('.modal-body').length > 0 ? $select.closest('.modal-body') : $(document.body);
// Disables search option for single selects
if ($select.hasClass('select2-nosearch')) {
opts.minimumResultsForSearch = Infinity;
}
// Initialize select2 with created options
$select.select2(opts);
// Fix Boostrap4 modal scrolling while Select is opened
// but disables dropdown auto position
if ($select.closest('.modal-body').length > 0) {
$select
.on('select2:open', function () {
$select.closest('.modal').off('scroll');
});
}
// Disables search option for multiple selects
if (multiple && $select.hasClass('select2-nosearch')) {
$select.on('select2:opening select2:closing', function (event) {
let $searchfield = $(this).parent().find('.select2-search__field');
$searchfield.prop('disabled', true);
});
}
if (jqueryTiki.select2_sortable && multiple) {
$select
.next('.select2-container')
.find('.select2-selection--multiple ul.select2-selection__rendered')
.sortable({
containment: "parent",
items: "> li.select2-selection__choice",
})
.on("sortstop", function (event, ui) {
sortOptionsBySortableOrder();
})
.parents("form:first:not(.customsearch_form)")
.submit(function () {
if (!$(this).data("select2-multi-submitted") && !$select.hasClass("noselect2")) {
sortOptionsBySortableOrder();
$select.trigger('change');
$(this).data("select2-multi-submitted", true);
}
})
;
}
function sortOptionsBySortableOrder() {
$select
.next('.select2-container')
.find("li.select2-selection__choice:not(.select2-search--inline) .select2-selection__choice__value")
.each(function () {
let option = $select.find(`option[value="${$(this).data("option-value")}"]`)[0];
moveElementToEndOfParent($(option));
});
function moveElementToEndOfParent($element) {
let parent = $element.parent();
$element.detach();
parent.append($element);
}
}
});
}
break;
} // end switch(func)
};
(function($) {
$.datepickerAdjustAltField = function(func, inst) {
$.datepicker._updateAlternate(inst); // make sure the hidden field is up to date
var val = $(inst.settings.altField).val(), timestamp;
if (func === "datetimepicker") {
val = val.substring(0, val.indexOf(" "));
timestamp = parseInt(val / 1000, 10);
if (!timestamp || isNaN(timestamp)) {
$.datepicker._setDateFromField(inst); // seems to need reminding when starting empty
$.datepicker._updateAlternate(inst);
val = $(inst.settings.altField).val();
val = val.substring(0, val.indexOf(" "));
timestamp = parseInt(val / 1000, 10);
}
if (timestamp && inst.settings && inst.settings.timepicker) { // if it's a datetimepicker add on the time
let date = new Date();
date.setTime(timestamp * 1000);
date.setHours(inst.settings.timepicker.hour, inst.settings.timepicker.minute);
timestamp = date.getTime() / 1000;
}
} else {
timestamp = parseInt(val / 1000, 10);
}
$(inst.settings.altField).val(timestamp ? timestamp : "").change();
};
// the jquery.ui _gotoToday function doesn't seem to work any more, so override that and add a call to _setDate
$.datepicker._jquibase_gotoToday = $.datepicker._gotoToday;
$.datepicker._gotoToday = function (id) {
var inst = this._getInst($(id)[0]);
this._jquibase_gotoToday(id);
this._setDate(inst, new Date());
// the alternate field gets updated when the dialog closes
};
/**
* Adds annotations to the content of text in ''container'' based on the
* content found in selected dts.
*
* Used in comments.tpl
*/
$.fn.addnotes = function( container ) {
return this.each(function(){
var comment = this;
var text = $('dt:contains("note")', comment).next('dd').text();
var title = $('h4:first', comment).clone();
var body = $('.comment-body:first', comment).clone();
body.find('dt:contains("note")').closest('dl').remove().addClass('card');
if( text.length > 0 ) {
var parents = container.find(':contains("' + text + '")').parent();
var node = container.find(':contains("' + text + '")').not(parents)
.addClass('note-editor-text alert-info')
.each( function() {
var child = $('dl.note-list',this);
if( ! child.length ) {
child = $('<dl class="note-list list-group-item-info"/>')
.appendTo(this)
.hide();
$(this).click( function() {
child.toggle();
} );
}
child.append( title )
.append( $('<dd/>').append(body) );
} );
}
});
};
/**
* Convert a zone to a note editor by attaching handlers on mouse events.
*/
$.fn.noteeditor = function (editlink, link) {
var hiddenParents = null;
var annote = $(link)
.click( function( e ) {
e.preventDefault();
var $block = $('<div/>');
var annotation = $(this).attr('annotation');
$(this).fadeOut(100);
$block.load(editlink.attr('href'), function () {
var msg = "";
if (annotation.length < 20) {
msg = tr("The text you have selected is quite short. Select a longer piece to ensure the note is associated with the correct text.") + "<br />";
}
msg = "<p class='description comment-info'>" + msg + tr("Tip: Leave the first line as it is, starting with \";note:\". This is required") + "</p>";
$block.prepend($(msg));
$('textarea', this)
.val(';note:' + annotation + "\n\n").focus();
$('form', this).submit(function () {
$.post($(this).attr('action'), $(this).serialize(), function () {
$block.dialog('destroy');
// update the comments list
editlink.closest('.comment-container').reload();
});
return false;
});
$block.dialog({
modal: true,
width: 500,
height: 400
});
});
} )
.appendTo(document.body);
$(this).mouseup(function( e ) {
var range;
if( window.getSelection && window.getSelection().rangeCount ) {
range = window.getSelection().getRangeAt(0);
} else if( window.selection ) {
range = window.selection.getRangeAt(0);
}
if( range ) {
var str = $.trim( range.toString() );
if( str.length && -1 === str.indexOf( "\n" ) ) {
annote.attr('annotation', str);
annote.fadeIn(100).position( {
of: e,
at: 'bottom left',
my: 'top left',
offset: '20 20'
} );
} else {
if (annote.css("display") != "none") {
annote.fadeOut(100);
}
if ($("form.comments").css("display") == "none") {
$("form.comments").show();
}
if (hiddenParents) {
hiddenParents.hide();
hiddenParents = null;
}
}
}
});
};
$.fn.browse_tree = function () {
this.each(function () {
$('.treenode:not(.done)', this)
.addClass('done')
.each(function () {
if (getCookie($('ul:first', this).attr('data-id'), $('ul:first', this).attr('data-prefix')) !== 'o') {
$('ul:first', this).css('display', 'none');
}
var $placeholder = $('span.ui-icon:first:not(.control)', this);
if ($('ul:first', this).length) {
var dir = $('ul:first', this).css('display') === 'block' ? 's' : 'e';
if ($placeholder.length) {
$placeholder.replaceWith('<span class="flipper ui-icon ui-icon-triangle-1-' + dir + '" style="float: left;margin-top:.2em;"/>');
} else {
$(this).prepend('<span class="flipper ui-icon ui-icon-triangle-1-' + dir + '" style="float: left;margin-top:.2em;"/>');
}
} else {
if ($placeholder.length) {
$placeholder.replaceWith('<span style="float:left;width:16px;height:16px;margin-top:.2em;"/>');
} else {
$(this).prepend('<span style="float:left;width:16px;height:16px;margin-top:.2em;"/>');
}
}
if ($('div.checkbox', this).length) {
$('div.checkbox', this).css("margin-left", "16px");
}
});
$('.flipper:not(.done)')
.addClass('done')
.css('cursor', 'pointer')
.click(function () {
var body = $(this).parent().find('ul:first');
if ('block' === body.css('display')) {
$(this).removeClass('ui-icon-triangle-1-s').addClass('ui-icon-triangle-1-e');
body.hide('fast');
setCookie(body.data("id"), "", body.data("prefix"));
} else {
$(this).removeClass('ui-icon-triangle-1-e').addClass('ui-icon-triangle-1-s');
body.show('fast');
setCookie(body.data("id"), "o", body.data("prefix"));
}
});
});
return this;
};
$.initTrees = function () {
$(".tree.root:not(.init)").browse_tree().addClass("init");
};
setTimeout($.initTrees, 100);
var fancy_filter_create_token = function(value, label) {
var close, token;
console.log();
close = $('<span class="ui-icon ui-icon-close"/>')
.click(function () {
var ed = $(this).parent().parent();
$(this).parent().remove();
ed.change();
return false;
});
token = $('<span class="token"/>')
.attr('data-value', value)
.text(label)
.attr('contenteditable', false)
.disableSelection()
.append(close);
return token[0];
};
var fancy_filter_build_init = function(editable, str, options) {
if (str === '') {
str = '&nbsp;';
}
editable.html(str.replace(/(\d+)/g, '<span>$1</span>'));
if (options && options.map) {
editable.find('span').each(function () {
var val = $(this).text();
$(this).replaceWith(fancy_filter_create_token(val, JSON.parse(options.map)[val] ? JSON.parse(options.map)[val] : val));
});
}
};
$.fn.fancy_filter = function (operation, options) {
this.each(function () {
switch (operation) {
case 'init':
var editable = $('<div class="fancyfilter form-control"/>'), input = this;
if (editable[0].contentEditable !== null) {
fancy_filter_build_init(editable, $(this).val(), options);
editable.attr('contenteditable', true);
$(this).after(editable).hide();
}
editable
.keyup(function() {
$(this).change();
$(this).mouseup();
})
.change(function () {
$(input).val($('<span/>')
.html(editable.html())
.find('span').each(function() {
$(this).replaceWith(' ' + $(this).attr('data-value') + ' ');
})
.end().text().replace(/\s+/g, ' '));
})
.mouseup(function () {
input.lastRange = window.getSelection().getRangeAt(0);
});
break;
case 'add':
var editable = $(this).next();
editable.find("span[data-value='"+ options.token +"']").remove();
var node = fancy_filter_create_token(options.token, options.label);
editable.append(node);
editable.change();
break;
}
});
return this;
};
$.fn.drawGraph = function () {
this.each(function () {
var $this = $(this);
var width = $this.width();
var height = $this.height() ? $this.height() : Math.ceil( width * 9 / 16 );
var nodes = $this.data('graph-nodes');
var edges = $this.data('graph-edges');
var g = new Graph;
$.each(nodes, function (k, i) {
g.addNode(i);
});
$.each(edges, function (k, i) {
var style = { directed: true };
if( i.preserve ) {
style.color = 'red';
}
g.addEdge( i.from, i.to, style );
});
var layouter = new Graph.Layout.Spring(g);
layouter.layout();
var renderer = new Graph.Renderer.Raphael($this.attr('id'), g, width, height );
renderer.draw();
});
return this;
};
/**
* Handle textarea and input text selections
* Code from:
*
* jQuery Autocomplete plugin 1.1
* Copyright (c) 2009 Jörn Zaefferer
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Now deprecated and replaced in Tiki 7 by jquery-ui autocomplete
*/
$.fn.selection = function(start, end) {
if (start !== undefined) {
if (end === undefined) {
end = start;
}
return this.each(function() {
if( this.selectionStart) {
this.selectionStart = start;
this.selectionEnd = end;
} else if( this.setSelectionRange ){
this.setSelectionRange(start, end);
} else if( this.createTextRange ){
var selRange = this.createTextRange();
if (start == end) {
selRange.move("character", start);
selRange.select();
} else {
selRange.collapse(true);
selRange.moveStart("character", start);
selRange.moveEnd("character", end - start); // moveEnd is relative
selRange.select();
}
}
});
}
var field = this[0];
if( field.selectionStart !== undefined) {
return {
start: field.selectionStart,
end: field.selectionEnd
}
} else if ( field.createTextRange ) {
// from http://the-stickman.com/web-development/javascript/finding-selection-cursor-position-in-a-textarea-in-internet-explorer/
// The current selection
var range = document.selection.createRange();
// We'll use this as a 'dummy'
var stored_range = range.duplicate();
// Select all text
stored_range.moveToElementText( field );
// Now move 'dummy' end point to end point of original range
stored_range.setEndPoint( 'EndToEnd', range );
// Now we can calculate start and end points
var textProperty = range.htmlText ? "htmlText" : "text"; // behaviour changed in IE10 (approx) so htmlText has unix line-ends which works (not 100% sure why)
var selectionStart = stored_range[textProperty].length - range[textProperty].length;
var selectionEnd = selectionStart + range[textProperty].length;
return {
start: selectionStart,
end: selectionEnd
}
} };
$.fn.comment_toggle = function () {
this.each(function () {
var $target = $(this.hash);
$target.hide();
$(this).click(function () {
if ($target.is(':visible')) {
$target.hide(function () {
$(this).empty();
});
} else {
$target.comment_load($(this).attr('href'));
}
return false;
});
if (location.search.indexOf("comzone=show") > -1 || location.hash.match(/threadId=?(\d+)/)) {
var comButton = this;
setTimeout(function() {
$(comButton).click();
}, 500);
}
});
return this;
};
$.fn.comment_load = function (url) {
var $top = $('#top');
$('.note-list', $top).remove();
this.each(function () {
var comment_container = this;
if (! comment_container.reload) {
comment_container.reload = function () {
$(comment_container).empty().comment_load(url);
};
}
$(this).addClass('comment-container');
$(this).load(url, function (response, status) {
$(this).show();
if (jqueryTiki.useInlineComment && ! jqueryTiki.useInlineAnnotations) {
$('.comment.inline dt:contains("note")', this)
.closest('.comment')
.addnotes($top);
$top.noteeditor($('.comment-form:last a', comment_container), '#note-editor-comment');
}
$('.button.comment-form.autoshow a').addClass('autoshown').click().removeClass('autoshown'); // allow autoshowing of comment forms through autoshow css class
var match = location.hash.match(/threadId=?(\d+)/);
if (match) {
var $tab = $(this).parents(".tab-pane"),
$tabContent = $(this).parents(".tab-content");
// if we're in an inactive tab then show the right one for this comment threadId
if ($tab.length && ! $tab.is(".active")) {
$tabContent.find(".tab-pane").each(function (index) {
if (this === $tab[0]) {
$(".nav-tabs li:nth(" + index + ") a", $tabContent.parent()).tab("show");
}
})
}
var $comment = $(".comment[data-comment-thread-id=" + match[1] + "]");
var top = $comment.offset().top;
$('html, body').animate({
scrollTop: top
},{
duration: 2000,
complete: function() {
$comment.addClass("comment-highlight");
}
});
}
});
});
return this;
};
$(document).on('click', '.comment-form.buttons > a.btn', function () {
var comment_container = $(this).closest('.comment-container, .ui-dialog-content')[0];
$('.comment-form form:not(.commentRatingForm)', comment_container).each(function() { // remove other forms apart from the ratings vote form
var $p = $(this).parent();
$p.empty().addClass('button').addClass('buttons').append($p.data('previous'));
});
if (!$(this).hasClass('autoshown')) {
$(".comment").each(function () {
$("article > *:not(ol)", this).each(function () {
$(this).css("opacity", 0.6);
});
});
}
$(this).parents('.comment:first').find("*").css("opacity", 1);
var $formContainer = null;
if ($(this).data('target')) {
$formContainer = $($(this).data('target'));
} else {
$formContainer = $(this).parents('.buttons');
}
$(this).parents('.buttons').data('previous', $(this).siblings().addBack()).empty().removeClass('buttons').removeClass('button');
// Update buttons if loaded as a modal
$('.modal.fade.show').trigger('tiki.modal.redraw');
$formContainer.load($(this).attr('href'), function () {
var form = $('form', this).submit(function () {
var errors, current = this;
$(current).tikiModal(tr("Saving..."));
//Synchronize textarea and codemirror before comment is posted
if (typeof syntaxHighlighter.sync === 'function') {
syntaxHighlighter.sync($(current).find("textarea.wikiedit"));
}
$.post($(current).attr('action'), $(current).serialize(), function (data, st) {
$(current).tikiModal();
if (data.threadId) {
let threadId = data.threadId;
$(current).closest('.comment-container').reload();
$('span.count_comments').each(function () {
var action = $(current).attr('action').match(/tiki-comment-(\w*)/),
count = parseInt($(this).text());
if (action) {
//noinspection FallThroughInSwitchStatementJS
switch (action[1]) {
case "remove":
count--;
break;
case "post":
count++;
// fall through to adjust threadId if necessary
case "edit":
case "moderate":
location.hash = location.hash.replace(/threadId=\d+/, "threadId=" + threadId);
break;
}
$(this).text(count);
}
});
if (data.feedback && data.feedback[0]) {
alert(data.feedback.join("\n"));
}
} else {
errors = $('ol.errors', form).empty();
if (!errors.length) {
$(':submit', current).after(errors = $('<ul class="alert-warning"/>'));
}
$.each(data.errors, function (k, v) {
errors.append($('<li/>').text(v));
});
}
}, 'json');
return false;
});
//allow syntax highlighting
if ($.fn.flexibleSyntaxHighlighter) {
window.codeMirrorEditor = [];
form.find('textarea.wikiedit').flexibleSyntaxHighlighter();
}
$(document).trigger("tiki.ajax.redraw");
});
return false;
});
// scroll to post if #threadId on url in forums
if ($("body.tiki_forums").length) {
let match = location.hash.match(/threadId=?(\d+)/);
if (match) {
let $comment = $("#" + match[0].replace("=", "") + ".post");
let top = $comment.offset().top;
$('html, body').animate({
scrollTop: top
}, 2000, function () {
$comment.animate({
backgroundColor: "#ff8"
}, 250, function () {
$comment.animate({
backgroundColor: ""
}, 1000);
});
});
}
}
$.fn.input_csv = function (operation, separator, value) {
this.each(function () {
var values = $(this).val().split(separator);
if (values[0] === '') {
values.shift();
}
if (operation === 'add' && -1 === values.indexOf("" + value)) {
values.push(value);
} else if (operation === 'delete') {
value = String(value);
while (-1 !== $.inArray(value, values)) {
values.splice($.inArray(value, values), 1);
}
}
$(this).val(values.join(separator));
});
return this;
};
$.service = function (controller, action, query) {
var append = '';
if (query) {
append = '?' + $.buildParams(query);
}
if (action) {
return 'tiki-' + controller + '-' + action + append;
} else {
return 'tiki-' + controller + '-x' + append;
}
};
$.serviceUrl = function (options) {
var o = $.extend({}, options), controller = options.controller, action = options.action;
delete(o.controller);
delete(o.action);
return $.service(controller, action, o);
};
$.buildParams = function (query, prefix, suffix) {
prefix = prefix || '';
suffix = suffix || '';
return $.map(query, function (v, k) {
if ($.isPlainObject(v)) {
return $.buildParams(v, k + '[', ']');
} else {
return prefix + k + suffix + '=' + tiki_encodeURIComponent(v);
}
}).join('&');
};
$.fn.serviceDialog = function (options) {
this.each(function () {
var $dialog = $('<div/>'), origin = this, buttons = {};
$(this).append($dialog).data('serviceDialog', $dialog);
if (! options.hideButtons) {
buttons[tr('OK')] = function () {
$dialog.find('form:visible').submit();
};
buttons[tr('Cancel')] = function () {
$dialog.dialog('close');
if ($dialog.data('ui-dialog')) {
$dialog.dialog('destroy');
}
};
}
$dialog.dialog({
title: options.title,
minWidth: options.width ? options.width : 500,
height: (options.fullscreen ? $window.height() - 20 : (options.height ? options.height : 600)),
width: (options.fullscreen ? $window.width() - 20 : null),
close: function () {
if (options.close) {
options.close.apply([], this);
}
if ($(this).data('ui-dialog')) {
$(this).dialog('destroy').remove();
}
},
buttons: buttons,
modal: options.modal,
zIndex: options.zIndex
});
$dialog.loadService(options.data, $.extend(options, {origin: origin}));
});
return this;
};
$.fn.loadService = function (data, options) {
var $dialog = this, controller = options.controller, action = options.action, url;
this.each(function () {
if (! this.reload) {
this.reload = function () {
$(this).loadService(data, options);
};
}
});
if (typeof data === "string") {
data = parseQuery(data);
}
if (data && data.controller) {
controller = data.controller;
}
if (data && data.action) {
action = data.action;
}
if (options.origin && $(options.origin).is('a')) {
url = $(options.origin).attr('href');
} else if (options.url) {
url = options.url;
} else {
url = $.service(controller, action);
}
$dialog.tikiModal(tr("Loading..."));
$.ajax(url, {
data: data,
error: function (jqxhr) {
$dialog.html(jqxhr.responseText);
},
success: function (data) {
$dialog.html(data);
$dialog.find('.ajax').click(function (e) {
$dialog.loadService(null, {origin: this});
return false;
});
$dialog.find('.service-dialog').click(function (e) {
if ($dialog.data('ui-dialog')) {
$dialog.dialog('close');
}
return true;
});
$dialog.find('form .submit').hide();
$dialog.find('form:not(.no-ajax)').off("submit").submit(ajaxSubmitEventHandler(function (data) {
data = (data ? data : {});
if (data.FORWARD) {
$dialog.loadService(data.FORWARD, options);
} else if ($dialog.data('ui-dialog')) {
$dialog.dialog('destroy').remove();
}
if (options.success) {
options.success.apply(options.origin, [data]);
}
}));
if (options.load) {
options.load.apply($dialog[0], [data]);
}
$('.confirm-prompt', this).requireConfirm({
success: function (data) {
if (data.FORWARD) {
$dialog.loadService(data.FORWARD, options);
} else {
$dialog.loadService(options.data, options);
}
}
});
},
complete: function () {
$dialog.tikiModal();
if ($dialog.find('form').size() == 0 && $dialog.data('ui-dialog')) {
// If the result contains no form, skip OK/Cancel, and just allow to close
var buttons = $dialog.dialog('option', 'buttons'), n = {};
if (buttons[tr('Cancel')]) {
n[tr('OK')] = buttons[tr('Cancel')];
$dialog.dialog('option', 'buttons', n);
}
}
}
});
};
$.fn.confirmationDialog = function (options) {
var modal = $('#bootstrap-modal'),
modalHeader = modal.find('.modal-header'),
modalBody = $('<div class="modal-body"><p></p></div>'),
modalFooter = $('<div class="modal-footer"></div>'),
modalFooterConfirm = $('<button type="button" class="btn btn-success">' + tr('Confirm') + '</button>'),
modalFooterClose = $('<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">' + tr('Close') + '</button>');
//needed because modal-content is globally being cleared after appearing for the first time
if (!modalHeader || modalHeader.length === 0) {
modal.find('.modal-content').append('<div class="modal-header"><h4></h4></div>');
modalHeader = modal.find('.modal-header');
}
if (options.title) {
modalHeader.find('h4').text(options.title);
} else {
modalHeader.find('h4').text(tr('Confirmation Modal'));
}
if (options.message) {
modalBody.find('p').text(options.message);
} else {
modalBody.find('p').text(tr('Please confirm you want to perform this action.'));
}
modalFooter.append(modalFooterClose, modalFooterConfirm);
modalHeader.after(modalBody);
modalBody.after(modalFooter);
modal.on('hidden.bs.modal', function (e) {
if (options.close) {
options.close();
}
});
modalFooterConfirm.on('click', function (e) {
modal.modal('hide');
if (options.success) {
options.success();
}
});
modal.modal('show');
return this;
};
$.fn.requireConfirm = function (options) {
this.click(function (e) {
e.preventDefault();
$(this).doConfirm(options);
return false;
});
return this;
};
$.fn.doConfirm = function (options) {
var message = options.message, link = this;
if (! message) {
message = $(this).data('confirm');
}
if (confirm (message)) {
var $this = $(this);
$this.tikiModal(" ");
$.ajax($(this).attr('href'), {
type: 'POST',
dataType: 'json',
data: {
'confirm': 1
},
success: function (data) {
$this.tikiModal();
options.success.apply(link, [data]);
},
error: function (jqxhr) {
$this.tikiModal();
$(link).closest('form').showError(jqxhr);
}
});
}
};
$.fn.showError = function (message) {
if (message.responseText) {
if (message.getResponseHeader("Content-Type").indexOf("text/html") === -1) {
var data = JSON.parse(message.responseText);
message = data.message;
} else {
message = $(message.responseText).text(); // can be html
}
} else if (typeof message !== 'string') {
message = "";
}
this.each(function () {
var parts, that = this;
if (parts = message.match(/^<!--field\[([^\]]+)\]-->(.*)$/)) {
field = parts[1];
message = parts[2];
if (that[field]) {
that = that[field];
}
}
var validate = false, errors = {}, field, $additional = $('<ul>');
if (jqueryTiki.validate) {
validate = $(that).closest('form').validate()
}
if (validate) {
if (! $(that).attr('name')) {
$(that).attr('name', $(that).attr('id'));
}
if (that !== validate.currentForm) {
field = $(that).attr('name');
}
if (field) {
errors[field] = message;
validate.showErrors(errors);
} else {
// No specific field, assign as form error
$additional.append($('<li>').text(message));
}
setTimeout(function () {
$('#tikifeedback li').filter(function () {
return $(this).text() === message;
}).remove();
if ($('#tikifeedback ul').is(':empty')) {
$('#tikifeedback').empty();
}
}, 100);
} else {
$additional.append($('<li>').text(message));
}
if (! $additional.is(':empty')) {
// Write form errors at the top, please stop removing them
$('.ajax-errors', this).remove();
$('<div class="ajax-errors alert alert-danger alert-dismissable"><button type="button" class="close" data-bs-dismiss="alert" aria-hidden="true">&times;</button></div>')
.prependTo(this)
.append($additional);
}
// Style the bootstrap form-group as an error
$('.form-group').removeClass('has-error')
.find('label.error:visible')
.addClass('form-text')
.prepend('<span class="fas fa-flag"/> ')
.closest('.form-group').addClass('has-error')
});
return this;
};
$.fn.clearError = function () {
this.each(function () {
$(this).closest('form').find('label.error[for="' + $(this).attr('name') + '"]').remove();
$(this).closest('form').find('.form-group.has-error').removeClass('has-error');
});
return this;
};
// sort result containing all galleries
function sortResult(result) {
result.sort(function(a, b) {
if (a.parent_title) {
var titleA = a.parent_title.toUpperCase(); // ignore upper and lowercase
var titleB = b.parent_title.toUpperCase(); // ignore upper and lowercase
if (titleA < titleB) {
return -1;
}
if (titleA > titleB) {
return 1;
}
}
// names must be equal
return 0;
});
}
// search the title of each parent gallery
function parentTitle(result, parent) {
var title='';
$.each(result, function(key, value) {
if(value.object_id === parent){
title= value.title + ' > ';
}
});
return title;
}
function loadSelectorData(filter, args, success) {
if (! $.object_selector_cache) {
$.object_selector_cache = {};
}
if ($.isFunction(args)) {
success = args;
args = {};
}
var item, url;
url = $.service('search', 'lookup', $.extend(args, {
filter: filter
}));
if (item = $.object_selector_cache[url]) {
if (item.data) {
success(item.data);
} else {
item.queue.push(success);
}
} else {
item = $.object_selector_cache[url] = {
data: null,
queue: [success]
};
$.getJSON(url, function (data) {
item.data = data;
$.each(item.queue, function (k, f) {
f(data);
});
item.queue = [];
});
}
}
$._object_selector_add_item = function (type, $select, $results, parent_title, item, title, status_icon, selected) {
var checkname = $select.closest('.object-selector, .object-selector-multi')
.find('.primary').attr('id') + '_sel';
var suffix = $results.find('.form-check').lenght || 0;
$('<option>')
.val(item)
.data('label', title)
.text((typeof parent_title === 'undefined' || parent_title === null ) ? title.replace(/\n/g, " / ") : parent_title + '' + title.replace(/\n/g, " / ")) //replace newline with a slash since it's in a select
.prop('selected', selected)
.appendTo($select);
$('<div class="form-check"><input type="' + type + '" class="form-check-input" ><label class="form-check-label"></label></div>')
.find('label').append(status_icon ? status_icon + ' ' + title : title).end()
.find(':radio, :checkbox')
.attr('name', checkname)
.prop('checked', selected)
.val(item)
.end()
.appendTo($results);
};
$.fn._object_selector_update_results = function (type, result, initial) {
var $container = this,
$results = $container.find('.results'),
$select = $container.find('select'),
$noresults = $('.no-result', this),
selection = [];
this.find(':radio:checked, :checkbox:checked')
.not('.protected')
.each(function () {
selection.push($(this).val());
});
this.find(':radio:not(:checked), :checkbox:not(:checked)')
.not('.protected')
.closest('.form-check')
.remove();
$select
.find('option:not(:checked)')
.not('.protected')
.remove();
$noresults.toggleClass('d-none', selection.length !== 0);
// add all galleries parent titles
$.each(result, function (key, value) {
if (value.parent_id) {
value.parent_title = parentTitle(result, value.parent_id);
}
result[key] = value;
});
// sort result by galleries parent titles
sortResult(result);
$.each(result, function (key, value) {
var current = value.object_type + ':' + value.object_id;
var selected = false;
if (value.object_id === '') {
current = value.object_type;
}
var currentValue = $select.data('current-value');
if (currentValue === current) {
selected = true;
}
if (-1 === selection.indexOf(current)) {
if (initial) {
$._object_selector_add_item(type, $select, $([]), value.parent_title, current, value.title, value.status_icon, selected);
} else {
$._object_selector_add_item(type, $select, $results, value.parent_title, current, value.title, value.status_icon, selected);
}
} else {
$("option[value='" + current + "']", $select).text(value.title);
}
});
$select.trigger("change.select2");
};
$.fn.object_selector = function (action, value, title) {
var args = arguments;
this.each(function () {
var input = this
, $simple = $(this).prev()
, filter = $(input).data('filters')
, threshold = $(input).data('threshold')
, format = $(input).data('format') || ''
, sort = $(input).data('sort') || 'score_desc'
, parentobject = $(input).data('parent')
, parentkey = $(input).data('parentkey')
, searchField = $(input).data('searchfield') || 'title'
;
$(input).addClass('primary').hide();
$simple.hide();
var $spinner = $(this).parent(),
$container = $(input).closest('.object-selector'),
$select = $container.find('select').first(),
$filter = $container.find(':text.filter').first(),
$search = $container.find('input.search').first(),
$panel = $container.find('.card').first();
if (action === 'set') {
$select.val(value);
if ($select.val() !== value && title) {
// for multilingual, object returned is JSON. try to parse and split
// with ' / ', otherwise catch and use title as is.
try
{
var titleObj = JSON.parse(title);
var titleArr = $.map(titleObj, function(el) { return el });
title = titleArr.join(" / ");
}
catch(e)
{
// do nothing
}
$._object_selector_add_item('radio', $select, $container.find('.results'), null, value, title, null, true);
$select.trigger("change.select2");
}
$(input)
.val(value)
.data('label', title)
.change();
return;
}
if (action === 'setfilter') {
filter[args[1]] = args[2];
$(input).data('filters', filter);
$container.find('.too-many').hide();
$search.click();
return;
}
if (parentobject && parentkey) {
filter[parentkey] = $(parentobject).val();
$(parentobject).on('change', function () {
$(input)
.data('use-threshold', 1)
.object_selector('setfilter', parentkey, $(this).val());
});
}
let triggerReady = function () {
$(document).trigger(
"ready.object_selector",
[$container]
);
};
if (threshold !== -1) {
$spinner.tikiModal(" ");
loadSelectorData(filter, {maxRecords: threshold, format: format, sort_order: sort}, function (data) {
$container._object_selector_update_results('radio', data.resultset.result, true);
$spinner.tikiModal();
if (data.resultset.count <= threshold) {
$select.parent().removeClass('d-none');
} else {
$panel.removeClass('d-none');
}
triggerReady();
});
} else {
$panel.removeClass('d-none');
triggerReady();
}
$panel.on('click', ':radio', function () {
if ($(this).is(':checked')) {
$(input).object_selector('set', $(this).val(), $(this).parent().text());
}
});
$(input).change(function () {
var val = $(this).val(), id = null;
if (val) {
var splitarray = val.split(':');
id = splitarray[1];
if(splitarray.length > 2){
for(var i = 2; i < splitarray.length; i++)
{
id = id + ':' + splitarray[i];
}
}
}
if ($simple.val() != id) {
$simple.val(id).change();
}
});
$simple.change(function () {
var target = filter.type + ':' + $(this).val();
if (filter.type && $(input).val() != target) {
$(input).val(target).change();
}
});
$select.change(function () {
if ($(input).val() != $select.val()) {
$(input).data('label', $select.find('option:selected').text());
$(input).val($select.val()).change();
}
});
$search.click(function () {
$spinner = $container.tikiModal(" ");
var selectorArgs = {format: format, sort_order: sort};
if ($(input).data('use-threshold') && threshold !== -1) {
selectorArgs.maxRecords = threshold;
$(input).data('use-threshold', 0);
}
filter[searchField] = $filter.val();
loadSelectorData(filter, selectorArgs, function (data) {
$container._object_selector_update_results('radio', data.resultset.result, false);
$spinner.tikiModal();
triggerReady();
});
});
$filter.keypress(function (e) {
if (e.which === 13) {
e.preventDefault();
$search.click();
}
});
});
return this;
};
$.fn.object_selector_multi = function (action) {
var args = arguments;
this.each(function () {
var $textarea = $(this).hide().addClass('primary')
, $container = $(this).closest('.object-selector-multi')
, $select = $container.find('select')
, $simpleinput = $textarea.prev(':text').hide()
, $basic = $container.find('.basic-selector')
, $panel = $container.find('.card')
, $search = $container.find('input.search').first()
, $filter = $container.find(':text.filter').first()
, filter = $textarea.data('filters')
, wildcard = $textarea.data('wildcard')
, threshold = $textarea.data('threshold')
, format = $textarea.data('format') || ''
, parentobject = $textarea.data('parent')
, parentkey = $textarea.data('parentkey')
, sort = $textarea.data('sort') || 'score_desc'
, initialValues = $select.val()
, separator = $simpleinput.data('separator')
, searchField = $textarea.data('searchfield') || 'title'
, extratype = $textarea.data('extratype')
;
if (action === 'setfilter') {
filter[args[1]] = args[2];
$textarea.data('filters', filter);
$container.find('.too-many').hide();
$search.click();
return;
}
if (parentobject && parentkey) {
filter[parentkey] = $(parentobject).val();
$(parentobject).on('change', function () {
$textarea
.data('use-threshold', 1)
.object_selector_multi('setfilter', parentkey, $(this).val());
});
}
let triggerReady = function () {
$(document).trigger(
"ready.object_selector_multi",
[$container]
);
};
if (threshold !== -1) {
$container.tikiModal(' ');
loadSelectorData(filter, {maxRecords: threshold, format: format, sort_order: sort}, function (data) {
$container.tikiModal('');
var results = data.resultset.result;
if (extratype) {
var objectIndex = results.length;
$.each(extratype, function(key, value) {
results[objectIndex] = {"object_type": key, "object_id": "", "title": value};
objectIndex++;
});
}
$container._object_selector_update_results('checkbox', results, true);
if (data.resultset.count <= threshold) {
$basic.removeClass('d-none');
} else {
$panel.removeClass('d-none');
// add .noselect2 here even though Select2 has been applied previously so select2_sortable doesn't update the value
$select.addClass("noselect2");
}
triggerReady();
});
} else {
$panel.removeClass('d-none');
triggerReady();
}
$filter.keypress(function (e) {
if (e.which === 13) {
e.preventDefault();
$search.click();
}
});
$search.click(function () {
var $spinner = $container.tikiModal(" ");
var selectorArgs = {format: format, sort_order: sort};
if ($textarea.data('use-threshold') && threshold !== -1) {
selectorArgs.maxRecords = threshold;
$textarea.data('use-threshold', 0);
}
if (wildcard == 'y') {
filter[searchField] = '*' + $filter.val() + '*';
} else {
filter[searchField] = $filter.val();
}
loadSelectorData(filter, selectorArgs, function (data) {
$container._object_selector_update_results('checkbox', data.resultset.result, false);
$spinner.tikiModal();
triggerReady();
});
});
$panel.on('click', ':checkbox', function () {
var list = $.makeArray($container.find(':checkbox:checked').map(function () {
return $(this).val();
}));
$textarea.val(list.join("\n")).change();
});
$select.on('change', function () {
var list = $(this).val() || [];
$textarea.val(list.join("\n")).change();
});
if (separator) {
$textarea.on('change', function () {
var lines = $(this).val().split("\n"), ids = [];
$.each(lines, function (k, line) {
var parts = line.split(':');
if (parts.length === 2) {
ids.push(parts[1]);
}
});
$simpleinput.val(ids.join(separator)).change();
});
}
});
};
$.fn.sortList = function () {
var list = $(this), items = list.children('li').get();
items.sort(function(a, b) {
var compA = $(a).text().toUpperCase();
var compB = $(b).text().toUpperCase();
return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
});
$.each(items, function(idx, itm) {
list.append(itm);
});
};
$.localStorage = {
store: function (key, value) {
var fullKey = this._build(key);
if (window.localStorage) {
if (value) {
window.localStorage[fullKey] = $.toJSON({
date: Date.now(),
data: value
});
} else {
delete window.localStorage[fullKey];
}
}
},
load: function (key, callback, fetch, duration) {
var payload, fullKey = this._build(key);
if (window.localStorage && window.localStorage[fullKey]) {
payload = JSON.parse(window.localStorage[fullKey]);
if (duration) {
// Expired, refetch
if (payload.date + duration*1000 < Date.now()) {
fetch(function (data) {
$.localStorage.store(key, data);
callback(data);
});
return;
}
}
callback(payload.data);
} else {
fetch(function (data) {
$.localStorage.store(key, data);
callback(data);
});
}
},
_build: function (key) {
// Use an alternate key to ensure old data structure
// does not collide
return key + "_2";
}
};
var favoriteList = [];
$.fn.favoriteToggle = function () {
this
.each(function () {
var type, obj, isFavorite, link = this;
type = $(this).queryParam('type');
obj = $(this).queryParam('object');
isFavorite = function () {
var ret = false;
$.each(favoriteList, function (k, v) {
if (v === type + ':' + obj) {
ret = true;
return false;
}
});
return ret;
};
$(this).find('span').remove(); //removes the previous star icon
$(this).prepend($('<span />').attr({
'class' : isFavorite() ? 'fas fa-star fa-fw' : 'far fa-star fa-fw',
'title' : isFavorite() ? tr('Remove from favorites') : tr('Add to favorites')
}));
if (isFavorite()) {
$(this).addClass( 'favorite_selected' );
$(this).removeClass( 'favorite_unselected' );
} else {
$(this).addClass( 'favorite_unselected' );
$(this).removeClass( 'favorite_selected' );
}
$(this)
.filter(':not(".register")')
.addClass('register')
.click(function () {
$.post($(this).attr('href'), {
target: isFavorite() ? 0 : 1
}, function (data) {
favoriteList = data.list;
$.localStorage.store($(link).data('key'), favoriteList);
$(link).favoriteToggle();
}, 'json');
return false;
});
});
return this;
};
$.fn.queryParam = function (name) {
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
var results = regex.exec(this[0].href);
if(results == null) {
return "";
} else {
return decodeURIComponent(results[1].replace(/\+/g, " "));
}
};
$(function () {
var list = $('.favorite-toggle');
if (list.length > 0) {
$.localStorage.load(
list.data('key'),
function (data) {
favoriteList = data;
list
.favoriteToggle()
.removeClass('favorite-toggle');
},
function (recv) {
$.getJSON($.service('favorite', 'list'), recv);
},
3600 // Valid for 1h
);
}
});
// global ajax event handlers
$document.ajaxComplete(function () {
$('.favorite-toggle')
.favoriteToggle()
.removeClass('favorite-toggle');
});
$document.ajaxError(function (event, jqxhr, settings, thrownError) {
if (settings.preventGlobalErrorHandle) {
return;
}
// elFinder handles it's own errors so should not close bootstrap modals
if (settings.url.indexOf("tiki-file_finder") === 0 || settings.url.indexOf("tiki-ajax_services.php?controller=file_finder") === 0) {
return;
}
$(".modal.fade.show").modal("hide");
var message;
if (!thrownError && jqxhr.status !== 200) {
$('.tiki-modal').hide();
if (jqxhr.status) {
message = jqxhr.status + " " + jqxhr.statusText;
} else {
message = "AJAX: " + jqxhr.statusText + " " + jqxhr.status + " (" + jqxhr.state() + ") for URL: " + settings.url;
}
}
if (message) {
$("#tikifeedback").showError(message);
}
});
/**
* Show a loading spinner on top of a button (or whatever)
*
* @param $spinner empty or jq object $spinner if empty, spinner is added and returned and element "disabled"
* if spinner then spinner is removed and element returned to normal
*
* @return jq object $spinner being shown or null when removing
*/
$.fn.showBusy = function( $spinner ) {
if (!$spinner) {
var pos = $(this).position();
$spinner = $("<img src='img/spinner.gif' alt='" + tr("Wait") + "' class='ajax-spinner' />").
css({
"position": "absolute",
"top": pos.top + ($(this).height() / 2),
"left": pos.left + ($(this).width() / 2) - 8
}).data("target", this);
$(this).parent().find(".ajax-spinner").remove();
$(this).parent().append($spinner);
$(this).attr("disabled", true).css("opacity", 0.5);
return $spinner;
} else {
$($spinner.data("target")).attr("disabled", false).css("opacity", 1);
$spinner.remove();
return null;
}
}
// copy tracker action column to 1st row if table has horizontal scrolling
// exclude tables where tablesorter is being applied
$('.table-responsive:not(.article-types):not(.ts-wrapperdiv)').each(function () {
var table = $(this);
// mobile friendly tables
if (table.find('table:not(.tablesorter) tbody tr').width() - 10 > table.width()) {
if (!table.hasClass('large-table-no-wrap')) table.addClass('large-table-no-wrap');
if (screen.width <= 767) {
$('<div class="toggle-table-wrap d-md-none"><button type="button" class="btn btn-link fas fa-toggle-off"></button></div>').insertBefore(table);
var checkall = false;
table.find('table.table:not(.caltable) th').each(function (e) {
var header = $(this).html();
if ($(this).find('div').hasClass('form-check') || ($(this).find('input').hasClass('form-check-input') && e == 0)) {
$(this).addClass('visible-header');
header = "";
}
// page history exception
if ($(this).find('input[name=compare]').length) {
$(this).addClass('visible-header').addClass('compare-pages');
header = "";
}
table.find('table.table tbody tr').each(function () {
var cell = $(this).find('td').eq(e);
if (checkall) cell.addClass('checkmargin');
if ($.trim(cell.html()) == '') {
cell.addClass('hidecell');
} else {
cell.prepend("<div class='header' style='display:none'>" + header + "</div>");
}
if (e == 0) {
if ((cell.hasClass('checkbox-cell') || cell.find('input:checkbox')) && cell.find('.header').html() == '') {
cell.addClass('checkall').removeClass('checkmargin');
checkall = true;
}
}
});
});
// this is for calendars
table.find('table.caltable > tbody > tr:first > td.heading').each(function (e) {
var header = $(this).html();
table.find('table.caltable > tbody > tr:not(:first)').each(function () {
var cell = $(this).find('> td').eq(e);
cell.prepend("<div class='header' style='display:none'>" + header + "</div>");
});
});
}
}
// action column
if (table.find('table').width() - 5 > table.width()) {
if (screen.width > 767) {
if ($('table tr td:last-child').hasClass('action')) {
table.find('table td.action').each(function () {
$(this).parent().prepend($(this).clone());
});
table.find('table tr').eq(0).prepend('<th style="width:20px;"></th>');
}
}
}
});
// mobile: wrap large table data on click
$('.toggle-table-wrap button').each(function () {
$(this).on('click', function () {
if ($(this).hasClass('fa-toggle-on')) {
$(this).removeClass('fa-toggle-on').addClass('fa-toggle-off');
$(this).parent().next().removeClass('large-table').addClass('large-table-no-wrap');
} else {
$(this).removeClass('fa-toggle-off').addClass('fa-toggle-on');
$(this).parent().next().removeClass('large-table-no-wrap').addClass('large-table');
}
});
});
})(jQuery);
// Prevent memory leaks in IE
// Window isn't included so as not to unbind existing unload events
// More info:
// - http://isaacschlueter.com/2006/10/msie-memory-leaks/
if ( window.attachEvent && !window.addEventListener ) {
window.attachEvent("onunload", function() {
for ( var id in jQuery.cache ) {
var item = jQuery.cache[ id ];
if ( item.handle ) {
if ( item.handle.elem === window ) {
for ( var type in item.events ) {
if ( type !== "unload" ) {
// Try/Catch is to handle iframes being unloaded, see #4280
try {
jQuery.event.remove( item.handle.elem, type );
} catch(e) {}
}
}
} else {
// Try/Catch is to handle iframes being unloaded, see #4280
try {
jQuery.event.remove( item.handle.elem );
} catch(e) {}
}
}
}
});
}
$.tikiModal = function(msg) {
return $('body').tikiModal(msg);
};
//Makes modal over window or object so ajax can load and user can't prevent action
$.fn.tikiModal = function(msg) {
var obj = $(this);
if (!obj.length) {
return null; // happens after search index rebuild in some conditions
}
var lastModal = obj.data('lastModal');
if (!lastModal) {
lastModal = Math.floor(Math.random() * 1000);
obj.data('lastModal', lastModal);
}
var box = {
top: obj.offset().top,
left: obj.offset().left,
height: obj.outerHeight(),
width: obj.outerWidth()
};
var modal = $('body').find('#modal_' + lastModal);
var spinner = $('<img src="img/spinner.gif" style="vertical-align: top; margin-right: .5em;" />');
if (!msg) {
modal
.fadeOut(function() {
$(this).remove();
});
obj.removeData('lastModal');
return obj;
}
if (modal.length) {
modal
.find('.dialog')
.empty()
.html(spinner)
.append(msg);
return obj;
}
modal = $('<div id="modal_' + lastModal + '" class="tiki-modal">' +
'<div class="mask" />' +
'<div class="dialog"></div>' +
'</div>')
.appendTo('body');
var zIndex = 0;
if (obj.is("body")) {
zIndex = 2147483646 - 1; // maximum
box.top = obj.offset().top + $window.scrollTop();
box.left = obj.offset().left + $window.scrollLeft();
} else {
obj.parents().addBack().each(function () {
var z = $(this).css("z-index");
if (z && z !== 'auto' && z > zIndex) {
zIndex = Number(z);
}
});
}
//Set height and width to mask to fill up the whole screen or the single element
modal
.width(box.width)
.height(box.height)
.css('top', box.top + 'px')
.css('left', box.left + 'px')
.find('.mask')
.height(box.height)
.fadeTo(1000, 0.6)
.parent()
.find('.dialog')
.hide()
.append(spinner)
.append(msg);
var dialog = modal.find('.dialog');
if (obj.is("body")) {
dialog.css({
top: (box.top + $window.innerHeight()/2 - $window.scrollTop()) + "px",
left: (box.left + $window.innerWidth()/2 - $window.scrollLeft()) + "px"
});
}
dialog.css({
marginTop: (dialog.height() / -2) + "px",
marginLeft: (dialog.width() / -2) + "px"
}).show();
if (zIndex) {
modal.css("z-index", zIndex + 1);
}
return obj;
};
//makes the width of an input change to the value
$.fn.valWidth = function() {
var me = $(this);
return me.ready(function() {
var h = me.height();
if (!h) {
h = me.offsetParent().css("font-size");
if (h) {
h = parseInt(h.replace("px", ""));
}
}
me.keyup(function() {
var width = me.val().length * h;
me
.stop()
.animate({
width: (width > h ? width : h)
}, 200);
})
.keyup();
});
};
//For making pagination have the ability to enter page/offset number and go
$.paginationHelper = function() {
$('.pagenums').each(function() {
var me = $(this);
var step = me.find('input.pagenumstep');
var endOffset = (me.find('input.pagenumend').val() - 1) * step.data('step');
var url = step.data('url');
var offset_jsvar = step.data('offset_jsvar');
var offset_arg = step.data('offset_arg');
me.find('span.pagenumstep').replaceWith(
$('<input type="text" style="font-size: inherit; " />')
.val(step.val())
.change(function() {
var newOffset = step.data('step') * ($(this).val() - 1);
if (newOffset >= 0) {
//make sure the offset isn't too high
newOffset = (newOffset > endOffset ? endOffset : newOffset);
//THis is for custom/ajax search handling
window[offset_jsvar] = newOffset;
if (step[0]) {
if (step.attr('onclick')) {
step[0].onclick();
return;
}
}
//if the above behavior isn't there, we update location
document.location = url + offset_arg + "=" + newOffset;
}
})
.keyup(function(e) {
switch(e.which) {
case 13: $(this).blur();
}
})
.valWidth()
);
});
};
//a sudo "onvisible" event
$.fn.visible = function(fn, isOne) {
if (fn) {
$(this).each(function() {
var me = $(this);
if (isOne) {
me.one('visible', fn);
} else {
me.on('visible', fn);
}
function visibilityHelper() {
if (!me.is(':visible')) {
setTimeout(visibilityHelper, 500);
} else {
me.trigger('visible');
}
}
visibilityHelper();
});
} else {
$(this).trigger('visible');
}
return this;
};
$.download = function(url, data, method){
//url and data options required
if( url && data ){
//data can be string of parameters or array/object
data = typeof data == 'string' ? data : jQuery.param(data);
//split params into form inputs
var inputs = '';
jQuery.each(data.split('&'), function(){
var pair = this.split('=');
inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
});
//send request
jQuery('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
.appendTo('body').submit().remove();
}
};
$.uiIcon = function(type) {
return $('<div style="width: 1.4em; height: 1.4em; margin: .2em; display: inline-block; cursor: pointer;">' +
'<span class="ui-icon ui-icon-' + type + '">&nbsp;</span>' +
'</div>')
.hover(function(){
$(this).addClass('ui-state-highlight');
}, function() {
$(this).removeClass('ui-state-highlight');
});
};
$.uiIconButton = function(type) {
return $.uiIcon(type).addClass('ui-state-default ui-corner-all');
};
$.rangySupported = function(fn) {
if (window.rangy) {
rangy.init();
var cssClassApplierModule = rangy.modules.CssClassApplier;
return fn();
}
};
$.fn.rangy = function(fn) {
var me = $(this);
$.rangySupported(function() {
$document.mouseup(function(e) {
if (me.data('rangyBusy')) return;
var selection = rangy.getSelection();
var html = selection.toHtml();
var text = selection.toString();
if (text.length > 3 && rangy.isUnique(me[0], text)) {
if (fn)
if ($.isFunction(fn))
fn({
text: text,
x: e.pageX,
y: e.pageY
});
}
});
});
return this;
};
$.fn.rangyRestore = function(phrase, fn) {
var me = $(this);
$.rangySupported(function() {
phrase = rangy.setPhrase(me[0], phrase);
if (fn)
if ($.isFunction(fn))
fn(phrase);
});
return this;
};
$.fn.rangyRestoreSelection = function(phrase, fn) {
var me = $(this);
$.rangySupported(function() {
phrase = rangy.setPhraseSelection(me[0], phrase);
if (fn)
if ($.isFunction(fn))
fn(phrase);
});
return this;
};
$.fn.realHighlight = function() {
var o = $(this);
$.rangySupported(function() {
rangy.setPhraseBetweenNodes(o.first(), o.last(), document);
});
return this;
};
$.fn.ajaxEditDraw = function(options) {
var me = $(this).attr('href', 'tiki-ajax_services.php');
//defaults
options = $.extend({
saved: function() {},
closed: function() {}
}, options);
$.tikiModal(tr('Loading editor'));
me.serviceDialog({
title: me.attr('title'),
data: {
controller: 'draw',
action: 'edit',
fileId: me.data('fileid'),
galleryId: me.data('galleryid'),
imgParams: me.data('imgparams'),
raw: true
},
modal: true,
zIndex: 9999,
fullscreen: true,
load: function (data) {
//prevent from happeneing over and over again
if (me.data('drawLoaded')) return false;
me.data('drawLoaded', true);
me.drawing = $('#tiki_draw')
.loadDraw({
fileId: me.data('fileid'),
galleryId: me.data('galleryid'),
name: me.data('name'),
imgParams: me.data('imgparams'),
data: $('#fileData').val()
})
.on('savedDraw', function(e, o) {
me.data('drawLoaded', false);
me.drawing.parent().dialog('destroy');
me.drawing.remove();
//update the image that did exist in the page with the new one that now exists
var img = $('.pluginImg' + me.data('fileid')).show();
if (img.length < 1) document.location = document.location + '';
var w = img.width(), h = img.height();
if (img.hasClass('regImage')) {
var replacement = $('<div />')
.attr('class', img.attr('class'))
.attr('style', img.attr('style'))
.attr('id', img.attr('id'))
.insertAfter(img);
img.remove();
img = replacement;
}
var src = me.data('src');
$('<div class=\"svgImage\" />')
.load(src ? src : 'tiki-download_file.php?fileId=' + o.fileId + '&display', function() {
$(this)
.css('position', 'absolute')
.fadeTo(0, 0.01)
.prependTo('body')
.find('img,svg')
.scaleImg({
width: w,
height: h
});
img.html($(this).children());
$(this).remove();
});
if (!options.saved) return;
options.saved(o.fileId);
me.data('fileid', o.fileId); // replace fileId on edit button
if (o.imgParams && o.imgParams.fileId) {
o.imgParams.fileId = o.fileId;
me.data('imgparams', o.imgParams);
}
})
.submit(function() {
me.drawing.saveDraw();
return false;
})
.on('loadedDraw', function() {
//kill the padding around the dialog so it looks like svg-edit is one single box
me.drawing
.parent()
.css('padding', '0px');
var serviceDialog = me.data('serviceDialog');
if (serviceDialog) {
var drawFrame = $('#svgedit');
serviceDialog
.on('dialogresize', function() {
drawFrame.height(serviceDialog.height() - 4);
})
.trigger('dialogresize');
}
$.tikiModal();
});
me.drawing.find('#drawMenu').remove();
},
close: function() {
if (me.data('drawLoaded')) {
me.data('drawLoaded', false);
me.drawing.remove();
if (!options.closed) return;
options.closed(me);
}
}
});
return false;
};
$.notify = function(msg, settings) {
settings = $.extend({
speed: 10000
},settings);
var notify = $('#notify');
if (!notify.length) {
notify = $('<div id="notify" />')
.css('top', '5px')
.css('right', '5px')
.css('position', 'fixed')
.css('z-index', 9999999)
.css('padding', '5px')
.width($window.width() / 5)
.prependTo('body');
}
var note = $('<div class="notify ui-state-error ui-corner-all ui-widget ui-widget-content" />')
.append(msg)
.css('padding', '5px')
.css('margin', '5px')
.mousedown(function() {
return false;
})
.hover(function() {
$(this)
.stop()
.fadeTo(500, 0.3)
}, function() {
$(this)
.stop()
.fadeTo(500, 1)
})
.prependTo(notify);
setTimeout(function() {
note
.fadeOut()
.slideUp();
//added outside of fadeOut to ensure removal
setTimeout(function() {
note.remove();
}, 1000);
}, settings.speed);
};
function delayedExecutor(delay, callback)
{
var timeout;
return function () {
var args = arguments;
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
timeout = setTimeout(function () {
callback.apply(this, args)
}, delay);
};
}
$(function () {
// Show/hide the sidebars //
////////////////////////////
$(".toggle_zone").click(function () {
var $this = $(this), zone="",
icon_left = "toggle-left", icon_right = "toggle-right";
if ($this.is(".right")) {
zone = "right";
icon_left = "toggle-right";
icon_right = "toggle-left"
} else if ($this.is(".left")) {
zone = "left";
}
if ($this.find(".icon-" + icon_left).length) { // hide it
$this.find(".icon").setIcon(icon_right);
setCookie("hide_zone_" + zone, 'y');
$("body").addClass("hide_zone_" + zone);
} else {
$this.find(".icon").setIcon(icon_left);
deleteCookie("hide_zone_" + zone);
$("body").removeClass("hide_zone_" + zone);
}
$(window).trigger("resize");
return false; // do not modify URL by adding # on the click
});
});
// try and reposition the menu ul within the browser window
$.fn.moveToWithinWindow = function() {
var $el = $(this);
var h = $el.height(),
w = $el.width(),
o = $el.offset(),
po = $el.parent().offset(),
st = $window.scrollTop(),
sl = $window.scrollLeft(),
wh = $window.height(),
ww = $window.width();
if (w + o.left > sl + ww) {
$el.animate({'left': sl + ww - w - po.left}, 'fast');
}
if (h + o.top > st + wh) {
$el.animate({'top': st + wh - (h > wh ? wh : h) - po.top}, 'fast');
} else if (o.top < st) {
$el.animate({'top': st - po.top}, 'fast');
}
};
$.fn.scaleImg = function (max) {
$(this).each(function() {
//Here we want to make sure that the displayed contents is the right size
var h, w, img = $(this),
actual = {
height: img.height(),
width: img.width()
},
original = $(this).clone(),
parent = img.parent();
var winner = '';
if (actual.height > max.height) {
winner = 'height';
} else if (actual.width > max.width) {
winner = 'width';
}
//if there is no winner, there is no need to resize
if (winner) {
//we resize both images and svg, we check svg first
var g = img.find('g');
if (g.length) {
img
.attr('preserveAspectRatio', 'xMinYMin meet');
parent
.css('overflow', 'hidden')
.width(max.width)
.height(max.height);
g.attr('transform', 'scale( ' + (100 / (actual[winner] / max[winner]) * 0.01) + ' )');
} else {
//now we resize regular images
if (actual.height > actual.width) {
h = max.height;
w = Math.ceil(actual.width / actual.height * max.height);
} else {
w = max.width;
h = Math.ceil(actual.height / actual.width * max.width);
}
img.css({ height: h, width: w });
}
img
.css('cursor', "url(img/icons/zoom.gif),auto")
.click(function () {
$('<div/>').append(original).dialog({
modal: true,
width: Math.min($(window).width(), actual.width + 20),
height: Math.min($(window).height(), actual.height + 50)
});
return false;
});
}
});
return this;
};
// Compatibility to old jquery to resolve a bug in fullcalendar
$.curCSS = function (element, property) {
return $(element).css(property);
};
$.fn.registerFacet = function () {
this.each(function () {
var element = this, entries = $($(this).data('for')).val()
.split(" " + $(this).data('join') + " ")
.map(function (value) {
return (value.charAt(0) === '"') ? value.substr(1, value.length - 2) : value;
});
function applyFilter(value) {
if (value) {
value = $.makeArray(value);
value = value
.map(function (value) {
return (-1 === value.indexOf(' ')) ? value : ('"' + value + '"');
})
.join(" " + $(element).data('join') + " ");
}
$($(element).data('for')).val(value).change();
}
if ($(element).is('select')) {
$(this)
.val(entries)
.trigger("change.select2") // for Select2
.change(function () {
var value = $(this).val();
applyFilter(value);
});
} else if ($(element).has(':checkbox').length) {
$(':checkbox', element)
.each(function () {
if (-1 !== $.inArray($(this).val(), entries)) {
$(this).prop('checked', true);
}
})
.on('click', function () {
applyFilter($(':checked', element).map(function () {
return $(this).val();
}));
});
}
var selected = $('option:selected, :checkbox:checked', this).length,
all = $('option, :checkbox', this).length;
if (all === 1 && selected === 0) {
$(this).closest('.facet-hide-group').hide();
}
});
return this;
};
$.fn.reload = function () {
this.each(function () {
if (this.reload) {
this.reload();
} else if($(this).data('reload')) {
$(this).loadService({}, {
url: $(this).data('reload')
});
}
});
return this;
};
$(document).on('mouseover', '.media[data-href]', function () {
$(this).css('cursor', 'pointer');
});
$(document).on('mouseout', '.media[data-href]', function () {
$(this).css('cursor', 'default');
});
$(document).on('click', '.media[data-href]', function () {
document.location.href = $(this).data('href');
});
$(document).on('hidden.bs.modal', '.footer-modal.fade', function () {
// Required for bootstrap to allow changing the content of a modal
$(this).removeData('bs.modal').find('.modal-content').empty();
// With multiple stacked modals, which is not officially supported by bootstrap,
// focus on the last modal would be lost.
$('body').toggleClass('modal-open', $('.modal.fade.show').length > 0);
$(".modal-backdrop.show:first").remove();
});
$(document).on('submit', '.modal-body form:not(.no-ajax):not(.ajax-reuse-modal)', ajaxSubmitEventHandler(function (data) {
//if FORWARD is set in the returned data, load the passed service into the modal
// rather than close the modal and refresh the page.
if (data && data.FORWARD) {
var $this = $(this);
if ($this.is("form")) {
$this = $this.parent();
}
$this.children().remove();
$this.loadService(data.FORWARD, {
origin: this,
load: function () {
$(this).closest('.modal').trigger('tiki.modal.redraw');
}
});
} else {
// reload() causes a request to update the browser cache - similar to pressing the reload button.
// so we must not reload() but set the href. This behaves simililar to clicking a link - which keeps the browser cache.
// The difference is: NOT loading about 50+ js / css files!
//document.location.reload();
document.location.href = document.location.href.replace(/#.*$/, ""); // remove the hash from the URL if there is one otherwise the page doesn't reload
}
}));
$(document).on('submit', '.modal-body form.ajax-reuse-modal', ajaxSubmitEventHandler(function (data) {
$(this).closest('.modal .modal-body').html(data);
}, 'html'));
// When data-size is set on the toggle-link, alter the size of the modal
$(document).on('click', '[data-bs-toggle=modal][data-size]', function () {
var target = $(this).data('target'), size = $(this).data('size');
$(target)
.one('hidden.bs.modal', function () {
$('.modal-dialog', this).removeClass(size);
})
.find('.modal-dialog').addClass(size)
;
});
$(document).on('click', '[data-bs-toggle=modal][data-modal-title]', function () {
var target = $(this).data('target'), title = $(this).data('modal-title');
$(target)
.one('loaded.bs.modal', function () {
$('.modal-title', this).text(title);
})
;
});
$(document).on('loaded.bs.modal', '.modal.fade', function () {
$(this).trigger('tiki.modal.redraw');
});
// START BOOTSTRAP 4 CHANGE
$(document).on('shown.bs.modal', '.modal', function (event) {
// fixes for bootstrap4
var $button = $(event.relatedTarget); // Button that triggered the modal
var remote = $button.data('remote'); // Extract info from data-* attributes
var $modal = $(this);
var href = remote ? remote : $button.attr("href");
if (href) {
$modal.find('.modal-content').load(href, function () {
try {
var data = JSON.parse($(this).text());
if (data.extra == 'close') {
$.closeModal();
return;
} else if (data.extra == 'refresh') {
window.location.href = window.location.href;
return;
}
} catch (e) {
// normal html output stays in the modal
}
$(this).trigger("tiki.modal.redraw");
});
} else {
$modal.trigger("tiki.modal.redraw");
}
// END BOOTSTRAP 4 CHANGE
});
$(document).on('tiki.modal.redraw', '.modal.fade', function () {
var modal = this, $button;
// On Modal show, find all buttons part of a .submit block and create
// proxies of them in the modal footer
$('.modal-footer .auto-btn', modal).remove();
$('div.submit .btn', modal).each(function () {
var $submit = $(this);
if ($submit.is('a:not(.custom-handling)')) {
$button = $submit;
} else {
$submit.hide();
$button = $('<button>')
.text($submit.val() || $submit.text())
.attr('class', $submit.attr('class'))
.addClass('auto-btn')
.click(function () {
if ($submit.data("alt_controller") && $submit.data("alt_action")) {
$submit.parents("form").attr("action", $.service($submit.data("alt_controller"), $submit.data("alt_action")));
}
$submit.click();
if (typeof $submit.parents("form").validate !== "function") {
// make the button look disabled and ignore further clicks
$button.off("click").css("opacity", 0.3);
}
});
}
$('.modal-footer', modal).append($button);
});
if ($.fn.flexibleSyntaxHighlighter) {
$('textarea', modal).flexibleSyntaxHighlighter();
}
$(".nav-tabs", this).each(function () {
if ($(".active", this).length === 0) {
$("li:first-child a", this).tab("show");
}
});
if ($.applySelect2) {
$(this).applySelect2();
}
if (jqueryTiki.colorbox) {
$(this).applyColorbox();
}
if (jqueryTiki.tooltips) {
$(this).tiki_popover();
}
$.initTrees();
// START BOOTSTRAP 4 CHANGE
$('.modal-body :input', modal).first().focus();
$('.modal-backdrop.show:not(.fade)').remove(); // Bootstrap keeps adding more of these
// handle $ajaxtimer for alerts that are not using the confirmAction handler
if ($("#timer-seconds", modal).length) {
var $seconds = $("#timer-seconds"),
counter = $seconds.text(),
timer = setInterval(function () {
$seconds.text(--counter);
if (counter === 0 || counter < 0) {
$.closeModal();
window.location = window.location.href;
}
}, 1000);
}
// END BOOTSTRAP 4 CHANGE
});
/**
* Make .depends elements show or hide depending on the state of the "on" element
* for checkboxes and select boxes with empty value
*/
$(document).on("ready tiki.modal.redraw", function () {
$(".depends").each(function () {
var $depends = $(this),
on = $depends.data("on");
$("[name=" + on + "]").change(function () {
if ($(this).is("input[type=checkbox]") && $(this).is(":checked")) {
$depends.show();
} else if ($(this).is("select") && $(this).val()) {
$depends.show();
} else if ($(this).is("input[type=text]") && $(this).val()) {
$depends.show();
} else {
$depends.hide();
}
}).change();
});
});
$(function () {
var $tabs = $('a[data-bs-toggle=tab][href="' + document.location.hash + '"]'),
tabShown = false,
notShown = [];
if (document.location.search.match(/cookietab=/)) {
tabShown = true;
} else if (document.location.hash && $tabs.length) {
$tabs.tab('show');
tabShown = true;
} else {
$(".tabs").each(function () {
var name = $(this).data("name"),
t = getCookie(name, "tabs", "notfound");
var $tab = $('a[data-bs-toggle=tab].active', this); // class "active" set serverside from $cookietab var
if (t && $tab.length === 0) {
$tab = $('a[data-bs-toggle=tab][href="' + t + '"]');
}
if ($tab.length) {
$tab.tab('show');
tabShown = true;
} else if (name) {
notShown.push(name);
}
});
}
if (typeof $().tab === "function") {
if (!tabShown && !notShown.length) {
$("a[data-bs-toggle=tab]:first").tab("show");
} else if (notShown.length) {
for (var i = 0; i < notShown.length; i++) {
$(".tabs[data-name=" + notShown[i] + "] a[data-bs-toggle=tab]:first").tab("show");
}
}
}
$('a[data-bs-toggle="tab"]').on('show.bs.tab', function (e) {
if ($(this).parents(".tab-content").length === 0) {
document.location.hash = $(e.target).attr("href");
}
setCookieBrowser($(this).parents(".tabs:first").data("name"), $(e.target).attr("href"), "tabs");
}).click(function () {
var scroll = $window.scrollTop(); // prevent window jumping to tabs on click
$(this).tab('show');
$window.scrollTop(scroll);
});
$("input[name='session_protected']").on('click', function () {
if ($(this).prop('type') == 'checkbox' && $(this).data('tiki-admin-child-block') == '#session_protected_childcontainer') {
var checkbox = $("input[name='session_protected']");
if (checkbox.prop('checked') && location.protocol != 'https:') {
$(this).confirmationDialog({
title: tr('Warning - Protect all sessions with HTTPS'),
message: tr('You seem to be accessing the website using HTTP only, if your HTTPS settings are not correct, you will be locked out of the website'),
success: function () {
checkbox.prop('checked', true);
}
});
return false;
}
}
});
});
$.openModal = function (options) {
var href = options.remote; // BOOTSTRAP 4 CHANGE
if (href.indexOf("modal=") === -1) {
if (-1 === href.indexOf('?')) {
href += '?modal=1';
} else {
href += '&modal=1';
}
}
var $spinner = $("#bootstrap-modal").tikiModal(tr('Loading...'))
// START BOOTSTRAP 4 CHANGE
$('.modal.fade:not(.show):first')
// Bind a single event to trigger as soon as the form appears
.one('hidden.bs.modal', options.close || function () {})
// Make the form load
.find(".modal-content")
.load(href, function () {
$spinner.tikiModal()
var $modal = $(this).parents(".modal");
$modal.modal(options);
if ($modal.is(':visible')){
$modal.trigger("tiki.modal.redraw");
} else {
$modal.modal("show");
}
$('.modal-title', this).text(options.title);
if (options.size) {
$modal.find('.modal-dialog').addClass(options.size);
$modal.one('hidden.bs.modal', function () {
$('.modal-dialog', this).removeClass(options.size);
});
}
if (options.open) {
options.open.apply(this);
}
// END BOOTSTRAP 4 CHANGE
});
};
$.closeModal = function (options) {
options = options || {};
var done = options.done;
if (done) {
done = function () {
// Wait until the event loop ends before considering really done
setTimeout(options.done, 0);
};
}
$('.modal.fade.show').last() // BOOTSTRAP 4 CHANGE
.one('hidden.bs.modal', done || function () {})
.modal('hide');
};
$.fn.clickModal = function (options, href) {
this.click($.clickModal(options, href));
return this;
};
$.clickModal = function (options, href) {
return function (e) {
var control = this, url;
if (! href) {
url = $(this).attr('href');
} else {
url = href;
}
if ($.isFunction(e.preventDefault)) {
e.preventDefault();
}
$.openModal({
title: options.title,
size: options.size,
remote: url,
backdrop: (typeof options.backdrop === "undefined") ? true : options.backdrop,
keyboard: (typeof options.keyboard === "undefined") ? true : options.keyboard,
focus: (typeof options.focus === "undefined") ? true : options.focus,
show: (typeof options.show === "undefined") ? true : options.show,
open: function () {
if (options.open) {
options.open.apply(this, []);
}
$('form:not(.no-ajax)', this)
.addClass('no-ajax') // Remove default ajax handling, we replace it
.submit(ajaxSubmitEventHandler(function (data) {
if (options.success) {
options.success.apply(control, [data]);
}
}));
}
});
};
};
/**
* Open a tab on the current page, e.g.:
* <a href="#" onclick="showTab(2); return false;">Open tab 2 on this page</a>
*
* Assumes the tab is in the main column of the page and that one tab is already showing.
*
* @param tabNumber number of the tab on the current page to show
* @returns {boolean}
*/
function showTab(tabNumber) {
var thisTabId = $('#col1').find('.tab-pane.active').attr('id'),
tabNames = thisTabId.substr(0, thisTabId.indexOf('-') + 1)
;
$('a[href="#' + tabNames + tabNumber + '"]').tab('show');
}
/**
* Send feedback to a popup modal or to div#tikifeedback using bootstrap alert variations (error, warning, success, info)
*
* @param mes array The message
* @param type string Type of alert: error, warning, success or info (default)
* @param modal boolean true for popup modal, false (default) to use the div#tikifeedback that is on every page
* @param title string Custom message title
* @param icon string Custom icon
* @param killall boolean true for removing other feedbacks already open, false (default) (only for non modal)
* @param custom string Custom target in jquery selection notation (only for non modal)
*/
function feedback (mes, type, modal, title, icon, killall, custom)
{
mes = mes || [];
if (!$.isArray(mes)) {
mes = [mes];
}
if (mes.length == 1) {
var meshtml = [mes][0];
} else {
var meshtml = '<ul>';
$.each(mes, function(i, val) {
if (val) {
meshtml += '<li>' + val + '</li>';
}
});
meshtml += '</ul>';
}
type = type || 'info'; modal = modal || false; killall = killall || false; custom = $(custom).length ? $(custom).first() : null;
var target, map =
{
'error': {title:tr('Error'), class:'danger', icon:'error'},
'warning': {title:tr('Warning'), class:'warning', icon:'warning'},
'success': {title:tr('Success'), class:'success', icon:'success'},
'info': {title:tr('Note'), class:'info', icon:'information'}
};
var check = ['error', 'warning', 'success', 'info'];
type = $.inArray(type, check) > -1 ? type : 'info';
title = title || map[type]['title'];
icon = icon || map[type]['icon'];
icon = $.fn.getIcon(icon);
if (modal) {
if (mes.length > 0) {
meshtml = '<div class="alert alert-dismissable alert-' + map[type]['class'] + '">' + meshtml + '</div>';
} else {
meshtml = '';
}
target = $('.modal.fade:not(.show)').first();
$('.modal-content', target).html(
'<div class="modal-header">' +
'<h4 class="text-' + map[type]['class'] + '">' + icon[0].outerHTML + ' ' + title + '</h4>' +
'<button type="button" class="close btn-close pull-right" aria-label="{tr}Close{/tr}" data-bs-dismiss="modal"></button>' +
'</div>' +
'<div class="modal-body">' +
meshtml +
'</div>'
);
target.modal();
} else {
var tfb = $(custom ? custom : 'div#tikifeedback');
if (killall) {
tfb.find('div.alert.alert-dismissable').remove();
}
if (mes.length == 0) {
meshtml = '';
}
tfb.append(
'<div class="alert alert-dismissable alert-' + map[type]['class'] + '">' +
'<button type="button" class="close btn-close" aria-label="{tr}Close{/tr}" data-bs-dismiss="modal"></button>' +
'<h4>' + icon[0].outerHTML + ' ' + title + '</h4>' +
meshtml +
'</div>'
).on('click' , 'button.close' , function() {
$(this).parent().remove();
});
placeFeedback(tfb);
}
}
/**
* Utility for tikifeedback to place the feedback at the top of the page (at div#tikifeedback) or viewport, whichever is
* lower to allow it to be seen. If shown at the top of the view port, after 5 seconds it will move to its normal
* position, which is the first element in div#col1
*
* @param object
*/
function placeFeedback(object) {
if ($('.modal.fade.show').length) {
$('#col1, #col2, #col3').css('z-index', 'auto');
object.css('z-index', 3000);
object.find('div').css('z-index', 3000);
}
if (object.offset().top < $(window).scrollTop()) {
object.find('div.alert').append('<div id="move-message" style="font-size:smaller;margin-top:10px"><em>'
+ tr('This message will move to the top of the page after a few seconds.') + '</em>');
//it's important to not define top until after the tfb object has been manipulated
object.offset({'top': $(window).scrollTop()});
object.css('z-index', 3000);
setTimeout(function() {
object.fadeOut(1000, function() {
//move back to usual position and clear style attribute so subsequent feedback appears properly
$('div#col1').prepend(object);
object.css({'z-index':'', 'position':'', 'top': ''});
object.find('div#move-message').css('visibility', 'hidden');
object.fadeIn();
});
}, 5000);
}
}
// thanks to Rob W on https://stackoverflow.com/a/8962023/2459703
$.fn.closestDescendent = function(filter) {
var $found = $(),
$currentSet = this; // Current place
while ($currentSet.length) {
$found = $currentSet.filter(filter);
if ($found.length) break; // At least one match: break loop
// Get all children of the current set
$currentSet = $currentSet.children();
}
return $found.first(); // Return first match of the collection
};
window.regCapsLock = function () {};
// Avoid that jquery appends a cachebuster to scripts loaded via a regular script tag when the base content was loaded from an xhr call
// I.e xhr call loads same html boilerplate and that boilerplate contains a script tag that loads some .js script.
// In this case, jquery would add a cachebuster to the js request, and no cache would be work.
$.ajaxPrefilter(function( options, originalOptions, jqXHR ) {
if ( options.dataType == 'script' || originalOptions.dataType == 'script' ) {
options.cache = true;
}
});
//Preview for the upload avatar popup
function readURL(input) {
if (input.files && input.files[0]) {
$(".btn-upload-avatar").removeClass('disabled');
var reader = new FileReader();
reader.onload = function (e) {
$('.user-avatar-preview img').attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
$(document).on('change', '#userfile', function(){
readURL(this);
});
function objectLockToggle(icon) {
var $this = $(icon).tikiModal(" "),
action = $this.data("is_locked") ? "unlock" : "lock";
$.post($.service(
"object",
action,
{
type: $this.data("type"),
object: $this.data("object"),
value: $this.data("is_locked") ? "" : jqueryTiki.username
}
), function (data) {
if (data && data.locked) {
$this.find(".icon").setIcon("lock");
$this.data("is_locked", "1")
.attr("title", tr("Locked by " + jqueryTiki.userRealName))
.parent().find("input[name=locked]").val(jqueryTiki.username);
} else {
$this.find(".icon").setIcon("unlock");
$this.data("is_locked", "")
.attr("title", "")
.parent().find("input[name=locked]").val("");
}
},
"json").done(function () {
$this.tikiModal();
});
return false;
}
/**
* Remove accents from chars
* vendor_bundled/vendor/mottie/tablesorter/js/jquery.tablesorter.combined.js:2373
*
* @param str
*/
(function setup_removediacritics_function(){
var characterEquivalents = {
'a' : '\u00e1\u00e0\u00e2\u00e3\u00e4\u0105\u00e5', // áàâãäąå
'A' : '\u00c1\u00c0\u00c2\u00c3\u00c4\u0104\u00c5', // ÁÀÂÃÄĄÅ
'c' : '\u00e7\u0107\u010d', // çćč
'C' : '\u00c7\u0106\u010c', // ÇĆČ
'e' : '\u00e9\u00e8\u00ea\u00eb\u011b\u0119', // éèêëěę
'E' : '\u00c9\u00c8\u00ca\u00cb\u011a\u0118', // ÉÈÊËĚĘ
'i' : '\u00ed\u00ec\u0130\u00ee\u00ef\u0131', // íìİîïı
'I' : '\u00cd\u00cc\u0130\u00ce\u00cf', // ÍÌİÎÏ
'o' : '\u00f3\u00f2\u00f4\u00f5\u00f6\u014d', // óòôõöō
'O' : '\u00d3\u00d2\u00d4\u00d5\u00d6\u014c', // ÓÒÔÕÖŌ
'ss': '\u00df', // ß (s sharp)
'SS': '\u1e9e', // ẞ (Capital sharp s)
'u' : '\u00fa\u00f9\u00fb\u00fc\u016f', // úùûüů
'U' : '\u00da\u00d9\u00db\u00dc\u016e' // ÚÙÛÜŮ
};
var characterRegex, characterRegexArray;
function removeDiacritics(str) {
var chr,
acc = '[',
eq = characterEquivalents;
if ( !characterRegex ) {
characterRegexArray = {};
for ( chr in eq ) {
if ( typeof chr === 'string' ) {
acc += eq[ chr ];
characterRegexArray[ chr ] = new RegExp( '[' + eq[ chr ] + ']', 'g' );
}
}
characterRegex = new RegExp( acc + ']' );
}
if ( characterRegex.test( str ) ) {
for ( chr in eq ) {
if ( typeof chr === 'string' ) {
str = str.replace( characterRegexArray[ chr ], chr );
}
}
}
return str;
}
window.removeDiacritics = removeDiacritics;
})();
/**
* Fetch feedback sent through ajax and place into the tikifeedback div that is on each page
* If tikifeedback div is outside of the viewport, place it at the top of the viewport and have it move to the
* normal position (first element in div#col1) after 5 seconds
*/
$(document).ajaxComplete(function (e, jqxhr) {
var feedback = jqxhr.getResponseHeader('X-Tiki-Feedback'),
tfb = $('#tikifeedback');
if (feedback) {
feedback = decodeURIComponent(feedback); // decodeURIComponent() reverses rawurlencode().
tfb.fadeIn(200, function() {
//place html from ajax X-Tiki-Feedback into the div#tikifeedback
tfb.html($($.parseHTML(feedback)).filter('#tikifeedback').html());
tfb.find('div.alert').each(function() {
var title = $(this).find('span.rboxtitle').text().trim(),
content = $(this).find('div.rboxcontent').text().trim();
$(this).find('span.rboxtitle').text(title);
$(this).find('div.rboxcontent').text(content);
});
//place tikifeedback div into window view if necessary
placeFeedback(tfb);
});
}
tfb.find('.clear').on('click', function () {
$(tfb).empty();
//move back to usual position and clear style attribute so subsequent feedback appears properly
$('div#col1').prepend(tfb);
tfb.css({'z-index':'', 'position':'', 'top': ''});
return true;
});
});
$(document).on('keydown', 'textarea.autoheight', function(evt){
var el = this;
var height = Math.max(el.clientHeight, el.offsetHeight, el.scrollHeight);
el.style.cssText = 'height:' + height + 'px; overflow-y: hidden';
setTimeout(function(){
el.scrollTo(0,0);
}, 0);
});
$(document).on('change', '.preference :checkbox:not(.pref-reset)', function () {
var childBlock = $(this).data('tiki-admin-child-block')
, childMode = $(this).data('tiki-admin-child-mode')
, checked = $(this).is(':checked')
, disabled = $(this).prop('disabled')
, $depedencies = $(this).parents(".adminoption").find(".pref_dependency")
, childrenElements = null
;
var childrenElements = $(this).parents('.adminoptionbox').nextAll('.adminoptionboxchild').eq(0).find(':input[id^="pref-"]');
if (childBlock) {
childrenElements = $(childBlock).find(':input[id^="pref-"]');
}
if (childMode === 'invert') {
// FIXME: Should only affect childBlock, not $depedencies. From r54386
checked = ! checked;
}
if (disabled && checked) {
$(childBlock).show('fast');
$depedencies.show('fast');
} else if (disabled || ! checked) {
/* Only hides child preferences if they are all at default values.
Purpose questioned in https://sourceforge.net/p/tikiwiki/mailman/tikiwiki-cvs/thread/F2DE8896807BF045932776107E2E783D350674DB%40CT20SEXCHP02.FONCIERQC.INTRA/#msg36171225
*/
var hideBlock = true;
childrenElements.each(function( index ) {
var value = $( this ).val();
var valueDefault = $( this ).siblings('span.pref-reset-wrapper').children('.pref-reset').attr('data-preference-default');
if (typeof valueDefault != 'undefined' && value != valueDefault) {
hideBlock = false;
}
});
if (hideBlock) {
$(childBlock).hide('fast');
$depedencies.hide('fast');
}
} else {
$(childBlock).show('fast');
$depedencies.show('fast');
}
});
$(document).on('click', '.pref-reset-wrapper a', function () {
var box = $(this).closest('span').find(':checkbox');
box.click();
$(this).closest('span').children( ".pref-reset-undo, .pref-reset-redo" ).toggle();
return false;
});
$(document).on('click', '.pref-reset', function() {
var c = $(this).prop('checked');
var $el = $(this).closest('.adminoptionbox').find('input:not(:hidden),select,textarea')
.not('.system').attr( 'disabled', c )
.css("opacity", c ? .6 : 1 );
var defval = $(this).data('preference-default');
if ($el.is(':checkbox')) {
$(this).data('preference-default', $el.prop('checked') ? 'y' : 'n');
$el.prop('checked', defval === "y");
} else {
$(this).data('preference-default', $el.val());
$el.val(defval);
}
$el.change();
if (jqueryTiki.select2) {
$el.trigger("change.select2");
}
});
$(document).on('change', '.preference select', function () {
var childBlock = $(this).data('tiki-admin-child-block')
, selected = $(this).val()
, childMode = $(this).data('tiki-admin-child-mode')
;
$(childBlock).hide();
$(childBlock + ' .modified').show();
$(childBlock + ' .modified').parent().show();
if (selected && /^[\w-]+$/.test(selected)) {
$(childBlock).filter('.' + selected).show();
}
if (childMode === 'notempty' && selected.length) {
$(childBlock).show();
}
});
$(document).on('change', '.preference :radio', function () {
var childBlock = $(this).data('tiki-admin-child-block');
if ($(this).prop('checked')) {
$(childBlock).show('fast');
$(this).closest('.preference').find(':radio').not(this).change();
} else {
$(childBlock).hide();
}
});
$(function () {
$('.preference :checkbox, .preference select, .preference :radio').change();
});