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