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(function($,Drupal,drupalSettings){"use strict";function doSlick(i,elm){var t=$("> .slick__slider",elm).length?$("> .slick__slider",elm):$(elm);var a=$("> .slick__arrow",elm);var o=t.data("slick")?$.extend({},drupalSettings.slick,t.data("slick")):drupalSettings.slick;var r=$.type(o.responsive)==="array"&&o.responsive.length?o.responsive:false;var d=o.appendDots;var b;var isBlazy=o.lazyLoad==="blazy"&&Drupal.blazy;var unSlick=t.hasClass("unslick");if(!unSlick){o.appendDots=d===".slick__arrow"?a:d||$(t)}if(r){for(b in r){if(r.hasOwnProperty(b)&&r[b].settings!=="unslick"){r[b].settings=$.extend({},drupalSettings.slick,globals(o),r[b].settings)}}}t.data("slick",o);o=t.data("slick");function beforeSlick(){if(o.randomize&&!t.hasClass("slick-initiliazed")){randomize()}if(!unSlick){t.on("init.sl",function(e,slick){if(d===".slick__arrow"){$(slick.$dots).insertAfter(slick.$prevArrow)}var $src=t.find(".slick-cloned.slick-active .b-lazy:not(.b-loaded)");if(isBlazy&&$src.length){Drupal.blazy.init.load($src)}})}if(isBlazy){t.on("beforeChange.sl",function(){preloadBlazy(true)})}else{$(".media--loading",t).closest(".slide__content").addClass("is-loading")}t.on("setPosition.sl",function(e,slick){setPosition(slick)})}function preloadBlazy(ahead){if(t.find(".b-lazy:not(.b-loaded)").length){var $src=t.find(ahead?".slide:not(.slick-cloned) .b-lazy:not(.b-loaded)":".slick-active .b-lazy:not(.b-loaded)");if($src.length){Drupal.blazy.init.load($src)}}}function afterChange(){if(isBlazy){preloadBlazy(false)}}function afterSlick(){t.parent().on("click.sl",".slick-down",function(e){e.preventDefault();var b=$(this);$("html, body").stop().animate({scrollTop:$(b.data("target")).offset().top-(b.data("offset")||0)},800,"easeOutQuad"in $.easing&&o.easing?o.easing:"swing")});if(o.mouseWheel){t.on("mousewheel.sl",function(e,delta){e.preventDefault();return t.slick(delta<0?"slickNext":"slickPrev")})}if(!isBlazy){t.on("lazyLoaded lazyLoadError",function(e,slick,img){setBackgroun
175d(img)})}t.on("afterChange.sl",afterChange)}function setBackground(img){var $img=$(img);var $bg=$img.closest(".media--background");var p=$img.closest(".slide")||$img.closest(".unslick");$img.parentsUntil(p).removeClass(function(index,css){return(css.match(/(\S+)loading/g)||[]).join(" ")});if($bg.length&&$bg.find("> img").length){$bg.css("background-image","url("+$img.attr("src")+")");$bg.find("> img").remove();$bg.removeAttr("data-lazy")}}function randomize(){t.children().sort(function(){return.5-Math.random()}).each(function(){t.append(this)})}function setPosition(slick){var less=slick.slideCount<=slick.options.slidesToShow;var hide=less||slick.options.arrows===false;if(t.attr("id")===slick.$slider.attr("id")){if(!slick.options.centerPadding||slick.options.centerPadding==="0"){slick.$list.css("padding","")}if(less&&slick.$slideTrack.width()<=slick.$slider.width()){slick.$slideTrack.css({left:"",transform:""})}a[hide?"addClass":"removeClass"]("element-invisible")}}function globals(o){return unSlick?{}:{slide:o.slide,lazyLoad:o.lazyLoad,dotsClass:o.dotsClass,rtl:o.rtl,customPaging:function(slick,i){var container=slick.$slides.eq(i).find("[data-thumb]")||null;var img='<img alt="'+Drupal.t(container.find("img").attr("alt"))+'" src="'+container.data("thumb")+'">';var dotsThumb=container.length&&o.dotsClass.indexOf("thumbnail")>0?'<div class="slick-dots__thumbnail">'+img+"</div>":"";var paging=slick.defaults.customPaging(slick,i);return dotsThumb?paging.add(dotsThumb):paging}}}beforeSlick();t.slick(globals(o));afterSlick();if(t.hasClass("unslick")){t.slick("unslick")}$(elm).addClass("slick--initialized")}Drupal.behaviors.slick={attach:function(context){$(".slick",context).once("slick",doSlick)}}})(jQuery,Drupal,Drupal.settings); 176; 177 178(function($) { 179 180/** 181 * Drupal FieldGroup object. 182 */ 183Drupal.FieldGroup = Drupal.FieldGroup || {}; 184Drupal.FieldGroup.Effects = Drupal.FieldGroup.Effects || {}; 185Drupal.FieldGroup.groupWithfocus = null; 186 187Drupal.FieldGroup.setGroupWithfocus = function(element) { 188 element.css({display: 'block'}); 189 Drupal.FieldGroup.groupWithfocus = element; 190} 191 192/** 193 * Implements Drupal.FieldGroup.processHook(). 194 */ 195Drupal.FieldGroup.Effects.processFieldset = { 196 execute: function (context, settings, type) { 197 if (type == 'form') { 198 // Add required fields mark to any fieldsets containing required fields 199 $('fieldset.fieldset', context).once('fieldgroup-effects', function(i) { 200 if ($(this).is('.required-fields') && $(this).find('.form-required').length > 0) { 201 $('legend span.fieldset-legend', $(this)).eq(0).append(' ').append($('.form-required').eq(0).clone()); 202 } 203 if ($('.error', $(this)).length) { 204 $('legend span.fieldset-legend', $(this)).eq(0).addClass('error'); 205 Drupal.FieldGroup.setGroupWithfocus($(this)); 206 } 207 }); 208 } 209 } 210} 211 212/** 213 * Implements Drupal.FieldGroup.processHook(). 214 */ 215Drupal.FieldGroup.Effects.processAccordion = { 216 execute: function (context, settings, type) { 217 $('div.field-group-accordion-wrapper', context).once('fieldgroup-effects', function () { 218 var wrapper = $(this); 219 220 // Get the index to set active. 221 var active_index = false; 222 wrapper.find('.accordion-item').each(function(i) { 223 if ($(this).hasClass('field-group-accordion-active')) { 224 active_index = i; 225 } 226 }); 227 228 wrapper.accordion({ 229 heightStyle: "content", // jQuery UI >= v1.9 230 autoHeight: false, // jQuery UI < v1.9 231 active: active_index, 232 collapsible: true, 233 changestart: function(event, ui) { 234 if ($(this).hasClass('effect-none')) { 235 ui.options.animated = false; 236 } 237 else { 238 ui.options.animated = 'slide'; 239 } 240 } 241 }); 242 243 if (type == 'form') { 244 245 var $firstErrorItem = false; 246 247 // Add required fields mark to any element containing required fields 248 wrapper.find('div.field-group-accordion-item').each(function(i) { 249 250 if ($(this).is('.required-fields') && $(this).find('.form-required').length > 0) { 251 $('h3.ui-accordion-header a').eq(i).append(' ').append($('.form-required').eq(0).clone()); 252 } 253 if ($('.error', $(this)).length) { 254 // Save first error item, for focussing it. 255 if (!$firstErrorItem) { 256 $firstErrorItem = $(this).parent().accordion("option", "active", i); 257 } 258 $('h3.ui-accordion-header').eq(i).addClass('error'); 259 } 260 }); 261 262 // Save first error item, for focussing it. 263 if (!$firstErrorItem) { 264 $('.ui-accordion-content-active', $firstErrorItem).css({height: 'auto', width: 'auto', display: 'block'}); 265 } 266 267 } 268 }); 269 } 270} 271 272/** 273 * Implements Drupal.FieldGroup.processHook(). 274 */ 275Drupal.FieldGroup.Effects.processHtabs = { 276 execute: function (context, settings, type) { 277 if (type == 'form') { 278 // Add required fields mark to any element containing required fields 279 $('fieldset.horizontal-tabs-pane', context).once('fieldgroup-effects', function(i) { 280 if ($(this).is('.required-fields') && $(this).find('.form-required').length > 0) { 281 $(this).data('horizontalTab').link.find('strong:first').after($('.form-required').eq(0).clone()).after(' '); 282 } 283 if ($('.error', $(this)).length) { 284 $(this).data('horizontalTab').link.parent().addClass('error'); 285 Drupal.FieldGroup.setGroupWithfocus($(this)); 286 $(this).data('horizontalTab').focus(); 287 } 288 }); 289 } 290 } 291} 292 293/** 294 * Implements Drupal.FieldGroup.processHook(). 295 */ 296Drupal.FieldGroup.Effects.processTabs = { 297 execute: function (context, settings, type) { 298 if (type == 'form') { 299 300 var errorFocussed = false;
301 302 // Add required fields mark to any fieldsets containing required fields 303 $('fieldset.vertical-tabs-pane', context).once('fieldgroup-effects', function(i) { 304 if ($(this).is('.required-fields') && $(this).find('.form-required').length > 0) { 305 $(this).data('verticalTab').link.find('strong:first').after($('.form-required').eq(0).clone()).after(' '); 306 } 307 if ($('.error', $(this)).length) { 308 $(this).data('verticalTab').link.parent().addClass('error'); 309 // Focus the first tab with error. 310 if (!errorFocussed) { 311 Drupal.FieldGroup.setGroupWithfocus($(this)); 312 $(this).data('verticalTab').focus(); 313 errorFocussed = true; 314 } 315 } 316 }); 317 } 318 } 319} 320 321/** 322 * Implements Drupal.FieldGroup.processHook(). 323 * 324 * TODO clean this up meaning check if this is really 325 * necessary. 326 */ 327Drupal.FieldGroup.Effects.processDiv = { 328 execute: function (context, settings, type) { 329 330 $('div.collapsible', context).once('fieldgroup-effects', function() { 331 var $wrapper = $(this); 332 333 // Turn the legend into a clickable link, but retain span.field-group-format-toggler 334 // for CSS positioning. 335 336 var $toggler = $('span.field-group-format-toggler:first', $wrapper); 337 var $link = $('<a class="field-group-format-title" href="#"></a>'); 338 $link.prepend($toggler.contents()); 339 340 // Add required field markers if needed 341 if ($(this).is('.required-fields') && $(this).find('.form-required').length > 0) { 342 $link.append(' ').append($('.form-required').eq(0).clone()); 343 } 344 345 $link.appendTo($toggler); 346 347 // .wrapInner() does not retain bound events. 348 $link.click(function () { 349 var wrapper = $wrapper.get(0); 350 // Don't animate multiple times. 351 if (!wrapper.animating) { 352 wrapper.animating = true; 353 var speed = $wrapper.hasClass('speed-fast') ? 300 : 1000; 354 if ($wrapper.hasClass('effect-none') && $wrapper.hasClass('speed-none')) { 355 $('> .field-group-format-wrapper', wrapper).toggle(); 356 wrapper.animating = false; 357 } 358 else if ($wrapper.hasClass('effect-blind')) { 359 $('> .field-group-format-wrapper', wrapper).toggle('blind', {}, speed); 360 wrapper.animating = false; 361 } 362 else { 363 $('> .field-group-format-wrapper', wrapper).toggle(speed, function() { 364 wrapper.animating = false; 365 }); 366 } 367 } 368 $wrapper.toggleClass('collapsed'); 369 return false; 370 }); 371 372 }); 373 } 374}; 375 376/** 377 * Behaviors. 378 */ 379Drupal.behaviors.fieldGroup = { 380 attach: function (context, settings) { 381 settings.field_group = settings.field_group || Drupal.settings.field_group; 382 if (settings.field_group == undefined) { 383 return; 384 } 385 386 // Execute all of them. 387 $.each(Drupal.FieldGroup.Effects, function (func) { 388 // We check for a wrapper function in Drupal.field_group as 389 // alternative for dynamic string function calls. 390 var type = func.toLowerCase().replace("process", ""); 391 if (settings.field_group[type] != undefined && $.isFunction(this.execute)) { 392 this.execute(context, settings, settings.field_group[type]); 393 } 394 }); 395 396 // Fixes css for fieldgroups under vertical tabs. 397 $('.fieldset-wrapper .fieldset > legend').css({display: 'block'}); 398 $('.vertical-tabs fieldset.fieldset').addClass('default-fallback'); 399 400 // Add a new ID to each fieldset. 401 $('.group-wrapper .horizontal-tabs-panes > fieldset', context).once('group-wrapper-panes-processed', function() { 402 // Tats bad, but we have to keep the actual id to prevent layouts to break. 403 var fieldgroupID = 'field_group-' + $(this).attr('id'); 404 $(this).attr('id', fieldgroupID); 405 }); 406 // Set the hash in url to remember last userselection. 407 $('.group-wrapper ul li').once('group-wrapper-ul-processed', function() { 408 var fieldGroupNavigationListIndex = $(this).index(); 409 $(this).children('a').click(function() { 410 var fieldset = $('.group-wrapper fieldset').get(fieldGroupNavigationListIndex); 411 // Grab the first id, holding the wanted hashurl. 412 var hashUrl = $(fieldset).attr('id').replace(/^field_group-/, '').split(' ')[0]; 413 window.location.hash = hashUrl; 414 }); 415 }); 416 417 } 418}; 419 420})(jQuery); 421; 422/** 423 * @file 424 */ 425 426(function ($) { 427 428 'use strict'; 429 430 Drupal.extlink = Drupal.extlink || {}; 431 432 Drupal.extlink.attach = function (context, settings) { 433 if (!settings.hasOwnProperty('extlink')) { 434 return; 435 } 436 437 // Strip the host name down, removing ports, subdomains, or www. 438 var pattern = /^(([^\/:]+?\.)*)([^\.:]{1,})((\.[a-z0-9]{1,253})*)(:[0-9]{1,5})?$/; 439 var host = window.location.host.replace(pattern, '$2$3$6'); 440 var subdomain = window.location.host.replace(host, ''); 441 442 // Determine what subdomains are considered internal. 443 var subdomains; 444 if (settings.extlink.extSubdomains) { 445 subdomains = '([^/]*\\.)?'; 446 } 447 else if (subdomain === 'www.' || subdomain === '') { 448 subdomains = '(www\\.)?'; 449 } 450 else { 451 subdomains = subdomain.replace('.', '\\.'); 452 } 453 454 // Build regular expressions that define an internal link. 455 var internal_link = new RegExp('^https?://([^@]*@)?' + subdomains + host, 'i'); 456 457 // Extra internal link matching. 458 var extInclude = false;
459 if (settings.extlink.extInclude) { 460 extInclude = new RegExp(settings.extlink.extInclude.replace(/\\/, '\\'), 'i'); 461 } 462 463 // Extra external link matching. 464 var extExclude = false; 465 if (settings.extlink.extExclude) { 466 extExclude = new RegExp(settings.extlink.extExclude.replace(/\\/, '\\'), 'i'); 467 } 468 469 // Extra external link CSS selector exclusion. 470 var extCssExclude = false; 471 if (settings.extlink.extCssExclude) { 472 extCssExclude = settings.extlink.extCssExclude; 473 } 474 475 // Extra external link CSS selector explicit. 476 var extCssExplicit = false; 477 if (settings.extlink.extCssExplicit) { 478 extCssExplicit = settings.extlink.extCssExplicit; 479 } 480 481 // Define the jQuery method (either 'append' or 'prepend') of placing the icon, defaults to 'append'. 482 var extIconPlacement = settings.extlink.extIconPlacement || 'append'; 483 484 // Find all links which are NOT internal and begin with http as opposed 485 // to ftp://, javascript:, etc. other kinds of links. 486 // When operating on the 'this' variable, the host has been appended to 487 // all links by the browser, even local ones. 488 // In jQuery 1.1 and higher, we'd use a filter method here, but it is not 489 // available in jQuery 1.0 (Drupal 5 default). 490 var external_links = []; 491 var mailto_links = []; 492 $('a:not([data-extlink]), area:not([data-extlink])', context).each(function (el) { 493 try { 494 var url = ''; 495 if (typeof this.href == 'string') { 496 url = this.href.toLowerCase(); 497 } 498 // Handle SVG links (xlink:href). 499 else if (typeof this.href == 'object') { 500 url = this.href.baseVal; 501 } 502 if (url.indexOf('http') === 0 503 && ((!url.match(internal_link) && !(extExclude && url.match(extExclude))) || (extInclude && url.match(extInclude))) 504 && !(extCssExclude && $(this).is(extCssExclude)) 505 && !(extCssExclude && $(this).parents(extCssExclude).length > 0) 506 && !(extCssExplicit && $(this).parents(extCssExplicit).length < 1)) { 507 external_links.push(this); 508 } 509 // Do not include area tags with begin with mailto: (this prohibits 510 // icons from being added to image-maps). 511 else if (this.tagName !== 'AREA' 512 && url.indexOf('mailto:') === 0 513 && !(extCssExclude && $(this).parents(extCssExclude).length > 0) 514 && !(extCssExplicit && $(this).parents(extCssExplicit).length < 1)) { 515 mailto_links.push(this); 516 } 517 } 518 // IE7 throws errors often when dealing with irregular links, such as: 519 // <a href="node/10"></a> Empty tags. 520 // <a href="http://user:[email protected]">example</a> User:pass syntax. 521 catch (error) { 522 return false; 523 } 524 }); 525 526 if (settings.extlink.extClass) { 527 Drupal.extlink.applyClassAndSpan(external_links, settings.extlink.extClass, extIconPlacement); 528 } 529 530 if (settings.extlink.mailtoClass) { 531 Drupal.extlink.applyClassAndSpan(mailto_links, settings.extlink.mailtoClass, extIconPlacement); 532 } 533 534 if (settings.extlink.extTarget) { 535 // Apply the target attribute to all links. 536 $(external_links).attr('target', settings.extlink.extTarget); 537 // Add rel attributes noopener and noreferrer. 538 $(external_links).attr('rel', function (i, val) { 539 // If no rel attribute is present, create one with the values noopener and noreferrer. 540 if (val == null) { 541 return 'noopener noreferrer'; 542 } 543 // Check to see if rel contains noopener or noreferrer. Add what doesn't exist. 544 if (val.indexOf('noopener') > -1 || val.indexOf('noreferrer') > -1) { 545 if (val.indexOf('noopener') === -1) { 546 return val + ' noopener'; 547 } 548 if (val.indexOf('noreferrer') === -1) { 549 return val + ' noreferrer'; 550 } 551 // Both noopener and noreferrer exist. Nothing needs to be added. 552 else { 553 return val; 554 } 555 } 556 // Else, append noopener and noreferrer to val. 557 else { 558 return val + ' noopener noreferrer'; 559 } 560 }); 561 } 562 563 Drupal.extlink = Drupal.extlink || {}; 564 565 // Set up default click function for the external links popup. This should be 566 // overridden by modules wanting to alter the popup. 567 Drupal.extlink.popupClickHandler = Drupal.extlink.popupClickHandler || function () { 568 if (settings.extlink.extAlert) { 569 return confirm(settings.extlink.extAlertText); 570 } 571 }; 572 573 $(external_links).click(function (e) { 574 return Drupal.extlink.popupClickHandler(e, this); 575 }); 576 }; 577 578 /** 579 * Apply a class and a trailing <span> to all links not containing images. 580 * 581 * @param {object[]} links 582 * An array of DOM elements representing the links. 583 * @param {string} class_name 584 * The class to apply to the links. 585 * @param {string} icon_placement 586 * 'append' or 'prepend' the icon to the link. 587 */ 588 Drupal.extlink.applyClassAndSpan = function (links, class_name, icon_placement) { 589 var $links_to_process; 590 if (Drupal.settings.extlink.extImgClass) { 591 $links_to_process = $(links); 592 } 593 else { 594 var links_with_images = $(links).find('img').parents('a'); 595 $links_to_process = $(links).not(links_with_images); 596 } 597 // Add data-extlink attribute. 598 $links_to_process.attr('data-extlink', ''); 599 var i; 600 var length = $links_to_process.length; 601 for (i = 0; i < length; i++) { 602 var $link = $($links_to_process[i]);
603 if ($link.css('display') === 'inline' || $link.css('display') === 'inline-block' || $link.css('display') === 'block') { 604 if (Drupal.settings.extlink.extUseFontAwesome) { 605 if (class_name === Drupal.settings.extlink.mailtoClass) { 606 $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>'); 607 } 608 else { 609 $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>'); 610 } 611 } 612 else { 613 if (class_name === Drupal.settings.extlink.mailtoClass) { 614 $link[icon_placement]('<span class="' + class_name + '"><span class="element-invisible">' + Drupal.settings.extlink.mailtoLabel + '</span></span>'); 615 } 616 else { 617 $link[icon_placement]('<span class="' + class_name + '"><span class="element-invisible">' + Drupal.settings.extlink.extLabel + '</span></span>'); 618 } 619 } 620 } 621 } 622 }; 623 624 Drupal.behaviors.extlink = Drupal.behaviors.extlink || {}; 625 Drupal.behaviors.extlink.attach = function (context, settings) { 626 // Backwards compatibility, for the benefit of modules overriding extlink 627 // functionality by defining an "extlinkAttach" global function. 628 if (typeof extlinkAttach === 'function') { 629 extlinkAttach(context); 630 } 631 else { 632 Drupal.extlink.attach(context, settings); 633 } 634 }; 635 636})(jQuery); 637; 638(function ($) { 639 640/** 641 * A progressbar object. Initialized with the given id. Must be inserted into 642 * the DOM afterwards through progressBar.element. 643 * 644 * method is the function which will perform the HTTP request to get the 645 * progress bar state. Either "GET" or "POST". 646 * 647 * e.g. pb = new progressBar('myProgressBar'); 648 * some_element.appendChild(pb.element); 649 */ 650Drupal.progressBar = function (id, updateCallback, method, errorCallback) { 651 var pb = this; 652 this.id = id; 653 this.method = method || 'GET'; 654 this.updateCallback = updateCallback; 655 this.errorCallback = errorCallback; 656 657 // The WAI-ARIA setting aria-live="polite" will announce changes after users 658 // have completed their current activity and not interrupt the screen reader. 659 this.element = $('<div class="progress" aria-live="polite"></div>').attr('id', id); 660 this.element.html('<div class="bar"><div class="filled"></div></div>' + 661 '<div class="percentage"></div>' + 662 '<div class="message"> </div>'); 663}; 664 665/** 666 * Set the percentage and status message for the progressbar. 667 */ 668Drupal.progressBar.prototype.setProgress = function (percentage, message) { 669 if (percentage >= 0 && percentage <= 100) { 670 $('div.filled', this.element).css('width', percentage + '%'); 671 $('div.percentage', this.element).html(percentage + '%'); 672 } 673 $('div.message', this.element).html(message); 674 if (this.updateCallback) { 675 this.updateCallback(percentage, message, this); 676 } 677}; 678 679/** 680 * Start monitoring progress via Ajax. 681 */ 682Drupal.progressBar.prototype.startMonitoring = function (uri, delay) { 683 this.delay = delay; 684 this.uri = uri; 685 this.sendPing(); 686}; 687 688/** 689 * Stop monitoring progress via Ajax. 690 */ 691Drupal.progressBar.prototype.stopMonitoring = function () { 692 clearTimeout(this.timer); 693 // This allows monitoring to be stopped from within the callback. 694 this.uri = null; 695}; 696 697/** 698 * Request progress data from server. 699 */ 700Drupal.progressBar.prototype.sendPing = function () { 701 if (this.timer) { 702 clearTimeout(this.timer); 703 } 704 if (this.uri) { 705 var pb = this; 706 // When doing a post request, you need non-null data. Otherwise a 707 // HTTP 411 or HTTP 406 (with Apache mod_security) error may result. 708 $.ajax({ 709 type: this.method, 710 url: this.uri, 711 data: '', 712 dataType: 'json', 713 success: function (progress) { 714 // Display errors. 715 if (progress.status == 0) { 716 pb.displayError(progress.data); 717 return; 718 } 719 // Update display. 720 pb.setProgress(progress.percentage, progress.message); 721 // Schedule next timer.
722 pb.timer = setTimeout(function () { pb.sendPing(); }, pb.delay); 723 }, 724 error: function (xmlhttp) { 725 pb.displayError(Drupal.ajaxError(xmlhttp, pb.uri)); 726 } 727 }); 728 } 729}; 730 731/** 732 * Display errors on the page. 733 */ 734Drupal.progressBar.prototype.displayError = function (string) { 735 var error = $('<div class="messages error"></div>').html(string); 736 $(this.element).before(error).hide(); 737 738 if (this.errorCallback) { 739 this.errorCallback(this); 740 } 741}; 742 743})(jQuery); 744; 745/** 746 * @file 747 * 748 * Implement a modal form. 749 * 750 * @see modal.inc for documentation. 751 * 752 * This javascript relies on the CTools ajax responder. 753 */ 754 755(function ($) { 756 // Make sure our objects are defined. 757 Drupal.CTools = Drupal.CTools || {}; 758 Drupal.CTools.Modal = Drupal.CTools.Modal || {}; 759 760 /** 761 * Display the modal 762 * 763 * @todo -- document the settings. 764 */ 765 Drupal.CTools.Modal.show = function(choice) { 766 var opts = {}; 767 768 if (choice && typeof choice == 'string' && Drupal.settings[choice]) { 769 // This notation guarantees we are actually copying it. 770 $.extend(true, opts, Drupal.settings[choice]); 771 } 772 else if (choice) { 773 $.extend(true, opts, choice); 774 } 775 776 var defaults = { 777 modalTheme: 'CToolsModalDialog', 778 throbberTheme: 'CToolsModalThrobber', 779 animation: 'show', 780 animationSpeed: 'fast', 781 modalSize: { 782 type: 'scale', 783 width: .8, 784 height: .8, 785 addWidth: 0, 786 addHeight: 0, 787 // How much to remove from the inner content to make space for the 788 // theming. 789 contentRight: 25, 790 contentBottom: 45 791 }, 792 modalOptions: { 793 opacity: .55, 794 background: '#fff' 795 }, 796 modalClass: 'default' 797 }; 798 799 var settings = {}; 800 $.extend(true, settings, defaults, Drupal.settings.CToolsModal, opts); 801 802 if (Drupal.CTools.Modal.currentSettings && Drupal.CTools.Modal.currentSettings != settings) { 803 Drupal.CTools.Modal.modal.remove(); 804 Drupal.CTools.Modal.modal = null; 805 } 806 807 Drupal.CTools.Modal.currentSettings = settings; 808 809 var resize = function(e) { 810 // When creating the modal, it actually exists only in a theoretical 811 // place that is not in the DOM. But once the modal exists, it is in the 812 // DOM so the context must be set appropriately. 813 var context = e ? document : Drupal.CTools.Modal.modal; 814 815 if (Drupal.CTools.Modal.currentSettings.modalSize.type == 'scale') { 816 var width = $(window).width() * Drupal.CTools.Modal.currentSettings.modalSize.width; 817 var height = $(window).height() * Drupal.CTools.Modal.currentSettings.modalSize.height; 818 } 819 else { 820 var width = Drupal.CTools.Modal.currentSettings.modalSize.width; 821 var height = Drupal.CTools.Modal.currentSettings.modalSize.height; 822 } 823 824 // Use the additionol pixels for creating the width and height. 825 $('div.ctools-modal-content', context).css({ 826 'width': width + Drupal.CTools.Modal.currentSettings.modalSize.addWidth + 'px', 827 'height': height + Drupal.CTools.Modal.currentSettings.modalSize.addHeight + 'px' 828 }); 829 $('div.ctools-modal-content .modal-content', context).css({ 830 'width': (width - Drupal.CTools.Modal.currentSettings.modalSize.contentRight) + 'px', 831 'height': (height - Drupal.CTools.Modal.currentSettings.modalSize.contentBottom) + 'px' 832 }); 833 }; 834 835 if (!Drupal.CTools.Modal.modal) { 836 Drupal.CTools.Modal.modal = $(Drupal.theme(settings.modalTheme)); 837 if (settings.modalSize.type == 'scale') { 838 $(window).bind('resize', resize); 839 } 840 } 841 842 resize(); 843 844 $('span.modal-title', Drupal.CTools.Modal.modal).html(Drupal.CTools.Modal.currentSettings.loadingText); 845 Drupal.CTools.Modal.modalContent(Drupal.CTools.Modal.modal, settings.modalOptions, settings.animation, settings.animationSpeed, settings.modalClass); 846 $('#modalContent .modal-content').html(Drupal.theme(settings.throbberTheme)).addClass('ctools-modal-loading'); 847 848 // Position autocomplete results based on the scroll position of the modal.
849 $('#modalContent .modal-content').delegate('input.form-autocomplete', 'keyup', function() { 850 $('#autocomplete').css('top', $(this).position().top + $(this).outerHeight() + $(this).offsetParent().filter('#modal-content').scrollTop()); 851 }); 852 }; 853 854 /** 855 * Hide the modal 856 */ 857 Drupal.CTools.Modal.dismiss = function() { 858 if (Drupal.CTools.Modal.modal) { 859 Drupal.CTools.Modal.unmodalContent(Drupal.CTools.Modal.modal); 860 } 861 }; 862 863 /** 864 * Provide the HTML to create the modal dialog. 865 */ 866 Drupal.theme.prototype.CToolsModalDialog = function () { 867 var html = ''; 868 html += '<div id="ctools-modal">'; 869 html += ' <div class="ctools-modal-content">'; // panels-modal-content 870 html += ' <div class="modal-header">'; 871 html += ' <a class="close" href="#">'; 872 html += Drupal.CTools.Modal.currentSettings.closeText + Drupal.CTools.Modal.currentSettings.closeImage; 873 html += ' </a>'; 874 html += ' <span id="modal-title" class="modal-title"> </span>'; 875 html += ' </div>'; 876 html += ' <div id="modal-content" class="modal-content">'; 877 html += ' </div>'; 878 html += ' </div>'; 879 html += '</div>'; 880 881 return html; 882 }; 883 884 /** 885 * Provide the HTML to create the throbber. 886 */ 887 Drupal.theme.prototype.CToolsModalThrobber = function () { 888 var html = ''; 889 html += '<div id="modal-throbber">'; 890 html += ' <div class="modal-throbber-wrapper">'; 891 html += Drupal.CTools.Modal.currentSettings.throbber; 892 html += ' </div>'; 893 html += '</div>'; 894 895 return html; 896 }; 897 898 /** 899 * Figure out what settings string to use to display a modal. 900 */ 901 Drupal.CTools.Modal.getSettings = function (object) { 902 var match = $(object).attr('class').match(/ctools-modal-(\S+)/); 903 if (match) { 904 return match[1]; 905 } 906 }; 907 908 /** 909 * Click function for modals that can be cached. 910 */ 911 Drupal.CTools.Modal.clickAjaxCacheLink = function () { 912 Drupal.CTools.Modal.show(Drupal.CTools.Modal.getSettings(this)); 913 return Drupal.CTools.AJAX.clickAJAXCacheLink.apply(this); 914 }; 915 916 /** 917 * Handler to prepare the modal for the response 918 */ 919 Drupal.CTools.Modal.clickAjaxLink = function () { 920 Drupal.CTools.Modal.show(Drupal.CTools.Modal.getSettings(this)); 921 return false; 922 }; 923 924 /** 925 * Submit responder to do an AJAX submit on all modal forms. 926 */ 927 Drupal.CTools.Modal.submitAjaxForm = function(e) { 928 var $form = $(this); 929 var url = $form.attr('action'); 930 931 setTimeout(function() { Drupal.CTools.AJAX.ajaxSubmit($form, url); }, 1); 932 return false; 933 }; 934 935 /** 936 * Bind links that will open modals to the appropriate function. 937 */ 938 Drupal.behaviors.ZZCToolsModal = { 939 attach: function(context) { 940 // Bind links 941 // Note that doing so in this order means that the two classes can be 942 // used together safely. 943 /* 944 * @todo remimplement the warm caching feature 945 $('a.ctools-use-modal-cache', context).once('ctools-use-modal', function() { 946 $(this).click(Drupal.CTools.Modal.clickAjaxCacheLink); 947 Drupal.CTools.AJAX.warmCache.apply(this); 948 }); 949 */ 950 951 $('area.ctools-use-modal, a.ctools-use-modal', context).once('ctools-use-modal', function() { 952 var $this = $(this); 953 $this.click(Drupal.CTools.Modal.clickAjaxLink); 954 // Create a drupal ajax object 955 var element_settings = {}; 956 if ($this.attr('href')) { 957 element_settings.url = $this.attr('href'); 958 element_settings.event = 'click'; 959 element_settings.progress = { type: 'throbber' }; 960 } 961 var base = $this.attr('href'); 962 Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings); 963 }); 964 965 // Bind buttons 966 $('input.ctools-use-modal, button.ctools-use-modal', context).once('ctools-use-modal', function() { 967 var $this = $(this); 968 $this.click(Drupal.CTools.Modal.clickAjaxLink); 969 var button = this; 970 var element_settings = {}; 971 972 // AJAX submits specified in this manner automatically submit to the 973 // normal form action. 974 element_settings.url = Drupal.CTools.Modal.findURL(this); 975 if (element_settings.url == '') { 976 element_settings.url = $(this).closest('form').attr('action'); 977 } 978 element_settings.event = 'click'; 979 element_settings.setClick = true; 980 981 var base = $this.attr('id'); 982 Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings); 983 984 // Make sure changes to settings are reflected in the URL. 985 $('.' + $(button).attr('id') + '-url').change(function() { 986 Drupal.ajax[base].options.url = Drupal.CTools.Modal.findURL(button); 987 }); 988 }); 989 990 // Bind our custom event to the form submit
991 $('#modal-content form', context).once('ctools-use-modal', function() { 992 var $this = $(this); 993 var element_settings = {}; 994 995 element_settings.url = $this.attr('action'); 996 element_settings.event = 'submit'; 997 element_settings.progress = { 'type': 'throbber' }; 998 var base = $this.attr('id'); 999 1000 Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings); 1001 Drupal.ajax[base].form = $this; 1002 1003 $('input[type=submit], button', this).click(function(event) { 1004 Drupal.ajax[base].element = this; 1005 this.form.clk = this; 1006 // Stop autocomplete from submitting. 1007 if (Drupal.autocompleteSubmit && !Drupal.autocompleteSubmit()) { 1008 return false; 1009 } 1010 // An empty event means we were triggered via .click() and 1011 // in jquery 1.4 this won't trigger a submit. 1012 // We also have to check jQuery version to prevent 1013 // IE8 + jQuery 1.4.4 to break on other events 1014 // bound to the submit button. 1015 if (jQuery.fn.jquery.substr(0, 3) === '1.4' && typeof event.bubbles === "undefined") { 1016 $(this.form).trigger('submit'); 1017 return false; 1018 } 1019 }); 1020 }); 1021 1022 // Bind a click handler to allow elements with the 'ctools-close-modal' 1023 // class to close the modal. 1024 $('.ctools-close-modal', context).once('ctools-close-modal') 1025 .click(function() { 1026 Drupal.CTools.Modal.dismiss(); 1027 return false; 1028 }); 1029 } 1030 }; 1031 1032 // The following are implementations of AJAX responder commands. 1033 1034 /** 1035 * AJAX responder command to place HTML within the modal. 1036 */ 1037 Drupal.CTools.Modal.modal_display = function(ajax, response, status) { 1038 var settings = response.settings || ajax.settings || Drupal.settings; 1039 // If the modal does not exist yet, create it. 1040 if ($('#modalContent').length == 0) { 1041 Drupal.CTools.Modal.show(Drupal.CTools.Modal.getSettings(ajax.element)); 1042 } 1043 // If the modal exists run detachBehaviors before removing existing content. 1044 else { 1045 Drupal.detachBehaviors($('#modalContent'), settings, 'unload'); 1046 } 1047 $('#modal-title').html(response.title); 1048 // Simulate an actual page load by scrolling to the top after adding the 1049 // content. This is helpful for allowing users to see error messages at the 1050 // top of a form, etc. 1051 $('#modal-content').html(response.output).scrollTop(0); 1052 $(document).trigger('CToolsAttachBehaviors', $('#modalContent')); 1053 1054 // Attach behaviors within a modal dialog. 1055 Drupal.attachBehaviors($('#modalContent'), settings); 1056 1057 if ($('#modal-content').hasClass('ctools-modal-loading')) { 1058 $('#modal-content').removeClass('ctools-modal-loading'); 1059 } 1060 else { 1061 // If the modal was already shown, and we are simply replacing its 1062 // content, then focus on the first focusable element in the modal. 1063 // (When first showing the modal, focus will be placed on the close 1064 // button by the show() function called above.) 1065 $('#modal-content :focusable:first').focus(); 1066 } 1067 1068 // Force all attached animations to finish, and trigger a window resize 1069 // event to center te modal after the ajax content has been added. 1070 $('#modalContent').stop(true, true); 1071 $(window).resize(); 1072 }; 1073 1074 /** 1075 * AJAX responder command to dismiss the modal. 1076 */ 1077 Drupal.CTools.Modal.modal_dismiss = function(command) { 1078 Drupal.CTools.Modal.dismiss(); 1079 $('link.ctools-temporary-css').remove(); 1080 }; 1081 1082 /** 1083 * Display loading 1084 */ 1085 //Drupal.CTools.AJAX.commands.modal_loading = function(command) { 1086 Drupal.CTools.Modal.modal_loading = function(command) { 1087 Drupal.CTools.Modal.modal_display({ 1088 output: Drupal.theme(Drupal.CTools.Modal.currentSettings.throbberTheme), 1089 title: Drupal.CTools.Modal.currentSettings.loadingText 1090 }); 1091 }; 1092 1093 /** 1094 * Find a URL for an AJAX button. 1095 * 1096 * The URL for this gadget will be composed of the values of items by 1097 * taking the ID of this item and adding -url and looking for that 1098 * class. They need to be in the form in order since we will 1099 * concat them all together using '/'. 1100 */ 1101 Drupal.CTools.Modal.findURL = function(item) { 1102 var url = ''; 1103 var url_class = '.' + $(item).attr('id') + '-url'; 1104 $(url_class).each( 1105 function() { 1106 var $this = $(this); 1107 if (url && $this.val()) { 1108 url += '/'; 1109 } 1110 url += $this.val(); 1111 }); 1112 return url; 1113 }; 1114 1115 1116 /** 1117 * modalContent 1118 * @param content string to display in the content box 1119 * @param css obj of css attributes 1120 * @param animation (fadeIn, slideDown, show) 1121 * @param speed (valid animation speeds slow, medium, fast or # in ms) 1122 * @param modalClass class added to div#modalContent 1123 */ 1124 Drupal.CTools.Modal.modalContent = function(content, css, animation, speed, modalClass) { 1125 // If our animation isn't set, make it just show/pop 1126 if (!animation) { 1127 animation = 'show'; 1128 } 1129 else { 1130 // If our animation isn't "fadeIn" or "slideDown" then it always is show 1131 if (animation != 'fadeIn' && animation != 'slideDown') { 1132 animation = 'show'; 1133 } 1134 } 1135 1136 if (!speed && 0 !== speed) { 1137 speed = 'fast'; 1138 } 1139 1140 // Build our base attributes and allow them to be overriden 1141 css = jQuery.extend({ 1142 position: 'absolute', 1143 left: '0px', 1144 margin: '0px', 1145 background: '#000', 1146 opacity: '.55' 1147 }, css); 1148 1149 // Add opacity handling for IE. 1150 css.filter = 'alpha(opacity=' + (100 * css.opacity) + ')'; 1151 content.hide(); 1152 1153 // If we already have modalContent, remove it. 1154 if ($('#modalBackdrop').length) $('#modalBackdrop').remove(); 1155 if ($('#modalContent').length) $('#modalContent').remove(); 1156 1157 // position code lifted from http://www.quirksmode.org/viewport/compatibility.html 1158 if (self.pageYOffset) { // all except Explorer 1159 var wt = self.pageYOffset; 1160 } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict 1161 var wt = document.documentElement.scrollTop; 1162 } else if (document.body) { // all other Explorers 1163 var wt = document.body.scrollTop; 1164 } 1165 1166 // Get our dimensions 1167 1168 // Get the docHeight and (ugly hack) add 50 pixels to make sure we dont have a *visible* border below our div 1169 var docHeight = $(document).height() + 50; 1170 var docWidth = $(document).width(); 1171 var winHeight = $(window).height(); 1172 var winWidth = $(window).width();
1173 if( docHeight < winHeight ) docHeight = winHeight; 1174 1175 // Create our divs 1176 $('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>'); 1177 1178 // Get a list of the tabbable elements in the modal content. 1179 var getTabbableElements = function () { 1180 var tabbableElements = $('#modalContent :tabbable'), 1181 radioButtons = tabbableElements.filter('input[type="radio"]'); 1182 1183 // The list of tabbable elements from jQuery is *almost* right. The 1184 // exception is with groups of radio buttons. The list from jQuery will 1185 // include all radio buttons, when in fact, only the selected radio button 1186 // is tabbable, and if no radio buttons in a group are selected, then only 1187 // the first is tabbable. 1188 if (radioButtons.length > 0) { 1189 // First, build up an index of which groups have an item selected or not. 1190 var anySelected = {}; 1191 radioButtons.each(function () { 1192 var name = this.name; 1193 1194 if (typeof anySelected[name] === 'undefined') { 1195 anySelected[name] = radioButtons.filter('input[name="' + name + '"]:checked').length !== 0; 1196 } 1197 }); 1198 1199 // Next filter out the radio buttons that aren't really tabbable. 1200 var found = {}; 1201 tabbableElements = tabbableElements.filter(function () { 1202 var keep = true; 1203 1204 if (this.type == 'radio') { 1205 if (anySelected[this.name]) { 1206 // Only keep the selected one. 1207 keep = this.checked; 1208 } 1209 else { 1210 // Only keep the first one. 1211 if (found[this.name]) { 1212 keep = false; 1213 } 1214 found[this.name] = true; 1215 } 1216 } 1217 1218 return keep; 1219 }); 1220 } 1221 1222 return tabbableElements.get(); 1223 }; 1224 1225 // Keyboard and focus event handler ensures only modal elements gain focus. 1226 modalEventHandler = function( event ) { 1227 target = null; 1228 if ( event ) { //Mozilla 1229 target = event.target; 1230 } else { //IE 1231 event = window.event; 1232 target = event.srcElement; 1233 } 1234 1235 var parents = $(target).parents().get(); 1236 for (var i = 0; i < parents.length; ++i) { 1237 var position = $(parents[i]).css('position'); 1238 if (position == 'absolute' || position == 'fixed') { 1239 return true; 1240 } 1241 } 1242 1243 if ($(target).is('#modalContent, body') || $(target).filter('*:visible').parents('#modalContent').length) { 1244 // Allow the event only if target is a visible child node 1245 // of #modalContent. 1246 return true; 1247 } 1248 else { 1249 getTabbableElements()[0].focus(); 1250 } 1251 1252 event.preventDefault(); 1253 }; 1254 $('body').bind( 'focus', modalEventHandler ); 1255 $('body').bind( 'keypress', modalEventHandler ); 1256 1257 // Keypress handler Ensures you can only TAB to elements within the modal. 1258 // Based on the psuedo-code from WAI-ARIA 1.0 Authoring Practices section 1259 // 3.3.1 "Trapping Focus". 1260 modalTabTrapHandler = function (evt) { 1261 // We only care about the TAB key. 1262 if (evt.which != 9) { 1263 return true; 1264 } 1265 1266 var tabbableElements = getTabbableElements(), 1267 firstTabbableElement = tabbableElements[0], 1268 lastTabbableElement = tabbableElements[tabbableElements.length - 1], 1269 singleTabbableElement = firstTabbableElement == lastTabbableElement, 1270 node = evt.target; 1271 1272 // If this is the first element and the user wants to go backwards, then 1273 // jump to the last element. 1274 if (node == firstTabbableElement && evt.shiftKey) { 1275 if (!singleTabbableElement) { 1276 lastTabbableElement.focus(); 1277 } 1278 return false; 1279 } 1280 // If this is the last element and the user wants to go forwards, then 1281 // jump to the first element. 1282 else if (node == lastTabbableElement && !evt.shiftKey) { 1283 if (!singleTabbableElement) { 1284 firstTabbableElement.focus(); 1285 } 1286 return false;
1287 } 1288 // If this element isn't in the dialog at all, then jump to the first 1289 // or last element to get the user into the game. 1290 else if ($.inArray(node, tabbableElements) == -1) { 1291 // Make sure the node isn't in another modal (ie. WYSIWYG modal). 1292 var parents = $(node).parents().get(); 1293 for (var i = 0; i < parents.length; ++i) { 1294 var position = $(parents[i]).css('position'); 1295 if (position == 'absolute' || position == 'fixed') { 1296 return true; 1297 } 1298 } 1299 1300 if (evt.shiftKey) { 1301 lastTabbableElement.focus(); 1302 } 1303 else { 1304 firstTabbableElement.focus(); 1305 } 1306 } 1307 }; 1308 $('body').bind('keydown', modalTabTrapHandler); 1309 1310 // Create our content div, get the dimensions, and hide it 1311 var modalContent = $('#modalContent').css('top','-1000px'); 1312 var $modalHeader = modalContent.find('.modal-header'); 1313 var mdcTop = wt + Math.max((winHeight / 2) - (modalContent.outerHeight() / 2), 0); 1314 var mdcLeft = ( winWidth / 2 ) - ( modalContent.outerWidth() / 2); 1315 $('#modalBackdrop').css(css).css('top', 0).css('height', docHeight + 'px').css('width', docWidth + 'px').show(); 1316 modalContent.css({top: mdcTop + 'px', left: mdcLeft + 'px'}).hide()[animation](speed); 1317 1318 // Bind a click for closing the modalContent 1319 modalContentClose = function(){close(); return false;}; 1320 $('.close', $modalHeader).bind('click', modalContentClose); 1321 1322 // Bind a keypress on escape for closing the modalContent 1323 modalEventEscapeCloseHandler = function(event) { 1324 if (event.keyCode == 27) { 1325 close(); 1326 return false; 1327 } 1328 }; 1329 1330 $(document).bind('keydown', modalEventEscapeCloseHandler); 1331 1332 // Per WAI-ARIA 1.0 Authoring Practices, initial focus should be on the 1333 // close button, but we should save the original focus to restore it after 1334 // the dialog is closed. 1335 var oldFocus = document.activeElement; 1336 $('.close', $modalHeader).focus(); 1337 1338 // Close the open modal content and backdrop 1339 function close() { 1340 // Unbind the events 1341 $(window).unbind('resize', modalContentResize); 1342 $('body').unbind( 'focus', modalEventHandler); 1343 $('body').unbind( 'keypress', modalEventHandler ); 1344 $('body').unbind( 'keydown', modalTabTrapHandler ); 1345 $('.close', $modalHeader).unbind('click', modalContentClose); 1346 $(document).unbind('keydown', modalEventEscapeCloseHandler); 1347 $(document).trigger('CToolsCloseModalBehaviors', $('#modalContent')); 1348 $(document).trigger('CToolsDetachBehaviors', $('#modalContent')); 1349 1350 // Closing animation. 1351 switch (animation) { 1352 case 'fadeIn': 1353 modalContent.fadeOut(speed, modalContentRemove); 1354 break; 1355 1356 case 'slideDown': 1357 modalContent.slideUp(speed, modalContentRemove); 1358 break; 1359 1360 case 'show': 1361 modalContent.hide(speed, modalContentRemove); 1362 break; 1363 } 1364 } 1365 1366 // Remove the content. 1367 modalContentRemove = function () { 1368 $('#modalContent').remove(); 1369 $('#modalBackdrop').remove(); 1370 1371 // Restore focus to where it was before opening the dialog. 1372 $(oldFocus).focus(); 1373 }; 1374 1375 // Move and resize the modalBackdrop and modalContent on window resize. 1376 modalContentResize = function () { 1377 // Reset the backdrop height/width to get accurate document size. 1378 $('#modalBackdrop').css('height', '').css('width', ''); 1379 1380 // Position code lifted from: 1381 // http://www.quirksmode.org/viewport/compatibility.html 1382 if (self.pageYOffset) { // all except Explorer 1383 var wt = self.pageYOffset; 1384 } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict 1385 var wt = document.documentElement.scrollTop; 1386 } else if (document.body) { // all other Explorers 1387 var wt = document.body.scrollTop; 1388 } 1389 1390 // Get our heights 1391 var docHeight = $(document).height(); 1392 var docWidth = $(document).width(); 1393 var winHeight = $(window).height(); 1394 var winWidth = $(window).width();
1395 if( docHeight < winHeight ) docHeight = winHeight; 1396 1397 // Get where we should move content to 1398 var modalContent = $('#modalContent'); 1399 var mdcTop = wt + Math.max((winHeight / 2) - (modalContent.outerHeight() / 2), 0); 1400 var mdcLeft = ( winWidth / 2 ) - ( modalContent.outerWidth() / 2); 1401 1402 // Apply the changes 1403 $('#modalBackdrop').css('height', docHeight + 'px').css('width', docWidth + 'px').show(); 1404 modalContent.css('top', mdcTop + 'px').css('left', mdcLeft + 'px').show(); 1405 }; 1406 $(window).bind('resize', modalContentResize); 1407 }; 1408 1409 /** 1410 * unmodalContent 1411 * @param content (The jQuery object to remove) 1412 * @param animation (fadeOut, slideUp, show) 1413 * @param speed (valid animation speeds slow, medium, fast or # in ms) 1414 */ 1415 Drupal.CTools.Modal.unmodalContent = function(content, animation, speed) 1416 { 1417 // If our animation isn't set, make it just show/pop 1418 if (!animation) { var animation = 'show'; } else { 1419 // If our animation isn't "fade" then it always is show 1420 if (( animation != 'fadeOut' ) && ( animation != 'slideUp')) animation = 'show'; 1421 } 1422 // Set a speed if we dont have one 1423 if ( !speed ) var speed = 'fast'; 1424 1425 // Unbind the events we bound 1426 $(window).unbind('resize', modalContentResize); 1427 $('body').unbind('focus', modalEventHandler); 1428 $('body').unbind('keypress', modalEventHandler); 1429 $('body').unbind( 'keydown', modalTabTrapHandler ); 1430 var $modalContent = $('#modalContent'); 1431 var $modalHeader = $modalContent.find('.modal-header'); 1432 $('.close', $modalHeader).unbind('click', modalContentClose); 1433 $(document).unbind('keydown', modalEventEscapeCloseHandler); 1434 $(document).trigger('CToolsDetachBehaviors', $modalContent); 1435 1436 // jQuery magic loop through the instances and run the animations or removal. 1437 content.each(function(){ 1438 if ( animation == 'fade' ) { 1439 $('#modalContent').fadeOut(speed, function() { 1440 $('#modalBackdrop').fadeOut(speed, function() { 1441 $(this).remove(); 1442 }); 1443 $(this).remove(); 1444 }); 1445 } else { 1446 if ( animation == 'slide' ) { 1447 $('#modalContent').slideUp(speed,function() { 1448 $('#modalBackdrop').slideUp(speed, function() { 1449 $(this).remove(); 1450 }); 1451 $(this).remove(); 1452 }); 1453 } else { 1454 $('#modalContent').remove(); 1455 $('#modalBackdrop').remove(); 1456 } 1457 } 1458 }); 1459 }; 1460 1461$(function() { 1462 Drupal.ajax.prototype.commands.modal_display = Drupal.CTools.Modal.modal_display; 1463 Drupal.ajax.prototype.commands.modal_dismiss = Drupal.CTools.Modal.modal_dismiss; 1464}); 1465 1466})(jQuery); 1467;
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.