1/** 2 * @file 3 * Colorbox module init js. 4 */ 5 6(function ($) { 7 8Drupal.behaviors.initColorbox = { 9 attach: function (context, settings) { 10 if (!$.isFunction($('a, area, input', context).colorbox) || typeof settings.colorbox === 'undefined') { 11 return; 12 } 13 14 if (settings.colorbox.mobiledetect && window.matchMedia) { 15 // Disable Colorbox for small screens. 16 var mq = window.matchMedia("(max-device-width: " + settings.colorbox.mobiledevicewidth + ")"); 17 if (mq.matches) { 18 return; 19 } 20 } 21 22 // Use "data-colorbox-gallery" if set otherwise use "rel". 23 settings.colorbox.rel = function () { 24 if ($(this).data('colorbox-gallery')) { 25 return $(this).data('colorbox-gallery'); 26 } 27 else { 28 return $(this).attr('rel'); 29 } 30 }; 31 32 $('.colorbox', context) 33 .once('init-colorbox').each(function(){ 34 // Only images are supported for the "colorbox" class. 35 // The "photo" setting forces the href attribute to be treated as an image. 36 var extendParams = { 37 photo: true 38 }; 39 // If a title attribute is supplied, sanitize it. 40 var title = $(this).attr('title'); 41 if (title) { 42 extendParams.title = Drupal.colorbox.sanitizeMarkup(title); 43 } 44 $(this).colorbox($.extend({}, settings.colorbox, extendParams)); 45 }); 46 47 $(context).bind('cbox_complete', function () { 48 Drupal.attachBehaviors($('#cboxLoadedContent')); 49 }); 50 } 51}; 52 53// Create colorbox namespace if it doesn't exist. 54if (!Drupal.hasOwnProperty('colorbox')) { 55 Drupal.colorbox = {}; 56} 57 58/** 59 * Global function to allow sanitizing captions and control strings. 60 * 61 * @param markup 62 * String containing potential markup. 63 * @return @string 64 * Sanitized string with potentially dangerous markup removed. 65 */ 66Drupal.colorbox.sanitizeMarkup = function(markup) { 67 // If DOMPurify installed, allow some HTML. Otherwise, treat as plain text. 68 if (typeof DOMPurify !== 'undefined') { 69 var purifyConfig = { 70 ALLOWED_TAGS: [ 71 'a', 72 'b', 73 'strong', 74 'i', 75 'em', 76 'u', 77 'cite', 78 'code', 79 'br' 80 ], 81 ALLOWED_ATTR: [ 82 'href', 83 'hreflang', 84 'title', 85 'target' 86 ] 87 } 88 if (Drupal.settings.hasOwnProperty('dompurify_custom_config')) { 89 purifyConfig = Drupal.settings.dompurify_custom_config; 90 } 91 return DOMPurify.sanitize(markup, purifyConfig); 92 } 93 else { 94 return Drupal.checkPlain(markup); 95 } 96} 97 98})(jQuery); 99; 100/** 101 * @file 102 * Colorbox module style js. 103 */ 104 105(function ($) { 106 107Drupal.behaviors.initColorboxDefaultStyle = { 108 attach: function (context, settings) { 109 $(context).bind('cbox_complete', function () { 110 // Only run if there is a title. 111 if ($('#cboxTitle:empty', context).length == false) { 112 $('#cboxLoadedContent img', context).bind('mouseover', function () { 113 $('#cboxTitle', context).slideDown(); 114 }); 115 $('#cboxOverlay', context).bind('mouseover', function () { 116 $('#cboxTitle', context).slideUp(); 117 }); 118 } 119 else { 120 $('#cboxTitle', context).hide(); 121 } 122 }); 123 } 124}; 125 126})(jQuery); 127; 128(function ($) { 129 130/** 131 * A progressbar object. Initialized with the given id. Must be inserted into 132 * the DOM afterwards through progressBar.element. 133 * 134 * method is the function which will perform the HTTP request to get the 135 * progress bar state. Either "GET" or "POST". 136 * 137 * e.g. pb = new progressBar('myProgressBar'); 138 * some_element.appendChild(pb.element); 139 */ 140Drupal.progressBar = function (id, updateCallback, method, errorCallback) { 141 var pb = this; 142 this.id = id; 143 this.method = method || 'GET'; 144 this.updateCallback = updateCallback; 145 this.errorCallback = errorCallback; 146 147 // The WAI-ARIA setting aria-live="polite" will announce changes after users 148 // have completed their current activity and not interrupt the screen reader. 149 this.element = $('<div class="progress" aria-live="polite"></div>').attr('id', id); 150 this.element.html('<div class="bar"><div class="filled"></div></div>' + 151 '<div class="percentage"></div>' + 152 '<div class="message"> </div>'); 153}; 154 155/** 156 * Set the percentage and status message for the progressbar. 157 */ 158Drupal.progressBar.prototype.setProgress = function (percentage, message) { 159 if (percentage >= 0 && percentage <= 100) { 160 $('div.filled', this.element).css('width', percentage + '%'); 161 $('div.percentage', this.element).html(percentage + '%'); 162 } 163 $('div.message', this.element).html(message); 164 if (this.updateCallback) { 165 this.updateCallback(percentage, message, this); 166 } 167}; 168 169/** 170 * Start monitoring progress via Ajax. 171 */ 172Drupal.progressBar.prototype.startMonitoring = function (uri, delay) { 173 this.delay = delay; 174 this.uri = uri; 175 this.sendPing(); 176}; 177 178/** 179 * Stop monitoring progress via Ajax. 180 */ 181Drupal.progressBar.prototype.stopMonitoring = function () { 182 clearTimeout(this.timer); 183 // This allows monitoring to be stopped from within the callback. 184 this.uri = null; 185}; 186 187/** 188 * Request progress data from server. 189 */ 190Drupal.progressBar.prototype.sendPing = function () { 191 if (this.timer) { 192 clearTimeout(this.timer); 193 } 194 if (this.uri) { 195 var pb = this; 196 // When doing a post request, you need non-null data. Otherwise a 197 // HTTP 411 or HTTP 406 (with Apache mod_security) error may result. 198 $.ajax({ 199 type: this.method, 200 url: this.uri, 201 data: '', 202 dataType: 'json', 203 success: function (progress) { 204 // Display errors. 205 if (progress.status == 0) { 206 pb.displayError(progress.data); 207 return; 208 } 209 // Update display. 210 pb.setProgress(progress.percentage, progress.message); 211 // Schedule next timer.
212 pb.timer = setTimeout(function () { pb.sendPing(); }, pb.delay); 213 }, 214 error: function (xmlhttp) { 215 pb.displayError(Drupal.ajaxError(xmlhttp, pb.uri)); 216 } 217 }); 218 } 219}; 220 221/** 222 * Display errors on the page. 223 */ 224Drupal.progressBar.prototype.displayError = function (string) { 225 var error = $('<div class="messages error"></div>').html(string); 226 $(this.element).before(error).hide(); 227 228 if (this.errorCallback) { 229 this.errorCallback(this); 230 } 231}; 232 233})(jQuery); 234; 235/** 236 * @file 237 * 238 * Implement a modal form. 239 * 240 * @see modal.inc for documentation. 241 * 242 * This javascript relies on the CTools ajax responder. 243 */ 244 245(function ($) { 246 // Make sure our objects are defined. 247 Drupal.CTools = Drupal.CTools || {}; 248 Drupal.CTools.Modal = Drupal.CTools.Modal || {}; 249 250 /** 251 * Display the modal 252 * 253 * @todo -- document the settings. 254 */ 255 Drupal.CTools.Modal.show = function(choice) { 256 var opts = {}; 257 258 if (choice && typeof choice == 'string' && Drupal.settings[choice]) { 259 // This notation guarantees we are actually copying it. 260 $.extend(true, opts, Drupal.settings[choice]); 261 } 262 else if (choice) { 263 $.extend(true, opts, choice); 264 } 265 266 var defaults = { 267 modalTheme: 'CToolsModalDialog', 268 throbberTheme: 'CToolsModalThrobber', 269 animation: 'show', 270 animationSpeed: 'fast', 271 modalSize: { 272 type: 'scale', 273 width: .8, 274 height: .8, 275 addWidth: 0, 276 addHeight: 0, 277 // How much to remove from the inner content to make space for the 278 // theming. 279 contentRight: 25, 280 contentBottom: 45 281 }, 282 modalOptions: { 283 opacity: .55, 284 background: '#fff' 285 }, 286 modalClass: 'default' 287 }; 288 289 var settings = {}; 290 $.extend(true, settings, defaults, Drupal.settings.CToolsModal, opts); 291 292 if (Drupal.CTools.Modal.currentSettings && Drupal.CTools.Modal.currentSettings != settings) { 293 Drupal.CTools.Modal.modal.remove(); 294 Drupal.CTools.Modal.modal = null; 295 } 296 297 Drupal.CTools.Modal.currentSettings = settings; 298 299 var resize = function(e) { 300 // When creating the modal, it actually exists only in a theoretical 301 // place that is not in the DOM. But once the modal exists, it is in the 302 // DOM so the context must be set appropriately. 303 var context = e ? document : Drupal.CTools.Modal.modal; 304 305 if (Drupal.CTools.Modal.currentSettings.modalSize.type == 'scale') { 306 var width = $(window).width() * Drupal.CTools.Modal.currentSettings.modalSize.width; 307 var height = $(window).height() * Drupal.CTools.Modal.currentSettings.modalSize.height; 308 } 309 else { 310 var width = Drupal.CTools.Modal.currentSettings.modalSize.width; 311 var height = Drupal.CTools.Modal.currentSettings.modalSize.height; 312 } 313 314 // Use the additionol pixels for creating the width and height. 315 $('div.ctools-modal-content', context).css({ 316 'width': width + Drupal.CTools.Modal.currentSettings.modalSize.addWidth + 'px', 317 'height': height + Drupal.CTools.Modal.currentSettings.modalSize.addHeight + 'px' 318 }); 319 $('div.ctools-modal-content .modal-content', context).css({ 320 'width': (width - Drupal.CTools.Modal.currentSettings.modalSize.contentRight) + 'px', 321 'height': (height - Drupal.CTools.Modal.currentSettings.modalSize.contentBottom) + 'px' 322 }); 323 }; 324 325 if (!Drupal.CTools.Modal.modal) { 326 Drupal.CTools.Modal.modal = $(Drupal.theme(settings.modalTheme)); 327 if (settings.modalSize.type == 'scale') { 328 $(window).bind('resize', resize); 329 } 330 } 331 332 resize(); 333 334 $('span.modal-title', Drupal.CTools.Modal.modal).html(Drupal.CTools.Modal.currentSettings.loadingText); 335 Drupal.CTools.Modal.modalContent(Drupal.CTools.Modal.modal, settings.modalOptions, settings.animation, settings.animationSpeed, settings.modalClass); 336 $('#modalContent .modal-content').html(Drupal.theme(settings.throbberTheme)).addClass('ctools-modal-loading'); 337 338 // Position autocomplete results based on the scroll position of the modal.
339 $('#modalContent .modal-content').delegate('input.form-autocomplete', 'keyup', function() { 340 $('#autocomplete').css('top', $(this).position().top + $(this).outerHeight() + $(this).offsetParent().filter('#modal-content').scrollTop()); 341 }); 342 }; 343 344 /** 345 * Hide the modal 346 */ 347 Drupal.CTools.Modal.dismiss = function() { 348 if (Drupal.CTools.Modal.modal) { 349 Drupal.CTools.Modal.unmodalContent(Drupal.CTools.Modal.modal); 350 } 351 }; 352 353 /** 354 * Provide the HTML to create the modal dialog. 355 */ 356 Drupal.theme.prototype.CToolsModalDialog = function () { 357 var html = ''; 358 html += '<div id="ctools-modal">'; 359 html += ' <div class="ctools-modal-content">'; // panels-modal-content 360 html += ' <div class="modal-header">'; 361 html += ' <a class="close" href="#">'; 362 html += Drupal.CTools.Modal.currentSettings.closeText + Drupal.CTools.Modal.currentSettings.closeImage; 363 html += ' </a>'; 364 html += ' <span id="modal-title" class="modal-title"> </span>'; 365 html += ' </div>'; 366 html += ' <div id="modal-content" class="modal-content">'; 367 html += ' </div>'; 368 html += ' </div>'; 369 html += '</div>'; 370 371 return html; 372 }; 373 374 /** 375 * Provide the HTML to create the throbber. 376 */ 377 Drupal.theme.prototype.CToolsModalThrobber = function () { 378 var html = ''; 379 html += '<div id="modal-throbber">'; 380 html += ' <div class="modal-throbber-wrapper">'; 381 html += Drupal.CTools.Modal.currentSettings.throbber; 382 html += ' </div>'; 383 html += '</div>'; 384 385 return html; 386 }; 387 388 /** 389 * Figure out what settings string to use to display a modal. 390 */ 391 Drupal.CTools.Modal.getSettings = function (object) { 392 var match = $(object).attr('class').match(/ctools-modal-(\S+)/); 393 if (match) { 394 return match[1]; 395 } 396 }; 397 398 /** 399 * Click function for modals that can be cached. 400 */ 401 Drupal.CTools.Modal.clickAjaxCacheLink = function () { 402 Drupal.CTools.Modal.show(Drupal.CTools.Modal.getSettings(this)); 403 return Drupal.CTools.AJAX.clickAJAXCacheLink.apply(this); 404 }; 405 406 /** 407 * Handler to prepare the modal for the response 408 */ 409 Drupal.CTools.Modal.clickAjaxLink = function () { 410 Drupal.CTools.Modal.show(Drupal.CTools.Modal.getSettings(this)); 411 return false; 412 }; 413 414 /** 415 * Submit responder to do an AJAX submit on all modal forms. 416 */ 417 Drupal.CTools.Modal.submitAjaxForm = function(e) { 418 var $form = $(this); 419 var url = $form.attr('action'); 420 421 setTimeout(function() { Drupal.CTools.AJAX.ajaxSubmit($form, url); }, 1); 422 return false; 423 }; 424 425 /** 426 * Bind links that will open modals to the appropriate function. 427 */ 428 Drupal.behaviors.ZZCToolsModal = { 429 attach: function(context) { 430 // Bind links 431 // Note that doing so in this order means that the two classes can be 432 // used together safely. 433 /* 434 * @todo remimplement the warm caching feature 435 $('a.ctools-use-modal-cache', context).once('ctools-use-modal', function() { 436 $(this).click(Drupal.CTools.Modal.clickAjaxCacheLink); 437 Drupal.CTools.AJAX.warmCache.apply(this); 438 }); 439 */ 440 441 $('area.ctools-use-modal, a.ctools-use-modal', context).once('ctools-use-modal', function() { 442 var $this = $(this); 443 $this.click(Drupal.CTools.Modal.clickAjaxLink); 444 // Create a drupal ajax object 445 var element_settings = {}; 446 if ($this.attr('href')) { 447 element_settings.url = $this.attr('href'); 448 element_settings.event = 'click'; 449 element_settings.progress = { type: 'throbber' }; 450 } 451 var base = $this.attr('href'); 452 Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings); 453 }); 454 455 // Bind buttons 456 $('input.ctools-use-modal, button.ctools-use-modal', context).once('ctools-use-modal', function() { 457 var $this = $(this); 458 $this.click(Drupal.CTools.Modal.clickAjaxLink); 459 var button = this; 460 var element_settings = {}; 461 462 // AJAX submits specified in this manner automatically submit to the 463 // normal form action. 464 element_settings.url = Drupal.CTools.Modal.findURL(this); 465 if (element_settings.url == '') { 466 element_settings.url = $(this).closest('form').attr('action'); 467 } 468 element_settings.event = 'click'; 469 element_settings.setClick = true; 470 471 var base = $this.attr('id'); 472 Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings); 473 474 // Make sure changes to settings are reflected in the URL. 475 $('.' + $(button).attr('id') + '-url').change(function() { 476 Drupal.ajax[base].options.url = Drupal.CTools.Modal.findURL(button); 477 }); 478 }); 479 480 // Bind our custom event to the form submit
481 $('#modal-content form', context).once('ctools-use-modal', function() { 482 var $this = $(this); 483 var element_settings = {}; 484 485 element_settings.url = $this.attr('action'); 486 element_settings.event = 'submit'; 487 element_settings.progress = { 'type': 'throbber' }; 488 var base = $this.attr('id'); 489 490 Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings); 491 Drupal.ajax[base].form = $this; 492 493 $('input[type=submit], button', this).click(function(event) { 494 Drupal.ajax[base].element = this; 495 this.form.clk = this; 496 // Stop autocomplete from submitting. 497 if (Drupal.autocompleteSubmit && !Drupal.autocompleteSubmit()) { 498 return false; 499 } 500 // An empty event means we were triggered via .click() and 501 // in jquery 1.4 this won't trigger a submit. 502 // We also have to check jQuery version to prevent 503 // IE8 + jQuery 1.4.4 to break on other events 504 // bound to the submit button. 505 if (jQuery.fn.jquery.substr(0, 3) === '1.4' && typeof event.bubbles === "undefined") { 506 $(this.form).trigger('submit'); 507 return false; 508 } 509 }); 510 }); 511 512 // Bind a click handler to allow elements with the 'ctools-close-modal' 513 // class to close the modal. 514 $('.ctools-close-modal', context).once('ctools-close-modal') 515 .click(function() { 516 Drupal.CTools.Modal.dismiss(); 517 return false; 518 }); 519 } 520 }; 521 522 // The following are implementations of AJAX responder commands. 523 524 /** 525 * AJAX responder command to place HTML within the modal. 526 */ 527 Drupal.CTools.Modal.modal_display = function(ajax, response, status) { 528 var settings = response.settings || ajax.settings || Drupal.settings; 529 // If the modal does not exist yet, create it. 530 if ($('#modalContent').length == 0) { 531 Drupal.CTools.Modal.show(Drupal.CTools.Modal.getSettings(ajax.element)); 532 } 533 // If the modal exists run detachBehaviors before removing existing content. 534 else { 535 Drupal.detachBehaviors($('#modalContent'), settings, 'unload'); 536 } 537 $('#modal-title').html(response.title); 538 // Simulate an actual page load by scrolling to the top after adding the 539 // content. This is helpful for allowing users to see error messages at the 540 // top of a form, etc. 541 $('#modal-content').html(response.output).scrollTop(0); 542 $(document).trigger('CToolsAttachBehaviors', $('#modalContent')); 543 544 // Attach behaviors within a modal dialog. 545 Drupal.attachBehaviors($('#modalContent'), settings); 546 547 if ($('#modal-content').hasClass('ctools-modal-loading')) { 548 $('#modal-content').removeClass('ctools-modal-loading'); 549 } 550 else { 551 // If the modal was already shown, and we are simply replacing its 552 // content, then focus on the first focusable element in the modal. 553 // (When first showing the modal, focus will be placed on the close 554 // button by the show() function called above.) 555 $('#modal-content :focusable:first').focus(); 556 } 557 }; 558 559 /** 560 * AJAX responder command to dismiss the modal. 561 */ 562 Drupal.CTools.Modal.modal_dismiss = function(command) { 563 Drupal.CTools.Modal.dismiss(); 564 $('link.ctools-temporary-css').remove(); 565 }; 566 567 /** 568 * Display loading 569 */ 570 //Drupal.CTools.AJAX.commands.modal_loading = function(command) { 571 Drupal.CTools.Modal.modal_loading = function(command) { 572 Drupal.CTools.Modal.modal_display({ 573 output: Drupal.theme(Drupal.CTools.Modal.currentSettings.throbberTheme), 574 title: Drupal.CTools.Modal.currentSettings.loadingText 575 }); 576 }; 577 578 /** 579 * Find a URL for an AJAX button. 580 * 581 * The URL for this gadget will be composed of the values of items by 582 * taking the ID of this item and adding -url and looking for that 583 * class. They need to be in the form in order since we will 584 * concat them all together using '/'. 585 */ 586 Drupal.CTools.Modal.findURL = function(item) { 587 var url = ''; 588 var url_class = '.' + $(item).attr('id') + '-url'; 589 $(url_class).each( 590 function() { 591 var $this = $(this); 592 if (url && $this.val()) { 593 url += '/'; 594 } 595 url += $this.val(); 596 }); 597 return url; 598 }; 599 600 601 /** 602 * modalContent 603 * @param content string to display in the content box 604 * @param css obj of css attributes 605 * @param animation (fadeIn, slideDown, show) 606 * @param speed (valid animation speeds slow, medium, fast or # in ms) 607 * @param modalClass class added to div#modalContent 608 */ 609 Drupal.CTools.Modal.modalContent = function(content, css, animation, speed, modalClass) { 610 // If our animation isn't set, make it just show/pop 611 if (!animation) { 612 animation = 'show'; 613 } 614 else { 615 // If our animation isn't "fadeIn" or "slideDown" then it always is show 616 if (animation != 'fadeIn' && animation != 'slideDown') { 617 animation = 'show'; 618 } 619 } 620 621 if (!speed && 0 !== speed) { 622 speed = 'fast'; 623 } 624 625 // Build our base attributes and allow them to be overriden 626 css = jQuery.extend({ 627 position: 'absolute', 628 left: '0px', 629 margin: '0px', 630 background: '#000', 631 opacity: '.55' 632 }, css); 633 634 // Add opacity handling for IE. 635 css.filter = 'alpha(opacity=' + (100 * css.opacity) + ')'; 636 content.hide(); 637 638 // If we already have modalContent, remove it. 639 if ($('#modalBackdrop').length) $('#modalBackdrop').remove(); 640 if ($('#modalContent').length) $('#modalContent').remove(); 641 642 // position code lifted from http://www.quirksmode.org/viewport/compatibility.html 643 if (self.pageYOffset) { // all except Explorer 644 var wt = self.pageYOffset; 645 } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict 646 var wt = document.documentElement.scrollTop; 647 } else if (document.body) { // all other Explorers 648 var wt = document.body.scrollTop; 649 } 650 651 // Get our dimensions 652 653 // Get the docHeight and (ugly hack) add 50 pixels to make sure we dont have a *visible* border below our div 654 var docHeight = $(document).height() + 50; 655 var docWidth = $(document).width(); 656 var winHeight = $(window).height(); 657 var winWidth = $(window).width();
658 if( docHeight < winHeight ) docHeight = winHeight; 659 660 // Create our divs 661 $('body').append('<div id="modalBackdrop" class="backdrop-' + modalClass + '" style="z-index: 1000; display: none;"></div><div id="modalContent" class="modal-' + modalClass + '" style="z-index: 1001; position: absolute;">' + $(content).html() + '</div>'); 662 663 // Get a list of the tabbable elements in the modal content. 664 var getTabbableElements = function () { 665 var tabbableElements = $('#modalContent :tabbable'), 666 radioButtons = tabbableElements.filter('input[type="radio"]'); 667 668 // The list of tabbable elements from jQuery is *almost* right. The 669 // exception is with groups of radio buttons. The list from jQuery will 670 // include all radio buttons, when in fact, only the selected radio button 671 // is tabbable, and if no radio buttons in a group are selected, then only 672 // the first is tabbable. 673 if (radioButtons.length > 0) { 674 // First, build up an index of which groups have an item selected or not. 675 var anySelected = {}; 676 radioButtons.each(function () { 677 var name = this.name; 678 679 if (typeof anySelected[name] === 'undefined') { 680 anySelected[name] = radioButtons.filter('input[name="' + name + '"]:checked').length !== 0; 681 } 682 }); 683 684 // Next filter out the radio buttons that aren't really tabbable. 685 var found = {}; 686 tabbableElements = tabbableElements.filter(function () { 687 var keep = true; 688 689 if (this.type == 'radio') { 690 if (anySelected[this.name]) { 691 // Only keep the selected one. 692 keep = this.checked; 693 } 694 else { 695 // Only keep the first one. 696 if (found[this.name]) { 697 keep = false; 698 } 699 found[this.name] = true; 700 } 701 } 702 703 return keep; 704 }); 705 } 706 707 return tabbableElements.get(); 708 }; 709 710 // Keyboard and focus event handler ensures only modal elements gain focus. 711 modalEventHandler = function( event ) { 712 target = null; 713 if ( event ) { //Mozilla 714 target = event.target; 715 } else { //IE 716 event = window.event; 717 target = event.srcElement; 718 } 719 720 var parents = $(target).parents().get(); 721 for (var i = 0; i < parents.length; ++i) { 722 var position = $(parents[i]).css('position'); 723 if (position == 'absolute' || position == 'fixed') { 724 return true; 725 } 726 } 727 728 if ($(target).is('#modalContent, body') || $(target).filter('*:visible').parents('#modalContent').length) { 729 // Allow the event only if target is a visible child node 730 // of #modalContent. 731 return true; 732 } 733 else { 734 getTabbableElements()[0].focus(); 735 } 736 737 event.preventDefault(); 738 }; 739 $('body').bind( 'focus', modalEventHandler ); 740 $('body').bind( 'keypress', modalEventHandler ); 741 742 // Keypress handler Ensures you can only TAB to elements within the modal. 743 // Based on the psuedo-code from WAI-ARIA 1.0 Authoring Practices section 744 // 3.3.1 "Trapping Focus". 745 modalTabTrapHandler = function (evt) { 746 // We only care about the TAB key. 747 if (evt.which != 9) { 748 return true; 749 } 750 751 var tabbableElements = getTabbableElements(), 752 firstTabbableElement = tabbableElements[0], 753 lastTabbableElement = tabbableElements[tabbableElements.length - 1], 754 singleTabbableElement = firstTabbableElement == lastTabbableElement, 755 node = evt.target; 756 757 // If this is the first element and the user wants to go backwards, then 758 // jump to the last element. 759 if (node == firstTabbableElement && evt.shiftKey) { 760 if (!singleTabbableElement) { 761 lastTabbableElement.focus(); 762 } 763 return false; 764 } 765 // If this is the last element and the user wants to go forwards, then 766 // jump to the first element. 767 else if (node == lastTabbableElement && !evt.shiftKey) { 768 if (!singleTabbableElement) { 769 firstTabbableElement.focus(); 770 } 771 return false;
772 } 773 // If this element isn't in the dialog at all, then jump to the first 774 // or last element to get the user into the game. 775 else if ($.inArray(node, tabbableElements) == -1) { 776 // Make sure the node isn't in another modal (ie. WYSIWYG modal). 777 var parents = $(node).parents().get(); 778 for (var i = 0; i < parents.length; ++i) { 779 var position = $(parents[i]).css('position'); 780 if (position == 'absolute' || position == 'fixed') { 781 return true; 782 } 783 } 784 785 if (evt.shiftKey) { 786 lastTabbableElement.focus(); 787 } 788 else { 789 firstTabbableElement.focus(); 790 } 791 } 792 }; 793 $('body').bind('keydown', modalTabTrapHandler); 794 795 // Create our content div, get the dimensions, and hide it 796 var modalContent = $('#modalContent').css('top','-1000px'); 797 var $modalHeader = modalContent.find('.modal-header'); 798 var mdcTop = wt + Math.max((winHeight / 2) - (modalContent.outerHeight() / 2), 0); 799 var mdcLeft = ( winWidth / 2 ) - ( modalContent.outerWidth() / 2); 800 $('#modalBackdrop').css(css).css('top', 0).css('height', docHeight + 'px').css('width', docWidth + 'px').show(); 801 modalContent.css({top: mdcTop + 'px', left: mdcLeft + 'px'}).hide()[animation](speed); 802 803 // Bind a click for closing the modalContent 804 modalContentClose = function(){close(); return false;}; 805 $('.close', $modalHeader).bind('click', modalContentClose); 806 807 // Bind a keypress on escape for closing the modalContent 808 modalEventEscapeCloseHandler = function(event) { 809 if (event.keyCode == 27) { 810 close(); 811 return false; 812 } 813 }; 814 815 $(document).bind('keydown', modalEventEscapeCloseHandler); 816 817 // Per WAI-ARIA 1.0 Authoring Practices, initial focus should be on the 818 // close button, but we should save the original focus to restore it after 819 // the dialog is closed. 820 var oldFocus = document.activeElement; 821 $('.close', $modalHeader).focus(); 822 823 // Close the open modal content and backdrop 824 function close() { 825 // Unbind the events 826 $(window).unbind('resize', modalContentResize); 827 $('body').unbind( 'focus', modalEventHandler); 828 $('body').unbind( 'keypress', modalEventHandler ); 829 $('body').unbind( 'keydown', modalTabTrapHandler ); 830 $('.close', $modalHeader).unbind('click', modalContentClose); 831 $(document).unbind('keydown', modalEventEscapeCloseHandler); 832 $(document).trigger('CToolsCloseModalBehaviors', $('#modalContent')); 833 $(document).trigger('CToolsDetachBehaviors', $('#modalContent')); 834 835 // Closing animation. 836 switch (animation) { 837 case 'fadeIn': 838 modalContent.fadeOut(speed, modalContentRemove); 839 break; 840 841 case 'slideDown': 842 modalContent.slideUp(speed, modalContentRemove); 843 break; 844 845 case 'show': 846 modalContent.hide(speed, modalContentRemove); 847 break; 848 } 849 } 850 851 // Remove the content. 852 modalContentRemove = function () { 853 $('#modalContent').remove(); 854 $('#modalBackdrop').remove(); 855 856 // Restore focus to where it was before opening the dialog. 857 $(oldFocus).focus(); 858 }; 859 860 // Move and resize the modalBackdrop and modalContent on window resize. 861 modalContentResize = function () { 862 // Reset the backdrop height/width to get accurate document size. 863 $('#modalBackdrop').css('height', '').css('width', ''); 864 865 // Position code lifted from: 866 // http://www.quirksmode.org/viewport/compatibility.html 867 if (self.pageYOffset) { // all except Explorer 868 var wt = self.pageYOffset; 869 } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict 870 var wt = document.documentElement.scrollTop; 871 } else if (document.body) { // all other Explorers 872 var wt = document.body.scrollTop; 873 } 874 875 // Get our heights 876 var docHeight = $(document).height(); 877 var docWidth = $(document).width(); 878 var winHeight = $(window).height(); 879 var winWidth = $(window).width();
880 if( docHeight < winHeight ) docHeight = winHeight; 881 882 // Get where we should move content to 883 var modalContent = $('#modalContent'); 884 var mdcTop = wt + Math.max((winHeight / 2) - (modalContent.outerHeight() / 2), 0); 885 var mdcLeft = ( winWidth / 2 ) - ( modalContent.outerWidth() / 2); 886 887 // Apply the changes 888 $('#modalBackdrop').css('height', docHeight + 'px').css('width', docWidth + 'px').show(); 889 modalContent.css('top', mdcTop + 'px').css('left', mdcLeft + 'px').show(); 890 }; 891 $(window).bind('resize', modalContentResize); 892 }; 893 894 /** 895 * unmodalContent 896 * @param content (The jQuery object to remove) 897 * @param animation (fadeOut, slideUp, show) 898 * @param speed (valid animation speeds slow, medium, fast or # in ms) 899 */ 900 Drupal.CTools.Modal.unmodalContent = function(content, animation, speed) 901 { 902 // If our animation isn't set, make it just show/pop 903 if (!animation) { var animation = 'show'; } else { 904 // If our animation isn't "fade" then it always is show 905 if (( animation != 'fadeOut' ) && ( animation != 'slideUp')) animation = 'show'; 906 } 907 // Set a speed if we dont have one 908 if ( !speed ) var speed = 'fast'; 909 910 // Unbind the events we bound 911 $(window).unbind('resize', modalContentResize); 912 $('body').unbind('focus', modalEventHandler); 913 $('body').unbind('keypress', modalEventHandler); 914 $('body').unbind( 'keydown', modalTabTrapHandler ); 915 var $modalContent = $('#modalContent'); 916 var $modalHeader = $modalContent.find('.modal-header'); 917 $('.close', $modalHeader).unbind('click', modalContentClose); 918 $(document).unbind('keydown', modalEventEscapeCloseHandler); 919 $(document).trigger('CToolsDetachBehaviors', $modalContent); 920 921 // jQuery magic loop through the instances and run the animations or removal. 922 content.each(function(){ 923 if ( animation == 'fade' ) { 924 $('#modalContent').fadeOut(speed, function() { 925 $('#modalBackdrop').fadeOut(speed, function() { 926 $(this).remove(); 927 }); 928 $(this).remove(); 929 }); 930 } else { 931 if ( animation == 'slide' ) { 932 $('#modalContent').slideUp(speed,function() { 933 $('#modalBackdrop').slideUp(speed, function() { 934 $(this).remove(); 935 }); 936 $(this).remove(); 937 }); 938 } else { 939 $('#modalContent').remove(); 940 $('#modalBackdrop').remove(); 941 } 942 } 943 }); 944 }; 945 946$(function() { 947 Drupal.ajax.prototype.commands.modal_display = Drupal.CTools.Modal.modal_display; 948 Drupal.ajax.prototype.commands.modal_dismiss = Drupal.CTools.Modal.modal_dismiss; 949}); 950 951})(jQuery); 952; 953/** 954* Provide the HTML to create the modal dialog. 955*/ 956Drupal.theme.prototype.ModalFormsPopup = function () { 957 var html = ''; 958 959 html += '<div id="ctools-modal" class="popups-box">'; 960 html += ' <div class="ctools-modal-content modal-forms-modal-content">'; 961 html += ' <div class="popups-container">'; 962 html += ' <div class="modal-header popups-title">'; 963 html += ' <span id="modal-title" class="modal-title"></span>'; 964 html += ' <span class="popups-close close">' + Drupal.CTools.Modal.currentSettings.closeText + '</span>'; 965 html += ' <div class="clear-block"></div>'; 966 html += ' </div>'; 967 html += ' <div class="modal-scroll"><div id="modal-content" class="modal-content popups-body"></div></div>'; 968 html += ' </div>'; 969 html += ' </div>'; 970 html += '</div>'; 971 972 return html; 973} 974; 975(function($) { 976 Drupal.behaviors.custom_search = { 977 attach: function(context) { 978 979 if (!Drupal.settings.custom_search.solr) { 980 // Check if the search box is not empty on submit 981 $('form.search-form', context).submit(function(){ 982 var $this = $(this); 983 var box = $this.find('input.custom-search-box'); 984 if (box.val() != undefined && box.val() == '') { 985 $this.find('input.custom-search-box').addClass('error'); 986 return false; 987 } 988 // If basic search is hidden, copy or value to the keys 989 if ($this.find('#edit-keys').parents('div.element-invisible').attr('class') == 'element-invisible') { 990 $this.find('#edit-keys').val($this.find('#edit-or').val()); 991 $this.find('#edit-or').val(''); 992 } 993 return true; 994 }); 995 } 996 997 // Search from target 998 $('form.search-form').attr('target', Drupal.settings.custom_search.form_target); 999 1000 // Displays Popup. 1001 $('form.search-form input.custom-search-box', context).bind('click focus', function(e){ 1002 var $parentForm = $(this).parents('form'); 1003 // check if there's something in the popup and displays it
1004 var popup = $parentForm.find('fieldset.custom_search-popup'); 1005 if (popup.find('input,select').length && !popup.hasClass('opened')) { 1006 popup.fadeIn().addClass('opened'); 1007 } 1008 e.stopPropagation(); 1009 }); 1010 $(document).bind('click focus', function(){ 1011 $('fieldset.custom_search-popup').hide().removeClass('opened'); 1012 }); 1013 1014 // Handle checkboxes 1015 $('.custom-search-selector input:checkbox', context).each(function(){ 1016 var el = $(this); 1017 if (el.val() == 'c-all') { 1018 el.change(function(){ 1019 $(this).parents('.custom-search-selector').find('input:checkbox[value!=c-all]').attr('checked', false); 1020 }); 1021 } 1022 else { 1023 if (el.val().substr(0,2) == 'c-') { 1024 el.change(function(){ 1025 $('.custom-search-selector input:checkbox').each(function(){ 1026 if ($(this).val().substr(0,2) == 'o-') { 1027 $(this).attr('checked', false); 1028 } 1029 }); 1030 $(this).parents('.custom-search-selector').find('input:checkbox[value=c-all]').attr('checked', false); 1031 }); 1032 } else { 1033 el.change(function(){ 1034 $(this).parents('.custom-search-selector').find('input:checkbox[value!=' + el.val() + ']').attr('checked', false); 1035 }); 1036 } 1037 } 1038 }); 1039 1040 // Handle popup. 1041 var popup = $('fieldset.custom_search-popup:not(.custom_search-processed)', context).addClass("custom_search-processed"); 1042 popup.click(function(e){ 1043 e.stopPropagation(); 1044 }) 1045 popup.append('<a class="custom_search-popup-close" href="#">' + Drupal.t('Close') + '</a>'); 1046 $('a.custom_search-popup-close').click(function(e){ 1047 $('fieldset.custom_search-popup.opened').hide().removeClass('opened'); 1048 e.preventDefault(); 1049 }); 1050 1051 } 1052 } 1053})(jQuery); 1054; 1055/** 1056 * @file 1057 * Some basic behaviors and utility functions for Views. 1058 */ 1059(function ($) { 1060 1061 Drupal.Views = {}; 1062 1063 /** 1064 * JQuery UI tabs, Views integration component. 1065 */ 1066 Drupal.behaviors.viewsTabs = { 1067 attach: function (context) { 1068 if ($.viewsUi && $.viewsUi.tabs) { 1069 $('#views-tabset').once('views-processed').viewsTabs({ 1070 selectedClass: 'active' 1071 }); 1072 } 1073 1074 $('a.views-remove-link').once('views-processed').click(function(event) { 1075 var id = $(this).attr('id').replace('views-remove-link-', ''); 1076 $('#views-row-' + id).hide(); 1077 $('#views-removed-' + id).attr('checked', true); 1078 event.preventDefault(); 1079 }); 1080 /** 1081 * Here is to handle display deletion 1082 * (checking in the hidden checkbox and hiding out the row). 1083 */ 1084 $('a.display-remove-link') 1085 .addClass('display-processed') 1086 .click(function() { 1087 var id = $(this).attr('id').replace('display-remove-link-', ''); 1088 $('#display-row-' + id).hide(); 1089 $('#display-removed-' + id).attr('checked', true); 1090 return false; 1091 }); 1092 } 1093 }; 1094 1095 /** 1096 * Helper function to parse a querystring. 1097 */ 1098 Drupal.Views.parseQueryString = function (query) { 1099 var args = {}; 1100 var pos = query.indexOf('?'); 1101 if (pos != -1) { 1102 query = query.substring(pos + 1); 1103 } 1104 var pairs = query.split('&'); 1105 for (var i in pairs) { 1106 if (typeof(pairs[i]) == 'string') { 1107 var pair = pairs[i].split('='); 1108 // Ignore the 'q' path argument, if present. 1109 if (pair[0] != 'q' && pair[1]) { 1110 args[decodeURIComponent(pair[0].replace(/\+/g, ' '))] = decodeURIComponent(pair[1].replace(/\+/g, ' ')); 1111 } 1112 } 1113 } 1114 return args; 1115 }; 1116 1117 /** 1118 * Helper function to return a view's arguments based on a path. 1119 */ 1120 Drupal.Views.parseViewArgs = function (href, viewPath) { 1121 1122 // Provide language prefix. 1123 if (Drupal.settings.pathPrefix) { 1124 var viewPath = Drupal.settings.pathPrefix + viewPath; 1125 } 1126 var returnObj = {}; 1127 var path = Drupal.Views.getPath(href); 1128 // Ensure we have a correct path. 1129 if (viewPath && path.substring(0, viewPath.length + 1) == viewPath + '/') { 1130 var args = decodeURIComponent(path.substring(viewPath.length + 1, path.length)); 1131 returnObj.view_args = args; 1132 returnObj.view_path = path; 1133 } 1134 return returnObj; 1135 }; 1136 1137 /** 1138 * Strip off the protocol plus domain from an href. 1139 */ 1140 Drupal.Views.pathPortion = function (href) { 1141 // Remove e.g. http://example.com if present. 1142 var protocol = window.location.protocol; 1143 if (href.substring(0, protocol.length) == protocol) { 1144 // 2 is the length of the '//' that normally follows the protocol. 1145 href = href.substring(href.indexOf('/', protocol.length + 2)); 1146 } 1147 return href; 1148 }; 1149 1150 /** 1151 * Return the Drupal path portion of an href. 1152 */ 1153 Drupal.Views.getPath = function (href) { 1154 href = Drupal.Views.pathPortion(href); 1155 href = href.substring(Drupal.settings.basePath.length, href.length); 1156 // 3 is the length of the '?q=' added to the url without clean urls. 1157 if (href.substring(0, 3) == '?q=') { 1158 href = href.substring(3, href.length); 1159 } 1160 var chars = ['#', '?', '&']; 1161 for (var i in chars) { 1162 if (href.indexOf(chars[i]) > -1) { 1163 href = href.substr(0, href.indexOf(chars[i])); 1164 } 1165 } 1166 return href; 1167 }; 1168 1169})(jQuery); 1170; 1171/** 1172 * @file 1173 * Handles AJAX fetching of views, including filter submission and response. 1174 */ 1175(function ($) { 1176 1177 /**
1178 * Attaches the AJAX behavior to exposed filter forms and key views links. 1179 */ 1180 Drupal.behaviors.ViewsAjaxView = {}; 1181 Drupal.behaviors.ViewsAjaxView.attach = function() { 1182 if (Drupal.settings && Drupal.settings.views && Drupal.settings.views.ajaxViews) { 1183 $.each(Drupal.settings.views.ajaxViews, function(i, settings) { 1184 Drupal.views.instances[i] = new Drupal.views.ajaxView(settings); 1185 }); 1186 } 1187 }; 1188 1189 Drupal.views = {}; 1190 Drupal.views.instances = {}; 1191 1192 /** 1193 * JavaScript object for a certain view. 1194 */ 1195 Drupal.views.ajaxView = function(settings) { 1196 var selector = '.view-dom-id-' + settings.view_dom_id; 1197 this.$view = $(selector); 1198 1199 // If view is not present return to prevent errors. 1200 if (!this.$view.length) { 1201 return; 1202 } 1203 1204 // Retrieve the path to use for views' ajax. 1205 var ajax_path = Drupal.settings.views.ajax_path; 1206 1207 // If there are multiple views this might've ended up showing up multiple 1208 // times. 1209 if (ajax_path.constructor.toString().indexOf("Array") != -1) { 1210 ajax_path = ajax_path[0]; 1211 } 1212 1213 // Check if there are any GET parameters to send to views. 1214 var queryString = window.location.search || ''; 1215 if (queryString !== '') { 1216 // Remove the question mark and Drupal path component if any. 1217 var queryString = queryString.slice(1).replace(/q=[^&]+&?|&?render=[^&]+/, ''); 1218 if (queryString !== '') { 1219 // If there is a '?' in ajax_path, clean url are on and & should be 1220 // used to add parameters. 1221 queryString = ((/\?/.test(ajax_path)) ? '&' : '?') + queryString; 1222 } 1223 } 1224 1225 this.element_settings = { 1226 url: ajax_path + queryString, 1227 submit: settings, 1228 setClick: true, 1229 event: 'click', 1230 selector: selector, 1231 progress: { 1232 type: 'throbber' 1233 } 1234 }; 1235 1236 this.settings = settings; 1237 1238 // Add the ajax to exposed forms. 1239 this.$exposed_form = $('#views-exposed-form-' + settings.view_name.replace(/_/g, '-') + '-' + settings.view_display_id.replace(/_/g, '-')); 1240 this.$exposed_form.once(jQuery.proxy(this.attachExposedFormAjax, this)); 1241 1242 // Store Drupal.ajax objects here for all pager links. 1243 this.links = []; 1244 1245 // Add the ajax to pagers. 1246 this.$view 1247 .once(jQuery.proxy(this.attachPagerAjax, this)); 1248 1249 // Add a trigger to update this view specifically. In order to trigger a 1250 // refresh use the following code. 1251 // 1252 // @code 1253 // jQuery('.view-name').trigger('RefreshView'); 1254 // @endcode 1255 // Add a trigger to update this view specifically. 1256 var self_settings = this.element_settings; 1257 self_settings.event = 'RefreshView'; 1258 var self = this; 1259 this.$view.once('refresh', function () { 1260 self.refreshViewAjax = new Drupal.ajax(self.selector, self.$view, self_settings); 1261 }); 1262 }; 1263 1264 Drupal.views.ajaxView.prototype.attachExposedFormAjax = function() { 1265 var button = $('input[type=submit], button[type=submit], input[type=image]', this.$exposed_form); 1266 button = button[0]; 1267 1268 // Call the autocomplete submit before doing AJAX. 1269 $(button).click(function () { 1270 if (Drupal.autocompleteSubmit) { 1271 Drupal.autocompleteSubmit(); 1272 } 1273 }); 1274 1275 this.exposedFormAjax = new Drupal.ajax($(button).attr('id'), button, this.element_settings); 1276 }; 1277 1278 /** 1279 * Attach the ajax behavior to each link. 1280 */ 1281 Drupal.views.ajaxView.prototype.attachPagerAjax = function() { 1282 this.$view.find('ul.pager > li > a, ol.pager > li > a, th.views-field a, .attachment .views-summary a') 1283 .each(jQuery.proxy(this.attachPagerLinkAjax, this)); 1284 }; 1285 1286 /** 1287 * Attach the ajax behavior to a single link. 1288 */ 1289 Drupal.views.ajaxView.prototype.attachPagerLinkAjax = function(id, link) { 1290 var $link = $(link); 1291 var viewData = {}; 1292 var href = $link.attr('href'); 1293 // Don't attach to pagers inside nested views. 1294 if ($link.closest('.view')[0] !== this.$view[0] && 1295 $link.closest('.view').parent().hasClass('attachment') === false) { 1296 return; 1297 } 1298 1299 // Provide a default page if none has been set. This must be done 1300 // prior to merging with settings to avoid accidentally using the 1301 // page landed on instead of page 1. 1302 if (typeof(viewData.page) === 'undefined') { 1303 viewData.page = 0; 1304 } 1305 1306 // Construct an object using the settings defaults and then overriding 1307 // with data specific to the link. 1308 $.extend( 1309 viewData, 1310 this.settings, 1311 Drupal.Views.parseQueryString(href), 1312 // Extract argument data from the URL. 1313 Drupal.Views.parseViewArgs(href, this.settings.view_base_path) 1314 ); 1315
1316 // For anchor tags, these will go to the target of the anchor rather 1317 // than the usual location. 1318 $.extend(viewData, Drupal.Views.parseViewArgs(href, this.settings.view_base_path)); 1319 1320 // Construct an object using the element settings defaults, 1321 // then overriding submit with viewData. 1322 var pager_settings = $.extend({}, this.element_settings); 1323 pager_settings.submit = viewData; 1324 this.pagerAjax = new Drupal.ajax(false, $link, pager_settings); 1325 this.links.push(this.pagerAjax); 1326 }; 1327 1328 Drupal.ajax.prototype.commands.viewsScrollTop = function (ajax, response, status) { 1329 // Scroll to the top of the view. This will allow users 1330 // to browse newly loaded content after e.g. clicking a pager 1331 // link. 1332 var offset = $(response.selector).offset(); 1333 // We can't guarantee that the scrollable object should be 1334 // the body, as the view could be embedded in something 1335 // more complex such as a modal popup. Recurse up the DOM 1336 // and scroll the first element that has a non-zero top. 1337 var scrollTarget = response.selector; 1338 while ($(scrollTarget).scrollTop() == 0 && $(scrollTarget).parent()) { 1339 scrollTarget = $(scrollTarget).parent(); 1340 } 1341 // Only scroll upward. 1342 if (offset.top - 10 < $(scrollTarget).scrollTop()) { 1343 $(scrollTarget).animate({scrollTop: (offset.top - 10)}, 500); 1344 } 1345 }; 1346 1347})(jQuery); 1348;
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.