1(function ($) { 2 3 Drupal.behaviors.esi = { 4 attach: function (context, settings) { 5 var contextualize_urls = (typeof Drupal.settings.ESI !== 'undefined') && Drupal.settings.ESI.contextualize_URLs; 6 if (contextualize_urls) { 7 $().esiTags().DrupalESIContextualizeURL().handleESI(); 8 } 9 else { 10 $().esiTags().handleESI(); 11 } 12 } 13 }; 14 15 // Add the ESI context from the cookie to the ESI URL. 16 $.fn.DrupalESIContextualizeURL = function() { 17 // Regex to find the ESI context from an ESI src URL. 18 var esi_context_regexp = /\/CACHE%3D([^\/]+)$/; 19 20 $(this).each(function() { 21 src = $(this).attr('src'); 22 23 // Get the context key from the ESI src attribute. 24 // Contexts are provided in the last element: e.g. /CACHE=FOO. 25 if (match = $(this).attr('src').match(esi_context_regexp)) { 26 context_key = match[1]; 27 context_val = esi_get_user_context(context_key); 28 new_src = src.replace(esi_context_regexp, '/CACHE=' + context_val); 29 $(this).attr('src', new_src); 30 } 31 }); 32 return this; 33 } 34 35 // Attach to ajax request, find block that was created and 36 // attachBehaviours to it. 37 $( document ).ajaxSuccess(function( event, request, settings ) { 38 if (event.target.nodeName.toLowerCase() == 'esi:include') { 39 var returned = $('<div></div>').html($.trim(request.responseText)).contents(); 40 if (returned[0] !== undefined) { 41 var elem = $('#' + returned[0].id); 42 if (elem !== undefined) { 43 Drupal.attachBehaviors(elem[0], Drupal.settings); 44 } 45 } 46 } 47 }); 48 49})(jQuery); 50 51/** 52 * Get the value of a particular context. 53 */ 54function esi_get_user_context(context_key) { 55 // Transform a context string (e.g. 'USER') into the actual cookie name. 56 cookie_name = Drupal.settings.ESI.cookie_prefix + context_key + Drupal.settings.ESI.cookie_suffix; 57 return esi_get_value_from_cookie(cookie_name); 58} 59 60/** 61 * Get the value of a named cookie. 62 */ 63function esi_get_value_from_cookie(cookie_name) { 64 var all_cookies = document.cookie.split(';'); 65 for (var i=0; i < all_cookies.length; i++) { 66 if (all_cookies[i].indexOf(cookie_name) === 1) { 67 var values = all_cookies[i].split('='); 68 return values[1]; 69 } 70 } 71 return ''; 72} 73; 74/** 75 * jQuery ESI Plugin v1.0 76 * http://github.com/manarth/jquery_esi 77 * 78 * Dual licensed under the MIT and GPL licenses: 79 * http://www.opensource.org/licenses/mit-license.php 80 * http://www.gnu.org/licenses/gpl.html 81 * 82 * Usage: 83 * $().esiTags().handleESI(); 84 */ 85 86(function ($) { 87 88 // Fetch all ESI tags. 89 $.fn.esiTags = function() { 90 // Initialise a jQuery collection to return. 91 esi_tags = $(); 92 93 // The handler can be called as $().esiTags, or $('foo').esiTags(). 94 // Ensure we have an iterable collection in either instance. 95 if (this.length == 0) { 96 collection = $('html'); 97 } 98 else { 99 collection = this; 100 } 101 102 collection.each(function() { 103 // Retrieve the base DOM element, in order to access the DOM method 104 // getElementsByTagName. 105 base_element = $(this).get(0); 106 // Discover the <esi:include> tags. 107 jQuery.each(base_element.getElementsByTagName('esi:include'), function(i, val) { 108 // Some DOMs fail to recognise that the ESI include tag is self- 109 // closing, so they treat following tags as child nodes. 110 if ($(this).children().length > 0) { 111 // Move the child nodes to become siblings. 112 children = $(this).children().detach(); 113 $(this).after(children); 114 } 115 esi_tags = esi_tags.add($(this)); 116 }); 117 // Discover the <esi:remove> tags. 118 jQuery.each(base_element.getElementsByTagName('esi:remove'), function(i, val) { 119 // ESI remove tags are not self-closing, so no special treatment for 120 // child nodes is needed. 121 esi_tags = esi_tags.add($(this)); 122 }); 123 }); 124 return esi_tags; 125 }; 126 127 // Move child nodes to siblings. 128 $.fn.handleESIChildren = function() { 129 $(this).each(function() { 130 }); 131 return this; 132 } 133 134 // Handle ESI tags. 135 // Delegates to either .handleESIInclude() or .handleESIRemove() as needed. 136 $.fn.handleESI = function() { 137 $(this).each(function() { 138 switch (this.nodeName.toLowerCase()) { 139 case 'esi:include': 140 $(this).handleESIInclude(); 141 break; 142 143 case 'esi:remove': 144 $(this).handleESIRemove(); 145 break; 146 } 147 }); 148 149 return this; 150 } 151 152 // Handle a single ESI Include tag. 153 $.fn.handleESIInclude = function() { 154 src = $(this).attr('src'); 155 jQuery.ajax({ 156 context: this, 157 headers: { 158 'X-ESI-REFERER-ORIGINAL' : document.referrer 159 }, 160 url: src, 161 success: function(data, textStatus, jqXHR) { 162 esiElement = $(this); 163 esiElement.replaceWith(data); 164 } 165 }); 166 } 167 168 // Handle a single ESI Remove tag. 169 $.fn.handleESIRemove = function() { 170 $(this).replaceWith(''); 171 } 172 173})(jQuery); 174; 175/** 176 * @file 177 * Attaches the calendar behavior to all required fields. 178 */ 179 180(function ($) { 181 function makeFocusHandler(e) { 182 if (!$(this).hasClass('date-popup-init')) { 183 var datePopup = e.data; 184 // Explicitely filter the methods we accept. 185 switch (datePopup.func) { 186 case 'datepicker': 187 $(this) 188 .datepicker(datePopup.settings) 189 .addClass('date-popup-init') 190 .focus(); 191 if (datePopup.settings.syncEndDate) {
192 $('.start-date-wrapper').each(function () { 193 var start_date_wrapper = this; 194 $(this).find('input:eq(0)').change(function () { 195 $(start_date_wrapper).next('.end-date-wrapper').find('input:eq(0)').val($(this).val()); 196 }); 197 }); 198 } 199 break; 200 201 case 'timeEntry': 202 $(this) 203 .timeEntry(datePopup.settings) 204 .addClass('date-popup-init') 205 .focus(); 206 break; 207 208 case 'timepicker': 209 // Translate the PHP date format into the style the timepicker uses. 210 datePopup.settings.timeFormat = datePopup.settings.timeFormat 211 // 12-hour, leading zero, 212 .replace('h', 'hh') 213 // 12-hour, no leading zero. 214 .replace('g', 'h') 215 // 24-hour, leading zero. 216 .replace('H', 'HH') 217 // 24-hour, no leading zero. 218 .replace('G', 'H') 219 // AM/PM. 220 .replace('A', 'p') 221 // Minutes with leading zero. 222 .replace('i', 'mm') 223 // Seconds with leading zero. 224 .replace('s', 'ss'); 225 226 datePopup.settings.startTime = new Date(datePopup.settings.startTime); 227 $(this) 228 .timepicker(datePopup.settings) 229 .addClass('date-popup-init') 230 .focus(); 231 break; 232 } 233 } 234 } 235 236 Drupal.behaviors.date_popup = { 237 attach: function (context) { 238 for (var id in Drupal.settings.datePopup) { 239 $('#' + id).bind('focus', Drupal.settings.datePopup[id], makeFocusHandler); 240 } 241 } 242 }; 243 244})(jQuery); 245; 246/** 247 * @file 248 */ 249 250(function ($) { 251 252 'use strict'; 253 254 Drupal.extlink = Drupal.extlink || {}; 255 256 Drupal.extlink.attach = function (context, settings) { 257 if (!settings.hasOwnProperty('extlink')) { 258 return; 259 } 260 261 // Strip the host name down, removing ports, subdomains, or www. 262 var pattern = /^(([^\/:]+?\.)*)([^\.:]{1,})((\.[a-z0-9]{1,253})*)(:[0-9]{1,5})?$/; 263 var host = window.location.host.replace(pattern, '$2$3$6'); 264 var subdomain = window.location.host.replace(host, ''); 265 266 // Determine what subdomains are considered internal. 267 var subdomains; 268 if (settings.extlink.extSubdomains) { 269 subdomains = '([^/]*\\.)?'; 270 } 271 else if (subdomain === 'www.' || subdomain === '') { 272 subdomains = '(www\\.)?'; 273 } 274 else { 275 subdomains = subdomain.replace('.', '\\.'); 276 } 277 278 // Build regular expressions that define an internal link. 279 var internal_link = new RegExp('^https?://([^@]*@)?' + subdomains + host, 'i'); 280 281 // Extra internal link matching. 282 var extInclude = false; 283 if (settings.extlink.extInclude) { 284 extInclude = new RegExp(settings.extlink.extInclude.replace(/\\/, '\\'), 'i'); 285 } 286 287 // Extra external link matching. 288 var extExclude = false; 289 if (settings.extlink.extExclude) { 290 extExclude = new RegExp(settings.extlink.extExclude.replace(/\\/, '\\'), 'i'); 291 } 292 293 // Extra external link CSS selector exclusion. 294 var extCssExclude = false; 295 if (settings.extlink.extCssExclude) { 296 extCssExclude = settings.extlink.extCssExclude; 297 } 298 299 // Extra external link CSS selector explicit. 300 var extCssExplicit = false; 301 if (settings.extlink.extCssExplicit) { 302 extCssExplicit = settings.extlink.extCssExplicit; 303 } 304 305 // Define the jQuery method (either 'append' or 'prepend') of placing the icon, defaults to 'append'. 306 var extIconPlacement = settings.extlink.extIconPlacement || 'append'; 307 308 // Find all links which are NOT internal and begin with http as opposed 309 // to ftp://, javascript:, etc. other kinds of links. 310 // When operating on the 'this' variable, the host has been appended to 311 // all links by the browser, even local ones. 312 // In jQuery 1.1 and higher, we'd use a filter method here, but it is not 313 // available in jQuery 1.0 (Drupal 5 default). 314 var external_links = []; 315 var mailto_links = []; 316 $('a:not([data-extlink]), area:not([data-extlink])', context).each(function (el) {
317 try { 318 var url = ''; 319 if (typeof this.href == 'string') { 320 url = this.href.toLowerCase(); 321 } 322 // Handle SVG links (xlink:href). 323 else if (typeof this.href == 'object') { 324 url = this.href.baseVal; 325 } 326 if (url.indexOf('http') === 0 327 && ((!url.match(internal_link) && !(extExclude && url.match(extExclude))) || (extInclude && url.match(extInclude))) 328 && !(extCssExclude && $(this).is(extCssExclude)) 329 && !(extCssExclude && $(this).parents(extCssExclude).length > 0) 330 && !(extCssExplicit && $(this).parents(extCssExplicit).length < 1)) { 331 external_links.push(this); 332 } 333 // Do not include area tags with begin with mailto: (this prohibits 334 // icons from being added to image-maps). 335 else if (this.tagName !== 'AREA' 336 && url.indexOf('mailto:') === 0 337 && !(extCssExclude && $(this).parents(extCssExclude).length > 0) 338 && !(extCssExplicit && $(this).parents(extCssExplicit).length < 1)) { 339 mailto_links.push(this); 340 } 341 } 342 // IE7 throws errors often when dealing with irregular links, such as: 343 // <a href="node/10"></a> Empty tags. 344 // <a href="http://user:[email protected]">example</a> User:pass syntax. 345 catch (error) { 346 return false; 347 } 348 }); 349 350 if (settings.extlink.extClass) { 351 Drupal.extlink.applyClassAndSpan(external_links, settings.extlink.extClass, extIconPlacement); 352 } 353 354 if (settings.extlink.mailtoClass) { 355 Drupal.extlink.applyClassAndSpan(mailto_links, settings.extlink.mailtoClass, extIconPlacement); 356 } 357 358 if (settings.extlink.extTarget) { 359 // Apply the target attribute to all links. 360 $(external_links).attr('target', settings.extlink.extTarget); 361 // Add rel attributes noopener and noreferrer. 362 $(external_links).attr('rel', function (i, val) { 363 // If no rel attribute is present, create one with the values noopener and noreferrer. 364 if (val == null) { 365 return 'noopener noreferrer'; 366 } 367 // Check to see if rel contains noopener or noreferrer. Add what doesn't exist. 368 if (val.indexOf('noopener') > -1 || val.indexOf('noreferrer') > -1) { 369 if (val.indexOf('noopener') === -1) { 370 return val + ' noopener'; 371 } 372 if (val.indexOf('noreferrer') === -1) { 373 return val + ' noreferrer'; 374 } 375 // Both noopener and noreferrer exist. Nothing needs to be added. 376 else { 377 return val; 378 } 379 } 380 // Else, append noopener and noreferrer to val. 381 else { 382 return val + ' noopener noreferrer'; 383 } 384 }); 385 } 386 387 Drupal.extlink = Drupal.extlink || {}; 388 389 // Set up default click function for the external links popup. This should be 390 // overridden by modules wanting to alter the popup. 391 Drupal.extlink.popupClickHandler = Drupal.extlink.popupClickHandler || function () { 392 if (settings.extlink.extAlert) { 393 return confirm(settings.extlink.extAlertText); 394 } 395 }; 396 397 $(external_links).click(function (e) { 398 return Drupal.extlink.popupClickHandler(e, this); 399 }); 400 }; 401 402 /** 403 * Apply a class and a trailing <span> to all links not containing images. 404 * 405 * @param {object[]} links 406 * An array of DOM elements representing the links. 407 * @param {string} class_name 408 * The class to apply to the links. 409 * @param {string} icon_placement 410 * 'append' or 'prepend' the icon to the link. 411 */ 412 Drupal.extlink.applyClassAndSpan = function (links, class_name, icon_placement) { 413 var $links_to_process; 414 if (Drupal.settings.extlink.extImgClass) { 415 $links_to_process = $(links); 416 } 417 else { 418 var links_with_images = $(links).find('img').parents('a'); 419 $links_to_process = $(links).not(links_with_images); 420 } 421 // Add data-extlink attribute. 422 $links_to_process.attr('data-extlink', ''); 423 var i; 424 var length = $links_to_process.length; 425 for (i = 0; i < length; i++) { 426 var $link = $($links_to_process[i]);
427 if ($link.css('display') === 'inline' || $link.css('display') === 'inline-block' || $link.css('display') === 'block') { 428 if (Drupal.settings.extlink.extUseFontAwesome) { 429 if (class_name === Drupal.settings.extlink.mailtoClass) { 430 $link[icon_placement]('<span class="fa-' + class_name + ' extlink"><span class="fa fa-envelope-o" title="' + Drupal.settings.extlink.mailtoLabel + '"></span><span class="element-invisible">' + Drupal.settings.extlink.mailtoLabel + '</span></span>'); 431 } 432 else { 433 $link[icon_placement]('<span class="fa-' + class_name + ' extlink"><span class="fa fa-external-link" title="' + Drupal.settings.extlink.extLabel + '"></span><span class="element-invisible">' + Drupal.settings.extlink.extLabel + '</span></span>'); 434 } 435 } 436 else { 437 if (class_name === Drupal.settings.extlink.mailtoClass) { 438 $link[icon_placement]('<span class="' + class_name + '"><span class="element-invisible">' + Drupal.settings.extlink.mailtoLabel + '</span></span>'); 439 } 440 else { 441 $link[icon_placement]('<span class="' + class_name + '"><span class="element-invisible">' + Drupal.settings.extlink.extLabel + '</span></span>'); 442 } 443 } 444 } 445 } 446 }; 447 448 Drupal.behaviors.extlink = Drupal.behaviors.extlink || {}; 449 Drupal.behaviors.extlink.attach = function (context, settings) { 450 // Backwards compatibility, for the benefit of modules overriding extlink 451 // functionality by defining an "extlinkAttach" global function. 452 if (typeof extlinkAttach === 'function') { 453 extlinkAttach(context); 454 } 455 else { 456 Drupal.extlink.attach(context, settings); 457 } 458 }; 459 460})(jQuery); 461; 462(function ($) { 463 464/** 465 * A progressbar object. Initialized with the given id. Must be inserted into 466 * the DOM afterwards through progressBar.element. 467 * 468 * method is the function which will perform the HTTP request to get the 469 * progress bar state. Either "GET" or "POST". 470 * 471 * e.g. pb = new progressBar('myProgressBar'); 472 * some_element.appendChild(pb.element); 473 */ 474Drupal.progressBar = function (id, updateCallback, method, errorCallback) { 475 var pb = this; 476 this.id = id; 477 this.method = method || 'GET'; 478 this.updateCallback = updateCallback; 479 this.errorCallback = errorCallback; 480 481 // The WAI-ARIA setting aria-live="polite" will announce changes after users 482 // have completed their current activity and not interrupt the screen reader. 483 this.element = $('<div class="progress" aria-live="polite"></div>').attr('id', id); 484 this.element.html('<div class="bar"><div class="filled"></div></div>' + 485 '<div class="percentage"></div>' + 486 '<div class="message"> </div>'); 487}; 488 489/** 490 * Set the percentage and status message for the progressbar. 491 */ 492Drupal.progressBar.prototype.setProgress = function (percentage, message) { 493 if (percentage >= 0 && percentage <= 100) { 494 $('div.filled', this.element).css('width', percentage + '%'); 495 $('div.percentage', this.element).html(percentage + '%'); 496 } 497 $('div.message', this.element).html(message); 498 if (this.updateCallback) { 499 this.updateCallback(percentage, message, this); 500 } 501}; 502 503/** 504 * Start monitoring progress via Ajax. 505 */ 506Drupal.progressBar.prototype.startMonitoring = function (uri, delay) { 507 this.delay = delay; 508 this.uri = uri; 509 this.sendPing(); 510}; 511 512/** 513 * Stop monitoring progress via Ajax. 514 */ 515Drupal.progressBar.prototype.stopMonitoring = function () { 516 clearTimeout(this.timer); 517 // This allows monitoring to be stopped from within the callback. 518 this.uri = null; 519}; 520 521/** 522 * Request progress data from server. 523 */ 524Drupal.progressBar.prototype.sendPing = function () { 525 if (this.timer) { 526 clearTimeout(this.timer); 527 } 528 if (this.uri) { 529 var pb = this; 530 // When doing a post request, you need non-null data. Otherwise a 531 // HTTP 411 or HTTP 406 (with Apache mod_security) error may result. 532 $.ajax({ 533 type: this.method, 534 url: this.uri, 535 data: '', 536 dataType: 'json', 537 success: function (progress) { 538 // Display errors. 539 if (progress.status == 0) { 540 pb.displayError(progress.data); 541 return; 542 } 543 // Update display. 544 pb.setProgress(progress.percentage, progress.message); 545 // Schedule next timer.
546 pb.timer = setTimeout(function () { pb.sendPing(); }, pb.delay); 547 }, 548 error: function (xmlhttp) { 549 pb.displayError(Drupal.ajaxError(xmlhttp, pb.uri)); 550 } 551 }); 552 } 553}; 554 555/** 556 * Display errors on the page. 557 */ 558Drupal.progressBar.prototype.displayError = function (string) { 559 var error = $('<div class="messages error"></div>').html(string); 560 $(this.element).before(error).hide(); 561 562 if (this.errorCallback) { 563 this.errorCallback(this); 564 } 565}; 566 567})(jQuery); 568; 569/** 570 * @file 571 * 572 * Implement a modal form. 573 * 574 * @see modal.inc for documentation. 575 * 576 * This javascript relies on the CTools ajax responder. 577 */ 578 579(function ($) { 580 // Make sure our objects are defined. 581 Drupal.CTools = Drupal.CTools || {}; 582 Drupal.CTools.Modal = Drupal.CTools.Modal || {}; 583 584 /** 585 * Display the modal 586 * 587 * @todo -- document the settings. 588 */ 589 Drupal.CTools.Modal.show = function(choice) { 590 var opts = {}; 591 592 if (choice && typeof choice == 'string' && Drupal.settings[choice]) { 593 // This notation guarantees we are actually copying it. 594 $.extend(true, opts, Drupal.settings[choice]); 595 } 596 else if (choice) { 597 $.extend(true, opts, choice); 598 } 599 600 var defaults = { 601 modalTheme: 'CToolsModalDialog', 602 throbberTheme: 'CToolsModalThrobber', 603 animation: 'show', 604 animationSpeed: 'fast', 605 modalSize: { 606 type: 'scale', 607 width: .8, 608 height: .8, 609 addWidth: 0, 610 addHeight: 0, 611 // How much to remove from the inner content to make space for the 612 // theming. 613 contentRight: 25, 614 contentBottom: 45 615 }, 616 modalOptions: { 617 opacity: .55, 618 background: '#fff' 619 }, 620 modalClass: 'default' 621 }; 622 623 var settings = {}; 624 $.extend(true, settings, defaults, Drupal.settings.CToolsModal, opts); 625 626 if (Drupal.CTools.Modal.currentSettings && Drupal.CTools.Modal.currentSettings != settings) { 627 Drupal.CTools.Modal.modal.remove(); 628 Drupal.CTools.Modal.modal = null; 629 } 630 631 Drupal.CTools.Modal.currentSettings = settings; 632 633 var resize = function(e) { 634 // When creating the modal, it actually exists only in a theoretical 635 // place that is not in the DOM. But once the modal exists, it is in the 636 // DOM so the context must be set appropriately. 637 var context = e ? document : Drupal.CTools.Modal.modal; 638 639 if (Drupal.CTools.Modal.currentSettings.modalSize.type == 'scale') { 640 var width = $(window).width() * Drupal.CTools.Modal.currentSettings.modalSize.width; 641 var height = $(window).height() * Drupal.CTools.Modal.currentSettings.modalSize.height; 642 } 643 else { 644 var width = Drupal.CTools.Modal.currentSettings.modalSize.width; 645 var height = Drupal.CTools.Modal.currentSettings.modalSize.height; 646 } 647 648 // Use the additionol pixels for creating the width and height. 649 $('div.ctools-modal-content', context).css({ 650 'width': width + Drupal.CTools.Modal.currentSettings.modalSize.addWidth + 'px', 651 'height': height + Drupal.CTools.Modal.currentSettings.modalSize.addHeight + 'px' 652 }); 653 $('div.ctools-modal-content .modal-content', context).css({ 654 'width': (width - Drupal.CTools.Modal.currentSettings.modalSize.contentRight) + 'px', 655 'height': (height - Drupal.CTools.Modal.currentSettings.modalSize.contentBottom) + 'px' 656 }); 657 }; 658 659 if (!Drupal.CTools.Modal.modal) { 660 Drupal.CTools.Modal.modal = $(Drupal.theme(settings.modalTheme)); 661 if (settings.modalSize.type == 'scale') { 662 $(window).bind('resize', resize); 663 } 664 } 665 666 resize(); 667 668 $('span.modal-title', Drupal.CTools.Modal.modal).html(Drupal.CTools.Modal.currentSettings.loadingText); 669 Drupal.CTools.Modal.modalContent(Drupal.CTools.Modal.modal, settings.modalOptions, settings.animation, settings.animationSpeed, settings.modalClass); 670 $('#modalContent .modal-content').html(Drupal.theme(settings.throbberTheme)).addClass('ctools-modal-loading'); 671 672 // Position autocomplete results based on the scroll position of the modal.
673 $('#modalContent .modal-content').delegate('input.form-autocomplete', 'keyup', function() { 674 $('#autocomplete').css('top', $(this).position().top + $(this).outerHeight() + $(this).offsetParent().filter('#modal-content').scrollTop()); 675 }); 676 }; 677 678 /** 679 * Hide the modal 680 */ 681 Drupal.CTools.Modal.dismiss = function() { 682 if (Drupal.CTools.Modal.modal) { 683 Drupal.CTools.Modal.unmodalContent(Drupal.CTools.Modal.modal); 684 } 685 }; 686 687 /** 688 * Provide the HTML to create the modal dialog. 689 */ 690 Drupal.theme.prototype.CToolsModalDialog = function () { 691 var html = ''; 692 html += '<div id="ctools-modal">'; 693 html += ' <div class="ctools-modal-content">'; // panels-modal-content 694 html += ' <div class="modal-header">'; 695 html += ' <a class="close" href="#">'; 696 html += Drupal.CTools.Modal.currentSettings.closeText + Drupal.CTools.Modal.currentSettings.closeImage; 697 html += ' </a>'; 698 html += ' <span id="modal-title" class="modal-title"> </span>'; 699 html += ' </div>'; 700 html += ' <div id="modal-content" class="modal-content">'; 701 html += ' </div>'; 702 html += ' </div>'; 703 html += '</div>'; 704 705 return html; 706 }; 707 708 /** 709 * Provide the HTML to create the throbber. 710 */ 711 Drupal.theme.prototype.CToolsModalThrobber = function () { 712 var html = ''; 713 html += '<div id="modal-throbber">'; 714 html += ' <div class="modal-throbber-wrapper">'; 715 html += Drupal.CTools.Modal.currentSettings.throbber; 716 html += ' </div>'; 717 html += '</div>'; 718 719 return html; 720 }; 721 722 /** 723 * Figure out what settings string to use to display a modal. 724 */ 725 Drupal.CTools.Modal.getSettings = function (object) { 726 var match = $(object).attr('class').match(/ctools-modal-(\S+)/); 727 if (match) { 728 return match[1]; 729 } 730 }; 731 732 /** 733 * Click function for modals that can be cached. 734 */ 735 Drupal.CTools.Modal.clickAjaxCacheLink = function () { 736 Drupal.CTools.Modal.show(Drupal.CTools.Modal.getSettings(this)); 737 return Drupal.CTools.AJAX.clickAJAXCacheLink.apply(this); 738 }; 739 740 /** 741 * Handler to prepare the modal for the response 742 */ 743 Drupal.CTools.Modal.clickAjaxLink = function () { 744 Drupal.CTools.Modal.show(Drupal.CTools.Modal.getSettings(this)); 745 return false; 746 }; 747 748 /** 749 * Submit responder to do an AJAX submit on all modal forms. 750 */ 751 Drupal.CTools.Modal.submitAjaxForm = function(e) { 752 var $form = $(this); 753 var url = $form.attr('action'); 754 755 setTimeout(function() { Drupal.CTools.AJAX.ajaxSubmit($form, url); }, 1); 756 return false; 757 }; 758 759 /** 760 * Bind links that will open modals to the appropriate function. 761 */ 762 Drupal.behaviors.ZZCToolsModal = { 763 attach: function(context) { 764 // Bind links 765 // Note that doing so in this order means that the two classes can be 766 // used together safely. 767 /* 768 * @todo remimplement the warm caching feature 769 $('a.ctools-use-modal-cache', context).once('ctools-use-modal', function() { 770 $(this).click(Drupal.CTools.Modal.clickAjaxCacheLink); 771 Drupal.CTools.AJAX.warmCache.apply(this); 772 }); 773 */ 774 775 $('area.ctools-use-modal, a.ctools-use-modal', context).once('ctools-use-modal', function() { 776 var $this = $(this); 777 $this.click(Drupal.CTools.Modal.clickAjaxLink); 778 // Create a drupal ajax object 779 var element_settings = {}; 780 if ($this.attr('href')) { 781 element_settings.url = $this.attr('href'); 782 element_settings.event = 'click'; 783 element_settings.progress = { type: 'throbber' }; 784 } 785 var base = $this.attr('href'); 786 Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings); 787 }); 788 789 // Bind buttons 790 $('input.ctools-use-modal, button.ctools-use-modal', context).once('ctools-use-modal', function() { 791 var $this = $(this); 792 $this.click(Drupal.CTools.Modal.clickAjaxLink); 793 var button = this; 794 var element_settings = {}; 795 796 // AJAX submits specified in this manner automatically submit to the 797 // normal form action. 798 element_settings.url = Drupal.CTools.Modal.findURL(this); 799 if (element_settings.url == '') { 800 element_settings.url = $(this).closest('form').attr('action'); 801 } 802 element_settings.event = 'click'; 803 element_settings.setClick = true; 804 805 var base = $this.attr('id'); 806 Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings); 807 808 // Make sure changes to settings are reflected in the URL. 809 $('.' + $(button).attr('id') + '-url').change(function() { 810 Drupal.ajax[base].options.url = Drupal.CTools.Modal.findURL(button); 811 }); 812 }); 813 814 // Bind our custom event to the form submit
815 $('#modal-content form', context).once('ctools-use-modal', function() { 816 var $this = $(this); 817 var element_settings = {}; 818 819 element_settings.url = $this.attr('action'); 820 element_settings.event = 'submit'; 821 element_settings.progress = { 'type': 'throbber' }; 822 var base = $this.attr('id'); 823 824 Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings); 825 Drupal.ajax[base].form = $this; 826 827 $('input[type=submit], button', this).click(function(event) { 828 Drupal.ajax[base].element = this; 829 this.form.clk = this; 830 // Stop autocomplete from submitting. 831 if (Drupal.autocompleteSubmit && !Drupal.autocompleteSubmit()) { 832 return false; 833 } 834 // An empty event means we were triggered via .click() and 835 // in jquery 1.4 this won't trigger a submit. 836 // We also have to check jQuery version to prevent 837 // IE8 + jQuery 1.4.4 to break on other events 838 // bound to the submit button. 839 if (jQuery.fn.jquery.substr(0, 3) === '1.4' && typeof event.bubbles === "undefined") { 840 $(this.form).trigger('submit'); 841 return false; 842 } 843 }); 844 }); 845 846 // Bind a click handler to allow elements with the 'ctools-close-modal' 847 // class to close the modal. 848 $('.ctools-close-modal', context).once('ctools-close-modal') 849 .click(function() { 850 Drupal.CTools.Modal.dismiss(); 851 return false; 852 }); 853 } 854 }; 855 856 // The following are implementations of AJAX responder commands. 857 858 /** 859 * AJAX responder command to place HTML within the modal. 860 */ 861 Drupal.CTools.Modal.modal_display = function(ajax, response, status) { 862 var settings = response.settings || ajax.settings || Drupal.settings; 863 // If the modal does not exist yet, create it. 864 if ($('#modalContent').length == 0) { 865 Drupal.CTools.Modal.show(Drupal.CTools.Modal.getSettings(ajax.element)); 866 } 867 // If the modal exists run detachBehaviors before removing existing content. 868 else { 869 Drupal.detachBehaviors($('#modalContent'), settings, 'unload'); 870 } 871 $('#modal-title').html(response.title); 872 // Simulate an actual page load by scrolling to the top after adding the 873 // content. This is helpful for allowing users to see error messages at the 874 // top of a form, etc. 875 $('#modal-content').html(response.output).scrollTop(0); 876 $(document).trigger('CToolsAttachBehaviors', $('#modalContent')); 877 878 // Attach behaviors within a modal dialog. 879 Drupal.attachBehaviors($('#modalContent'), settings); 880 881 if ($('#modal-content').hasClass('ctools-modal-loading')) { 882 $('#modal-content').removeClass('ctools-modal-loading'); 883 } 884 else { 885 // If the modal was already shown, and we are simply replacing its 886 // content, then focus on the first focusable element in the modal. 887 // (When first showing the modal, focus will be placed on the close 888 // button by the show() function called above.) 889 $('#modal-content :focusable:first').focus(); 890 } 891 892 // Force all attached animations to finish, and trigger a window resize 893 // event to center te modal after the ajax content has been added. 894 $('#modalContent').stop(true, true); 895 $(window).resize(); 896 }; 897 898 /** 899 * AJAX responder command to dismiss the modal. 900 */ 901 Drupal.CTools.Modal.modal_dismiss = function(command) { 902 Drupal.CTools.Modal.dismiss(); 903 $('link.ctools-temporary-css').remove(); 904 }; 905 906 /** 907 * Display loading 908 */ 909 //Drupal.CTools.AJAX.commands.modal_loading = function(command) { 910 Drupal.CTools.Modal.modal_loading = function(command) { 911 Drupal.CTools.Modal.modal_display({ 912 output: Drupal.theme(Drupal.CTools.Modal.currentSettings.throbberTheme), 913 title: Drupal.CTools.Modal.currentSettings.loadingText 914 }); 915 }; 916 917 /** 918 * Find a URL for an AJAX button. 919 * 920 * The URL for this gadget will be composed of the values of items by 921 * taking the ID of this item and adding -url and looking for that 922 * class. They need to be in the form in order since we will 923 * concat them all together using '/'. 924 */ 925 Drupal.CTools.Modal.findURL = function(item) { 926 var url = ''; 927 var url_class = '.' + $(item).attr('id') + '-url'; 928 $(url_class).each( 929 function() { 930 var $this = $(this); 931 if (url && $this.val()) { 932 url += '/'; 933 } 934 url += $this.val(); 935 }); 936 return url; 937 }; 938 939 940 /** 941 * modalContent 942 * @param content string to display in the content box 943 * @param css obj of css attributes 944 * @param animation (fadeIn, slideDown, show) 945 * @param speed (valid animation speeds slow, medium, fast or # in ms) 946 * @param modalClass class added to div#modalContent 947 */ 948 Drupal.CTools.Modal.modalContent = function(content, css, animation, speed, modalClass) { 949 // If our animation isn't set, make it just show/pop 950 if (!animation) { 951 animation = 'show'; 952 } 953 else { 954 // If our animation isn't "fadeIn" or "slideDown" then it always is show 955 if (animation != 'fadeIn' && animation != 'slideDown') { 956 animation = 'show'; 957 } 958 } 959 960 if (!speed && 0 !== speed) { 961 speed = 'fast'; 962 } 963 964 // Build our base attributes and allow them to be overriden 965 css = jQuery.extend({ 966 position: 'absolute', 967 left: '0px', 968 margin: '0px', 969 background: '#000', 970 opacity: '.55' 971 }, css); 972 973 // Add opacity handling for IE. 974 css.filter = 'alpha(opacity=' + (100 * css.opacity) + ')'; 975 content.hide(); 976 977 // If we already have modalContent, remove it. 978 if ($('#modalBackdrop').length) $('#modalBackdrop').remove(); 979 if ($('#modalContent').length) $('#modalContent').remove(); 980 981 // position code lifted from http://www.quirksmode.org/viewport/compatibility.html 982 if (self.pageYOffset) { // all except Explorer 983 var wt = self.pageYOffset; 984 } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict 985 var wt = document.documentElement.scrollTop; 986 } else if (document.body) { // all other Explorers 987 var wt = document.body.scrollTop; 988 } 989 990 // Get our dimensions 991 992 // Get the docHeight and (ugly hack) add 50 pixels to make sure we dont have a *visible* border below our div 993 var docHeight = $(document).height() + 50; 994 var docWidth = $(document).width(); 995 var winHeight = $(window).height(); 996 var winWidth = $(window).width();
997 if( docHeight < winHeight ) docHeight = winHeight; 998 999 // Create our divs 1000 $('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>'); 1001 1002 // Get a list of the tabbable elements in the modal content. 1003 var getTabbableElements = function () { 1004 var tabbableElements = $('#modalContent :tabbable'), 1005 radioButtons = tabbableElements.filter('input[type="radio"]'); 1006 1007 // The list of tabbable elements from jQuery is *almost* right. The 1008 // exception is with groups of radio buttons. The list from jQuery will 1009 // include all radio buttons, when in fact, only the selected radio button 1010 // is tabbable, and if no radio buttons in a group are selected, then only 1011 // the first is tabbable. 1012 if (radioButtons.length > 0) { 1013 // First, build up an index of which groups have an item selected or not. 1014 var anySelected = {}; 1015 radioButtons.each(function () { 1016 var name = this.name; 1017 1018 if (typeof anySelected[name] === 'undefined') { 1019 anySelected[name] = radioButtons.filter('input[name="' + name + '"]:checked').length !== 0; 1020 } 1021 }); 1022 1023 // Next filter out the radio buttons that aren't really tabbable. 1024 var found = {}; 1025 tabbableElements = tabbableElements.filter(function () { 1026 var keep = true; 1027 1028 if (this.type == 'radio') { 1029 if (anySelected[this.name]) { 1030 // Only keep the selected one. 1031 keep = this.checked; 1032 } 1033 else { 1034 // Only keep the first one. 1035 if (found[this.name]) { 1036 keep = false; 1037 } 1038 found[this.name] = true; 1039 } 1040 } 1041 1042 return keep; 1043 }); 1044 } 1045 1046 return tabbableElements.get(); 1047 }; 1048 1049 // Keyboard and focus event handler ensures only modal elements gain focus. 1050 modalEventHandler = function( event ) { 1051 target = null; 1052 if ( event ) { //Mozilla 1053 target = event.target; 1054 } else { //IE 1055 event = window.event; 1056 target = event.srcElement; 1057 } 1058 1059 var parents = $(target).parents().get(); 1060 for (var i = 0; i < parents.length; ++i) { 1061 var position = $(parents[i]).css('position'); 1062 if (position == 'absolute' || position == 'fixed') { 1063 return true; 1064 } 1065 } 1066 1067 if ($(target).is('#modalContent, body') || $(target).filter('*:visible').parents('#modalContent').length) { 1068 // Allow the event only if target is a visible child node 1069 // of #modalContent. 1070 return true; 1071 } 1072 else { 1073 getTabbableElements()[0].focus(); 1074 } 1075 1076 event.preventDefault(); 1077 }; 1078 $('body').bind( 'focus', modalEventHandler ); 1079 $('body').bind( 'keypress', modalEventHandler ); 1080 1081 // Keypress handler Ensures you can only TAB to elements within the modal. 1082 // Based on the psuedo-code from WAI-ARIA 1.0 Authoring Practices section 1083 // 3.3.1 "Trapping Focus". 1084 modalTabTrapHandler = function (evt) { 1085 // We only care about the TAB key. 1086 if (evt.which != 9) { 1087 return true; 1088 } 1089 1090 var tabbableElements = getTabbableElements(), 1091 firstTabbableElement = tabbableElements[0], 1092 lastTabbableElement = tabbableElements[tabbableElements.length - 1], 1093 singleTabbableElement = firstTabbableElement == lastTabbableElement, 1094 node = evt.target; 1095 1096 // If this is the first element and the user wants to go backwards, then 1097 // jump to the last element. 1098 if (node == firstTabbableElement && evt.shiftKey) { 1099 if (!singleTabbableElement) { 1100 lastTabbableElement.focus(); 1101 } 1102 return false; 1103 } 1104 // If this is the last element and the user wants to go forwards, then 1105 // jump to the first element. 1106 else if (node == lastTabbableElement && !evt.shiftKey) { 1107 if (!singleTabbableElement) { 1108 firstTabbableElement.focus(); 1109 } 1110 return false;
1111 } 1112 // If this element isn't in the dialog at all, then jump to the first 1113 // or last element to get the user into the game. 1114 else if ($.inArray(node, tabbableElements) == -1) { 1115 // Make sure the node isn't in another modal (ie. WYSIWYG modal). 1116 var parents = $(node).parents().get(); 1117 for (var i = 0; i < parents.length; ++i) { 1118 var position = $(parents[i]).css('position'); 1119 if (position == 'absolute' || position == 'fixed') { 1120 return true; 1121 } 1122 } 1123 1124 if (evt.shiftKey) { 1125 lastTabbableElement.focus(); 1126 } 1127 else { 1128 firstTabbableElement.focus(); 1129 } 1130 } 1131 }; 1132 $('body').bind('keydown', modalTabTrapHandler); 1133 1134 // Create our content div, get the dimensions, and hide it 1135 var modalContent = $('#modalContent').css('top','-1000px'); 1136 var $modalHeader = modalContent.find('.modal-header'); 1137 var mdcTop = wt + Math.max((winHeight / 2) - (modalContent.outerHeight() / 2), 0); 1138 var mdcLeft = ( winWidth / 2 ) - ( modalContent.outerWidth() / 2); 1139 $('#modalBackdrop').css(css).css('top', 0).css('height', docHeight + 'px').css('width', docWidth + 'px').show(); 1140 modalContent.css({top: mdcTop + 'px', left: mdcLeft + 'px'}).hide()[animation](speed); 1141 1142 // Bind a click for closing the modalContent 1143 modalContentClose = function(){close(); return false;}; 1144 $('.close', $modalHeader).bind('click', modalContentClose); 1145 1146 // Bind a keypress on escape for closing the modalContent 1147 modalEventEscapeCloseHandler = function(event) { 1148 if (event.keyCode == 27) { 1149 close(); 1150 return false; 1151 } 1152 }; 1153 1154 $(document).bind('keydown', modalEventEscapeCloseHandler); 1155 1156 // Per WAI-ARIA 1.0 Authoring Practices, initial focus should be on the 1157 // close button, but we should save the original focus to restore it after 1158 // the dialog is closed. 1159 var oldFocus = document.activeElement; 1160 $('.close', $modalHeader).focus(); 1161 1162 // Close the open modal content and backdrop 1163 function close() { 1164 // Unbind the events 1165 $(window).unbind('resize', modalContentResize); 1166 $('body').unbind( 'focus', modalEventHandler); 1167 $('body').unbind( 'keypress', modalEventHandler ); 1168 $('body').unbind( 'keydown', modalTabTrapHandler ); 1169 $('.close', $modalHeader).unbind('click', modalContentClose); 1170 $(document).unbind('keydown', modalEventEscapeCloseHandler); 1171 $(document).trigger('CToolsCloseModalBehaviors', $('#modalContent')); 1172 $(document).trigger('CToolsDetachBehaviors', $('#modalContent')); 1173 1174 // Closing animation. 1175 switch (animation) { 1176 case 'fadeIn': 1177 modalContent.fadeOut(speed, modalContentRemove); 1178 break; 1179 1180 case 'slideDown': 1181 modalContent.slideUp(speed, modalContentRemove); 1182 break; 1183 1184 case 'show': 1185 modalContent.hide(speed, modalContentRemove); 1186 break; 1187 } 1188 } 1189 1190 // Remove the content. 1191 modalContentRemove = function () { 1192 $('#modalContent').remove(); 1193 $('#modalBackdrop').remove(); 1194 1195 // Restore focus to where it was before opening the dialog. 1196 $(oldFocus).focus(); 1197 }; 1198 1199 // Move and resize the modalBackdrop and modalContent on window resize. 1200 modalContentResize = function () { 1201 // Reset the backdrop height/width to get accurate document size. 1202 $('#modalBackdrop').css('height', '').css('width', ''); 1203 1204 // Position code lifted from: 1205 // http://www.quirksmode.org/viewport/compatibility.html 1206 if (self.pageYOffset) { // all except Explorer 1207 var wt = self.pageYOffset; 1208 } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict 1209 var wt = document.documentElement.scrollTop; 1210 } else if (document.body) { // all other Explorers 1211 var wt = document.body.scrollTop; 1212 } 1213 1214 // Get our heights 1215 var docHeight = $(document).height(); 1216 var docWidth = $(document).width(); 1217 var winHeight = $(window).height(); 1218 var winWidth = $(window).width();
1219 if( docHeight < winHeight ) docHeight = winHeight; 1220 1221 // Get where we should move content to 1222 var modalContent = $('#modalContent'); 1223 var mdcTop = wt + Math.max((winHeight / 2) - (modalContent.outerHeight() / 2), 0); 1224 var mdcLeft = ( winWidth / 2 ) - ( modalContent.outerWidth() / 2); 1225 1226 // Apply the changes 1227 $('#modalBackdrop').css('height', docHeight + 'px').css('width', docWidth + 'px').show(); 1228 modalContent.css('top', mdcTop + 'px').css('left', mdcLeft + 'px').show(); 1229 }; 1230 $(window).bind('resize', modalContentResize); 1231 }; 1232 1233 /** 1234 * unmodalContent 1235 * @param content (The jQuery object to remove) 1236 * @param animation (fadeOut, slideUp, show) 1237 * @param speed (valid animation speeds slow, medium, fast or # in ms) 1238 */ 1239 Drupal.CTools.Modal.unmodalContent = function(content, animation, speed) 1240 { 1241 // If our animation isn't set, make it just show/pop 1242 if (!animation) { var animation = 'show'; } else { 1243 // If our animation isn't "fade" then it always is show 1244 if (( animation != 'fadeOut' ) && ( animation != 'slideUp')) animation = 'show'; 1245 } 1246 // Set a speed if we dont have one 1247 if ( !speed ) var speed = 'fast'; 1248 1249 // Unbind the events we bound 1250 $(window).unbind('resize', modalContentResize); 1251 $('body').unbind('focus', modalEventHandler); 1252 $('body').unbind('keypress', modalEventHandler); 1253 $('body').unbind( 'keydown', modalTabTrapHandler ); 1254 var $modalContent = $('#modalContent'); 1255 var $modalHeader = $modalContent.find('.modal-header'); 1256 $('.close', $modalHeader).unbind('click', modalContentClose); 1257 $(document).unbind('keydown', modalEventEscapeCloseHandler); 1258 $(document).trigger('CToolsDetachBehaviors', $modalContent); 1259 1260 // jQuery magic loop through the instances and run the animations or removal. 1261 content.each(function(){ 1262 if ( animation == 'fade' ) { 1263 $('#modalContent').fadeOut(speed, function() { 1264 $('#modalBackdrop').fadeOut(speed, function() { 1265 $(this).remove(); 1266 }); 1267 $(this).remove(); 1268 }); 1269 } else { 1270 if ( animation == 'slide' ) { 1271 $('#modalContent').slideUp(speed,function() { 1272 $('#modalBackdrop').slideUp(speed, function() { 1273 $(this).remove(); 1274 }); 1275 $(this).remove(); 1276 }); 1277 } else { 1278 $('#modalContent').remove(); 1279 $('#modalBackdrop').remove(); 1280 } 1281 } 1282 }); 1283 }; 1284 1285$(function() { 1286 Drupal.ajax.prototype.commands.modal_display = Drupal.CTools.Modal.modal_display; 1287 Drupal.ajax.prototype.commands.modal_dismiss = Drupal.CTools.Modal.modal_dismiss; 1288}); 1289 1290})(jQuery); 1291;
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.