1/*! 2 Modaal - accessible modals - v0.3.1 3 by Humaan, for all humans. 4 http://humaan.com 5 */ 6/** 7 Modaal jQuery Plugin : Accessible Modals 8 9 ==== General Options === 10 type (string) : ajax, inline, image, iframe, confirm. Defaults to 'inline' 11 animation (string) : Fade, expand, down, up. Defaults to 'fade' 12 after_callback_delay (integer) : Specify a delay value for the after open callbacks. This is necessary because with the bundled animations 13 have a set duration in the bundled CSS. Specify a delay of the same amount as the animation duration in so 14 more accurately fire the after open/close callbacks. Defaults 350, does not apply if animation is 'none', 15 after open callbacks are dispatched immediately 16 17 is_locked (boolean) : Set this to true to disable closing the modal via keypress or clicking the background. Beware that if 18 type != 'confirm' there will be no interface to dismiss the modal if is_locked = true, you'd have to 19 programmatically arrange to dismiss the modal. Confirm modals are always locked regardless of this option 20 Defaults to false 21 22 hide_close (boolean) : Set this to true to hide the close modal button. Key press and overlay click will still close the modal. 23 This method is best used when you want to put a custom close button inside the modal container space. 24 25 background (string) : Background overlay style. Defaults to '#000' 26 overlay_opacity (float) : Background overlay transparency. Defaults to 0.8 27 overlay_close (boolean) : Set this to false if you want to disable click to close on overlay background. 28 29 accessible_title (string) : Accessible title. Default 'Dialog Window' 30 start_open (boolean) : Set this to true to launch the Modaal window immediately on page open 31 fullscreen (boolean) : Set this to true to make the modaal fill the entire screen, false will default to own width/height attributes. 32 custom_class (string) : Fill in this string with a custom class that will be applied to the outer most modal wrapper. 33 34 width (integer) : Desired width of the modal. Required for iframe type. Defaults to undefined //TODO 35 height (integer) : Desired height of the modal. Required for iframe type. Defaults to undefined //TODO 36 37 background_scroll (boolean) : Set this to true to enable the page to scroll behind the open modal. 38 39 should_open (boolean|function) : Boolean or closure that returns a boolean to determine whether to open the modal or not. 40 41 close_text : String for close button text. Available for localisation and alternative languages to be used. 42 close_aria_label : String for close button aria-label attribute (value that screen readers will read out). Available for localisation and alternative languages to be used. 43 44 === Events === 45 before_open (function) : Callback function executed before modal is opened 46 after_open (function) : Callback function executed after modal is opened 47 before_close (function) : Callback function executed before modal is closed 48 after_close (function) : Callback function executed after modal is closed 49 source (function(element, src)) : Callback function executed on the default source, it is intended to transform the 50 source (href in an AJAX modal or iframe). The function passes in the triggering element 51 as well as the default source depending of the modal type. The default output of the 52 function is an untransformed default source. 53 54 55 === Confirm Options & Events === 56 confirm_button_text (string) : Text on the confirm button. Defaults to 'Confirm' 57 confirm_cancel_button_text (string) : Text on the confirm modal cancel button. Defaults to 'Cancel' 58 confirm_title (string) : Title for confirm modal. Default 'Confirm Title' 59 confirm_content (string) : HTML content for confirm message 60 confirm_callback (function) : Callback function for when the confirm button is pressed as opposed to cancel 61 confirm_cancel_callback (function) : Callback function for when the cancel button is pressed 62 63 64 === Gallery Options & Events === 65 gallery_active_class (string) : Active class applied to the currently active image or image slide in a gallery 'gallery_active_item' 66 before_image_change (function) : Callback function executed before the image slide changes in a gallery modal. Default function( current_item, incoming_item ) 67 after_image_change (function) : Callback function executed after the image slide changes in a gallery modal. Default function ( current_item ) 68 69 70 === AJAX Options & Events === 71 loading_content (string) : HTML content for loading message. Default 'Loading â¦' 72 loading_class (string) : Class name to be applied while content is loaded via AJAX. Default 'is_loading' 73 ajax_error_class (string) : Class name to be applied when content has failed to load. Default is 'modaal-error' 74 ajax_success (function) : Callback for when AJAX content is loaded in 75 76 77 === SOCIAL CONTENT ===
78 instagram_id (string) : Unique photo ID for an Instagram photo. 79 80*/ 81( function( $ ) { 82 83 var modaal_loading_spinner = '<div class="modaal-loading-spinner"><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div></div>' 84 85 var Modaal = { 86 init : function(options, elem) { 87 var self = this; 88 89 self.dom = $('body'); 90 91 self.$elem = $(elem); 92 self.options = $.extend({}, $.fn.modaal.options, self.$elem.data(), options); 93 self.xhr = null; 94 95 // set up the scope 96 self.scope = { 97 is_open: false, 98 id: 'modaal_' + ( new Date().getTime() ) + ( Math.random().toString(16).substring(2) ) 99 }; 100 101 // add scope attribute to trigger element 102 self.$elem.attr('data-modaal-scope', self.scope.id); 103 104 // private options 105 self.private_options = { 106 active_class: 'is_active' 107 }; 108 109 self.lastFocus = null; 110 111 // if is_locked 112 if ( self.options.is_locked || self.options.type == 'confirm' || self.options.hide_close ) { 113 self.scope.close_btn = ''; 114 } else { 115 self.scope.close_btn = '<button type="button" class="modaal-close" id="modaal-close" aria-label="' + self.options.close_aria_label + '"><span>' + self.options.close_text + '</span></button>'; 116 } 117 118 // reset animation_speed 119 if (self.options.animation === 'none' ){ 120 self.options.animation_speed = 0; 121 self.options.after_callback_delay = 0; 122 } 123 124 // On click to open modal 125 $(elem).on('click.Modaal', function(e) { 126 e.preventDefault(); 127 128 var source; 129 130 // Save last active state before modal 131 self.lastFocus = document.activeElement; 132 133 if ( self.options.should_open === false || ( typeof self.options.should_open === 'function' && self.options.should_open() === false ) ) { 134 return; 135 } 136 137 // CB: before_open 138 self.options.before_open.call(self, e); 139 140 switch (self.options.type) { 141 case 'inline': 142 self.create_basic(); 143 break; 144 145 case 'ajax': 146 source = self.options.source( self.$elem, self.$elem.attr('href') ); 147 self.fetch_ajax( source ); 148 break; 149 150 case 'confirm': 151 self.options.is_locked = true; 152 self.create_confirm(); 153 break; 154 155 case 'image': 156 self.create_image(); 157 break; 158 159 case 'iframe': 160 source = self.options.source( self.$elem, self.$elem.attr('href') ); 161 self.create_iframe( source ); 162 break; 163 164 case 'video': 165 self.create_video(self.$elem.attr('href')); 166 break; 167 168 case 'instagram': 169 self.create_instagram(); 170 break; 171 } 172 173 // call events to be watched (click, tab, keyup, keydown etc.) 174 self.watch_events(); 175 176 }); 177 178 179 // Check for start_open 180 if (self.options.start_open === true ){ 181 $(elem).click(); 182 } 183 }, 184 185 // Watching Modal 186 // ---------------------------------------------------------------- 187 watch_events : function() { 188 var self = this; 189 190 self.dom.off('click.Modaal keyup.Modaal keydown.Modaal'); 191 192 // Body keydown 193 self.dom.on('keydown.Modaal', function(e) { 194 var key = e.keyCode; 195 var target = e.target; 196 197 // look for tab change and reset focus to modal window 198 // done in keydown so the check fires repeatedly when you hold the tab key down 199 if (key == 9 && self.scope.is_open) { 200 if (!$.contains(document.getElementById(self.scope.id), target) ) { 201 $('#' + self.scope.id).find('*[tabindex="0"]').focus(); 202 } 203 } 204 }); 205 206 // Body keyup 207 self.dom.on('keyup.Modaal', function(e) { 208 var key = e.keyCode; 209 var target = e.target; 210 211 if ( (e.shiftKey && e.keyCode == 9) && self.scope.is_open) { 212 // Watch for shift + tab key press. if open shift focus to close button. 213 if (!$.contains(document.getElementById(self.scope.id), target) ) { 214 $('#' + self.scope.id).find('.modaal-close').focus(); 215 } 216 } 217 218 if ( !self.options.is_locked ){ 219 // On escape key press close modal 220 if (key == 27 && self.scope.is_open ) { 221 if ( $(document.activeElement).is('input:not(:checkbox):not(:radio)') ) { 222 return false; 223 } 224 225 self.modaal_close(); 226 return; 227 } 228 } 229 230 // is gallery open and images length is > 1 231 if ( self.options.type == 'image' ) { 232 // arrow left for back 233 if (key == 37 && self.scope.is_open && (!$('#' + self.scope.id + ' .modaal-gallery-prev').hasClass('is_hidden')) ) { 234 self.gallery_update('prev'); 235 } 236 // arrow right for next 237 if (key == 39 && self.scope.is_open && (!$('#' + self.scope.id + ' .modaal-gallery-next').hasClass('is_hidden')) ) { 238 self.gallery_update('next'); 239 } 240 return; 241 } 242 }); 243 244 // Body click
vendor: 8,238 bytes, lines 245-483
245 self.dom.on('click.Modaal', function(e) { 246 var trigger = $(e.target); 247 248 // General Controls: If it's not locked allow greedy close 249 if ( !self.options.is_locked ){ 250 if ( (self.options.overlay_close && trigger.is('.modaal-inner-wrapper')) || trigger.is('.modaal-close') || trigger.closest('.modaal-close').length ) { 251 self.modaal_close(); 252 return; 253 } 254 } 255 256 //Confirm Controls 257 if ( trigger.is('.modaal-confirm-btn' ) ){ 258 // if 'OK' button is clicked, run confirm_callback() 259 if ( trigger.is('.modaal-ok') ) { 260 self.options.confirm_callback.call(self, self.lastFocus); 261 } 262 263 if ( trigger.is('.modaal-cancel') ) { 264 self.options.confirm_cancel_callback.call(self, self.lastFocus); 265 } 266 self.modaal_close(); 267 return; 268 } 269 270 // Gallery Controls 271 if ( trigger.is( '.modaal-gallery-control' ) ){ 272 // it not active, don't do nuthin! 273 if ( trigger.hasClass('is_hidden') ) { 274 return; 275 } 276 277 // trigger previous 278 if ( trigger.is('.modaal-gallery-prev') ) { 279 self.gallery_update('prev'); 280 } 281 // trigger next 282 if ( trigger.is('.modaal-gallery-next') ) { 283 self.gallery_update('next'); 284 } 285 return; 286 } 287 }); 288 }, 289 290 // Append markup into DOM 291 build_modal : function(content) { 292 var self = this; 293 294 // if is instagram 295 var igClass = ''; 296 if ( self.options.type == 'instagram' ) { 297 igClass = ' modaal-instagram'; 298 } 299 300 var wrap_class = (self.options.type == 'video') ? 'modaal-video-wrap' : 'modaal-content'; 301 302 /* 303 modaal-start_none : fully hidden via display:none; 304 modaal-start_fade : hidden via opacity:0 305 modaal-start_slidedown : ... 306 307 */ 308 var animation_class; 309 switch ( self.options.animation ) { 310 case 'fade' : 311 animation_class = ' modaal-start_fade'; 312 break; 313 case 'slide-down' : 314 animation_class = ' modaal-start_slidedown'; 315 break; 316 default : 317 animation_class = ' modaal-start_none' 318 } 319 320 // fullscreen check 321 var fullscreen_class = ''; 322 if ( self.options.fullscreen ) { 323 fullscreen_class = ' modaal-fullscreen'; 324 } 325 326 // custom class check 327 if ( self.options.custom_class !== '' || typeof(self.options.custom_class) !== 'undefined' ) { 328 self.options.custom_class = ' ' + self.options.custom_class; 329 } 330 331 // if width and heights exists and is typeof number 332 var dimensionsStyle = ''; 333 if ( self.options.width && self.options.height && typeof self.options.width == 'number' && typeof self.options.height == 'number' ) { 334 // if width and height exist, and they are both numbers 335 dimensionsStyle = ' style="max-width:' + self.options.width + 'px;height:' + self.options.height + 'px;overflow:auto;"'; 336 } else if ( self.options.width && typeof self.options.width == 'number' ) { 337 // if only width 338 dimensionsStyle = ' style="max-width:' + self.options.width + 'px;"'; 339 } else if ( self.options.height && typeof self.options.height == 'number' ) { 340 // if only height 341 dimensionsStyle = ' style="height:' + self.options.height + 'px;overflow:auto;"'; 342 } 343 344 // Reset dimensions style (width and height) for certain types 345 if ( self.options.type == 'image' || self.options.type == 'video' || self.options.type == 'instagram' || self.options.fullscreen ) { 346 dimensionsStyle = ''; 347 } 348 349 //var build_markup = '<div class="modaal-wrapper modaal-start_fade' + igClass + '" id="' + self.scope.id + '"><div class="modaal-outer-wrapper"><div class="modaal-inner-wrapper">'; 350 var build_markup = '<div class="modaal-wrapper modaal-' + self.options.type + animation_class + igClass + fullscreen_class + self.options.custom_class + '" id="' + self.scope.id + '"><div class="modaal-outer-wrapper"><div class="modaal-inner-wrapper">'; 351 352 // hide if video 353 if (self.options.type != 'video') { 354 build_markup += '<div class="modaal-container"' + dimensionsStyle + '>'; 355 } 356 357 // add the guts of the content 358 build_markup += '<div class="' + wrap_class + ' modaal-focus" aria-hidden="false" aria-label="' + self.options.accessible_title + ' (Press escape to close)" role="dialog">'; 359 360 // If it's inline type, we want to clone content instead of dropping it straight in 361 if (self.options.type == 'inline') { 362 build_markup += '<div class="modaal-content-container"></div>'; 363 } else { 364 // Drop in the content if it's not inline 365 build_markup += content; 366 } 367 368 // close wrap_class 369 build_markup += '</div>' + self.scope.close_btn; 370 371 // hide if video 372 if (self.options.type != 'video') { 373 build_markup += '</div>'; 374 } 375 376 // close off modaal-wrapper 377 build_markup += '</div></div></div>'; 378 379 // append ajax modal markup to dom 380 self.dom.append(build_markup); 381 382 // if inline, clone content into space 383 if (self.options.type == 'inline') { 384 content.appendTo('#' + self.scope.id + ' .modaal-content-container'); 385 } 386 387 // Trigger overlay show (which triggers modal show) 388 self.modaal_overlay('show'); 389 }, 390 391 // Create Basic Inline Modal 392 // ---------------------------------------------------------------- 393 create_basic : function() { 394 var self = this; 395 // if $elem is a link, then href points to the target, otherwise assume target was passed in initially 396 var target = (self.$elem.is('[href]')) ? $(self.$elem.attr('href')) : self.$elem; 397 var content = ''; 398 399 if (target.length) { 400 content = target.contents().clone(true,true); 401 target.empty(); 402 } else { 403 content = 'Content could not be loaded. Please check the source and try again.'; 404 } 405 406 // now push content into markup 407 self.build_modal(content); 408 }, 409 410 // Create Instagram Modal 411 // ---------------------------------------------------------------- 412 create_instagram : function() { 413 var self = this; 414 var id = self.options.instagram_id; 415 var content = ''; 416 417 var error_msg = 'Instagram photo couldn\'t be loaded, please check the embed code and try again.'; 418 419 self.build_modal('<div class="modaal-content-container' + ( self.options.loading_class != '' ? ' ' + self.options.loading_class : '' ) + '">' + self.options.loading_content + '</div>' ); 420 421 // ID exists, is not empty null or undefined. 422 if ( id != '' && id !== null && id !== undefined ) { 423 // set up oembed url 424 var ig_url = 'https://api.instagram.com/oembed?url=http://instagr.am/p/' + id + '/'; 425 426 $.ajax({ 427 url: ig_url, 428 dataType: "jsonp", 429 cache: false, 430 success: function (data) { 431 // set up the new content 432 content = data.html; 433 434 // now set location for new content 435 var target = $('#' + self.scope.id + ' .modaal-content-container'); 436 if ( target.length > 0) { 437 // add HTML into target region 438 target.removeClass( self.options.loading_class ); 439 target.html(content); 440 441 // now trigger an instagram refresh 442 window.instgrm.Embeds.process(); 443 } 444 }, 445 error: function() { 446 content = error_msg; 447 448 // now set location for new content 449 var target = $('#' + self.scope.id + ' .modaal-content-container'); 450 if ( target.length > 0) { 451 target.removeClass( self.options.loading_class ).addClass( self.options.ajax_error_class ); 452 target.html(content); 453 } 454 } 455 }); 456 457 } else { 458 content = error_msg; 459 } 460 461 return false; 462 }, 463 464 // Fetch Ajax Data 465 // ---------------------------------------------------------------- 466 fetch_ajax : function(url) { 467 var self = this; 468 var content = ''; 469 470 // If no accessible title, set it to 'Dialog Window' 471 if ( self.options.accessible_title == null ) { 472 self.options.accessible_title = 'Dialog Window' 473 } 474 475 if ( self.xhr !== null ){ 476 self.xhr.abort(); 477 self.xhr = null; 478 } 479 480 self.build_modal('<div class="modaal-content-container' + ( self.options.loading_class != '' ? ' ' + self.options.loading_class : '' ) + '">' + self.options.loading_content + '</div>' ); 481 482 self.xhr = $.ajax(url, { 483 success: function(data) {
484 // content fetch is successful so push it into markup 485 var target = $('#' + self.scope.id).find('.modaal-content-container'); 486 if ( target.length > 0){ 487 target.removeClass( self.options.loading_class ); 488 target.html( data ); 489 490 self.options.ajax_success.call(self, target); 491 } 492 }, 493 error: function( xhr ) { 494 // There were some errors so return an error message 495 if ( xhr.statusText == 'abort' ){ 496 return; 497 } 498 499 var target = $('#' + self.scope.id + ' .modaal-content-container'); 500 if ( target.length > 0){ 501 target.removeClass( self.options.loading_class ).addClass( self.options.ajax_error_class ); 502 target.html( 'Content could not be loaded. Please check the source and try again.' ); 503 } 504 } 505 }); 506 }, 507 508 // Create Confirm Modal 509 // ---------------------------------------------------------------- 510 create_confirm : function() { 511 var self = this; 512 var content; 513 514 content = '<div class="modaal-content-container">' + 515 '<h1 id="modaal-title">' + self.options.confirm_title + '</h1>' + 516 '<div class="modaal-confirm-content">' + self.options.confirm_content + '</div>' + 517 '<div class="modaal-confirm-wrap">' + 518 '<button type="button" class="modaal-confirm-btn modaal-ok" aria-label="Confirm">' + self.options.confirm_button_text + '</button>' + 519 '<button type="button" class="modaal-confirm-btn modaal-cancel" aria-label="Cancel">' + self.options.confirm_cancel_button_text + '</button>' + 520 '</div>' + 521 '</div>' + 522 '</div>'; 523 524 // now push content into markup 525 self.build_modal(content); 526 }, 527 528 // Create Image/Gallery Modal 529 // ---------------------------------------------------------------- 530 create_image : function() { 531 var self = this; 532 var content; 533 534 var modaal_image_markup = ''; 535 var gallery_total; 536 var prev_btn = '<button type="button" class="modaal-gallery-control modaal-gallery-prev" id="modaal-gallery-prev" aria-label="Previous image (use left arrow to change)"><span>Previous Image</span></button>'; 537 var next_btn = '<button type="button" class="modaal-gallery-control modaal-gallery-next" id="modaal-gallery-next" aria-label="Next image (use right arrow to change)"><span>Next Image</span></button>'; 538 539 // If has rel attribute 540 if ( self.$elem.is('[rel]') ) { 541 // find gallery rel 542 var gallery_group = self.$elem.attr('rel'); 543 var gallery_group_items = $('[rel="' + gallery_group + '"]'); 544 545 // remove any previous active attribute to any in the group 546 gallery_group_items.removeAttr('data-gallery-active', 'is_active'); 547 // add active attribute to the item clicked 548 self.$elem.attr('data-gallery-active', 'is_active'); 549 550 // how many in the rel grouping are there (-1 to connect with each function starting with 0) 551 gallery_total = gallery_group_items.length - 1; 552 553 // prepare array for gallery data 554 var gallery = []; 555 556 // start preparing markup 557 modaal_image_markup = '<div class="modaal-gallery-item-wrap">'; 558 559 // loop each grouping item and push it into our gallery array 560 gallery_group_items.each(function(i, item) { 561 // setup default content 562 var img_src = ''; 563 var img_alt = ''; 564 var img_description = ''; 565 var img_active = false; 566 567 var data_modaal_desc = item.getAttribute('data-modaal-desc'); 568 var data_item_active = item.getAttribute('data-gallery-active'); 569 570 // is it an img SRC or link HREF value 571 if ( item.href !== '' || item.href !== undefined ) { 572 img_src = item.href; 573 } else if ( item.src !== '' || item.src !== undefined ) { 574 img_src = item.src; 575 } 576 577 // Does it have a modaal description 578 if ( data_modaal_desc != '' && data_modaal_desc !== null && data_modaal_desc !== undefined ) { 579 img_alt = data_modaal_desc; 580 img_description = '<div class="modaal-gallery-label"><span class="modaal-accessible-hide">Image ' + (i+1) + ' - </span>' + data_modaal_desc + '</div>' 581 } else { 582 img_description = '<div class="modaal-gallery-label"><span class="modaal-accessible-hide">Image ' + (i+1) + '</span></div>'; 583 } 584 585 // is it the active item 586 if ( data_item_active ) { 587 img_active = true 588 } 589 590 // set new object for values we want 591 var gallery_item = { 592 'url': img_src, 593 'alt': img_alt, 594 'rawdesc': data_modaal_desc, 595 'desc': img_description, 596 'active': img_active 597 }; 598 599 // push object into gallery array 600 gallery.push( gallery_item ); 601 }); 602 603 // now loop through all items in the gallery and build up the markup 604 for (var i = 0; i < gallery.length; i++) { 605 // Set default active class, then check if array item active is true and update string for class 606 var is_active = ''; 607 var aria_label = gallery[i].rawdesc ? 'Image: ' + gallery[i].rawdesc : 'Image ' + i + ' no description'; 608 609 if ( gallery[i].active ) { 610 is_active = ' ' + self.private_options.active_class; 611 } 612 613 // for each item build up the markup 614 modaal_image_markup += '<div class="modaal-gallery-item gallery-item-' + i + is_active + '" aria-label="' + aria_label + '">' + 615 '<img src="../../re_adluck/js/' + gallery[i].url + '" alt=" " style="width:100%">' + 616 gallery[i].desc + 617 '</div>'; 618 } 619 620 // close off the markup for the gallery and add next/previous buttons 621 modaal_image_markup += '</div>' + prev_btn + next_btn; 622 } else { 623 // This is only a single gallery item so let's grab the necessary values 624 625 // Setup selected image 626 var this_img_src = self.$elem.attr('href'); 627 var this_img_alt_txt = ''; 628 var this_img_alt = ''; 629 var aria_label = ''; 630 631 if ( self.$elem.attr('data-modaal-desc') ) { 632 aria_label = self.$elem.attr('data-modaal-desc'); 633 this_img_alt_txt = self.$elem.attr('data-modaal-desc'); 634 this_img_alt = '<div class="modaal-gallery-label"><span class="modaal-accessible-hide">Image - </span>' + this_img_alt_txt + '</div>'; 635 } else { 636 aria_label = "Image with no description"; 637 } 638 639 // build up the html 640 modaal_image_markup = '<div class="modaal-gallery-item is_active" aria-label="' + aria_label + '">' + 641 '<img src="../../re_adluck/js/' + this_img_src + '" alt=" " style="width:100%">' + 642 this_img_alt + 643 '</div>'; 644 } 645 646 // Update content variable 647 content = modaal_image_markup; 648 649 // now push content into markup 650 self.build_modal(content); 651 652 // setup next & prev buttons 653 if ( $('.modaal-gallery-item.is_active').is('.gallery-item-0') ) { 654 $('.modaal-gallery-prev').hide(); 655 } 656 if ( $('.modaal-gallery-item.is_active').is('.gallery-item-' + gallery_total) ) { 657 $('.modaal-gallery-next').hide(); 658 } 659 }, 660 661 // Gallery Change Image 662 // ---------------------------------------------------------------- 663 gallery_update : function(direction) { 664 var self = this; 665 var this_gallery = $('#' + self.scope.id); 666 var this_gallery_item = this_gallery.find('.modaal-gallery-item'); 667 var this_gallery_total = this_gallery_item.length - 1; 668 669 // if single item, don't proceed 670 if ( this_gallery_total == 0 ) { 671 return false; 672 } 673 674 var prev_btn = this_gallery.find('.modaal-gallery-prev'), 675 next_btn = this_gallery.find('.modaal-gallery-next'); 676 677 var duration = 250; 678 679 var new_img_w = 0, 680 new_img_h = 0; 681
vendor: 9,023 bytes, lines 682-944
682 // CB: Before image change 683 var current_item = this_gallery.find( '.modaal-gallery-item.' + self.private_options.active_class ), 684 incoming_item = ( direction == 'next' ? current_item.next( '.modaal-gallery-item' ) : current_item.prev( '.modaal-gallery-item' ) ); 685 self.options.before_image_change.call(self, current_item, incoming_item); 686 687 // stop change if at start of end 688 if ( direction == 'prev' && this_gallery.find('.gallery-item-0').hasClass('is_active') ) { 689 return false; 690 } else if ( direction == 'next' && this_gallery.find('.gallery-item-' + this_gallery_total).hasClass('is_active') ) { 691 return false; 692 } 693 694 695 // lock dimensions 696 current_item.stop().animate({ 697 opacity: 0 698 }, duration, function(){ 699 // Move to appropriate image 700 incoming_item.addClass('is_next').css({ 701 'position': 'absolute', 702 'display': 'block', 703 'opacity': 0 704 }); 705 706 // Collect doc width 707 var doc_width = $(document).width(); 708 var width_threshold = doc_width > 1140 ? 280 : 50; 709 710 // start toggle to 'is_next' 711 new_img_w = this_gallery.find('.modaal-gallery-item.is_next').width(); 712 new_img_h = this_gallery.find('.modaal-gallery-item.is_next').height(); 713 714 var new_natural_w = this_gallery.find('.modaal-gallery-item.is_next img').prop('naturalWidth'); 715 var new_natural_h = this_gallery.find('.modaal-gallery-item.is_next img').prop('naturalHeight'); 716 717 // if new image is wider than doc width 718 if ( new_natural_w > (doc_width - width_threshold) ) { 719 // set new width just below doc width 720 new_img_w = doc_width - width_threshold; 721 722 // Set temp widths so we can calulate the correct height; 723 this_gallery.find('.modaal-gallery-item.is_next').css({ 'width': new_img_w }); 724 this_gallery.find('.modaal-gallery-item.is_next img').css({ 'width': new_img_w }); 725 726 // Set new height variable 727 new_img_h = this_gallery.find('.modaal-gallery-item.is_next').find('img').height(); 728 } else { 729 // new img is not wider than screen, so let's set the new dimensions 730 new_img_w = new_natural_w; 731 new_img_h = new_natural_h; 732 } 733 734 // resize gallery region 735 this_gallery.find('.modaal-gallery-item-wrap').stop().animate({ 736 'width': new_img_w, 737 'height': new_img_h 738 }, duration, function() { 739 // hide old active image 740 current_item.removeClass(self.private_options.active_class + ' ' + self.options.gallery_active_class).removeAttr('style'); 741 current_item.find('img').removeAttr('style'); 742 743 // show new image 744 incoming_item.addClass(self.private_options.active_class + ' ' + self.options.gallery_active_class).removeClass('is_next').css('position',''); 745 746 // animate in new image (now has the normal is_active class 747 incoming_item.stop().animate({ 748 opacity: 1 749 }, duration, function(){ 750 $(this).removeAttr('style').css({ 751 'width': '100%' 752 }); 753 $(this).find('img').css('width', '100%'); 754 755 // remove dimension lock 756 this_gallery.find('.modaal-gallery-item-wrap').removeAttr('style'); 757 758 // CB: After image change 759 self.options.after_image_change.call( self, incoming_item ); 760 }); 761 762 // Focus on the new gallery item 763 this_gallery.find('.modaal-gallery-item').removeAttr('tabindex'); 764 this_gallery.find('.modaal-gallery-item.' + self.private_options.active_class + '').attr('tabindex', '0').focus(); 765 766 // hide/show next/prev 767 if ( this_gallery.find('.modaal-gallery-item.' + self.private_options.active_class).is('.gallery-item-0') ) { 768 prev_btn.stop().animate({ 769 opacity: 0 770 }, 150, function(){ 771 $(this).hide(); 772 }); 773 } else { 774 prev_btn.stop().css({ 775 'display': 'block', 776 'opacity': prev_btn.css('opacity') 777 }).animate({ 778 opacity: 1 779 }, 150); 780 } 781 if ( this_gallery.find('.modaal-gallery-item.' + self.private_options.active_class).is('.gallery-item-' + this_gallery_total) ) { 782 next_btn.stop().animate({ 783 opacity: 0 784 }, 150, function(){ 785 $(this).hide(); 786 }); 787 } else { 788 next_btn.stop().css({ 789 'display': 'block', 790 'opacity': prev_btn.css('opacity') 791 }).animate({ 792 opacity: 1 793 }, 150); 794 } 795 }); 796 }); 797 }, 798 799 // Create Video Modal 800 // ---------------------------------------------------------------- 801 create_video : function(url) { 802 var self = this; 803 var content; 804 805 // video markup 806 content = '<iframe src="../../re_adluck/js/' + url + '" class="modaal-video-frame" frameborder="0" allowfullscreen></iframe>'; 807 808 // now push content into markup 809 self.build_modal('<div class="modaal-video-container">' + content + '</div>'); 810 }, 811 812 // Create iFrame Modal 813 // ---------------------------------------------------------------- 814 create_iframe : function(url) { 815 var self = this; 816 var content; 817 818 if ( self.options.width !== null || self.options.width !== undefined || self.options.height !== null || self.options.height !== undefined ) { 819 // video markup 820 content = '<iframe src="../../re_adluck/js/' + url + '" class="modaal-iframe-elem" frameborder="0" allowfullscreen></iframe>'; 821 } else { 822 content = '<div class="modaal-content-container">Please specify a width and height for your iframe</div>'; 823 } 824 825 // now push content into markup 826 self.build_modal(content); 827 }, 828 829 // Open Modaal 830 // ---------------------------------------------------------------- 831 modaal_open : function() { 832 var self = this; 833 var modal_wrapper = $( '#' + self.scope.id ); 834 var animation_type = self.options.animation; 835 836 if (animation_type === 'none' ){ 837 modal_wrapper.removeClass('modaal-start_none'); 838 self.options.after_open.call(self, modal_wrapper); 839 } 840 841 // Open with fade 842 if (animation_type === 'fade') { 843 modal_wrapper.removeClass('modaal-start_fade'); 844 } 845 846 // Open with slide down 847 if (animation_type === 'slide-down') { 848 modal_wrapper.removeClass('modaal-start_slide_down'); 849 } 850 851 var focusTarget = modal_wrapper; 852 853 // Switch focusTarget tabindex (switch from other modal if exists) 854 $('.modaal-wrapper *[tabindex=0]').removeAttr('tabindex'); 855 856 if ( self.options.type == 'image' ) { 857 focusTarget = $('#' + self.scope.id).find('.modaal-gallery-item.' + self.private_options.active_class); 858 859 } else if ( modal_wrapper.find('.modaal-iframe-elem').length ) { 860 focusTarget = modal_wrapper.find('.modaal-iframe-elem'); 861 862 } else if ( modal_wrapper.find('.modaal-video-wrap').length ) { 863 focusTarget = modal_wrapper.find('.modaal-video-wrap'); 864 865 } else { 866 focusTarget = modal_wrapper.find('.modaal-focus'); 867 868 } 869 870 // now set the focus 871 focusTarget.attr('tabindex', '0').focus(); 872 873 // Run after_open 874 if (animation_type !== 'none') { 875 // CB: after_open 876 setTimeout(function() { 877 self.options.after_open.call(self, modal_wrapper) 878 }, self.options.after_callback_delay); 879 } 880 }, 881 882 // Close Modal 883 // ---------------------------------------------------------------- 884 modaal_close : function() { 885 var self = this; 886 var modal_wrapper = $( '#' + self.scope.id ); 887 888 // CB: before_close 889 self.options.before_close.call(self, modal_wrapper); 890 891 if (self.xhr !== null){ 892 self.xhr.abort(); 893 self.xhr = null; 894 } 895 896 // Now we close the modal 897 if (self.options.animation === 'none' ){ 898 modal_wrapper.addClass('modaal-start_none'); 899 } 900 901 // Close with fade 902 if (self.options.animation === 'fade') { 903 modal_wrapper.addClass('modaal-start_fade'); 904 } 905 906 // Close with slide up (using initial slide down) 907 if (self.options.animation === 'slide-down') { 908 modal_wrapper.addClass('modaal-start_slide_down'); 909 } 910 911 // CB: after_close and remove 912 setTimeout(function() { 913 // clone inline content back to origin place 914 if (self.options.type == 'inline') { 915 $('#' + self.scope.id + ' .modaal-content-container').contents().clone(true,true).appendTo( self.$elem.attr('href') ) 916 } 917 // remove markup from dom 918 modal_wrapper.remove(); 919 // CB: after_close 920 self.options.after_close.call(self); 921 // scope is now closed 922 self.scope.is_open = false; 923 924 }, self.options.after_callback_delay); 925 926 // Call overlay hide 927 self.modaal_overlay('hide'); 928 929 // Roll back to last focus state before modal open. If was closed programmatically, this might not be set 930 if (self.lastFocus != null) { 931 self.lastFocus.focus(); 932 } 933 }, 934 935 // Overlay control (accepts action for show or hide) 936 // ---------------------------------------------------------------- 937 modaal_overlay : function(action) { 938 var self = this; 939 940 if (action == 'show') { 941 // Modal is open so update scope 942 self.scope.is_open = true; 943 944 // set body to overflow hidden if background_scroll is false
945 if (! self.options.background_scroll) { 946 self.dom.addClass('modaal-noscroll'); 947 } 948 949 // append modaal overlay 950 self.dom.append('<div class="modaal-overlay" id="' + self.scope.id + '_overlay"></div>'); 951 952 // now show 953 $('#' + self.scope.id + '_overlay').css('background', self.options.background).stop().animate({ 954 opacity: self.options.overlay_opacity 955 }, self.options.animation_speed, function(){ 956 // now open the modal 957 self.modaal_open(); 958 }); 959 960 } else if (action == 'hide') { 961 // remove body overflow lock 962 self.dom.removeClass('modaal-noscroll'); 963 964 // now hide the overlay 965 $('#' + self.scope.id + '_overlay').stop().animate({ 966 opacity: 0 967 }, self.options.animation_speed, function(){ 968 // remove overlay from dom 969 $(this).remove(); 970 }); 971 } 972 } 973 }; 974 975 // Declare the modaal jQuery method 976 // ------------------------------------------------------------ 977 $.fn.modaal = function(options) { 978 return this.each(function () { 979 var existing_modaal = $(this).data('modaal'); 980 981 if ( existing_modaal ){ 982 // Checking for string value, used for methods 983 if (typeof(options) == 'string'){ 984 switch (options) { 985 case 'close': 986 existing_modaal.modaal_close(); 987 break; 988 } 989 } 990 } else { 991 // Not a string, so let's setup the modal ready to use 992 var modaal = Object.create(Modaal); 993 modaal.init(options, this); 994 $.data(this, "modaal", modaal); 995 } 996 }); 997 }; 998 999 // Default options 1000 // ------------------------------------------------------------ 1001 $.fn.modaal.options = { 1002 1003 //General 1004 type : 'inline', 1005 animation : 'fade', 1006 animation_speed : 300, 1007 after_callback_delay : 350, 1008 is_locked : false, 1009 hide_close: false, 1010 background: '#000', 1011 overlay_opacity: '0.8', 1012 overlay_close: true, 1013 accessible_title: 'Dialog Window', 1014 start_open: false, 1015 fullscreen: false, 1016 custom_class: '', 1017 background_scroll: false, 1018 should_open: true, 1019 close_text: 'Close', 1020 close_aria_label: 'Close (Press escape to close)', 1021 width: null, 1022 height: null, 1023 1024 //Events 1025 before_open : function(){}, 1026 after_open : function(){}, 1027 before_close : function(){}, 1028 after_close : function(){}, 1029 source : function( element, src ){ 1030 return src; 1031 }, 1032 1033 //Confirm Modal 1034 confirm_button_text: 'Confirm', // text on confirm button 1035 confirm_cancel_button_text: 'Cancel', 1036 confirm_title: 'Confirm Title', // title for confirm modal 1037 confirm_content: '<p>This is the default confirm dialog content. Replace me through the options</p>', // html for confirm message 1038 confirm_callback: function() {}, 1039 confirm_cancel_callback: function() {}, 1040 1041 1042 //Gallery Modal 1043 gallery_active_class: 'gallery_active_item', 1044 before_image_change: function( current_item, incoming_item ) {}, 1045 after_image_change: function( current_item ) {}, 1046 1047 //Ajax Modal 1048 loading_content : modaal_loading_spinner, 1049 loading_class : 'is_loading', 1050 ajax_error_class : 'modaal-error', 1051 ajax_success : function(){}, 1052 1053 //Instagram 1054 instagram_id : null 1055 }; 1056 1057 // On body load (or now, if already loaded), init any modaals defined inline 1058 // Ensure this is done after $.fn.modaal and default options are declared 1059 // ---------------------------------------------------------------- 1060 $(function(){ 1061 var single_modaal = $('.modaal'); 1062 1063 if ( single_modaal.length ) { 1064 single_modaal.each(function() { 1065 var self = $(this); 1066 1067 // new empty options 1068 var options = {}; 1069 var inline_options = false; 1070 1071 // option: type 1072 if ( self.attr('data-modaal-type') ) { 1073 inline_options = true; 1074 options.type = self.attr('data-modaal-type'); 1075 } 1076 1077 // option: animation 1078 if ( self.attr('data-modaal-animation') ) { 1079 inline_options = true; 1080 options.animation = self.attr('data-modaal-animation'); 1081 } 1082 1083 // option: animation_speed 1084 if ( self.attr('data-modaal-animation-speed') ) { 1085 inline_options = true; 1086 options.animation_speed = self.attr('data-modaal-animation-speed'); 1087 } 1088 1089 // option: after_callback_delay 1090 if ( self.attr('data-modaal-after-callback-delay') ) { 1091 inline_options = true; 1092 options.after_callback_delay = self.attr('data-modaal-after-callback-delay'); 1093 } 1094 1095 // option: after_callback_delay 1096 if ( self.attr('data-modaal-is-locked') ) { 1097 inline_options = true; 1098 options.is_locked = (self.attr('data-modaal-is-locked') === 'true' ? true : false); 1099 } 1100 1101 // option: hide_close 1102 if ( self.attr('data-modaal-hide-close') ) { 1103 inline_options = true; 1104 options.hide_close = (self.attr('data-modaal-hide-close') === 'true' ? true : false); 1105 } 1106 1107 // option: background 1108 if ( self.attr('data-modaal-background') ) { 1109 inline_options = true; 1110 options.background = self.attr('data-modaal-background'); 1111 } 1112 1113 // option: overlay_opacity 1114 if ( self.attr('data-modaal-overlay-opacity') ) { 1115 inline_options = true; 1116 options.overlay_opacity = self.attr('data-modaal-overlay-opacity'); 1117 } 1118 1119 // option: overlay_close 1120 if ( self.attr('data-modaal-overlay-close') ) { 1121 inline_options = true; 1122 options.overlay_close = (self.attr('data-modaal-overlay-close') === 'false' ? false : true); 1123 } 1124 1125 // option: accessible_title 1126 if ( self.attr('data-modaal-accessible-title') ) { 1127 inline_options = true; 1128 options.accessible_title = self.attr('data-modaal-accessible-title'); 1129 } 1130 1131 // option: start_open 1132 if ( self.attr('data-modaal-start-open') ) { 1133 inline_options = true; 1134 options.start_open = (self.attr('data-modaal-start-open') === 'true' ? true : false); 1135 } 1136 1137 // option: fullscreen 1138 if ( self.attr('data-modaal-fullscreen') ) { 1139 inline_options = true; 1140 options.fullscreen = (self.attr('data-modaal-fullscreen') === 'true' ? true : false); 1141 } 1142 1143 // option: custom_class 1144 if ( self.attr('data-modaal-custom-class') ) { 1145 inline_options = true; 1146 options.custom_class = self.attr('data-modaal-custom-class'); 1147 } 1148 1149 // option: close_text 1150 if ( self.attr('data-modaal-close-text') ) { 1151 inline_options = true; 1152 options.close_text = self.attr('data-modaal-close-text'); 1153 } 1154 1155 // option: close_aria_label 1156 if ( self.attr('data-modaal-close-aria-label') ) { 1157 inline_options = true; 1158 options.close_aria_label = self.attr('data-modaal-close-aria-label'); 1159 } 1160 1161 // option: background_scroll 1162 if ( self.attr('data-modaal-background-scroll') ) { 1163 inline_options = true; 1164 options.background_scroll = (self.attr('data-modaal-background-scroll') === 'true' ? true : false); 1165 } 1166 1167 // option: width 1168 if ( self.attr('data-modaal-width') ) { 1169 inline_options = true; 1170 options.width = parseInt( self.attr('data-modaal-width') ); 1171 } 1172 1173 // option: height 1174 if ( self.attr('data-modaal-height') ) { 1175 inline_options = true; 1176 options.height = parseInt( self.attr('data-modaal-height') ); 1177 } 1178 1179 // option: confirm_button_text 1180 if ( self.attr('data-modaal-confirm-button-text') ) { 1181 inline_options = true; 1182 options.confirm_button_text = self.attr('data-modaal-confirm-button-text'); 1183 } 1184 1185 // option: confirm_cancel_button_text 1186 if ( self.attr('data-modaal-confirm-cancel-button-text') ) { 1187 inline_options = true; 1188 options.confirm_cancel_button_text = self.attr('data-modaal-confirm-cancel-button-text'); 1189 } 1190 1191 // option: confirm_title 1192 if ( self.attr('data-modaal-confirm-title') ) { 1193 inline_options = true; 1194 options.confirm_title = self.attr('data-modaal-confirm-title'); 1195 } 1196 1197 // option: confirm_content 1198 if ( self.attr('data-modaal-confirm-content') ) { 1199 inline_options = true; 1200 options.confirm_content = self.attr('data-modaal-confirm-content'); 1201 } 1202 1203 // option: gallery_active_class 1204 if ( self.attr('data-modaal-gallery-active-class') ) { 1205 inline_options = true; 1206 options.gallery_active_class = self.attr('data-modaal-gallery-active-class'); 1207 } 1208 1209 // option: loading_content 1210 if ( self.attr('data-modaal-loading-content') ) { 1211 inline_options = true; 1212 options.loading_content = self.attr('data-modaal-loading-content'); 1213 } 1214 1215 // option: loading_class 1216 if ( self.attr('data-modaal-loading-class') ) { 1217 inline_options = true; 1218 options.loading_class = self.attr('data-modaal-loading-class'); 1219 } 1220 1221 // option: ajax_error_class 1222 if ( self.attr('data-modaal-ajax-error-class') ) { 1223 inline_options = true; 1224 options.ajax_error_class = self.attr('data-modaal-ajax-error-class'); 1225 } 1226 1227 // option: start_open 1228 if ( self.attr('data-modaal-instagram-id') ) { 1229 inline_options = true; 1230 options.instagram_id = self.attr('data-modaal-instagram-id'); 1231 } 1232 1233 // now set it up for the trigger, but only if inline_options is true 1234 if ( inline_options ) { 1235 self.modaal(options); 1236 } 1237 }); 1238 } 1239 }); 1240 1241} ( jQuery, window, document ) );
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.