1/* global $ */ 2 3// $Id$ 4// JavaScript glue for jQuery in Tiki 5// 6// Tiki 6 - $ is now initialised in jquery.js 7// but let's keep $jq available too for legacy custom code 8 9var $jq = $, 10 $window = $(window), 11 $document = $(document); 12 13// Check / Uncheck all Checkboxes 14function switchCheckboxes (tform, elements_name, state, hiddenToo) { 15 // checkboxes need to have the same name elements_name 16 // e.g. <input type="checkbox" name="my_ename[]">, will arrive as Array in php. 17 if (hiddenToo == undefined) { 18 hiddenToo = false; 19 } 20 var closeTag; 21 if (hiddenToo) { 22 closeTag = '"]'; 23 } else { 24 closeTag = '"]:visible'; 25 } 26 $(tform).contents().find('input[name="' + jQuery.escapeSelector(elements_name) + closeTag).prop('checked', state).change(); 27} 28 29// add id's of any elements that don't like being animated here 30var jqNoAnimElements = ['help_sections', 'ajaxLoading']; 31 32function show(foo, f, section) { 33 if ($.inArray(foo, jqNoAnimElements) > -1 || typeof jqueryTiki === 'undefined') { // exceptions that don't animate reliably 34 $("#" + foo).show(); 35 } else if ($("#" + foo).hasClass("tabcontent")) { // different anim prefs for tabs 36 showJQ("#" + foo, jqueryTiki.effect_tabs, jqueryTiki.effect_tabs_speed, jqueryTiki.effect_tabs_direction); 37 } else { 38 showJQ("#" + foo, jqueryTiki.effect, jqueryTiki.effect_speed, jqueryTiki.effect_direction); 39 } 40 if (f) {setCookie(foo, "o", section);} 41} 42 43function hide(foo, f, section) { 44 if ($.inArray(foo, jqNoAnimElements) > -1 || typeof jqueryTiki === 'undefined') { // exceptions 45 $("#" + foo).hide(); 46 } else if ($("#" + foo).hasClass("tabcontent")) { 47 hideJQ("#" + foo, jqueryTiki.effect_tabs, jqueryTiki.effect_tabs_speed, jqueryTiki.effect_tabs_direction); 48 } else { 49 hideJQ("#" + foo, jqueryTiki.effect, jqueryTiki.effect_speed, jqueryTiki.effect_direction); 50 } 51 if (f) { 52// var wasnot = getCookie(foo, section, 'x') == 'x'; 53 setCookie(foo, "c", section); 54// if (wasnot) { 55// history.go(0); // used to reload the page with all menu items closed - broken since 3.x 56// } 57 } 58} 59 60// flip function... unfortunately didn't use show/hide (ay?) 61function flip(foo, style) { 62 var $foo = $("#" + foo); 63 if (style && style !== 'block' || foo === 'help_sections' || foo === 'fgalexplorer' || typeof jqueryTiki === 'undefined') { // TODO find a better way? 64 $foo.toggle(); // inlines don't animate reliably (yet) (also help) 65 if ($foo.css('display') === 'none') { 66 setSessionVar('show_' + tiki_encodeURIComponent(foo), 'n'); 67 } else { 68 setSessionVar('show_' + tiki_encodeURIComponent(foo), 'y'); 69 } 70 } else { 71 if ($foo.css("display") === "none") { 72 setSessionVar('show_' + tiki_encodeURIComponent(foo), 'y'); 73 show(foo); 74 } 75 else { 76 setSessionVar('show_' + tiki_encodeURIComponent(foo), 'n'); 77 hide(foo); 78 } 79 } 80} 81 82// handle JQ effects 83function showJQ(selector, effect, speed, dir) { 84 if (effect === 'none') { 85 $(selector).show(); 86 } else if (effect === '' || effect === 'normal') { 87 $(selector).show(400); // jquery 1.4 no longer seems to understand 'nnormal' as a speed 88 } else if (effect == 'slide') { 89 $(selector).slideDown(speed); 90 } else if (effect === 'fade') { 91 $(selector).fadeIn(speed); 92 } else if (effect.match(/(.*)_ui$/).length > 1) { 93 $(selector).show(effect.match(/(.*)_ui$/)[1], {direction: dir}, speed); 94 } else { 95 $(selector).show(); 96 } 97} 98 99function hideJQ(selector, effect, speed, dir) { 100 if (effect === 'none') { 101 $(selector).hide(); 102 } else if (effect === '' || effect === 'normal') { 103 $(selector).hide(400); // jquery 1.4 no longer seems to understand 'nnormal' as a speed 104 } else if (effect === 'slide') { 105 $(selector).slideUp(speed); 106 } else if (effect === 'fade') { 107 $(selector).fadeOut(speed); 108 } else if (effect.match(/(.*)_ui$/).length > 1) { 109 $(selector).hide(effect.match(/(.*)_ui$/)[1], {direction: dir}, speed); 110 } else { 111 $(selector).hide(); 112 } 113} 114 115// ajax loading indicator 116 117function ajaxLoadingShow(destName) { 118 var $dest, $loading, pos, x, y, w, h; 119 120 if (typeof destName === 'string') { 121 $dest = $('#' + destName); 122 } else { 123 $dest = $(destName); 124 } 125 if ($dest.length === 0 || $dest.parents(":hidden").length > 0) { 126 return; 127 } 128 $loading = $('#ajaxLoading'); 129 130 // find area of destination element 131 pos = $dest.offset(); 132 // clip to page 133 if (pos.left + $dest.width() > $window.width()) { 134 w = $window.width() - pos.left; 135 } else { 136 w = $dest.width(); 137 } 138 if (pos.top + $dest.height() > $window.height()) { 139 h = $window.height() - pos.top; 140 } else {
141 h = $dest.height(); 142 } 143 x = pos.left + (w / 2) - ($loading.width() / 2); 144 y = pos.top + (h / 2) - ($loading.height() / 2); 145 146 147 // position loading div 148 $loading.css('left', x).css('top', y); 149 // now BG 150 x = pos.left + ccsValueToInteger($dest.css("margin-left")); 151 y = pos.top + ccsValueToInteger($dest.css("margin-top")); 152 w = ccsValueToInteger($dest.css("padding-left")) + $dest.width() + ccsValueToInteger($dest.css("padding-right")); 153 h = ccsValueToInteger($dest.css("padding-top")) + $dest.height() + ccsValueToInteger($dest.css("padding-bottom")); 154 $('#ajaxLoadingBG').css('left', pos.left).css('top', pos.top).width(w).height(h).fadeIn("fast"); 155 156 show('ajaxLoading'); 157 158 159} 160 161function ajaxLoadingHide() { 162 hide('ajaxLoading'); 163 $('#ajaxLoadingBG').fadeOut("fast"); 164} 165 166 167function ajaxSubmitEventHandler(successCallback, dataType) { 168 return function (e) { 169 e.preventDefault(); 170 var form = this, act; 171 act = $(form).attr('action'); 172 var modal = $(form).closest('.modal-dialog'); 173 174 if (! act) { 175 act = url; 176 } 177 178 dataType = dataType || 'json'; 179 180 if (typeof $(form).valid === "function") { 181 if (!$(form).valid()) { 182 return false; 183 } else if ($(form).validate().pendingRequest > 0) { 184 $(form).validate(); 185 setTimeout(function() {$(form).submit();}, 500); 186 return false; 187 } 188 } 189 190 modal.tikiModal(tr('Loading...')); 191 192 // if there is a file is included in form, use FormData, otherwise, serialize the form input values. 193 // FormData still has issues in IE, though they've been fixed in Edge. 194 if ($(form).find("input[type=file]").length){ 195 var formData = new FormData(form); 196 } else { 197 var formData= $(form).serialize(); 198 } 199 200 var formSubmission = { 201 type: 'POST', 202 data: formData, 203 dataType: dataType, 204 success: function (data) { 205 successCallback.apply(form, [data]); 206 }, 207 error: function (jqxhr) { 208 // Headers sent from Feedback class already handled through an ajaxComplete 209 if (! jqxhr.getResponseHeader('X-Tiki-Feedback')) { 210 modal.tikiModal(); 211 $(form).showError(jqxhr); 212 } 213 }, 214 complete: function () { 215 modal.tikiModal(); 216 } 217 }; 218 219 // if the encryption type on the form is set to 'multipart/form-data' or formData is a FormData object 220 // we must set contentType and processData to false on the ajax submission 221 if (form.enctype === "multipart/form-data" || formData.constructor === FormData) { 222 formSubmission.contentType = false;
223 formSubmission.processData = false; 224 } 225 226 $.ajax(act, formSubmission); 227 return false; 228 }; 229} 230 231function checkDuplicateRows( button, columnSelector, rowSelector, parentSelector ) { 232 if (typeof columnSelector === 'undefined') { 233 columnSelector = "td"; 234 } 235 if (typeof rowSelector === 'undefined') { 236 rowSelector = "tr:not(:first)"; 237 } 238 if (typeof parentSelector === 'undefined') { 239 parentSelector = "table:first"; 240 } 241 var $rows = $(button).parents(parentSelector).find(rowSelector); 242 $rows.each(function( ix, el ){ 243 if ($("input:checked", el).length === 0) { 244 var $el = $(el); 245 var line = $el.find(columnSelector).text(); 246 $rows.each(function( ix, el ){ 247 if ($el[0] !== el && $("input:checked", el).length === 0) { 248 if (line === $(el).find(columnSelector).text()) { 249 $(":checkbox:first", el).prop("checked", true); 250 } 251 } 252 }); 253 } 254 }); 255} 256 257$.fn.tiki_popover = function () { 258 var list, $container = this; 259 260 // To allow table elements etc in tips and popovers 261 if (typeof $.fn.tooltip.Constructor.Default.whiteList === "object") { 262 var myDefaultWhiteList = $.fn.tooltip.Constructor.Default.whiteList; 263 264 myDefaultWhiteList.table = []; 265 myDefaultWhiteList.thead = []; 266 myDefaultWhiteList.tbody = []; 267 myDefaultWhiteList.tr = []; 268 myDefaultWhiteList.th = []; 269 myDefaultWhiteList.td = []; 270 myDefaultWhiteList.form = ["action", "method"]; 271 myDefaultWhiteList.input = ["name", "value", "type"]; 272 myDefaultWhiteList.button = ["type", "disabled", "name", "value", "onclick"]; 273 myDefaultWhiteList.time = ["datetime"]; // for timeago 274 myDefaultWhiteList.a = ["target", "href", "title", "rel", "data-toggle", "data-backdrop", 275 "data-target", "onclick"]; // data items for smarty_function_bootstrap_modal 276 } 277 278 /* 279 * Prepare the data so all elements so the data is all in the right format for bootstrap popovers 280 */ 281 list = $container.find('.tips[title!=""], .tikihelp[title!=""]') 282 .each(function () { 283 var $element = $(this); 284 285 if ($element.attr('title')) { 286 $.each(['|', ':', '<br/>', '<br>'], function (key, sep) { 287 var parts = $element.attr('title').split(sep); 288 if (parts.length > 1) { 289 $element.attr('title', parts.shift()); 290 $element.data('content', parts.join(sep)); 291 } 292 }); 293 } else { 294 $element.attr('title', ''); 295 } 296 297 if (! $element.data('trigger')) { 298 $element.data('trigger', 'hover'); 299 } 300 // default Tiki delay 301 $element.data('delay', { "show": 0, "hide": 10 }); 302 }); 303 304 $.merge(list, $container.find("a[data-toggle=popover]:not(.tips[title!='']):not(.tikihelp[title!=''])")); 305 306 list.filter('.bottom').data('placement', 'bottom'); 307 list.filter('.left').data('placement', 'left'); 308 list.filter('.slow').data('delay', { "show": 500, "hide": 0 }); 309 310 list.find('img').attr('title', ''); // Remove the img title to avoid browser tooltip 311 list.filter('[data-trigger="click"]') 312 .click(function (e) { 313 e.preventDefault(); 314 }); 315 316 // Handle common cases 317 list 318 .popover({ 319 container: 'body', 320 html: true, 321 boundary: "window", 322 placement: $.tikiPopoverWhereToPlace 323 }); 324 325 $container.find('.ajaxtips').each(function() { 326 var me = $(this), 327 trigger = me.data('trigger') || 'hover'; 328 329 $(this).popover({ 330 trigger: trigger, 331 html: true, 332 delay: { "show": 0, "hide": 10 }, 333 placement: $.tikiPopoverWhereToPlace, 334 boundary: "window", 335 content: function () { 336 var link = this, content = $(this).data('content'); 337 338 if (!content) { 339 $.get($(this).data('ajaxtips'), function (data) { 340 content = data; 341 342 $(link).data('content', content); 343 $(link).popover('show'); 344 }); 345 346 // display a spinner while waiting for the ajax call to return the content 347 content = "<div class='text-center p-3'><img src='img/spinner.gif' alt='Loading...'></div>"; 348 } 349 350 return content; 351 } 352 }); 353 }); 354 355 // only have one popover showing at a time 356 $document.on("show.bs.popover", function ( e ) { 357 var event = e; 358 $('.popover:visible:not(.tour-tour)').each(function () { 359 if (this.previousElementSibling !== event.target) { 360 $(this).popover('hide'); 361 } 362 }); 363 }); 364 365 $document.on("hide.bs.popover", function ( e ) { 366 var $popover = $('.popover:visible:not(.tour-tour)'); 367 368 // if mouse is over the popover 369 if ($popover.find(":hover").length) { 370 // change the leave event to be when leaving the popover 371 $popover.mouseleave(function () { 372 $(this).popover("hide"); 373 }); 374 // and cancel the hide event 375 e.preventDefault(); 376 return false;
377 } 378 }); 379 380 return $container; 381}; 382 383$.tikiPopoverWhereToPlace = function (pop, el) { 384 var pxNum = function(str) { 385 return (str || '').replace('px', '') * 1; 386 }, 387 $win = $(window), 388 $el = $(el), 389 width = $el.offsetParent().width(), 390 height = $el.offsetParent().height(), 391 $pop = $(pop), 392 allowedImgWidth = width * 0.60, 393 allowedImgHeight = height * 0.60, 394 manualImageWidth = $el.data('width'), 395 leftPos = $el.offset().left, 396 rightPos = leftPos + $el.outerWidth(), 397 bottomPos = $el.offset().top + $el.outerHeight() - $win.scrollTop(), 398 $img = $pop.find('div[style*="background-image"],img').first(), 399 $imgContainer = $img.parent(), 400 $imgPopover = $imgContainer.parent(), 401 imgWidth = pxNum($img.css('width')), 402 imgHeight = pxNum($img.css('height')), 403 newImgWidth, 404 newImgHeight, 405 widthBuffer, 406 heightBuffer; 407 408 if ($el.data("placement")) { 409 return $el.data("placement"); // element already has popover placement set 410 } 411 412 if (manualImageWidth) { 413 $img.css({ 414 width: manualImageWidth + 'px' 415 }); 416 417 $pop.css({ 418 "max-width" : '100%' 419 }); 420 421 imgWidth = manualImageWidth; 422 } 423 424 //lets check the size of the popover img 425 if (imgWidth > allowedImgWidth || imgHeight > allowedImgHeight) { 426 widthBuffer = (pxNum($imgContainer.css('padding-left')) + pxNum($imgContainer.css('margin-left')) + pxNum($imgContainer.css('border-left-width'))) * 2; 427 heightBuffer = (pxNum($imgContainer.css('padding-top')) + pxNum($imgContainer.css('margin-top')) + pxNum($imgContainer.css('border-top-width'))) * 2; 428 429 // proportionate the image relative to what is allowed 430 if(allowedImgWidth/imgWidth > allowedImgHeight/imgHeight){ 431 newImgWidth = allowedImgWidth; 432 newImgHeight = imgHeight*(allowedImgWidth/imgWidth); 433 } else { 434 newImgWidth = imgWidth*(allowedImgHeight/imgHeight); 435 newImgHeight = allowedImgHeight; 436 } 437 438 $img.css({ 439 backgroundSize: newImgWidth + 'px ' + newImgHeight + 'px', 440 width: newImgWidth + 'px', 441 height: newImgHeight + 'px' 442 }); 443 444 $imgPopover.css({ 445 maxWidth: (newImgWidth + widthBuffer) + 'px', 446 maxHeight: (newImgHeight + heightBuffer) +'px' 447 }); 448 } 449 450 451 var $popTemp = $("<div class='popover temp'><div class='popover-body'>" + $el.data("content") + "</div></div>"); 452 $("body").append($popTemp); 453 var popWidth = $popTemp.outerWidth(), 454 popHeight = $popTemp.outerHeight(); 455 456 $popTemp.remove(); 457 458 if (width - leftPos < popWidth && width - rightPos < popWidth) { 459 if (bottomPos > popHeight || 460 bottomPos + popHeight > $win.height()) { 461 return 'top'; 462 } else { 463 return 'bottom'; 464 } 465 } else if (width - leftPos > popWidth) { 466 return 'left'; 467 } else if (width - rightPos > popWidth) { 468 return 'right'; 469 } 470 if (imgWidth && width - leftPos + imgWidth > width) return 'bottom'; 471 472 return 'auto'; 473}; 474 475/* 476 keep the popover open while the user is hovering over it 477 based on http://jsfiddle.net/raving/2thfaxeu thanks 478*/ 479if ($.fn.popover) { 480 var originalLeave = $.fn.popover.Constructor.prototype.leave; 481 $.fn.popover.Constructor.prototype.leave = function(obj){ 482 var self = obj instanceof this.constructor ? 483 obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type); 484 var container, timeout; 485 486 originalLeave.call(this, obj); 487 488 if(self.$tip) { 489 container = self.$tip; 490 timeout = self.timeout; 491 container.one('mouseenter', function(){ 492 //We entered the actual popover â call off the dogs 493 clearTimeout(timeout); 494 //Let's monitor popover content instead 495 container.one('mouseleave', function(){ 496 $.fn.popover.Constructor.prototype.leave.call(self, self); 497 }); 498 }) 499 } 500 }; 501} 502 503 504 505$(function() { // JQuery's DOM is ready event - before onload 506 if (!window.jqueryTiki) window.jqueryTiki = {}; 507 508 // Reflections 509 if (jqueryTiki.reflection) { 510 $("img.reflect").reflect({}); 511 } 512 513 if (jqueryTiki.tooltips) { 514 $(document).tiki_popover(); 515 } 516 517 // superfish setup (CSS menu effects) 518 if (jqueryTiki.superfish) { 519 $('ul.cssmenu_horiz').supersubs({ 520 minWidth: 11, // minimum width of sub-menus in em units 521 maxWidth: 20, // maximum width of sub-menus in em units 522 extraWidth: 1 // extra width can ensure lines don't sometimes turn over 523 // due to slight rounding differences and font-family 524 }); 525 $('ul.cssmenu_vert').supersubs({ 526 minWidth: 11, // minimum width of sub-menus in em units 527 maxWidth: 20, // maximum width of sub-menus in em units 528 extraWidth: 1 // extra width can ensure lines don't sometimes turn over 529 // due to slight rounding differences and font-family 530 }); 531 $('ul.cssmenu_horiz').superfish({ 532 animation: {opacity:'show', height:'show'}, // fade-in and slide-down animation 533 speed: 'fast', // faster animation speed 534 onShow: function(){ 535 if ($(this).data('active')) return; 536 537 $(this) 538 .data('active', true) 539 .moveToWithinWindow(); 540 }, 541 onHide: function(){ 542 $(this).removeData('active'); 543 } 544 }); 545 $('ul.cssmenu_vert').superfish({ 546 animation: {opacity:'show', height:'show'}, // fade-in and slide-down animation 547 speed: 'fast', // faster animation speed 548 onShow: function(){ 549 if ($(this).data('active')) return; 550 551 $(this) 552 .data('active', true) 553 .moveToWithinWindow(); 554 }, 555 onHide: function(){ 556 $(this).removeData('active'); 557 } 558 }); 559 } 560 561 $.fn.applyColorbox = function() { 562 $(this).find("a[data-box*='box']").colorbox({ 563 rel: function(){ 564 return $(this).attr('data-box'); 565 }, 566 transition: "elastic", 567 maxHeight:"95%", 568 maxWidth:"95%", 569 overlayClose: true, 570 current: jqueryTiki.cboxCurrent 571 }); 572 573 // now, first let suppose that we want to display images in ColorBox by default: 574 575 // this matches data-box attributes containing type=img or no type= specified 576 $(this).find("a[data-box*='box'][data-box*='type=img'], a[data-box*='box'][data-box!='type=']:not([data-is-text])").colorbox({ 577 photo: true 578 }); 579 580 // data-box attributes containing slideshow (this one must be without #col1) 581 $(this).find("a[data-box*='box'][data-box*='slideshow']").colorbox({ 582 photo: true, 583 slideshow: true, 584 slideshowSpeed: 3500, 585 preloading: false, 586 width: "100%", 587 height: "100%" 588 }); 589 // this are the defaults matching all *box links which are not obviously links to images... 590 // (if we need to support more, add here... otherwise it is possible to override with type=iframe in data-box attribute of a link) 591 // (from here one to speed it up, matches any link in #col1 only - the main content column) 592 $(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({ 593 iframe: true, 594 width: "95%", 595 height: "95%" 596 }); 597 // hrefs starting with ftp(s) 598 $(this).find("#col1 a[data-box*='box'][href^='ftp://'], #col1 a[data-box*='box'][href^='ftps://']").colorbox({ 599 iframe: true, 600 width: "95%", 601 height: "95%" 602 }); 603 // data-box attributes containing type=flash 604 $(this).find("#col1 a[data-box*='box'][data-box*='type=flash']").colorbox({ 605 inline: true, 606 width: "60%", 607 height: "60%", 608 href: function () { 609 var $el = $("#cb_swf_player"); 610 if ($el.length === 0) { 611 $el = $("<div id='cb_swf_player' />"); 612 $(document.body).append($("<div />").hide().append($el)); 613 } 614 //$(this).media.swf(el, { width: 400, height: 300, autoplay: true, src: $(this).attr("href") }); 615 swfobject.embedSWF($(this).attr("href"), "cb_swf_player", "100%", "90%", "9.0.0", "vendor_bundled/vendor/bower-asset/swfobject/swfobject/expressInstall.swf"); 616 return $("#cb_swf_player"); 617 } 618 }); 619 620 // data-box attributes containing type=video 621 $(this).find("#col1 a[data-box*='box'][data-box*='type=video']").colorbox({ 622 iframe: true, 623 fastIframe: false, 624 height:"90%", 625 width:"90%", 626 html: function() { 627 return '<video controls autoplay>' + 628 '<source src="' + $(this).attr('href') + '">' + 629 + 'Your browser does not support the video tag.' + 630 '</video>'; 631 }, 632 onComplete : function() { 633 let video = $('#colorbox iframe').contents().find('video'); 634 635 setTimeout(function() { 636 $(this).colorbox.resize({ 637 innerHeight : video.height() + 'px', 638 innerWidth : video.width() + 'px' 639 }); 640 }, 100); 641 } 642 }); 643 644 // data-box attributes with type=iframe (if someone needs to override anything above) 645 $(this).find("#col1 a[data-box*='box'][data-box*='type=iframe']").colorbox({ 646 iframe: true 647 }); 648 // inline content: hrefs starting with # 649 $(this).find("#col1 a[data-box*='box'][href^='#']").colorbox({ 650 inline: true, 651 width: "50%", 652 height: "50%", 653 href: function(){ 654 return $(this).attr('href'); 655 } 656 }); 657 658 // titles (for captions): 659 660 // by default get title from the title attribute of the link (in all columns) 661 $(this).find("a[data-box*='box'][title]").colorbox({ 662 title: function(){ 663 return $(this).attr('title'); 664 } 665 }); 666 // but prefer the title from title attribute of a wrapped image if any (in all columns) 667 $(this).find("a[data-box*='box'] img[title]").colorbox({ 668 title: function(){ 669 return $(this).attr('title'); 670 }, 671 photo: true, // and if you take title from the image you need photo 672 href: function(){ // and href as well (for colobox 1.3.6 tiki 5.0) 673 return $(this).parent().attr("href"); 674 } 675 }); 676 677 /* Shadowbox params compatibility extracted using regexp functions */ 678 var re, ret; 679 // data-box attributes containing title param overrides title attribute of the link (shadowbox compatible) 680 $(this).find("#col1 a[data-box*='box'][data-box*='title=']").colorbox({ 681 title: function () { 682 re = /(title=([^;\"]+))/i; 683 ret = $(this).attr("data-box").match(re); 684 return ret[2]; 685 } 686 }); 687 // data-box attributes containing height param (shadowbox compatible) 688 $(this).find("#col1 a[data-box*='box'][data-box*='height=']").colorbox({ 689 height: function () { 690 re = /(height=([^;\"]+))/i; 691 ret = $(this).attr("data-box").match(re); 692 return ret[2]; 693 } 694 }); 695 // data-box attributes containing width param (shadowbox compatible) 696 $(this).find("#col1 a[data-box*='box'][data-box*='width=']").colorbox({ 697 width: function () { 698 re = /(width=([^;\"]+))/i; 699 ret = $(this).attr("data-box").match(re); 700 return ret[2]; 701 } 702 }); 703 704 // links generated by the {COLORBOX} plugin 705 if (jqueryTiki.colorbox) { 706 $(this).find("a[data-box^='shadowbox[colorbox']").each(function () {$(this).attr('savedTitle', $(this).attr('title'));}); 707 $(this).find("a[data-box^='shadowbox[colorbox']").colorbox({ 708 title: function() { 709 return $(this).attr('savedTitle'); // this fix not required is colorbox was disabled 710 } 711 }); 712 } 713 }; 714 715 $.applyColorbox = function() { 716 if (jqueryTiki.colorbox) { 717 $('body').applyColorbox(); 718 } 719 }; 720 721 // ColorBox setup (Shadowbox, actually "<any>box" replacement) 722 if (jqueryTiki.colorbox && !jqueryTiki.mobile) { 723 $().on('cbox_complete', function(){ 724 $("#cboxTitle").wrapInner("<div></div>"); 725 }); 726 727 $.applyColorbox(); 728 } // end if (jqueryTiki.colorbox) 729 730 if (jqueryTiki.zoom) { 731 $("a[data-box*=zoom]").each(function () { 732 $(this)
733 .wrap('<span class="img_zoom"></span>') 734 .parent() 735 .zoom({ 736 url: $(this).attr("href") 737 }); 738 }); 739 } 740 741 $.fn.applyChosen = function () { 742 if (jqueryTiki.chosen) { 743 $("select:not(.allow_single_deselect):not(.nochosen)").tiki("chosen"); 744 } 745 }; 746 747 $.applyChosen = function() { 748 return $('body').applyChosen(); 749 }; 750 751 if (jqueryTiki.chosen) { 752 $.applyChosen(); 753 } 754 755 $( function() { 756 $("#keepOpenCbx").click(function() { 757 if (this.checked) { 758 setCookie("fgalKeepOpen", "1"); 759 } else { 760 setCookie("fgalKeepOpen", ""); 761 } 762 }); 763 var keepopen = getCookie("fgalKeepOpen"); 764 $("#keepOpenCbx").prop("checked", !! keepopen); 765 }); 766 // end fgal fns 767 768 769 $.paginationHelper(); 770 771 // bind clickModal to links with or in click-modal class 772 $(document).on('click', 'a.click-modal, .click-modal a', $.clickModal({ 773 size: 'modal-lg', 774 backdrop: 'static', 775 success: function (data) { 776 let redirect = $(this).data('modal-submit-redirect-url') || $(this).parent().data('modal-submit-redirect-url'); 777 window.location.href = redirect || window.location.href.replace(/#.*$/, ''); 778 } 779 })); 780 781 if (jqueryTiki.numericFieldScroll === "none" || jqueryTiki.numericFieldScroll === null){ 782 // disable mousewheel on a input number field when in focus 783 // (to prevent browsers change the value when scrolling) 784 $('form').on('focus', 'input[type=number]', function (e) { 785 $(this).on('wheel.disableScroll', function (e) { 786 e.preventDefault() 787 }) 788 }); 789 $('form').on('blur', 'input[type=number]', function (e) { 790 $(this).off('wheel.disableScroll') 791 }); 792 } 793}); // end $document.ready 794 795//For ajax/custom search 796$document.on('pageSearchReady', function() { 797 $.paginationHelper(); 798}); 799 800// moved from tiki-list_file_gallery.tpl in tiki 6 801function checkClose() { 802 if (!$("#keepOpenCbx").prop("checked")) { 803 window.close(); 804 } else { 805 window.blur(); 806 if (window.opener) { 807 window.opener.focus(); 808 } 809 } 810} 811 812 813/* 814 * JS only textarea fullscreen function (for Tiki 5+) 815 */ 816 817$(function() { // if in translation-diff-mode go fullscreen automatically 818 if ($("#diff_outer").length && !$.trim($(".wikipreview .wikitext").html()).length) { // but not if previewing (TODO better) 819 toggleFullScreen("editwiki"); 820 } 821}); 822 823function sideBySideDiff() { 824 if ($('.side-by-side-fullscreen').size()) { 825 $('.side-by-side-fullscreen').remove(); 826 return; 827 } 828 829 var $diff = $('#diff_outer').remove(), $zone = $('.edit-zone'); 830 $zone.after($diff.addClass('side-by-side-fullscreen')); 831 $diff.find('#diff_history').height(''); 832} 833 834function toggleFullScreen(area_id) { 835 836 if ($("input[name=wysiwyg]").val() === "y") { // quick fix to disable side-by-side translation for wysiwyg 837 $("#diff_outer").css({ 838 position: "inherit", 839 height: "400px", 840 overflowX: "auto" 841 }); 842 return; 843 } 844 845 var textarea = $("#" + area_id); 846 847 //codemirror interation and preservation 848 var textareaEditor = syntaxHighlighter.get(textarea); 849 if (textareaEditor) { 850 syntaxHighlighter.fullscreen(textarea); 851 sideBySideDiff(); 852 return; 853 } 854 855 var toolbar = $('#editwiki_toolbar'), 856 preview = $("#autosave_preview"), 857 comment = $("#comment").parents("fieldset:first"), 858 screen = $('.TextArea-fullscreen'), 859 zone = $('.edit-zone', screen); 860 861 screen.add(textarea).css('height', ''); 862 863 //removes wiki command buttons (save, cancel, preview) from fullscreen view 864 $('.TextArea-fullscreen .actions').remove(); 865 if (textarea.parent().hasClass("ui-wrapper")) { 866 textarea.resizable("destroy"); // if codemirror is off, jquery-ui resizable messes this up 867 } 868 869 var textareaParent = textarea.parents(".tab-content:first").toggleClass('TextArea-fullscreen'); 870 871 if (textareaParent.hasClass('TextArea-fullscreen')) { 872 $('body').css('overflow', 'hidden'); 873 $('.tabs,.rbox-title').toggle(); 874 $('#fullscreenbutton').hide(); 875 876 var win = $window 877 .data('cm-resize', true), 878 diff = $("#diff_outer"), 879 msg = $(".translation_message"), 880 actions = $('.actions', textarea.parents("form")); 881 882 //adds wiki command buttons (save, cancel, preview) to fullscreen view 883 actions.clone().appendTo('.TextArea-fullscreen'); 884 actions = $('.actions', $('.TextArea-fullscreen')); 885 886 comment.css({ // fix comments fieldset to bottom and hide others (like contributions) 887 position: "absolute", 888 bottom: actions.outerHeight() + "px", 889 width: "100%" 890 }).nextAll("fieldset").hide(); 891 892 preview.css({ 893 position: "absolute", 894 top: 0, 895 left: 0 896 }); 897 898 win.resize(function() { 899 screen = $('.TextArea-fullscreen'); 900 actions = $('.actions', screen); 901 comment = $("#comment").parents("fieldset:first"); 902 if (win.data('cm-resize') && screen) { 903 screen.css('height', win.height() + 'px'); 904 var swidth = win.width() + "px"; 905 var commentMargin = parseInt(comment.css("paddingTop").replace("px", "")) * 4; 906 commentMargin += parseInt(comment.css("borderBottomWidth").replace("px", "")) * 2; 907 var innerHeight = win.height() - comment.outerHeight() - commentMargin - actions.outerHeight(); 908 // reducing innerHeight by 85px in prev line makes the "Describe the change you made:" and 909 // "Monitor this page:" edit fields visible and usable. Tested in all 22 themes in Tiki-12 r.48429 910 911 if (diff.length) { 912 swidth = (screen.width() / 2) + "px"; 913 innerHeight -= msg.outerHeight(); 914 msg.css("width", (screen.width() / 2 - msg.css("paddingLeft").replace("px", "") - msg.css("paddingRight").replace("px", "")) + "px"); 915 diff.css({ 916 width: swidth,
917 height: innerHeight + 'px' 918 }); 919 $('#diff_history').height(innerHeight + "px"); 920 } 921 textarea.css("width", swidth); 922 toolbar.css('width', swidth); 923 zone.css("width", swidth); 924 preview.css("width", swidth); 925 textarea.css('height', (innerHeight - toolbar.outerHeight()) + "px"); 926 } 927 }); 928 setTimeout(function () {$window.resize();}, 500); // some themes (coelesce) don't show scrollbars unless this is delayed a bit 929 } else { 930 textarea.css("width", ""); 931 toolbar.css('width', ""); 932 zone.css({ width: "", height: ""}); 933 screen.css("width", ""); 934 comment.css({ position: "", bottom: "", width: "" }).nextAll("fieldset").show(); 935 preview.css({ position: "", top: "", left: "" }); 936 $('body').css('overflow', ''); 937 $('.tabs,.rbox-title').toggle(); 938 $('#fullscreenbutton').show(); 939 $window.removeData('cm-resize'); 940 } 941 942 sideBySideDiff(); 943} 944 945/* Simple tiki plugin for jQuery 946 * Helpers for autocomplete and sheet 947 */ 948var xhrCache = {}, lastXhr; // for jq-ui autocomplete 949 950$.fn.tiki = function(func, type, options, excludepage) { 951 var opts = {}, opt; 952 switch (func) { 953 case "autocomplete": 954 if (jqueryTiki.autocomplete && jqueryTiki.ui) { 955 if (typeof type === 'undefined') { // func and type given 956 // setup error - alert here? 957 return null; 958 } 959 options = options || {}; 960 var requestData = {}, _renderItem = null; 961 962 var url = ""; 963 switch (type) { 964 case "pagename": 965 url = "tiki-listpages.php?listonly&initial=" + (options.initial ? options.initial + "&nonamespace" : "")+"&exclude_page="+excludepage; 966 break; 967 case "groupname": 968 url = "tiki-ajax_services.php?listonly=groups"; 969 break; 970 case "username": 971 url = "tiki-ajax_services.php?listonly=users"; 972 break; 973 case "usersandcontacts": 974 url = "tiki-ajax_services.php?listonly=usersandcontacts"; 975 break; 976 case "userrealname": 977 url = "tiki-ajax_services.php?listonly=userrealnames"; 978 break; 979 case "tag": 980 url = "tiki-ajax_services.php?listonly=tags&separator=+"; 981 break; 982 case "icon": 983 url = null; 984 opts.source = Object.keys(jqueryTiki.iconset.icons); 985 986 _renderItem = function(ul, item) { 987 return $("<li>") 988 .attr("data-value", item.value ) 989 .append($().getIcon(item.value)) 990 .append(" ") 991 .append(item.label) 992 .appendTo(ul); 993 }; 994 break; 995 case 'trackername': 996 url = "tiki-ajax_services.php?listonly=trackername"; 997 break; 998 case 'trackervalue': 999 if (typeof options.fieldId === "undefined") { 1000 // error 1001 return null; 1002 } 1003 $.extend( requestData, options ); 1004 options = {}; 1005 url = "list-tracker_field_values_ajax.php"; 1006 break; 1007 case "reference": 1008 url = "tiki-ajax_services.php?listonly=references"; 1009 break; 1010 } 1011 var multiple = options.multiple && (type == 'usersandcontacts' || type == 'userrealname' || type == 'username' || type == 'reference'); 1012 opts = $.extend({ // default options for autocompletes in tiki 1013 minLength: 2, 1014 source: function( request, response ) { 1015 if( multiple ) { 1016 request.term = (''+request.term).split( /,\s*/ ).pop(); 1017 } 1018 if (options.tiki_replace_term) { 1019 request.term = options.tiki_replace_term.apply(null, [request.term]); 1020 } 1021 var cacheKey = "ac." + type + "." + request.term; 1022 if ( cacheKey in xhrCache ) { 1023 response( xhrCache[ cacheKey ] ); 1024 return; 1025 } 1026 request.q = request.term; 1027 $.extend( request, requestData ); 1028 lastXhr = $.getJSON( url, request, function( data, status, xhr ) { 1029 xhrCache[ cacheKey ] = data; 1030 if ( xhr === lastXhr ) { 1031 response( data, function (item) { 1032 return item 1033 }) 1034 }}); 1035 }, 1036 focus: function(ev) { 1037 // Important for usability handling below to prevent non-valid selections 1038 ev.preventDefault(); 1039 }, 1040 search: function() { 1041 if( multiple ) { 1042 // custom minLength 1043 var term = (''+this.value).split( /,\s*/ ).pop(); 1044 if ( term.length < 2 ) { 1045 return false;
1046 } 1047 } 1048 }, 1049 select: function(e, ui) { 1050 if( multiple ) { 1051 var terms = ''+this.value; 1052 terms = terms.replace(';', ','); 1053 terms = terms.split( /,\s*/ ); 1054 // remove the current input 1055 terms.pop(); 1056 // add the selected item 1057 terms.push( ui.item.value ); 1058 // add placeholder to get the comma-and-space at the end 1059 terms.push( "" ); 1060 this.value = terms.join( ", " ); 1061 return false; 1062 } else { 1063 $(this).data('selected', true); 1064 } 1065 } 1066 }, opts); 1067 $.extend(opts, options); 1068 1069 if(options.mustMatch && multiple) { 1070 // Control editing of autocomplete to avoid messing with selection 1071 this.on("keydown", function (e) { 1072 if (e.which === 8 || e.which === 46) { 1073 e.preventDefault(); 1074 var terms = ''+this.value; 1075 terms = terms.replace(';', ','); 1076 terms = terms.split( /,\s*/ ); 1077 // remove the current input and the last previous item 1078 var lastterm = terms.pop(); 1079 if (lastterm === '') { 1080 terms.pop(); 1081 } 1082 // add placeholder to get the comma-and-space at the end 1083 terms.push( "" ); 1084 this.value = terms.join( ", " ); 1085 } else if (e.which === 37 || e.which === 39) { 1086 e.preventDefault(); 1087 } 1088 }); 1089 this.on("focus click", function() { 1090 var currentVal = $(this).val(); 1091 $(this).val('').val(currentVal); 1092 }); 1093 } else if (options.mustMatch) { 1094 if ($(this).val()) { 1095 // if there is value to begin then consider as selected 1096 $(this).data('selected', true); 1097 } 1098 $(this).on("blur", function() { 1099 if (! $(this).data('selected')) { 1100 $(this).val(''); 1101 } 1102 }); 1103 $(this).on("keydown", function(e) { 1104 if ($(this).data('selected') && (e.which === 8 || e.which === 46)) { 1105 e.preventDefault(); 1106 $(this).val(''); 1107 $(this).data('selected', false); 1108 } else if ($(this).data('selected') && (e.which > 47 || e.which === 32)) { 1109 e.preventDefault(); 1110 } else if (e.which === 13 && !$(this).data('selected')) { 1111 e.preventDefault(); 1112 } 1113 }); 1114 } 1115 1116 return this.each(function() { 1117 var $element = $(this).autocomplete(opts).blur( function() { 1118 $(this).removeClass( "ui-autocomplete-loading").change(); 1119 }); 1120 if (_renderItem && $element.length) { 1121 $element.autocomplete("instance")._renderItem = _renderItem; 1122 } 1123 }); 1124 } 1125 break; 1126 case "carousel": 1127 if (jqueryTiki.carousel) { 1128 opts = { 1129 imagePath: "vendor_bundled/vendor/jquery-plugins/infinitecarousel/images/", 1130 autoPilot: true 1131 }; 1132 $.extend(opts, options); 1133 return this.each(function() { 1134 $(this).infiniteCarousel(opts); 1135 }); 1136 } 1137 break; 1138 case "datepicker": 1139 case "datetimepicker": 1140 if (jqueryTiki.ui) { 1141 switch (type) { 1142 case "jscalendar": // replacements for jscalendar 1143 // timestamp result goes in the options.altField 1144 if (typeof options.altField === "undefined") { 1145 alert("jQuery.ui datepicker jscalendar replacement setup error: options.altField not set for " + $(this).attr("id")); 1146 debugger; 1147 } 1148 opts = { 1149 showOn: "both", 1150 buttonText: '', 1151 dateFormat: jqueryTiki.shortDateFormat, 1152 timeFormat: jqueryTiki.shortTimeFormat, 1153 showButtonPanel: true, 1154 altFormat: "@", 1155 altFieldTimeOnly: false, 1156 onClose: function(dateText, inst) { 1157 $.datepickerAdjustAltField(func, inst); 1158 } 1159 }; 1160 break; 1161 default: 1162 opts = { 1163 showOn: "both", 1164 buttonText: '', 1165 dateFormat: jqueryTiki.shortDateFormat, 1166 showButtonPanel: true, 1167 firstDay: jqueryTiki.firstDayofWeek 1168 }; 1169 break; 1170 } 1171 $.extend(opts, options); 1172 if (func === "datetimepicker") { 1173 return this.each(function() { 1174 $(this).datetimepicker(opts); 1175 }); 1176 } else { 1177 return this.each(function() { 1178 $(this).datepicker(opts); 1179 }); 1180 } 1181 } 1182 break; 1183 case "accordion": 1184 if (jqueryTiki.ui) { 1185 opts = { 1186 autoHeight: false, 1187 collapsible: true, 1188 navigation: true 1189// change: function(event, ui) { 1190// // sadly accordion active property is broken in 1.7, but fix is coming in 1.8 so TODO
1191// setCookie(ui, ui.options.active, "accordion"); 1192// } 1193 }; 1194 $.extend(opts, options); 1195 return this.each(function() { 1196 $(this).accordion(opts); 1197 }); 1198 } 1199 break; 1200 case "chosen": 1201 if (jqueryTiki.chosen) { 1202 opts = { allow_single_deselect: true, search_contains: true }; // allow_single_deselect happens if first item is empty 1203 if ($("html").attr("dir") === "rtl") { 1204 $(this).addClass("chosen-rtl"); 1205 } 1206 $.map({ // translate the strings 1207 placeholder_text_multiple: "Select Some Options", 1208 placeholder_text_single: "Select an Option", 1209 no_results_text: "No results match" 1210 }, function (v, k) { 1211 opts[k] = tr(v); 1212 }); 1213 $.extend(opts, options); 1214 return this.each(function() { 1215 var opts2 = $.extend({}, opts), 1216 $select = $(this); 1217 1218 if ($select.is(":hidden") && !opts.width) { 1219 // from https://github.com/harvesthq/chosen/pull/1580 1220 var $hiddenElement = $select.clone().appendTo("body"); 1221 if ($select.is('.form-control')) { 1222 opts2.width = '100%'; 1223 } else { 1224 opts2.width = $hiddenElement.outerWidth(); 1225 } 1226 $hiddenElement.remove(); 1227 } 1228 var hidden = $(); 1229 $select.chosen(opts2) 1230 .on('chosen:showing_dropdown', function(){ 1231 hidden = $(this).parents(":not(body,.modal)").filter(function(){ 1232 return $(this).css('overflow') === 'hidden'; 1233 }).css('overflow', 'visible'); 1234 }) 1235 .on('chosen:hiding_dropdown', function(){ 1236 hidden.css('overflow', 'hidden'); 1237 }); 1238 1239 if (jqueryTiki.chosen_sortable && $select.prop("multiple")) { 1240 var $choices = $select.next(".chosen-container-multi").find("> .chosen-choices"); 1241 $choices 1242 .mousedown(function (event) { 1243 if ($(event.target).is('span')) { 1244 event.stopPropagation(); 1245 } 1246 }) 1247 .sortable({ 1248 items: "li:not(.search-field)" 1249 }) 1250 .parents("form:first").submit(function () { 1251 if (! $(this).data("chosen-multi-submitted") && ! $select.hasClass("nochosen")) { 1252 var $options = $choices.find("li:not(.search-field) a").map(function () { 1253 return $select.find("option")[$(this).data("option-array-index")]; 1254 }); 1255 if ($options.length) { 1256 $select.children().remove(); 1257 $select.append($options).change(); 1258 } 1259 $(this).data("chosen-multi-submitted", true); 1260 } 1261 }); 1262 } 1263 }); 1264 } 1265 break; 1266 } // end switch(func) 1267}; 1268 1269(function($) { 1270 $.datepickerAdjustAltField = function(func, inst) { 1271 $.datepicker._updateAlternate(inst); // make sure the hidden field is up to date 1272 var val = $(inst.settings.altField).val(), timestamp; 1273 if (func === "datetimepicker") { 1274 val = val.substring(0, val.indexOf(" ")); 1275 timestamp = parseInt(val / 1000, 10); 1276 if (!timestamp || isNaN(timestamp)) { 1277 $.datepicker._setDateFromField(inst); // seems to need reminding when starting empty 1278 $.datepicker._updateAlternate(inst); 1279 val = $(inst.settings.altField).val(); 1280 val = val.substring(0, val.indexOf(" ")); 1281 timestamp = parseInt(val / 1000, 10); 1282 } 1283 if (timestamp && inst.settings && inst.settings.timepicker) { // if it's a datetimepicker add on the time 1284 var time = inst.settings.timepicker.hour * 3600 + 1285 inst.settings.timepicker.minute * 60 + 1286 inst.settings.timepicker.second; 1287 timestamp += time; 1288 } 1289 } else { 1290 timestamp = parseInt(val / 1000, 10); 1291 } 1292 $(inst.settings.altField).val(timestamp ? timestamp : "").change(); 1293 }; 1294 1295 // the jquery.ui _gotoToday function doesn't seem to work any more, so override that and add a call to _setDate 1296 $.datepicker._jquibase_gotoToday = $.datepicker._gotoToday; 1297 $.datepicker._gotoToday = function (id) { 1298 var inst = this._getInst($(id)[0]); 1299 this._jquibase_gotoToday(id); 1300 this._setDate(inst, new Date()); 1301 // the alternate field gets updated when the dialog closes 1302 }; 1303 1304 1305 /** 1306 * Adds annotations to the content of text in ''container'' based on the 1307 * content found in selected dts. 1308 * 1309 * Used in comments.tpl 1310 */ 1311 $.fn.addnotes = function( container ) { 1312 return this.each(function(){ 1313 var comment = this; 1314 var text = $('dt:contains("note")', comment).next('dd').text(); 1315 var title = $('h4:first', comment).clone(); 1316 var body = $('.comment-body:first', comment).clone(); 1317 body.find('dt:contains("note")').closest('dl').remove().addClass('card'); 1318 1319 if( text.length > 0 ) {
1320 var parents = container.find(':contains("' + text + '")').parent(); 1321 var node = container.find(':contains("' + text + '")').not(parents) 1322 .addClass('note-editor-text alert-info') 1323 .each( function() { 1324 var child = $('dl.note-list',this); 1325 if( ! child.length ) { 1326 child = $('<dl class="note-list list-group-item-info"/>') 1327 .appendTo(this) 1328 .hide(); 1329 1330 $(this).click( function() { 1331 child.toggle(); 1332 } ); 1333 } 1334 1335 child.append( title ) 1336 .append( $('<dd/>').append(body) ); 1337 } ); 1338 } 1339 }); 1340 }; 1341 1342 /** 1343 * Convert a zone to a note editor by attaching handlers on mouse events. 1344 */ 1345 $.fn.noteeditor = function (editlink, link) { 1346 var hiddenParents = null; 1347 var annote = $(link) 1348 .click( function( e ) { 1349 e.preventDefault(); 1350 1351 var $block = $('<div/>'); 1352 var annotation = $(this).attr('annotation'); 1353 $(this).fadeOut(100); 1354 1355 $block.load(editlink.attr('href'), function () { 1356 var msg = ""; 1357 if (annotation.length < 20) { 1358 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 />"; 1359 } 1360 1361 msg = "<p class='description comment-info'>" + msg + tr("Tip: Leave the first line as it is, starting with \";note:\". This is required") + "</p>"; 1362 $block.prepend($(msg)); 1363 $('textarea', this) 1364 .val(';note:' + annotation + "\n\n").focus(); 1365 1366 $('form', this).submit(function () { 1367 $.post($(this).attr('action'), $(this).serialize(), function () { 1368 $block.dialog('destroy'); 1369 // update the comments list 1370 editlink.closest('.comment-container').reload(); 1371 }); 1372 return false; 1373 }); 1374 1375 $block.dialog({ 1376 modal: true, 1377 width: 500, 1378 height: 400 1379 }); 1380 }); 1381 } ) 1382 .appendTo(document.body); 1383 1384 $(this).mouseup(function( e ) { 1385 var range; 1386 if( window.getSelection && window.getSelection().rangeCount ) { 1387 range = window.getSelection().getRangeAt(0); 1388 } else if( window.selection ) { 1389 range = window.selection.getRangeAt(0); 1390 } 1391 1392 if( range ) { 1393 var str = $.trim( range.toString() ); 1394 1395 if( str.length && -1 === str.indexOf( "\n" ) ) { 1396 annote.attr('annotation', str); 1397 annote.fadeIn(100).position( { 1398 of: e, 1399 at: 'bottom left', 1400 my: 'top left', 1401 offset: '20 20' 1402 } ); 1403 } else { 1404 if (annote.css("display") != "none") { 1405 annote.fadeOut(100); 1406 } 1407 if ($("form.comments").css("display") == "none") { 1408 $("form.comments").show(); 1409 } 1410 if (hiddenParents) { 1411 hiddenParents.hide(); 1412 hiddenParents = null; 1413 } 1414 } 1415 } 1416 }); 1417 }; 1418 1419 $.fn.browse_tree = function () { 1420 this.each(function () { 1421 $('.treenode:not(.done)', this) 1422 .addClass('done') 1423 .each(function () { 1424 if (getCookie($('ul:first', this).attr('data-id'), $('ul:first', this).attr('data-prefix')) !== 'o') { 1425 $('ul:first', this).css('display', 'none'); 1426 } 1427 var $placeholder = $('span.ui-icon:first:not(.control)', this); 1428 if ($('ul:first', this).length) { 1429 var dir = $('ul:first', this).css('display') === 'block' ? 's' : 'e'; 1430 if ($placeholder.length) { 1431 $placeholder.replaceWith('<span class="flipper ui-icon ui-icon-triangle-1-' + dir + '" style="float: left;margin-top:.2em;"/>'); 1432 } else { 1433 $(this).prepend('<span class="flipper ui-icon ui-icon-triangle-1-' + dir + '" style="float: left;margin-top:.2em;"/>'); 1434 } 1435 } else { 1436 if ($placeholder.length) { 1437 $placeholder.replaceWith('<span style="float:left;width:16px;height:16px;margin-top:.2em;"/>'); 1438 } else { 1439 $(this).prepend('<span style="float:left;width:16px;height:16px;margin-top:.2em;"/>'); 1440 } 1441 } 1442 if ($('div.checkbox', this).length) { 1443 $('div.checkbox', this).css("margin-left", "16px"); 1444 } 1445 }); 1446 1447 $('.flipper:not(.done)') 1448 .addClass('done') 1449 .css('cursor', 'pointer') 1450 .click(function () { 1451 var body = $(this).parent().find('ul:first'); 1452 if ('block' === body.css('display')) { 1453 $(this).removeClass('ui-icon-triangle-1-s').addClass('ui-icon-triangle-1-e'); 1454 body.hide('fast');
1455 setCookie(body.data("id"), "", body.data("prefix")); 1456 } else { 1457 $(this).removeClass('ui-icon-triangle-1-e').addClass('ui-icon-triangle-1-s'); 1458 body.show('fast'); 1459 setCookie(body.data("id"), "o", body.data("prefix")); 1460 } 1461 }); 1462 }); 1463 1464 return this; 1465 }; 1466 1467 var fancy_filter_create_token = function(value, label) { 1468 var close, token; 1469 console.log(); 1470 1471 close = $('<span class="ui-icon ui-icon-close"/>') 1472 .click(function () { 1473 var ed = $(this).parent().parent(); 1474 $(this).parent().remove(); 1475 ed.change(); 1476 return false; 1477 }); 1478 1479 token = $('<span class="token"/>') 1480 .attr('data-value', value) 1481 .text(label) 1482 .attr('contenteditable', false) 1483 .disableSelection() 1484 .append(close); 1485 1486 return token[0]; 1487 }; 1488 1489 var fancy_filter_build_init = function(editable, str, options) { 1490 if (str === '') { 1491 str = ' '; 1492 } 1493 1494 editable.html(str.replace(/(\d+)/g, '<span>$1</span>')); 1495 1496 if (options && options.map) { 1497 editable.find('span').each(function () { 1498 var val = $(this).text(); 1499 $(this).replaceWith(fancy_filter_create_token(val, JSON.parse(options.map)[val] ? JSON.parse(options.map)[val] : val)); 1500 }); 1501 } 1502 }; 1503 1504 $.fn.fancy_filter = function (operation, options) { 1505 this.each(function () { 1506 switch (operation) { 1507 case 'init': 1508 var editable = $('<div class="fancyfilter form-control"/>'), input = this; 1509 1510 if (editable[0].contentEditable !== null) { 1511 fancy_filter_build_init(editable, $(this).val(), options); 1512 editable.attr('contenteditable', true); 1513 $(this).after(editable).hide(); 1514 } 1515 1516 editable 1517 .keyup(function() { 1518 $(this).change(); 1519 $(this).mouseup(); 1520 }) 1521 .change(function () { 1522 $(input).val($('<span/>') 1523 .html(editable.html()) 1524 .find('span').each(function() { 1525 $(this).replaceWith(' ' + $(this).attr('data-value') + ' '); 1526 }) 1527 .end().text().replace(/\s+/g, ' ')); 1528 }) 1529 .mouseup(function () { 1530 input.lastRange = window.getSelection().getRangeAt(0); 1531 }); 1532 1533 break; 1534 case 'add': 1535 var editable = $(this).next(); 1536 editable.find("span[data-value='"+ options.token +"']").remove(); 1537 var node = fancy_filter_create_token(options.token, options.label); 1538 editable.append(node); 1539 editable.change(); 1540 break; 1541 } 1542 }); 1543 1544 return this; 1545 }; 1546 1547 $.fn.drawGraph = function () { 1548 this.each(function () { 1549 var $this = $(this); 1550 var width = $this.width(); 1551 var height = $this.height() ? $this.height() : Math.ceil( width * 9 / 16 ); 1552 var nodes = $this.data('graph-nodes'); 1553 var edges = $this.data('graph-edges'); 1554 1555 var g = new Graph; 1556 $.each(nodes, function (k, i) { 1557 g.addNode(i); 1558 }); 1559 $.each(edges, function (k, i) { 1560 var style = { directed: true }; 1561 if( i.preserve ) { 1562 style.color = 'red'; 1563 } 1564 g.addEdge( i.from, i.to, style ); 1565 }); 1566 1567 var layouter = new Graph.Layout.Spring(g); 1568 layouter.layout(); 1569 1570 var renderer = new Graph.Renderer.Raphael($this.attr('id'), g, width, height ); 1571 renderer.draw(); 1572 }); 1573 1574 return this; 1575 }; 1576 1577 /** 1578 * Handle textarea and input text selections 1579 * Code from: 1580 * 1581 * jQuery Autocomplete plugin 1.1 1582 * Copyright (c) 2009 Jörn Zaefferer 1583 * 1584 * Dual licensed under the MIT and GPL licenses: 1585 * http://www.opensource.org/licenses/mit-license.php 1586 * http://www.gnu.org/licenses/gpl.html 1587 * 1588 * Now deprecated and replaced in Tiki 7 by jquery-ui autocomplete 1589 */ 1590 $.fn.selection = function(start, end) { 1591 if (start !== undefined) { 1592 if (end === undefined) { 1593 end = start; 1594 } 1595 return this.each(function() { 1596 if( this.selectionStart) { 1597 this.selectionStart = start; 1598 this.selectionEnd = end; 1599 } else if( this.setSelectionRange ){ 1600 this.setSelectionRange(start, end); 1601 } else if( this.createTextRange ){ 1602 var selRange = this.createTextRange(); 1603 if (start == end) { 1604 selRange.move("character", start); 1605 selRange.select(); 1606 } else { 1607 selRange.collapse(true); 1608 selRange.moveStart("character", start); 1609 selRange.moveEnd("character", end - start); // moveEnd is relative 1610 selRange.select(); 1611 } 1612 } 1613 }); 1614 } 1615 var field = this[0]; 1616 if( field.selectionStart !== undefined) { 1617 return { 1618 start: field.selectionStart, 1619 end: field.selectionEnd 1620 } 1621 } else if ( field.createTextRange ) { 1622 // from http://the-stickman.com/web-development/javascript/finding-selection-cursor-position-in-a-textarea-in-internet-explorer/ 1623 // The current selection 1624 var range = document.selection.createRange(); 1625 // We'll use this as a 'dummy' 1626 var stored_range = range.duplicate(); 1627 // Select all text 1628 stored_range.moveToElementText( field ); 1629 // Now move 'dummy' end point to end point of original range 1630 stored_range.setEndPoint( 'EndToEnd', range ); 1631 // Now we can calculate start and end points 1632 var textProperty = range.htmlText ? "htmlText" : "text"; // behaviour changed in IE10 (approx) so htmlText has unix line-ends which w
1632orks (not 100% sure why) 1633 var selectionStart = stored_range[textProperty].length - range[textProperty].length; 1634 var selectionEnd = selectionStart + range[textProperty].length; 1635 return { 1636 start: selectionStart, 1637 end: selectionEnd 1638 } 1639 1640 } }; 1641 1642 $.fn.comment_toggle = function () { 1643 this.each(function () { 1644 var $target = $(this.hash); 1645 $target.hide(); 1646 1647 $(this).click(function () { 1648 if ($target.is(':visible')) { 1649 $target.hide(function () { 1650 $(this).empty(); 1651 }); 1652 } else { 1653 $target.comment_load($(this).attr('href')); 1654 } 1655 1656 return false; 1657 }); 1658 if (location.search.indexOf("comzone=show") > -1 || location.hash.match(/threadId=?(\d+)/)) { 1659 var comButton = this; 1660 setTimeout(function() { 1661 $(comButton).click(); 1662 }, 500); 1663 } 1664 }); 1665 1666 return this; 1667 }; 1668 1669 $.fn.comment_load = function (url) { 1670 var $top = $('#top'); 1671 $('.note-list', $top).remove(); 1672 1673 this.each(function () { 1674 var comment_container = this; 1675 if (! comment_container.reload) { 1676 comment_container.reload = function () { 1677 $(comment_container).empty().comment_load(url); 1678 }; 1679 } 1680 $(this).addClass('comment-container'); 1681 $(this).load(url, function (response, status) { 1682 $(this).show(); 1683 1684 if (jqueryTiki.useInlineComment && ! jqueryTiki.useInlineAnnotations) { 1685 $('.comment.inline dt:contains("note")', this) 1686 .closest('.comment') 1687 .addnotes($top); 1688 1689 $top.noteeditor($('.comment-form:last a', comment_container), '#note-editor-comment'); 1690 } 1691 1692 $('.button.comment-form.autoshow a').addClass('autoshown').click().removeClass('autoshown'); // allow autoshowing of comment forms through autoshow css class 1693 1694 var match = location.hash.match(/threadId=?(\d+)/); 1695 if (match) { 1696 var $tab = $(this).parents(".tab-pane"), 1697 $tabContent = $(this).parents(".tab-content"); 1698 1699 // if we're in an inactive tab then show the right one for this comment threadId 1700 if ($tab.length && ! $tab.is(".active")) { 1701 $tabContent.find(".tab-pane").each(function (index) { 1702 if (this === $tab[0]) { 1703 $(".nav-tabs li:nth(" + index + ") a", $tabContent.parent()).tab("show"); 1704 } 1705 }) 1706 } 1707 var $comment = $(".comment[data-comment-thread-id=" + match[1] + "]"); 1708 var top = $comment.offset().top; 1709 $('html, body').animate({ 1710 scrollTop: top 1711 }, 2000, function () { 1712 $comment.animate({ 1713 backgroundColor: "#ff8" 1714 }, 250, function () { 1715 $comment.animate({ 1716 backgroundColor: "" 1717 }, 1000); 1718 }); 1719 }); 1720 1721 } 1722 }); 1723 }); 1724 1725 return this; 1726 }; 1727 1728 $(document).on('click', '.comment-form.buttons > a.btn', function () { 1729 var comment_container = $(this).closest('.comment-container, .ui-dialog-content')[0]; 1730 1731 $('.comment-form form:not(.commentRatingForm)', comment_container).each(function() { // remove other forms apart from the ratings vote form 1732 var $p = $(this).parent(); 1733 $p.empty().addClass('button').addClass('buttons').append($p.data('previous')); 1734 }); 1735 if (!$(this).hasClass('autoshown')) { 1736 $(".comment").each(function () { 1737 $("article > *:not(ol)", this).each(function () { 1738 $(this).css("opacity", 0.6); 1739 }); 1740 }); 1741 } 1742 $(this).parents('.comment:first').find("*").css("opacity", 1); 1743 1744 var $formContainer = null; 1745 if ($(this).data('target')) { 1746 $formContainer = $($(this).data('target')); 1747 } else { 1748 $formContainer = $(this).parents('.buttons'); 1749 } 1750 $(this).parents('.buttons').data('previous', $(this).siblings().addBack()).empty().removeClass('buttons').removeClass('button'); 1751 1752 // Update buttons if loaded as a modal 1753 $('.modal.fade.show').trigger('tiki.modal.redraw'); 1754 1755 $formContainer.load($(this).attr('href'), function () { 1756 var form = $('form', this).submit(function () { 1757 var errors, current = this; 1758 $(current).tikiModal(tr("Saving...")); 1759 //Synchronize textarea and codemirror before comment is posted 1760 if (typeof syntaxHighlighter.sync === 'function') { 1761 syntaxHighlighter.sync($(current).find("textarea.wikiedit")); 1762 } 1763 $.post($(current).attr('action'), $(current).serialize(), function (data, st) { 1764 $(current).tikiModal(); 1765 if (data.threadId) { 1766 let threadId = data.threadId; 1767 $(current).closest('.comment-container').reload(); 1768 $('span.count_comments').each(function () { 1769 1770 var action = $(current).attr('action').match(/tiki-comment-(\w*)/), 1771 count = parseInt($(this).text()); 1772 1773 if (action) { 1774 //noinspection FallThroughInSwitchStatementJS 1775 switch (action[1]) { 1776 case "remove": 1777 count--; 1778 break; 1779 case "post": 1780 count++; 1781 // fall through to adjust threadId if necessary 1782 case "edit": 1783 case "moderate": 1784 location.hash = location.hash.replace(/threadId=\d+/, "threadId=" + threadId); 1785 break; 1786 } 1787 $(this).text(count); 1788 } 1789 }); 1790 if (data.feedback && data.feedback[0]) { 1791 alert(data.feedback.join("\n")); 1792 } 1793 } else { 1794 errors = $('ol.errors', form).empty(); 1795 if (!errors.length) { 1796 $(':submit', current).after(errors = $('<ul class="alert-warning"/>')); 1797 } 1798 1799 $.each(data.errors, function (k, v) { 1800 errors.append($('<li/>').text(v)); 1801 }); 1802 } 1803 }, 'json'); 1804 return false;
1805 }); 1806 1807 //allow syntax highlighting 1808 if ($.fn.flexibleSyntaxHighlighter) { 1809 window.codeMirrorEditor = []; 1810 form.find('textarea.wikiedit').flexibleSyntaxHighlighter(); 1811 } 1812 }); 1813 return false; 1814 }); 1815 1816 // scroll to post if #threadId on url in forums 1817 if ($("body.tiki_forums").length) { 1818 let match = location.hash.match(/threadId=?(\d+)/); 1819 if (match) { 1820 let $comment = $("#" + match[0] + ".post"); 1821 let top = $comment.offset().top; 1822 $('html, body').animate({ 1823 scrollTop: top 1824 }, 2000, function () { 1825 $comment.animate({ 1826 backgroundColor: "#ff8" 1827 }, 250, function () { 1828 $comment.animate({ 1829 backgroundColor: "" 1830 }, 1000); 1831 }); 1832 }); 1833 } 1834 } 1835 1836 $.fn.input_csv = function (operation, separator, value) { 1837 this.each(function () { 1838 var values = $(this).val().split(separator); 1839 if (values[0] === '') { 1840 values.shift(); 1841 } 1842 1843 if (operation === 'add' && -1 === values.indexOf("" + value)) { 1844 values.push(value); 1845 } else if (operation === 'delete') { 1846 value = String(value); 1847 while (-1 !== $.inArray(value, values)) { 1848 values.splice($.inArray(value, values), 1); 1849 } 1850 } 1851 1852 $(this).val(values.join(separator)); 1853 }); 1854 1855 return this; 1856 }; 1857 1858 $.service = function (controller, action, query) { 1859 var append = ''; 1860 1861 if (query) { 1862 append = '?' + $.buildParams(query); 1863 } 1864 1865 if (action) { 1866 return 'tiki-' + controller + '-' + action + append; 1867 } else { 1868 return 'tiki-' + controller + '-x' + append; 1869 } 1870 }; 1871 1872 $.serviceUrl = function (options) { 1873 var o = $.extend({}, options), controller = options.controller, action = options.action; 1874 delete(o.controller); 1875 delete(o.action); 1876 return $.service(controller, action, o); 1877 }; 1878 1879 $.buildParams = function (query, prefix, suffix) { 1880 prefix = prefix || ''; 1881 suffix = suffix || ''; 1882 1883 return $.map(query, function (v, k) { 1884 if ($.isPlainObject(v)) { 1885 return $.buildParams(v, k + '[', ']'); 1886 } else { 1887 return prefix + k + suffix + '=' + tiki_encodeURIComponent(v); 1888 } 1889 }).join('&'); 1890 }; 1891 1892 $.fn.serviceDialog = function (options) { 1893 this.each(function () { 1894 var $dialog = $('<div/>'), origin = this, buttons = {}; 1895 $(this).append($dialog).data('serviceDialog', $dialog); 1896 1897 if (! options.hideButtons) { 1898 buttons[tr('OK')] = function () { 1899 $dialog.find('form:visible').submit(); 1900 }; 1901 buttons[tr('Cancel')] = function () { 1902 $dialog.dialog('close'); 1903 if ($dialog.data('ui-dialog')) { 1904 $dialog.dialog('destroy'); 1905 } 1906 }; 1907 } 1908 1909 $dialog.dialog({ 1910 title: options.title, 1911 minWidth: options.width ? options.width : 500, 1912 height: (options.fullscreen ? $window.height() - 20 : (options.height ? options.height : 600)), 1913 width: (options.fullscreen ? $window.width() - 20 : null), 1914 close: function () { 1915 if (options.close) { 1916 options.close.apply([], this); 1917 } 1918 if ($(this).data('ui-dialog')) { 1919 $(this).dialog('destroy').remove(); 1920 } 1921 }, 1922 buttons: buttons, 1923 modal: options.modal, 1924 zIndex: options.zIndex 1925 }); 1926 1927 $dialog.loadService(options.data, $.extend(options, {origin: origin})); 1928 }); 1929 1930 return this; 1931 }; 1932 $.fn.loadService = function (data, options) { 1933 var $dialog = this, controller = options.controller, action = options.action, url; 1934 1935 this.each(function () { 1936 if (! this.reload) { 1937 this.reload = function () { 1938 $(this).loadService(data, options); 1939 }; 1940 } 1941 }); 1942 1943 if (typeof data === "string") { 1944 data = parseQuery(data); 1945 } 1946 if (data && data.controller) { 1947 controller = data.controller; 1948 } 1949 1950 if (data && data.action) { 1951 action = data.action; 1952 } 1953 1954 if (options.origin && $(options.origin).is('a')) { 1955 url = $(options.origin).attr('href'); 1956 } else if (options.url) { 1957 url = options.url; 1958 } else { 1959 url = $.service(controller, action); 1960 } 1961 1962 $dialog.tikiModal(tr("Loading...")); 1963 1964 $.ajax(url, { 1965 data: data, 1966 error: function (jqxhr) { 1967 $dialog.html(jqxhr.responseText); 1968 }, 1969 success: function (data) { 1970 $dialog.html(data); 1971 $dialog.find('.ajax').click(function (e) { 1972 $dialog.loadService(null, {origin: this}); 1973 return false;
1974 }); 1975 $dialog.find('.service-dialog').click(function (e) { 1976 if ($dialog.data('ui-dialog')) { 1977 $dialog.dialog('close'); 1978 } 1979 return true; 1980 }); 1981 1982 $dialog.find('form .submit').hide(); 1983 1984 $dialog.find('form:not(.no-ajax)').off("submit").submit(ajaxSubmitEventHandler(function (data) { 1985 data = (data ? data : {}); 1986 1987 if (data.FORWARD) { 1988 $dialog.loadService(data.FORWARD, options); 1989 } else if ($dialog.data('ui-dialog')) { 1990 $dialog.dialog('destroy').remove(); 1991 } 1992 1993 if (options.success) { 1994 options.success.apply(options.origin, [data]); 1995 } 1996 })); 1997 1998 if (options.load) { 1999 options.load.apply($dialog[0], [data]); 2000 } 2001 2002 $('.confirm-prompt', this).requireConfirm({ 2003 success: function (data) { 2004 if (data.FORWARD) { 2005 $dialog.loadService(data.FORWARD, options); 2006 } else { 2007 $dialog.loadService(options.data, options); 2008 } 2009 } 2010 }); 2011 }, 2012 complete: function () { 2013 $dialog.tikiModal(); 2014 if ($dialog.find('form').size() == 0 && $dialog.data('ui-dialog')) {
2015 // If the result contains no form, skip OK/Cancel, and just allow to close 2016 var buttons = $dialog.dialog('option', 'buttons'), n = {}; 2017 if (buttons[tr('Cancel')]) { 2018 n[tr('OK')] = buttons[tr('Cancel')]; 2019 $dialog.dialog('option', 'buttons', n); 2020 } 2021 } 2022 } 2023 }); 2024 }; 2025 2026 $.fn.confirmationDialog = function (options) { 2027 var modal = $('#bootstrap-modal'), 2028 modalHeader = modal.find('.modal-header'), 2029 modalBody = $('<div class="modal-body"><p></p></div>'), 2030 modalFooter = $('<div class="modal-footer"></div>'), 2031 modalFooterConfirm = $('<button type="button" class="btn btn-success">' + tr('Confirm') + '</button>'), 2032 modalFooterClose = $('<button type="button" class="btn btn-secondary" data-dismiss="modal">' + tr('Close') + '</button>'); 2033 2034 //needed because modal-content is globally being cleared after appearing for the first time 2035 if (!modalHeader || modalHeader.length === 0) { 2036 modal.find('.modal-content').append('<div class="modal-header"><h4></h4></div>'); 2037 modalHeader = modal.find('.modal-header'); 2038 } 2039 2040 if (options.title) { 2041 modalHeader.find('h4').text(options.title); 2042 } else { 2043 modalHeader.find('h4').text(tr('Confirmation Modal')); 2044 } 2045 2046 if (options.message) { 2047 modalBody.find('p').text(options.message); 2048 } else { 2049 modalBody.find('p').text(tr('Please confirm you want to perform this action.')); 2050 } 2051 2052 modalFooter.append(modalFooterClose, modalFooterConfirm); 2053 modalHeader.after(modalBody); 2054 modalBody.after(modalFooter); 2055 2056 modal.on('hidden.bs.modal', function (e) { 2057 if (options.close) { 2058 options.close(); 2059 } 2060 }); 2061 2062 modalFooterConfirm.on('click', function (e) { 2063 modal.modal('hide'); 2064 if (options.success) { 2065 options.success(); 2066 } 2067 }); 2068 2069 modal.modal('show'); 2070 2071 return this; 2072 }; 2073 2074 $.fn.requireConfirm = function (options) { 2075 this.click(function (e) { 2076 e.preventDefault(); 2077 $(this).doConfirm(options); 2078 return false; 2079 }); 2080 2081 return this; 2082 }; 2083 2084 $.fn.doConfirm = function (options) { 2085 var message = options.message, link = this; 2086 2087 if (! message) { 2088 message = $(this).data('confirm'); 2089 } 2090 2091 if (confirm (message)) { 2092 var $this = $(this); 2093 $this.tikiModal(" "); 2094 2095 $.ajax($(this).attr('href'), { 2096 type: 'POST', 2097 dataType: 'json', 2098 data: { 2099 'confirm': 1 2100 }, 2101 success: function (data) { 2102 $this.tikiModal(); 2103 options.success.apply(link, [data]); 2104 }, 2105 error: function (jqxhr) { 2106 $this.tikiModal(); 2107 $(link).closest('form').showError(jqxhr); 2108 } 2109 }); 2110 } 2111 }; 2112 2113 $.fn.showError = function (message) { 2114 if (message.responseText) { 2115 if (message.getResponseHeader("Content-Type").indexOf("text/html") === -1) { 2116 var data = JSON.parse(message.responseText); 2117 message = data.message; 2118 } else { 2119 message = $(message.responseText).text(); // can be html 2120 } 2121 } else if (typeof message !== 'string') { 2122 message = ""; 2123 } 2124 this.each(function () { 2125 var parts, that = this; 2126 if (parts = message.match(/^<!--field\[([^\]]+)\]-->(.*)$/)) { 2127 field = parts[1]; 2128 message = parts[2]; 2129 2130 if (that[field]) { 2131 that = that[field]; 2132 } 2133 } 2134 2135 var validate = false, errors = {}, field, $additional = $('<ul>'); 2136 2137 if (jqueryTiki.validate) { 2138 validate = $(that).closest('form').validate() 2139 } 2140 2141 if (validate) { 2142 if (! $(that).attr('name')) { 2143 $(that).attr('name', $(that).attr('id')); 2144 } 2145 2146 if (that !== validate.currentForm) { 2147 field = $(that).attr('name'); 2148 } 2149 2150 if (field) { 2151 errors[field] = message; 2152 validate.showErrors(errors); 2153 } else { 2154 // No specific field, assign as form error 2155 $additional.append($('<li>').text(message)); 2156 } 2157 2158 setTimeout(function () {
2159 $('#tikifeedback li').filter(function () { 2160 return $(this).text() === message; 2161 }).remove(); 2162 2163 if ($('#tikifeedback ul').is(':empty')) { 2164 $('#tikifeedback').empty(); 2165 } 2166 }, 100); 2167 } else { 2168 $additional.append($('<li>').text(message)); 2169 } 2170 2171 if (! $additional.is(':empty')) { 2172 // Write form errors at the top, please stop removing them 2173 $('.ajax-errors', this).remove(); 2174 $('<div class="ajax-errors alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button></div>') 2175 .prependTo(this) 2176 .append($additional); 2177 } 2178 2179 // Style the bootstrap form-group as an error 2180 $('.form-group').removeClass('has-error') 2181 .find('label.error:visible') 2182 .addClass('form-text') 2183 .prepend('<span class="fas fa-flag"/> ') 2184 .closest('.form-group').addClass('has-error') 2185 }); 2186 2187 return this; 2188 }; 2189 2190 $.fn.clearError = function () { 2191 this.each(function () { 2192 $(this).closest('form').find('label.error[for="' + $(this).attr('name') + '"]').remove(); 2193 $(this).closest('form').find('.form-group.has-error').removeClass('has-error'); 2194 }); 2195 2196 return this; 2197 }; 2198 2199 // sort result containing all galleries 2200 function sortResult(result) { 2201 result.sort(function(a, b) { 2202 var titleA = a.parent_title.toUpperCase(); // ignore upper and lowercase 2203 var titleB = b.parent_title.toUpperCase(); // ignore upper and lowercase 2204 if (titleA < titleB) { 2205 return -1; 2206 } 2207 if (titleA > titleB) { 2208 return 1; 2209 } 2210 2211 // names must be equal 2212 return 0; 2213 }); 2214 2215 } 2216 2217 // search the title of each parent gallery 2218 function parentTitle(result, parent) { 2219 var title=''; 2220 $.each(result, function(key, value) { 2221 if(value.object_id == parent){ 2222 title= value.title + ' > '; 2223 } 2224 }); 2225 return title; 2226 } 2227 2228 function loadSelectorData(filter, args, success) { 2229 if (! $.object_selector_cache) { 2230 $.object_selector_cache = {}; 2231 } 2232 2233 if ($.isFunction(args)) { 2234 success = args; 2235 args = {}; 2236 } 2237 2238 var item, url; 2239 2240 url = $.service('search', 'lookup', $.extend(args, { 2241 filter: filter 2242 })); 2243 2244 if (item = $.object_selector_cache[url]) { 2245 if (item.data) { 2246 success(item.data); 2247 } else { 2248 item.queue.push(success); 2249 } 2250 } else { 2251 item = $.object_selector_cache[url] = { 2252 data: null, 2253 queue: [success] 2254 }; 2255 $.getJSON(url, function (data) { 2256 item.data = data; 2257 $.each(item.queue, function (k, f) { 2258 f(data); 2259 }); 2260 item.queue = []; 2261 }); 2262 } 2263 } 2264 $._object_selector_add_item = function (type, $select, $results, parent_title, item, title, status_icon, selected) { 2265 var checkname = $select.closest('.object-selector, .object-selector-multi') 2266 .find('.primary').attr('id') + '_sel'; 2267 var suffix = $results.find('.form-check').lenght || 0; 2268 2269 $('<option>') 2270 .val(item) 2271 .data('label', title) 2272 .text(parent_title + '' + title.replace(/\n/g, " / ")) //replace newline with a slash since it's in a select 2273 .prop('selected', selected)
2274 .appendTo($select); 2275 2276 $('<div class="form-check"><input type="' + type + '" class="form-check-input" ><label class="form-check-label"></label></div>') 2277 .find('label').append(status_icon ? status_icon + ' ' + title : title).end() 2278 .find(':radio, :checkbox') 2279 .attr('name', checkname) 2280 .prop('checked', selected) 2281 .val(item) 2282 .end() 2283 .appendTo($results); 2284 }; 2285 2286 $.fn._object_selector_update_results = function (type, result, initial) { 2287 var $container = this, 2288 $results = $container.find('.results'), 2289 $select = $container.find('select'), 2290 $noresults = $('.no-result', this), 2291 selection = []; 2292 2293 this.find(':radio:checked, :checkbox:checked') 2294 .not('.protected') 2295 .each(function () { 2296 selection.push($(this).val()); 2297 }); 2298 2299 this.find(':radio:not(:checked), :checkbox:not(:checked)') 2300 .not('.protected') 2301 .closest('.form-check') 2302 .remove(); 2303 2304 $select 2305 .find('option:not(:checked)') 2306 .not('.protected') 2307 .remove(); 2308 2309 $noresults.toggleClass('d-none', selection.length !== 0); 2310 2311 // add all galleries parent titles 2312 $.each(result, function (key, value) { 2313 value.parent_title = parentTitle(result, value.parent_id); 2314 result[key] = value; 2315 }); 2316 2317 // sort result by galleries parent titles 2318 sortResult(result); 2319 2320 $.each(result, function (key, value) { 2321 var current = value.object_type + ':' + value.object_id; 2322 var selected = false; 2323 2324 if (value.object_id == '') { 2325 current = value.object_type; 2326 } 2327 2328 var currentValue = $select.data('current-value'); 2329 if (currentValue == current) { 2330 selected = true; 2331 } 2332 2333 if (-1 === selection.indexOf(current)) { 2334 if (initial) { 2335 $._object_selector_add_item(type, $select, $([]), value.parent_title, current, value.title, value.status_icon, selected); 2336 } else { 2337 $._object_selector_add_item(type, $select, $results, value.parent_title, current, value.title, value.status_icon, selected); 2338 } 2339 } else { 2340 $("option[value='" + current + "']", $select).text(value.title); 2341 } 2342 }); 2343 2344 $select.trigger('chosen:updated'); 2345 }; 2346 2347 $.fn.object_selector = function (action, value, title) { 2348 var args = arguments; 2349 2350 this.each(function () { 2351 var input = this 2352 , $simple = $(this).prev() 2353 , filter = $(input).data('filters') 2354 , threshold = $(input).data('threshold') 2355 , format = $(input).data('format') || '' 2356 , sort = $(input).data('sort') || 'score_desc' 2357 , parentobject = $(input).data('parent') 2358 , parentkey = $(input).data('parentkey') 2359 , searchField = $(input).data('searchfield') || 'title' 2360 ; 2361 2362 $(input).addClass('primary').hide(); 2363 $simple.hide(); 2364 2365 var $spinner = $(this).parent(), 2366 $container = $(input).closest('.object-selector'), 2367 $select = $container.find('select').first(), 2368 $filter = $container.find(':text.filter').first(), 2369 $search = $container.find('input.search').first(), 2370 $panel = $container.find('.card').first(); 2371 2372 if (action === 'set') { 2373 $select.val(value); 2374 if ($select.val() !== value && title) { 2375 // for multilingual, object returned is JSON. try to parse and split 2376 // with ' / ', otherwise catch and use title as is. 2377 try 2378 { 2379 var titleObj = JSON.parse(title); 2380 var titleArr = $.map(titleObj, function(el) { return el }); 2381 title = titleArr.join(" / "); 2382 } 2383 catch(e) 2384 { 2385 // do nothing 2386 } 2387 $._object_selector_add_item('radio', $select, $container.find('.results'), null, value, title, null, true); 2388 2389 $select.trigger('chosen:updated'); 2390 } 2391 2392 $(input) 2393 .val(value) 2394 .data('label', title) 2395 .change(); 2396 2397 return; 2398 } 2399 2400 if (action === 'setfilter') { 2401 filter[args[1]] = args[2]; 2402 $(input).data('filters', filter); 2403 $container.find('.too-many').hide(); 2404 $search.click(); 2405 return; 2406 } 2407 2408 if (parentobject && parentkey) { 2409 filter[parentkey] = $(parentobject).val(); 2410 $(parentobject).on('change', function () { 2411 $(input) 2412 .data('use-threshold', 1) 2413 .object_selector('setfilter', parentkey, $(this).val()); 2414 }); 2415 } 2416 2417 if (threshold !== -1) { 2418 $spinner.tikiModal(" "); 2419 loadSelectorData(filter, {maxRecords: threshold, format: format, sort_order: sort}, function (data) { 2420 $container._object_selector_update_results('radio', data.resultset.result, true); 2421 2422 $spinner.tikiModal(); 2423 2424 if (data.resultset.count <= threshold) { 2425 $select.parent().removeClass('d-none'); 2426 } else { 2427 $panel.removeClass('d-none'); 2428 } 2429 }); 2430 } else { 2431 $panel.removeClass('d-none'); 2432 } 2433 $panel.on('click', ':radio', function () { 2434 if ($(this).is(':checked')) { 2435 $(input).object_selector('set', $(this).val(), $(this).parent().text()); 2436 } 2437 }); 2438 2439 $(input).change(function () { 2440 var val = $(this).val(), id = null; 2441 if (val) { 2442 var splitarray = val.split(':'); 2443 id = splitarray[1]; 2444 if(splitarray.length > 2){ 2445 for(var i = 2; i < splitarray.length; i++) 2446 { 2447 id = id + ':' + splitarray[i]; 2448 } 2449 } 2450 } 2451
2452 if ($simple.val() != id) { 2453 $simple.val(id).change(); 2454 } 2455 }); 2456 $simple.change(function () { 2457 var target = filter.type + ':' + $(this).val(); 2458 2459 if (filter.type && $(input).val() != target) { 2460 $(input).val(target).change(); 2461 } 2462 }); 2463 $select.change(function () { 2464 if ($(input).val() != $select.val()) { 2465 $(input).data('label', $select.find('option:selected').text()); 2466 $(input).val($select.val()).change(); 2467 } 2468 }); 2469 2470 $search.click(function () { 2471 $spinner = $filter.parent().tikiModal(" "); 2472 var selectorArgs = {format: format, sort_order: sort}; 2473 if ($(input).data('use-threshold') && threshold !== -1) { 2474 selectorArgs.maxRecords = threshold; 2475 $(input).data('use-threshold', 0); 2476 } 2477 filter[searchField] = $filter.val(); 2478 loadSelectorData(filter, selectorArgs, function (data) { 2479 $container._object_selector_update_results('radio', data.resultset.result, false); 2480 2481 $spinner.tikiModal(); 2482 }); 2483 }); 2484 2485 $filter.keypress(function (e) { 2486 if (e.which === 13) { 2487 e.preventDefault(); 2488 $search.click(); 2489 } 2490 }); 2491 }); 2492 2493 return this; 2494 }; 2495 2496 $.fn.object_selector_multi = function (action) { 2497 var args = arguments; 2498 this.each(function () { 2499 var $textarea = $(this).hide().addClass('primary') 2500 , $container = $(this).closest('.object-selector-multi') 2501 , $select = $container.find('select') 2502 , $simpleinput = $textarea.prev(':text').hide() 2503 , $basic = $container.find('.basic-selector') 2504 , $panel = $container.find('.card') 2505 , $search = $container.find('input.search').first() 2506 , $filter = $container.find(':text.filter').first() 2507 , filter = $textarea.data('filters') 2508 , wildcard = $textarea.data('wildcard') 2509 , threshold = $textarea.data('threshold') 2510 , format = $textarea.data('format') || '' 2511 , parentobject = $textarea.data('parent') 2512 , parentkey = $textarea.data('parentkey') 2513 , sort = $textarea.data('sort') || 'score_desc' 2514 , initialValues = $select.val() 2515 , separator = $simpleinput.data('separator') 2516 , searchField = $textarea.data('searchfield') || 'title' 2517 , extratype = $textarea.data('extratype') 2518 ; 2519 2520 if (action === 'setfilter') { 2521 filter[args[1]] = args[2]; 2522 $textarea.data('filters', filter); 2523 $container.find('.too-many').hide(); 2524 $search.click(); 2525 return; 2526 } 2527 2528 if (parentobject && parentkey) { 2529 filter[parentkey] = $(parentobject).val(); 2530 $(parentobject).on('change', function () { 2531 $textarea 2532 .data('use-threshold', 1) 2533 .object_selector_multi('setfilter', parentkey, $(this).val()); 2534 }); 2535 } 2536 2537 if (threshold !== -1) { 2538 $container.tikiModal(' '); 2539 loadSelectorData(filter, {maxRecords: threshold, format: format, sort_order: sort}, function (data) { 2540 $container.tikiModal(''); 2541 var results = data.resultset.result; 2542 if (extratype) { 2543 var objectIndex = data.resultset.count; 2544 $.each(extratype, function(key, value) { 2545 results[objectIndex] = {"object_type": key, "object_id": "", "title": value}; 2546 objectIndex++; 2547 }); 2548 } 2549 $container._object_selector_update_results('checkbox', results, true); 2550 2551 if (data.resultset.count <= threshold) { 2552 $basic.removeClass('d-none'); 2553 } else { 2554 $panel.removeClass('d-none'); 2555 // add .nochosen here even though Chosen has been applied previously so chosen_sortable doesn't update the value 2556 $select.addClass("nochosen"); 2557 } 2558 }); 2559 } else { 2560 $panel.removeClass('d-none'); 2561 } 2562 2563 $filter.keypress(function (e) { 2564 if (e.which === 13) { 2565 e.preventDefault(); 2566 $search.click(); 2567 } 2568 }); 2569 2570 $search.click(function () { 2571 var $spinner = $filter.parent().tikiModal(" "); 2572 var selectorArgs = {format: format, sort_order: sort}; 2573 if ($textarea.data('use-threshold') && threshold !== -1) { 2574 selectorArgs.maxRecords = threshold; 2575 $textarea.data('use-threshold', 0); 2576 } 2577 if (wildcard == 'y') { 2578 filter[searchField] = '*' + $filter.val() + '*'; 2579 } else { 2580 filter[searchField] = $filter.val(); 2581 } 2582 loadSelectorData(filter, selectorArgs, function (data) { 2583 $container._object_selector_update_results('checkbox', data.resultset.result, false); 2584 $spinner.tikiModal(); 2585 }); 2586 }); 2587 2588 $panel.on('click', ':checkbox', function () { 2589 var list = $.makeArray($container.find(':checkbox:checked').map(function () { 2590 return $(this).val(); 2591 })); 2592 $textarea.val(list.join("\n")).change(); 2593 }); 2594 $select.on('change', function () { 2595 var list = $(this).val() || []; 2596 $textarea.val(list.join("\n")).change(); 2597 }); 2598 2599 if (separator) { 2600 $textarea.on('change', function () { 2601 var lines = $(this).val().split("\n"), ids = []; 2602 $.each(lines, function (k, line) { 2603 var parts = line.split(':'); 2604 if (parts.length === 2) { 2605 ids.push(parts[1]); 2606 } 2607 }); 2608 $simpleinput.val(ids.join(separator)).change(); 2609 }); 2610 } 2611 }); 2612 }; 2613 2614 $.fn.sortList = function () { 2615 var list = $(this), items = list.children('li').get(); 2616 2617 items.sort(function(a, b) { 2618 var compA = $(a).text().toUpperCase(); 2619 var compB = $(b).text().toUpperCase(); 2620 return (compA < compB) ? -1 : (compA > compB) ? 1 : 0; 2621 }); 2622 2623 $.each(items, function(idx, itm) { 2624 list.append(itm); 2625 }); 2626 }; 2627 $.localStorage = { 2628 store: function (key, value) { 2629 var fullKey = this._build(key); 2630 if (window.localStorage) { 2631 if (value) { 2632 window.localStorage[fullKey] = $.toJSON({ 2633 date: Date.now(), 2634 data: value 2635 }); 2636 } else { 2637 delete window.localStorage[fullKey]; 2638 } 2639 } 2640 }, 2641 load: function (key, callback, fetch, duration) { 2642 var payload, fullKey = this._build(key); 2643 2644 if (window.localStorage && window.localStorage[fullKey]) { 2645 payload = JSON.parse(window.localStorage[fullKey]); 2646 2647 if (duration) { 2648 // Expired, refetch 2649 if (payload.date + duration*1000 < Date.now()) { 2650 fetch(function (data) { 2651 $.localStorage.store(key, data); 2652 callback(data); 2653 }); 2654 return; 2655 } 2656 } 2657 2658 callback(payload.data); 2659 } else { 2660 fetch(function (data) { 2661 $.localStorage.store(key, data); 2662 callback(data); 2663 }); 2664 } 2665 }, 2666 _build: function (key) { 2667 // Use an alternate key to ensure old data structure 2668 // does not collide 2669 return key + "_2"; 2670 } 2671 }; 2672 2673 var favoriteList = []; 2674 $.fn.favoriteToggle = function () { 2675 this 2676 .each(function () { 2677 var type, obj, isFavorite, link = this; 2678 type = $(this).queryParam('type'); 2679 obj = $(this).queryParam('object'); 2680 2681 2682 isFavorite = function () { 2683 var ret = false;
2684 $.each(favoriteList, function (k, v) { 2685 if (v === type + ':' + obj) { 2686 ret = true; 2687 return false; 2688 } 2689 }); 2690 2691 return ret; 2692 }; 2693 2694 $(this).find('span').remove(); //removes the previous star icon 2695 $(this).prepend($('<span />').attr({ 2696 'class' : isFavorite() ? 'fas fa-star fa-fw' : 'far fa-star fa-fw', 2697 'title' : isFavorite() ? tr('Remove from favorites') : tr('Add to favorites') 2698 })); 2699 2700 if (isFavorite()) { 2701 $(this).addClass( 'favorite_selected' ); 2702 $(this).removeClass( 'favorite_unselected' ); 2703 } else { 2704 $(this).addClass( 'favorite_unselected' ); 2705 $(this).removeClass( 'favorite_selected' ); 2706 } 2707 $(this) 2708 .filter(':not(".register")') 2709 .addClass('register') 2710 .click(function () { 2711 $.post($(this).attr('href'), { 2712 target: isFavorite() ? 0 : 1 2713 }, function (data) { 2714 favoriteList = data.list; 2715 $.localStorage.store($(link).data('key'), favoriteList); 2716 2717 $(link).favoriteToggle(); 2718 }, 'json'); 2719 return false; 2720 }); 2721 }); 2722 return this; 2723 }; 2724 2725 $.fn.queryParam = function (name) { 2726 name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); 2727 var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"); 2728 var results = regex.exec(this[0].href); 2729 2730 if(results == null) { 2731 return ""; 2732 } else { 2733 return decodeURIComponent(results[1].replace(/\+/g, " ")); 2734 } 2735 }; 2736 2737 $(function () { 2738 var list = $('.favorite-toggle'); 2739 2740 if (list.length > 0) { 2741 $.localStorage.load( 2742 list.data('key'), 2743 function (data) { 2744 favoriteList = data; 2745 list 2746 .favoriteToggle() 2747 .removeClass('favorite-toggle'); 2748 }, 2749 function (recv) { 2750 $.getJSON($.service('favorite', 'list'), recv); 2751 }, 2752 3600 // Valid for 1h 2753 ); 2754 } 2755 }); 2756 2757 // global ajax event handlers 2758 $document.ajaxComplete(function () { 2759 $('.favorite-toggle') 2760 .favoriteToggle() 2761 .removeClass('favorite-toggle'); 2762 }); 2763 2764 $document.ajaxError(function (event, jqxhr, settings, thrownError) { 2765 2766 if (settings.preventGlobalErrorHandle) { 2767 return; 2768 } 2769 2770 $(".modal.fade.show").modal("hide"); 2771 var message; 2772 if (!thrownError && jqxhr.status !== 200) { 2773 if (jqxhr.status) { 2774 message = jqxhr.status + " " + jqxhr.statusText; 2775 } else { 2776 message = "AJAX: " + jqxhr.statusText + " " + jqxhr.status + " (" + jqxhr.state() + ") for URL: " + settings.url; 2777 } 2778 } 2779 if (message) { 2780 $("#tikifeedback").showError(message); 2781 } 2782 }); 2783 2784 /** 2785 * Show a loading spinner on top of a button (or whatever) 2786 * 2787 * @param $spinner empty or jq object $spinner if empty, spinner is added and returned and element "disabled" 2788 * if spinner then spinner is removed and element returned to normal 2789 * 2790 * @return jq object $spinner being shown or null when removing 2791 */ 2792 2793 $.fn.showBusy = function( $spinner ) { 2794 if (!$spinner) { 2795 var pos = $(this).position(); 2796 $spinner = $("<img src='img/spinner.gif' alt='" + tr("Wait") + "' class='ajax-spinner' />"). 2797 css({ 2798 "position": "absolute", 2799 "top": pos.top + ($(this).height() / 2), 2800 "left": pos.left + ($(this).width() / 2) - 8 2801 }).data("target", this); 2802 $(this).parent().find(".ajax-spinner").remove(); 2803 $(this).parent().append($spinner); 2804 $(this).attr("disabled", true).css("opacity", 0.5); 2805 return $spinner; 2806 } else { 2807 $($spinner.data("target")).attr("disabled", false).css("opacity", 1); 2808 $spinner.remove(); 2809 return null; 2810 } 2811 } 2812 2813 // copy tracker action column to 1st row if table has horizontal scrolling 2814 $('.table-responsive:not(.article-types)').each(function () { 2815 var table = $(this); 2816 // mobile friendly tables 2817 if (table.find('table:not(.tablesorter) tbody tr').width() - 10 > table.width()) { 2818 if (!table.hasClass('large-table-no-wrap')) table.addClass('large-table-no-wrap'); 2819 if (screen.width <= 767) { 2820 $('<div class="toggle-table-wrap d-md-none"><button type="button" class="btn btn-link fas fa-toggle-off"></button></div>').insertBefore(table); 2821 var checkall = false; 2822 table.find('table.table:not(.caltable) th').each(function (e) { 2823 var header = $(this).html(); 2824 if ($(this).find('div').hasClass('form-check') || ($(this).find('input').hasClass('form-check-input') && e == 0)) { 2825 $(this).addClass('visible-header'); 2826 header = ""; 2827 } 2828 2829 // page history exception 2830 if ($(this).find('input[name=compare]').length) { 2831 $(this).addClass('visible-header').addClass('compare-pages'); 2832 header = ""; 2833 } 2834 2835 table.find('table.table tbody tr').each(function () { 2836 var cell = $(this).find('td').eq(e); 2837 if (checkall) cell.addClass('checkmargin'); 2838 if ($.trim(cell.html()) == '') { 2839 cell.addClass('hidecell'); 2840 } else { 2841 cell.prepend("<div class='header' style='display:none'>" + header + "</div>"); 2842 } 2843 if (e == 0) {
2844 if ((cell.hasClass('checkbox-cell') || cell.find('input:checkbox')) && cell.find('.header').html() == '') { 2845 cell.addClass('checkall').removeClass('checkmargin'); 2846 checkall = true; 2847 } 2848 } 2849 }); 2850 }); 2851 2852 // this is for calendars 2853 table.find('table.caltable > tbody > tr:first > td.heading').each(function (e) { 2854 var header = $(this).html(); 2855 table.find('table.caltable > tbody > tr:not(:first)').each(function () { 2856 var cell = $(this).find('> td').eq(e); 2857 cell.prepend("<div class='header' style='display:none'>" + header + "</div>"); 2858 }); 2859 }); 2860 } 2861 } 2862 2863 // action column 2864 if (table.find('table').width() - 5 > table.width()) { 2865 if (screen.width > 767) { 2866 if (table.find('table td.action').length) { 2867 table.find('table td.action').each(function () { 2868 $(this).parent().prepend($(this).clone()); 2869 }); 2870 table.find('table tr').eq(0).prepend('<th style="width:20px;"></th>'); 2871 } 2872 } 2873 } 2874 }); 2875 2876 // mobile: wrap large table data on click 2877 $('.toggle-table-wrap button').each(function () { 2878 $(this).on('click', function () { 2879 if ($(this).hasClass('fa-toggle-on')) { 2880 $(this).removeClass('fa-toggle-on').addClass('fa-toggle-off'); 2881 $(this).parent().next().removeClass('large-table').addClass('large-table-no-wrap'); 2882 } else { 2883 $(this).removeClass('fa-toggle-off').addClass('fa-toggle-on'); 2884 $(this).parent().next().removeClass('large-table-no-wrap').addClass('large-table'); 2885 } 2886 }); 2887 }); 2888 2889})(jQuery); 2890 2891// Prevent memory leaks in IE 2892// Window isn't included so as not to unbind existing unload events 2893// More info: 2894// - http://isaacschlueter.com/2006/10/msie-memory-leaks/ 2895if ( window.attachEvent && !window.addEventListener ) { 2896 window.attachEvent("onunload", function() { 2897 for ( var id in jQuery.cache ) { 2898 var item = jQuery.cache[ id ]; 2899 if ( item.handle ) { 2900 if ( item.handle.elem === window ) { 2901 for ( var type in item.events ) { 2902 if ( type !== "unload" ) { 2903 // Try/Catch is to handle iframes being unloaded, see #4280 2904 try { 2905 jQuery.event.remove( item.handle.elem, type ); 2906 } catch(e) {} 2907 } 2908 } 2909 } else { 2910 // Try/Catch is to handle iframes being unloaded, see #4280 2911 try { 2912 jQuery.event.remove( item.handle.elem ); 2913 } catch(e) {} 2914 } 2915 } 2916 } 2917 }); 2918} 2919 2920$.tikiModal = function(msg) { 2921 return $('body').tikiModal(msg); 2922}; 2923 2924//Makes modal over window or object so ajax can load and user can't prevent action 2925$.fn.tikiModal = function(msg) { 2926 var obj = $(this); 2927 if (!obj.length) { 2928 return null; // happens after search index rebuild in some conditions 2929 } 2930 var lastModal = obj.data('lastModal'); 2931 2932 if (!lastModal) { 2933 lastModal = Math.floor(Math.random() * 1000); 2934 obj.data('lastModal', lastModal); 2935 } 2936 var box = { 2937 top: obj.offset().top, 2938 left: obj.offset().left, 2939 height: obj.outerHeight(), 2940 width: obj.outerWidth() 2941 }; 2942 var modal = $('body').find('#modal_' + lastModal); 2943 var spinner = $('<img src="img/spinner.gif" style="vertical-align: top; margin-right: .5em;" />'); 2944 2945 if (!msg) { 2946 modal 2947 .fadeOut(function() { 2948 $(this).remove(); 2949 }); 2950 obj.removeData('lastModal'); 2951 return obj; 2952 } 2953 2954 if (modal.length) { 2955 modal 2956 .find('.dialog') 2957 .empty() 2958 .html(spinner) 2959 .append(msg); 2960 return obj; 2961 } 2962 2963 modal = $('<div id="modal_' + lastModal + '" class="tiki-modal">' + 2964 '<div class="mask" />' + 2965 '<div class="dialog"></div>' + 2966 '</div>') 2967 .appendTo('body'); 2968 2969 var zIndex = 0; 2970 if (obj.is("body")) { 2971 zIndex = 2147483646 - 1; // maximum 2972 box.top = obj.offset().top + $window.scrollTop(); 2973 box.left = obj.offset().left + $window.scrollLeft(); 2974 } else { 2975 obj.parents().addBack().each(function () { 2976 var z = $(this).css("z-index"); 2977 if (z && z !== 'auto' && z > zIndex) { 2978 zIndex = Number(z); 2979 } 2980 }); 2981 } 2982 2983 //Set height and width to mask to fill up the whole screen or the single element 2984 modal 2985 .width(box.width) 2986 .height(box.height) 2987 .css('top', box.top + 'px') 2988 .css('left', box.left + 'px') 2989 .find('.mask') 2990 .height(box.height) 2991 .fadeTo(1000, 0.6) 2992 .parent() 2993 .find('.dialog') 2994 .hide() 2995 .append(spinner) 2996 .append(msg); 2997 var dialog = modal.find('.dialog'); 2998 if (obj.is("body")) { 2999 dialog.css({ 3000 top: (box.top + $window.innerHeight()/2 - $window.scrollTop()) + "px", 3001 left: (box.left + $window.innerWidth()/2 - $window.scrollLeft()) + "px" 3002 }); 3003 } 3004 dialog.css({ 3005 marginTop: (dialog.height() / -2) + "px", 3006 marginLeft: (dialog.width() / -2) + "px" 3007 }).show(); 3008 3009 if (zIndex) { 3010 modal.css("z-index", zIndex + 1); 3011 } 3012 return obj; 3013}; 3014 3015//makes the width of an input change to the value 3016$.fn.valWidth = function() { 3017 var me = $(this); 3018 return me.ready(function() {
3019 var h = me.height(); 3020 if (!h) { 3021 h = me.offsetParent().css("font-size"); 3022 if (h) { 3023 h = parseInt(h.replace("px", "")); 3024 } 3025 } 3026 me.keyup(function() { 3027 var width = me.val().length * h; 3028 3029 me 3030 .stop() 3031 .animate({ 3032 width: (width > h ? width : h) 3033 }, 200); 3034 }) 3035 .keyup(); 3036 }); 3037}; 3038 3039//For making pagination have the ability to enter page/offset number and go 3040$.paginationHelper = function() { 3041 $('.pagenums').each(function() { 3042 var me = $(this); 3043 var step = me.find('input.pagenumstep'); 3044 var endOffset = (me.find('input.pagenumend').val() - 1) * step.data('step'); 3045 var url = step.data('url'); 3046 var offset_jsvar = step.data('offset_jsvar'); 3047 var offset_arg = step.data('offset_arg'); 3048 3049 me.find('span.pagenumstep').replaceWith( 3050 $('<input type="text" style="font-size: inherit; " />') 3051 .val(step.val()) 3052 .change(function() { 3053 var newOffset = step.data('step') * ($(this).val() - 1); 3054 3055 if (newOffset >= 0) { 3056 //make sure the offset isn't too high 3057 newOffset = (newOffset > endOffset ? endOffset : newOffset); 3058 3059 //THis is for custom/ajax search handling 3060 window[offset_jsvar] = newOffset; 3061 if (step[0]) { 3062 if (step.attr('onclick')) { 3063 step[0].onclick(); 3064 return; 3065 } 3066 } 3067 3068 //if the above behavior isn't there, we update location 3069 document.location = url + offset_arg + "=" + newOffset; 3070 } 3071 }) 3072 .keyup(function(e) { 3073 switch(e.which) { 3074 case 13: $(this).blur(); 3075 } 3076 }) 3077 .valWidth() 3078 ); 3079 }); 3080}; 3081 3082//a sudo "onvisible" event 3083$.fn.visible = function(fn, isOne) { 3084 if (fn) { 3085 $(this).each(function() { 3086 var me = $(this); 3087 if (isOne) { 3088 me.one('visible', fn); 3089 } else { 3090 me.on('visible', fn); 3091 } 3092 3093 function visibilityHelper() { 3094 if (!me.is(':visible')) { 3095 setTimeout(visibilityHelper, 500); 3096 } else { 3097 me.trigger('visible'); 3098 } 3099 } 3100 3101 visibilityHelper(); 3102 }); 3103 } else { 3104 $(this).trigger('visible'); 3105 } 3106 3107 return this; 3108}; 3109 3110$.download = function(url, data, method){ 3111 //url and data options required 3112 if( url && data ){ 3113 //data can be string of parameters or array/object 3114 data = typeof data == 'string' ? data : jQuery.param(data); 3115 //split params into form inputs 3116 var inputs = ''; 3117 jQuery.each(data.split('&'), function(){ 3118 var pair = this.split('='); 3119 inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />'; 3120 }); 3121 //send request 3122 jQuery('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>') 3123 .appendTo('body').submit().remove(); 3124 } 3125}; 3126 3127$.uiIcon = function(type) { 3128 return $('<div style="width: 1.4em; height: 1.4em; margin: .2em; display: inline-block; cursor: pointer;">' + 3129 '<span class="ui-icon ui-icon-' + type + '"> </span>' + 3130 '</div>') 3131 .hover(function(){ 3132 $(this).addClass('ui-state-highlight'); 3133 }, function() { 3134 $(this).removeClass('ui-state-highlight'); 3135 }); 3136}; 3137 3138$.uiIconButton = function(type) { 3139 return $.uiIcon(type).addClass('ui-state-default ui-corner-all'); 3140}; 3141 3142$.rangySupported = function(fn) { 3143 if (window.rangy) { 3144 rangy.init(); 3145 var cssClassApplierModule = rangy.modules.CssClassApplier; 3146 return fn(); 3147 } 3148}; 3149 3150$.fn.rangy = function(fn) { 3151 var me = $(this); 3152 $.rangySupported(function() { 3153 $document.mouseup(function(e) { 3154 if (me.data('rangyBusy')) return; 3155 3156 var selection = rangy.getSelection(); 3157 var html = selection.toHtml(); 3158 var text = selection.toString(); 3159 3160 if (text.length > 3 && rangy.isUnique(me[0], text)) { 3161 if (fn) 3162 if ($.isFunction(fn)) 3163 fn({ 3164 text: text, 3165 x: e.pageX, 3166 y: e.pageY 3167 }); 3168 } 3169 }); 3170 }); 3171 return this; 3172}; 3173 3174$.fn.rangyRestore = function(phrase, fn) { 3175 var me = $(this); 3176 $.rangySupported(function() { 3177 phrase = rangy.setPhrase(me[0], phrase); 3178 3179 if (fn) 3180 if ($.isFunction(fn)) 3181 fn(phrase); 3182 }); 3183 return this; 3184}; 3185 3186$.fn.rangyRestoreSelection = function(phrase, fn) { 3187 var me = $(this); 3188 $.rangySupported(function() { 3189 phrase = rangy.setPhraseSelection(me[0], phrase); 3190 3191 if (fn) 3192 if ($.isFunction(fn)) 3193 fn(phrase); 3194 }); 3195 return this; 3196}; 3197 3198$.fn.realHighlight = function() { 3199 var o = $(this); 3200 $.rangySupported(function() { 3201 rangy.setPhraseBetweenNodes(o.first(), o.last(), document); 3202 }); 3203 return this; 3204}; 3205 3206$.fn.ajaxEditDraw = function(options) { 3207 var me = $(this).attr('href', 'tiki-ajax_services.php'); 3208 3209 //defaults 3210 options = $.extend({ 3211 saved: function() {}, 3212 closed: function() {} 3213 }, options); 3214 3215 $.tikiModal(tr('Loading editor')); 3216 3217 me.serviceDialog({ 3218 title: me.attr('title'), 3219 data: { 3220 controller: 'draw', 3221 action: 'edit', 3222 fileId: me.data('fileid'), 3223 galleryId: me.data('galleryid'), 3224 imgParams: me.data('imgparams'), 3225 raw: true 3226 }, 3227 modal: true, 3228 zIndex: 9999, 3229 fullscreen: true, 3230 load: function (data) { 3231 //prevent from happeneing over and over again 3232 if (me.data('drawLoaded')) return false;
3233 3234 me.data('drawLoaded', true); 3235 3236 me.drawing = $('#tiki_draw') 3237 .loadDraw({ 3238 fileId: me.data('fileid'), 3239 galleryId: me.data('galleryid'), 3240 name: me.data('name'), 3241 imgParams: me.data('imgparams'), 3242 data: $('#fileData').val() 3243 }) 3244 .on('savedDraw', function(e, o) { 3245 me.data('drawLoaded', false); 3246 me.drawing.parent().dialog('destroy'); 3247 me.drawing.remove(); 3248 3249 //update the image that did exist in the page with the new one that now exists 3250 var img = $('.pluginImg' + me.data('fileid')).show(); 3251 3252 if (img.length < 1) document.location = document.location + ''; 3253 3254 var w = img.width(), h = img.height(); 3255 3256 if (img.hasClass('regImage')) { 3257 var replacement = $('<div />') 3258 .attr('class', img.attr('class')) 3259 .attr('style', img.attr('style')) 3260 .attr('id', img.attr('id')) 3261 .insertAfter(img); 3262 3263 img.remove(); 3264 img = replacement; 3265 } 3266 3267 var src = me.data('src'); 3268 3269 $('<div class=\"svgImage\" />') 3270 .load(src ? src : 'tiki-download_file.php?fileId=' + o.fileId + '&display', function() { 3271 3272 $(this) 3273 .css('position', 'absolute') 3274 .fadeTo(0, 0.01) 3275 .prependTo('body') 3276 .find('img,svg') 3277 .scaleImg({ 3278 width: w, 3279 height: h 3280 }); 3281 3282 img.html($(this).children()); 3283 3284 $(this).remove(); 3285 }); 3286 3287 if (!options.saved) return; 3288 3289 options.saved(o.fileId); 3290 3291 me.data('fileid', o.fileId); // replace fileId on edit button 3292 if (o.imgParams && o.imgParams.fileId) { 3293 o.imgParams.fileId = o.fileId; 3294 me.data('imgparams', o.imgParams); 3295 } 3296 }) 3297 .submit(function() { 3298 me.drawing.saveDraw(); 3299 return false; 3300 }) 3301 .on('loadedDraw', function() { 3302 //kill the padding around the dialog so it looks like svg-edit is one single box 3303 me.drawing 3304 .parent() 3305 .css('padding', '0px'); 3306 3307 var serviceDialog = me.data('serviceDialog'); 3308 if (serviceDialog) { 3309 var drawFrame = $('#svgedit'); 3310 serviceDialog 3311 .on('dialogresize', function() { 3312 drawFrame.height(serviceDialog.height() - 4); 3313 }) 3314 .trigger('dialogresize'); 3315 } 3316 3317 $.tikiModal(); 3318 }); 3319 3320 me.drawing.find('#drawMenu').remove(); 3321 }, 3322 close: function() { 3323 if (me.data('drawLoaded')) { 3324 me.data('drawLoaded', false); 3325 me.drawing.remove(); 3326 3327 if (!options.closed) return; 3328 options.closed(me); 3329 } 3330 } 3331 }); 3332 3333 return false; 3334}; 3335 3336$.notify = function(msg, settings) { 3337 settings = $.extend({ 3338 speed: 10000 3339 },settings); 3340 3341 var notify = $('#notify'); 3342 3343 if (!notify.length) { 3344 notify = $('<div id="notify" />') 3345 .css('top', '5px') 3346 .css('right', '5px') 3347 .css('position', 'fixed') 3348 .css('z-index', 9999999) 3349 .css('padding', '5px') 3350 .width($window.width() / 5) 3351 .prependTo('body'); 3352 } 3353 3354 var note = $('<div class="notify ui-state-error ui-corner-all ui-widget ui-widget-content" />') 3355 .append(msg) 3356 .css('padding', '5px') 3357 .css('margin', '5px') 3358 .mousedown(function() { 3359 return false; 3360 }) 3361 .hover(function() { 3362 $(this) 3363 .stop() 3364 .fadeTo(500, 0.3) 3365 }, function() { 3366 $(this) 3367 .stop() 3368 .fadeTo(500, 1) 3369 }) 3370 .prependTo(notify); 3371 3372 setTimeout(function() { 3373 note 3374 .fadeOut() 3375 .slideUp(); 3376 3377 //added outside of fadeOut to ensure removal 3378 setTimeout(function() { 3379 note.remove(); 3380 }, 1000); 3381 3382 }, settings.speed); 3383}; 3384 3385function delayedExecutor(delay, callback) 3386{ 3387 var timeout; 3388 3389 return function () { 3390 var args = arguments; 3391 if (timeout) { 3392 clearTimeout(timeout); 3393 timeout = null; 3394 } 3395 3396 timeout = setTimeout(function () { 3397 callback.apply(this, args) 3398 }, delay); 3399 }; 3400} 3401 3402$(function () { 3403 3404 // Show/hide the sidebars // 3405 //////////////////////////// 3406 3407 $(".toggle_zone").click(function () { 3408 var $this = $(this), zone="", 3409 icon_left = "toggle-left", icon_right = "toggle-right"; 3410 3411 if ($this.is(".right")) { 3412 zone = "right"; 3413 icon_left = "toggle-right"; 3414 icon_right = "toggle-left" 3415 } else if ($this.is(".left")) { 3416 zone = "left"; 3417 } 3418 if ($this.find(".icon-" + icon_left).length) { // hide it 3419 $this.find(".icon").setIcon(icon_right); 3420 setCookie("hide_zone_" + zone, 'y'); 3421 $("body").addClass("hide_zone_" + zone); 3422 } else { 3423 $this.find(".icon").setIcon(icon_left); 3424 deleteCookie("hide_zone_" + zone); 3425 $("body").removeClass("hide_zone_" + zone); 3426 } 3427 3428 $(window).trigger("resize"); 3429 3430 return false; // do not modify URL by adding # on the click 3431 }); 3432}); 3433 3434// try and reposition the menu ul within the browser window 3435$.fn.moveToWithinWindow = function() { 3436 var $el = $(this); 3437 var h = $el.height(), 3438 w = $el.width(), 3439 o = $el.offset(), 3440 po = $el.parent().offset(), 3441 st = $window.scrollTop(), 3442 sl = $window.scrollLeft(), 3443 wh = $window.height(), 3444 ww = $window.width(); 3445 3446 if (w + o.left > sl + ww) {
3447 $el.animate({'left': sl + ww - w - po.left}, 'fast'); 3448 } 3449 if (h + o.top > st + wh) { 3450 $el.animate({'top': st + wh - (h > wh ? wh : h) - po.top}, 'fast'); 3451 } else if (o.top < st) { 3452 $el.animate({'top': st - po.top}, 'fast'); 3453 } 3454}; 3455 3456$.fn.scaleImg = function (max) { 3457 $(this).each(function() { 3458 //Here we want to make sure that the displayed contents is the right size 3459 var h, w, img = $(this), 3460 actual = { 3461 height: img.height(), 3462 width: img.width() 3463 }, 3464 original = $(this).clone(), 3465 parent = img.parent(); 3466 3467 var winner = ''; 3468 3469 if (actual.height > max.height) { 3470 winner = 'height'; 3471 } else if (actual.width > max.width) { 3472 winner = 'width'; 3473 } 3474 3475 //if there is no winner, there is no need to resize 3476 if (winner) { 3477 //we resize both images and svg, we check svg first 3478 var g = img.find('g'); 3479 if (g.length) { 3480 img 3481 .attr('preserveAspectRatio', 'xMinYMin meet'); 3482 3483 parent 3484 .css('overflow', 'hidden') 3485 .width(max.width) 3486 .height(max.height); 3487 3488 g.attr('transform', 'scale( ' + (100 / (actual[winner] / max[winner]) * 0.01) + ' )'); 3489 } else { 3490 //now we resize regular images 3491 if (actual.height > actual.width) { 3492 h = max.height; 3493 w = Math.ceil(actual.width / actual.height * max.height); 3494 } else { 3495 w = max.width; 3496 h = Math.ceil(actual.height / actual.width * max.width); 3497 } 3498 img.css({ height: h, width: w }); 3499 } 3500 3501 img 3502 .css('cursor', "url(img/icons/zoom.gif),auto") 3503 .click(function () { 3504 $('<div/>').append(original).dialog({ 3505 modal: true, 3506 width: Math.min($(window).width(), actual.width + 20), 3507 height: Math.min($(window).height(), actual.height + 50) 3508 }); 3509 return false; 3510 }); 3511 } 3512 }); 3513 3514 return this; 3515}; 3516 3517 3518// Compatibility to old jquery to resolve a bug in fullcalendar 3519$.curCSS = function (element, property) { 3520 return $(element).css(property); 3521}; 3522 3523 3524$.fn.registerFacet = function () { 3525 this.each(function () { 3526 var element = this, entries = $($(this).data('for')).val() 3527 .split(" " + $(this).data('join') + " ") 3528 .map(function (value) { 3529 return (value.charAt(0) === '"') ? value.substr(1, value.length - 2) : value; 3530 }); 3531 3532 function applyFilter(value) { 3533 if (value) { 3534 value = $.makeArray(value); 3535 value = value 3536 .map(function (value) { 3537 return (-1 === value.indexOf(' ')) ? value : ('"' + value + '"'); 3538 }) 3539 .join(" " + $(element).data('join') + " "); 3540 } 3541 $($(element).data('for')).val(value).change(); 3542 } 3543 3544 if ($(element).is('select')) { 3545 $(this) 3546 .val(entries) 3547 .trigger("chosen:updated") // for chosen 3548 .change(function () { 3549 var value = $(this).val(); 3550 applyFilter(value); 3551 }); 3552 } else if ($(element).has(':checkbox').length) { 3553 $(':checkbox', element) 3554 .each(function () { 3555 if (-1 !== $.inArray($(this).val(), entries)) { 3556 $(this).prop('checked', true); 3557 } 3558 }) 3559 .on('click', function () { 3560 applyFilter($(':checked', element).map(function () { 3561 return $(this).val(); 3562 })); 3563 }); 3564 } 3565 3566 var selected = $('option:selected, :checkbox:checked', this).length, 3567 all = $('option, :checkbox', this).length; 3568 3569 if (all === 1 && selected === 0) { 3570 $(this).closest('.facet-hide-group').hide(); 3571 } 3572 }); 3573 3574 return this; 3575}; 3576 3577$.fn.reload = function () { 3578 this.each(function () { 3579 if (this.reload) { 3580 this.reload(); 3581 } else if($(this).data('reload')) { 3582 $(this).loadService({}, { 3583 url: $(this).data('reload') 3584 }); 3585 } 3586 }); 3587 return this; 3588}; 3589 3590$(document).on('mouseover', '.media[data-href]', function () { 3591 $(this).css('cursor', 'pointer'); 3592}); 3593$(document).on('mouseout', '.media[data-href]', function () { 3594 $(this).css('cursor', 'default'); 3595}); 3596$(document).on('click', '.media[data-href]', function () { 3597 document.location.href = $(this).data('href'); 3598}); 3599 3600$(document).on('hidden.bs.modal', '.footer-modal.fade', function () { 3601 // Required for bootstrap to allow changing the content of a modal 3602 $(this).removeData('bs.modal').find('.modal-content').empty(); 3603 3604 // With multiple stacked modals, which is not officially supported by bootstrap, 3605 // focus on the last modal would be lost. 3606 $('body').toggleClass('modal-open', $('.modal.fade.show').length > 0); 3607 $(".modal-backdrop.show:first").remove(); 3608}); 3609
3610$(document).on('submit', '.modal-body form:not(.no-ajax)', ajaxSubmitEventHandler(function (data) { 3611 //if FORWARD is set in the returned data, load the passed service into the modal 3612 // rather than close the modal and refresh the page. 3613 if (data && data.FORWARD) { 3614 var $this = $(this); 3615 if ($this.is("form")) { 3616 $this = $this.parent(); 3617 } 3618 $this.children().remove(); 3619 $this.loadService(data.FORWARD, { 3620 origin: this, 3621 load: function () { 3622 $(this).closest('.modal').trigger('tiki.modal.redraw'); 3623 } 3624 }); 3625 } else { 3626 // reload() causes a request to update the browser cache - similar to pressing the reload button. 3627 // so we must not reload() but set the href. This behaves simililar to clicking a link - which keeps the browser cache. 3628 // The difference is: NOT loading about 50+ js / css files! 3629 //document.location.reload(); 3630 document.location.href = document.location.href.replace(/#.*$/, ""); // remove the hash from the URL if there is one otherwise the page doesn't reload 3631 } 3632})); 3633 3634// When data-size is set on the toggle-link, alter the size of the modal 3635$(document).on('click', '[data-toggle=modal][data-size]', function () { 3636 var target = $(this).data('target'), size = $(this).data('size'); 3637 3638 $(target) 3639 .one('hidden.bs.modal', function () { 3640 $('.modal-dialog', this).removeClass(size); 3641 }) 3642 .find('.modal-dialog').addClass(size) 3643 ; 3644}); 3645 3646$(document).on('click', '[data-toggle=modal][data-modal-title]', function () { 3647 var target = $(this).data('target'), title = $(this).data('modal-title'); 3648 3649 $(target) 3650 .one('loaded.bs.modal', function () { 3651 $('.modal-title', this).text(title); 3652 }) 3653 ; 3654}); 3655 3656$(document).on('loaded.bs.modal', '.modal.fade', function () { 3657 $(this).trigger('tiki.modal.redraw'); 3658}); 3659 3660// START BOOTSTRAP 4 CHANGE 3661$(document).on('shown.bs.modal', '.modal', function (event) { 3662 // fixes for bootstrap4 3663 var $button = $(event.relatedTarget); // Button that triggered the modal 3664 var remote = $button.data('remote'); // Extract info from data-* attributes 3665 var $modal = $(this); 3666 3667 var href = remote ? remote : $button.attr("href"); 3668 3669 if (href) { 3670 $modal.find('.modal-content').load(href, function () { 3671 $(this).trigger("tiki.modal.redraw"); 3672 }); 3673 } else { 3674 $modal.trigger("tiki.modal.redraw"); 3675 } 3676 // END BOOTSTRAP 4 CHANGE 3677}); 3678 3679$(document).on('tiki.modal.redraw', '.modal.fade', function () { 3680 var modal = this, $button; 3681 3682 // On Modal show, find all buttons part of a .submit block and create 3683 // proxies of them in the modal footer 3684 $('.modal-footer .auto-btn', modal).remove(); 3685 $('div.submit .btn', modal).each(function () { 3686 var $submit = $(this); 3687 if ($submit.is('a:not(.custom-handling)')) { 3688 $button = $submit; 3689 } else { 3690 $submit.hide(); 3691 $button = $('<button>') 3692 .text($submit.val() || $submit.text()) 3693 .attr('class', $submit.attr('class')) 3694 .attr('data-dismiss', 'modal') 3695 .addClass('auto-btn') 3696 .click(function () { 3697 if ($submit.data("alt_controller") && $submit.data("alt_action")) { 3698 $submit.parents("form").attr("action", $.service($submit.data("alt_controller"), $submit.data("alt_action"))); 3699 } 3700 $submit.click(); 3701 if (typeof $submit.parents("form").validate !== "function") { 3702 // make the button look disabled and ignore further clicks 3703 $button.off("click").css("opacity", 0.3); 3704 } 3705 }); 3706 } 3707 $('.modal-footer', modal).append($button); 3708 }); 3709 3710 if ($.fn.flexibleSyntaxHighlighter) { 3711 $('textarea', modal).flexibleSyntaxHighlighter(); 3712 } 3713 3714 $(".nav-tabs", this).each(function () { 3715 if ($(".active", this).length === 0) { 3716 $("li:first-child a", this).tab("show"); 3717 } 3718 }); 3719 3720 if ($.applyChosen) { 3721 $(this).applyChosen(); 3722 } 3723 3724 if (jqueryTiki.colorbox) { 3725 $(this).applyColorbox(); 3726 } 3727 3728 if (jqueryTiki.tooltips) { 3729 $(this).tiki_popover(); 3730 } 3731 // START BOOTSTRAP 4 CHANGE 3732 3733 $('.modal-body :input', modal).first().focus(); 3734 $('.modal-backdrop.show:not(.fade)').remove(); // Bootstrap keeps adding more of these 3735 3736 // handle $ajaxtimer for alerts that are not using the confirmAction handler 3737 if ($("#timer-seconds", modal).length) { 3738 var $seconds = $("#timer-seconds"), 3739 counter = $seconds.text(), 3740 timer = setInterval(function () { 3741 $seconds.text(--counter); 3742 if (counter === 0 || counter < 0) { 3743 $.closeModal(); 3744 window.location = window.location.href; 3745 } 3746 }, 1000); 3747 } 3748 // END BOOTSTRAP 4 CHANGE 3749}); 3750 3751/** 3752 * Make .depends elements show or hide depending on the state of the "on" element 3753 * for checkboxes only so far 3754 */ 3755$(document).on("ready tiki.modal.redraw", function () { 3756 $(".depends").each(function () {
3757 var $depends = $(this), 3758 on = $depends.data("on"); 3759 3760 $("[name=" + on + "]").change(function () { 3761 if ($(this).is("input[type=checkbox]") && $(this).is(":checked")) { 3762 $depends.show(); 3763 } else { 3764 $depends.hide(); 3765 } 3766 }).change(); 3767 3768 }); 3769}); 3770 3771$(function () { 3772 var $tabs = $('a[data-toggle=tab][href="' + document.location.hash + '"]'), 3773 tabShown = false, 3774 notShown = []; 3775 3776 if (document.location.search.match(/cookietab=/)) { 3777 tabShown = true; 3778 } else if (document.location.hash && $tabs.length) { 3779 $tabs.tab('show'); 3780 tabShown = true; 3781 } else { 3782 $(".tabs").each(function () { 3783 var name = $(this).data("name"), 3784 t = getCookie(name, "tabs", "notfound"); 3785 3786 var $tab = $('a[data-toggle=tab].active', this); // class "active" set serverside from $cookietab var 3787 if (t && $tab.length === 0) { 3788 $tab = $('a[data-toggle=tab][href="' + t + '"]'); 3789 } 3790 3791 if ($tab.length) { 3792 $tab.tab('show'); 3793 tabShown = true; 3794 } else if (name) { 3795 notShown.push(name); 3796 } 3797 }); 3798 } 3799 if (typeof $().tab === "function") { 3800 if (!tabShown && !notShown.length) { 3801 $("a[data-toggle=tab]:first").tab("show"); 3802 } else if (notShown.length) { 3803 for (var i = 0; i < notShown.length; i++) { 3804 $(".tabs[data-name=" + notShown[i] + "] a[data-toggle=tab]:first").tab("show"); 3805 } 3806 } 3807 } 3808 3809 $('a[data-toggle="tab"]').on('show.bs.tab', function (e) { 3810 if ($(this).parents(".tab-content").length === 0) { 3811 document.location.hash = $(e.target).attr("href"); 3812 } 3813 setCookieBrowser($(this).parents(".tabs:first").data("name"), $(e.target).attr("href"), "tabs"); 3814 }).click(function () { 3815 var scroll = $window.scrollTop(); // prevent window jumping to tabs on click 3816 $(this).tab('show'); 3817 $window.scrollTop(scroll); 3818 }); 3819 3820 $("input[name='session_protected']").on('click', function () { 3821 if ($(this).prop('type') == 'checkbox' && $(this).data('tiki-admin-child-block') == '#session_protected_childcontainer') { 3822 var checkbox = $("input[name='session_protected']"); 3823 if (checkbox.prop('checked') && location.protocol != 'https:') { 3824 $(this).confirmationDialog({ 3825 title: tr('Warning - Protect all sessions with HTTPS'), 3826 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'), 3827 success: function () { 3828 checkbox.prop('checked', true); 3829 } 3830 }); 3831 return false; 3832 } 3833 } 3834 }); 3835}); 3836 3837$.openModal = function (options) { 3838 var href = options.remote; // BOOTSTRAP 4 CHANGE 3839 3840 if (-1 === href.indexOf('?')) { 3841 href += '?modal=1'; 3842 } else { 3843 href += '&modal=1'; 3844 } 3845 3846 // START BOOTSTRAP 4 CHANGE 3847 $('.modal.fade:not(.show):first') 3848 // Bind a single event to trigger as soon as the form appears 3849 .one('hidden.bs.modal', options.close || function () {}) 3850 // Make the form load 3851 .find(".modal-content") 3852 .load(href, function () { 3853 3854 var $modal = $(this).parents(".modal"); 3855 3856 $modal.modal(options); 3857 3858 if ($modal.is(':visible')){ 3859 $modal.trigger("tiki.modal.redraw"); 3860 } else { 3861 $modal.modal("show"); 3862 } 3863 3864 $('.modal-title', this).text(options.title); 3865 3866 if (options.size) { 3867 $modal.find('.modal-dialog').addClass(options.size); 3868 $modal.one('hidden.bs.modal', function () { 3869 $('.modal-dialog', this).removeClass(options.size); 3870 }); 3871 } 3872 3873 if (options.open) { 3874 options.open.apply(this); 3875 } 3876 3877 // END BOOTSTRAP 4 CHANGE 3878 }); 3879 3880}; 3881 3882$.closeModal = function (options) { 3883 options = options || {}; 3884 var done = options.done; 3885 if (done) { 3886 done = function () { 3887 // Wait until the event loop ends before considering really done 3888 setTimeout(options.done, 0); 3889 }; 3890 } 3891 3892 $('.modal.fade.show').last() // BOOTSTRAP 4 CHANGE 3893 .one('hidden.bs.modal', done || function () {}) 3894 .modal('hide'); 3895}; 3896 3897$.fn.clickModal = function (options, href) { 3898 this.click($.clickModal(options, href)); 3899 return this; 3900}; 3901 3902$.clickModal = function (options, href) { 3903 return function (e) { 3904 var control = this, url; 3905 if (! href) { 3906 url = $(this).attr('href'); 3907 } else { 3908 url = href; 3909 } 3910 if ($.isFunction(e.preventDefault)) { 3911 e.preventDefault(); 3912 } 3913 3914 $.openModal({ 3915 title: options.title, 3916 size: options.size, 3917 remote: url, 3918 open: function () { 3919 if (options.open) { 3920 options.open.apply(this, []); 3921 } 3922 3923 $('form:not(.no-ajax)', this) 3924 .addClass('no-ajax') // Remove default ajax handling, we replace it 3925 .submit(ajaxSubmitEventHandler(function (data) { 3926 if (options.success) { 3927 options.success.apply(control, [data]); 3928 } 3929 })); 3930 } 3931 }); 3932 }; 3933}; 3934 3935/** 3936 * Open a tab on the current page, e.g.: 3937 * <a href="#" onclick="showTab(2); return false;">Open tab 2 on this page</a> 3938 * 3939 * Assumes the tab is in the main column of the page and that one tab is already showing. 3940 * 3941 * @param tabNumber number of the tab on the current page to show 3942 * @returns {boolean} 3943 */ 3944function showTab(tabNumber) { 3945 var thisTabId = $('#col1').find('.tab-pane.active').attr('id'), 3946 tabNames = thisTabId.substr(0, thisTabId.indexOf('-') + 1) 3947 ; 3948 $('a[href="#' + tabNames + tabNumber + '"]').tab('show'); 3949} 3950 3951/** 3952 * Send feedback to a popup modal or to div#tikifeedback using bootstrap alert variations (error, warning, success, info) 3953 * 3954 * @param mes array The message
3955 * @param type string Type of alert: error, warning, success or info (default) 3956 * @param modal boolean true for popup modal, false (default) to use the div#tikifeedback that is on every page 3957 * @param title string Custom message title 3958 * @param icon string Custom icon 3959 * @param killall boolean true for removing other feedbacks already open, false (default) (only for non modal) 3960 * @param custom string Custom target in jquery selection notation (only for non modal) 3961 */ 3962function feedback (mes, type, modal, title, icon, killall, custom) 3963{ 3964 mes = mes || []; 3965 if (!$.isArray(mes)) { 3966 mes = [mes]; 3967 } 3968 if (mes.length == 1) { 3969 var meshtml = [mes][0]; 3970 } else { 3971 var meshtml = '<ul>'; 3972 $.each(mes, function(i, val) { 3973 if (val) { 3974 meshtml += '<li>' + val + '</li>'; 3975 } 3976 }); 3977 meshtml += '</ul>'; 3978 } 3979 type = type || 'info'; modal = modal || false; killall = killall || false; custom = $(custom).length ? $(custom).first() : null; 3980 var target, map = 3981 { 3982 'error': {title:tr('Error'), class:'danger', icon:'error'}, 3983 'warning': {title:tr('Warning'), class:'warning', icon:'warning'}, 3984 'success': {title:tr('Success'), class:'success', icon:'success'}, 3985 'info': {title:tr('Note'), class:'info', icon:'information'} 3986 }; 3987 var check = ['error', 'warning', 'success', 'info']; 3988 type = $.inArray(type, check) > -1 ? type : 'info'; 3989 title = title || map[type]['title']; 3990 icon = icon || map[type]['icon']; 3991 icon = $.fn.getIcon(icon); 3992 if (modal) { 3993 if (mes.length > 0) { 3994 meshtml = '<div class="alert alert-dismissable alert-' + map[type]['class'] + '">' + meshtml + '</div>'; 3995 } else { 3996 meshtml = ''; 3997 } 3998 target = $('.modal.fade:not(.show)').first(); 3999 $('.modal-content', target).html( 4000 '<div class="modal-header">' + 4001 '<h4 class="text-' + map[type]['class'] + '">' + icon[0].outerHTML + ' ' + title + '</h4>' + 4002 '<button type="button" class="close pull-right" data-dismiss="modal">x</button>' + 4003 '</div>' + 4004 '<div class="modal-body">' + 4005 meshtml + 4006 '</div>' 4007 ); 4008 target.modal(); 4009 } else { 4010 var tfb = $(custom ? custom : 'div#tikifeedback'); 4011 if (killall) { 4012 tfb.find('div.alert.alert-dismissable').remove(); 4013 } 4014 if (mes.length == 0) { 4015 meshtml = ''; 4016 } 4017 tfb.append( 4018 '<div class="alert alert-dismissable alert-' + map[type]['class'] + '">' + 4019 '<button type="button" class="close" data-dismiss="modal">x</button>' + 4020 '<h4>' + icon[0].outerHTML + ' ' + title + '</h4>' + 4021 meshtml + 4022 '</div>' 4023 ).on('click' , 'button.close' , function() { 4024 $(this).parent().remove(); 4025 }); 4026 placeFeedback(tfb); 4027 } 4028} 4029 4030/** 4031 * Utility for tikifeedback to place the feedback at the top of the page (at div#tikifeedback) or viewport, whichever is 4032 * 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 4033 * position, which is the first element in div#col1 4034 * 4035 * @param object 4036 */ 4037function placeFeedback(object) { 4038 if ($('.modal.fade.show').length) { 4039 $('#col1, #col2, #col3').css('z-index', 'auto'); 4040 object.css('z-index', 3000); 4041 object.find('div').css('z-index', 3000); 4042 } 4043 if (object.offset().top < $(window).scrollTop()) { 4044 object.find('div.alert').append('<div id="move-message" style="font-size:smaller;margin-top:10px"><em>' 4045 + tr('This message will move to the top of the page after a few seconds.') + '</em>'); 4046 //it's important to not define top until after the tfb object has been manipulated 4047 object.offset({'top': $(window).scrollTop()}); 4048 object.css('z-index', 3000); 4049 setTimeout(function() { 4050 object.fadeOut(1000, function() { 4051 //move back to usual position and clear style attribute so subsequent feedback appears properly 4052 $('div#col1').prepend(object); 4053 object.css({'z-index':'', 'position':'', 'top': ''}); 4054 object.find('div#move-message').css('visibility', 'hidden'); 4055 object.fadeIn(); 4056 }); 4057 }, 5000); 4058 } 4059 4060} 4061 4062// thanks to Rob W on https://stackoverflow.com/a/8962023/2459703 4063$.fn.closestDescendent = function(filter) { 4064 var $found = $(), 4065 $currentSet = this; // Current place 4066 while ($currentSet.length) { 4067 $found = $currentSet.filter(filter); 4068 if ($found.length) break; // At least one match: break loop 4069 // Get all children of the current set 4070 $currentSet = $currentSet.children(); 4071 }
4072 return $found.first(); // Return first match of the collection 4073}; 4074 4075window.regCapsLock = function () {}; 4076 4077// Avoid that jquery appends a cachebuster to scripts loaded via a regular script tag when the base content was loaded from an xhr call 4078// I.e xhr call loads same html boilerplate and that boilerplate contains a script tag that loads some .js script. 4079// In this case, jquery would add a cachebuster to the js request, and no cache would be work. 4080$.ajaxPrefilter(function( options, originalOptions, jqXHR ) { 4081 if ( options.dataType == 'script' || originalOptions.dataType == 'script' ) { 4082 options.cache = true; 4083 } 4084}); 4085 4086//Preview for the upload avatar popup 4087function readURL(input) { 4088 if (input.files && input.files[0]) { 4089 $(".btn-upload-avatar").removeClass('disabled'); 4090 var reader = new FileReader(); 4091 reader.onload = function (e) { 4092 $('.user-avatar-preview img').attr('src', e.target.result); 4093 } 4094 reader.readAsDataURL(input.files[0]); 4095 } 4096} 4097$(document).on('change', '#userfile', function(){ 4098 readURL(this); 4099}); 4100 4101 4102function objectLockToggle(icon) { 4103 4104 var $this = $(icon).tikiModal(" "), 4105 action = $this.data("is_locked") ? "unlock" : "lock"; 4106 4107 $.post($.service( 4108 "object", 4109 action, 4110 { 4111 type: $this.data("type"), 4112 object: $this.data("object"), 4113 value: $this.data("is_locked") ? "" : jqueryTiki.username 4114 } 4115 ), function (data) { 4116 if (data && data.locked) { 4117 $this.find(".icon").setIcon("lock"); 4118 $this.data("is_locked", "1") 4119 .attr("title", tr("Locked by " + jqueryTiki.userRealName)) 4120 .parent().find("input[name=locked]").val(jqueryTiki.username); 4121 } else { 4122 $this.find(".icon").setIcon("unlock"); 4123 $this.data("is_locked", "") 4124 .attr("title", "") 4125 .parent().find("input[name=locked]").val(""); 4126 } 4127 }, 4128 "json").done(function () { 4129 $this.tikiModal(); 4130 }); 4131 4132 return false; 4133} 4134 4135 4136/** 4137 * Remove accents from chars 4138 * vendor_bundled/vendor/mottie/tablesorter/js/jquery.tablesorter.combined.js:2373 4139 * 4140 * @param str 4141 */ 4142(function setup_removediacritics_function(){ 4143 var characterEquivalents = { 4144 'a' : '\u00e1\u00e0\u00e2\u00e3\u00e4\u0105\u00e5', // Ã¡Ã Ã¢Ã£Ã¤Ä Ã¥ 4145 'A' : '\u00c1\u00c0\u00c2\u00c3\u00c4\u0104\u00c5', // ÃÃÃÃÃÄà 4146 'c' : '\u00e7\u0107\u010d', // çÄÄ 4147 'C' : '\u00c7\u0106\u010c', // ÃÄÄ 4148 'e' : '\u00e9\u00e8\u00ea\u00eb\u011b\u0119', // éèêëÄÄ 4149 'E' : '\u00c9\u00c8\u00ca\u00cb\u011a\u0118', // ÃÃÃÃÄÄ 4150 'i' : '\u00ed\u00ec\u0130\u00ee\u00ef\u0131', // Ãìİîïı 4151 'I' : '\u00cd\u00cc\u0130\u00ce\u00cf', // ÃÃİÃà 4152 'o' : '\u00f3\u00f2\u00f4\u00f5\u00f6\u014d', // óòôõöŠ4153 'O' : '\u00d3\u00d2\u00d4\u00d5\u00d6\u014c', // ÃÃÃÃÃÅ 4154 'ss': '\u00df', // à (s sharp) 4155 'SS': '\u1e9e', // Ạ(Capital sharp s) 4156 'u' : '\u00fa\u00f9\u00fb\u00fc\u016f', // úùûüů 4157 'U' : '\u00da\u00d9\u00db\u00dc\u016e' // ÃÃÃÃÅ® 4158 }; 4159 4160 var characterRegex, characterRegexArray; 4161 4162 function removeDiacritics(str) { 4163 var chr, 4164 acc = '[', 4165 eq = characterEquivalents; 4166 4167 if ( !characterRegex ) { 4168 characterRegexArray = {}; 4169 for ( chr in eq ) { 4170 if ( typeof chr === 'string' ) { 4171 acc += eq[ chr ]; 4172 characterRegexArray[ chr ] = new RegExp( '[' + eq[ chr ] + ']', 'g' ); 4173 } 4174 } 4175 characterRegex = new RegExp( acc + ']' ); 4176 } 4177 if ( characterRegex.test( str ) ) { 4178 for ( chr in eq ) { 4179 if ( typeof chr === 'string' ) { 4180 str = str.replace( characterRegexArray[ chr ], chr ); 4181 } 4182 } 4183 } 4184 return str; 4185 } 4186 4187 window.removeDiacritics = removeDiacritics; 4188})(); 4189 4190/** 4191 * Fetch feedback sent through ajax and place into the tikifeedback div that is on each page 4192 * If tikifeedback div is outside of the viewport, place it at the top of the viewport and have it move to the 4193 * normal position (first element in div#col1) after 5 seconds 4194 */ 4195$(document).ajaxComplete(function (e, jqxhr) { 4196 var feedback = jqxhr.getResponseHeader('X-Tiki-Feedback'), 4197 tfb = $('#tikifeedback'); 4198 if (feedback) { 4199 feedback = decodeURIComponent(feedback); // decodeURIComponent() reverses rawurlencode(). 4200 tfb.fadeIn(200, function() { 4201 //place html from ajax X-Tiki-Feedback into the div#tikifeedback 4202 tfb.html($($.parseHTML(feedback)).filter('#tikifeedback').html()); 4203 tfb.find('div.alert').each(function() { 4204 var title = $(this).find('span.rboxtitle').text().trim(), 4205 content = $(this).find('div.rboxcontent').text().trim(); 4206 $(this).find('span.rboxtitle').text(title); 4207 $(this).find('div.rboxcontent').text(content); 4208 }); 4209 //place tikifeedback div into window view if necessary 4210 placeFeedback(tfb); 4211 }); 4212 } 4213 tfb.find('.clear').on('click', function () { 4214 $(tfb).empty(); 4215 //move back to usual position and clear style attribute so subsequent feedback appears properly 4216 $('div#col1').prepend(tfb); 4217 tfb.css({'z-index':'', 'position':'', 'top': ''}); 4218 return true; 4219 }); 4220}); 4221 4222$(document).on('keydown', 'textarea.autoheight', function(evt){ 4223 var el = this; 4224 var height = Math.max(el.clientHeight, el.offsetHeight, el.scrollHeight); 4225 el.style.cssText = 'height:' + height + 'px; overflow-y: hidden'; 4226 setTimeout(function(){ 4227 el.scrollTo(0,0); 4228 }, 0); 4229}); 4230 4231$(document).on('change', '.preference :checkbox:not(.pref-reset)', function () { 4232 var childBlock = $(this).data('tiki-admin-child-block') 4233 , childMode = $(this).data('tiki-admin-child-mode') 4234 , checked = $(this).is(':checked') 4235 , disabled = $(this).prop('disabled') 4236 , $depedencies = $(this).parents(".adminoption").find(".pref_dependency") 4237 , childrenElements = null 4238 ;
4239 var childrenElements = $(this).parents('.adminoptionbox').nextAll('.adminoptionboxchild').eq(0).find(':input[id^="pref-"]'); 4240 4241 if (childBlock) { 4242 childrenElements = $(childBlock).find(':input[id^="pref-"]'); 4243 } 4244 4245 if (childMode === 'invert') { 4246 // FIXME: Should only affect childBlock, not $depedencies. From r54386 4247 checked = ! checked; 4248 } 4249 4250 if (disabled && checked) { 4251 $(childBlock).show('fast'); 4252 $depedencies.show('fast'); 4253 } else if (disabled || ! checked) { 4254 /* Only hides child preferences if they are all at default values. 4255 Purpose questioned in https://sourceforge.net/p/tikiwiki/mailman/tikiwiki-cvs/thread/F2DE8896807BF045932776107E2E783D350674DB%40CT20SEXCHP02.FONCIERQC.INTRA/#msg36171225 4256 */ 4257 var hideBlock = true; 4258 childrenElements.each(function( index ) { 4259 var value = $( this ).val(); 4260 var valueDefault = $( this ).siblings('span.pref-reset-wrapper').children('.pref-reset').attr('data-preference-default'); 4261 4262 if (typeof valueDefault != 'undefined' && value != valueDefault) { 4263 hideBlock = false; 4264 } 4265 }); 4266 4267 if (hideBlock) { 4268 $(childBlock).hide('fast'); 4269 $depedencies.hide('fast'); 4270 } 4271 } else { 4272 $(childBlock).show('fast'); 4273 $depedencies.show('fast'); 4274 } 4275}); 4276 4277$(document).on('click', '.pref-reset-wrapper a', function () { 4278 var box = $(this).closest('span').find(':checkbox'); 4279 box.click(); 4280 $(this).closest('span').children( ".pref-reset-undo, .pref-reset-redo" ).toggle(); 4281 return false; 4282}); 4283 4284$(document).on('click', '.pref-reset', function() { 4285 var c = $(this).prop('checked'); 4286 var $el = $(this).closest('.adminoptionbox').find('input:not(:hidden),select,textarea') 4287 .not('.system').attr( 'disabled', c ) 4288 .css("opacity", c ? .6 : 1 ); 4289 var defval = $(this).data('preference-default'); 4290 4291 if ($el.is(':checkbox')) { 4292 $(this).data('preference-default', $el.prop('checked') ? 'y' : 'n'); 4293 $el.prop('checked', defval === "y"); 4294 } else { 4295 $(this).data('preference-default', $el.val()); 4296 $el.val(defval); 4297 } 4298 $el.change(); 4299 if (jqueryTiki.chosen) { 4300 $el.trigger("chosen:updated"); 4301 } 4302}); 4303 4304$(document).on('change', '.preference select', function () { 4305 var childBlock = $(this).data('tiki-admin-child-block') 4306 , selected = $(this).val() 4307 , childMode = $(this).data('tiki-admin-child-mode') 4308 ; 4309 4310 $(childBlock).hide(); 4311 $(childBlock + ' .modified').show(); 4312 $(childBlock + ' .modified').parent().show(); 4313 4314 if (selected && /^[\w-]+$/.test(selected)) { 4315 $(childBlock).filter('.' + selected).show(); 4316 } 4317 if (childMode === 'notempty' && selected.length) { 4318 $(childBlock).show(); 4319 } 4320}); 4321 4322$(document).on('change', '.preference :radio', function () { 4323 var childBlock = $(this).data('tiki-admin-child-block'); 4324 4325 if ($(this).prop('checked')) { 4326 $(childBlock).show('fast'); 4327 $(this).closest('.preference').find(':radio').not(this).change(); 4328 } else { 4329 $(childBlock).hide(); 4330 } 4331}); 4332 4333$(function () { 4334 $('.preference :checkbox, .preference select, .preference :radio').change(); 4335});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.