vendor: 19,147 bytes, lines 1-838
1 ! (function(root, factory) { 2 if (typeof define === 'function' && define.amd) { 3 define( 4 ['jquery'], function($) { 5 return factory( root, $ ); 6 } 7 ); 8 } else if (typeof exports === 'object') { 9 factory( root, require( 'jquery' ) ); 10 } else { 11 factory( root, root.jQuery || root.Zepto ); 12 } 13})( 14 this, function(global, $) { 15 16 'use strict'; 17 18 /** 19 * Name of the plugin 20 * 21 * @private 22 * @const 23 * @type {String} 24 */ 25 var PLUGIN_NAME = 'remodal'; 26 27 /** 28 * Namespace for CSS and events 29 * 30 * @private 31 * @const 32 * @type {String} 33 */ 34 var NAMESPACE = global.REMODAL_GLOBALS && global.REMODAL_GLOBALS.NAMESPACE || PLUGIN_NAME; 35 36 /** 37 * Animationstart event with vendor prefixes 38 * 39 * @private 40 * @const 41 * @type {String} 42 */ 43 var ANIMATIONSTART_EVENTS = $.map( 44 ['animationstart', 'webkitAnimationStart', 'MSAnimationStart', 'oAnimationStart'], 45 function(eventName) { 46 return eventName + '.' + NAMESPACE; 47 } 48 ).join( ' ' ); 49 50 /** 51 * Animationend event with vendor prefixes 52 * 53 * @private 54 * @const 55 * @type {String} 56 */ 57 var ANIMATIONEND_EVENTS = $.map( 58 ['animationend', 'webkitAnimationEnd', 'MSAnimationEnd', 'oAnimationEnd'], 59 function(eventName) { 60 return eventName + '.' + NAMESPACE; 61 } 62 ).join( ' ' ); 63 64 /** 65 * Default settings 66 * 67 * @private 68 * @const 69 * @type {Object} 70 */ 71 var DEFAULTS = $.extend( 72 { 73 hashTracking: true, 74 closeOnConfirm: true, 75 closeOnCancel: true, 76 closeOnEscape: true, 77 closeOnOutsideClick: true, 78 modifier: '', 79 appendTo: null 80 }, global.REMODAL_GLOBALS && global.REMODAL_GLOBALS.DEFAULTS 81 ); 82 83 /** 84 * States of the Remodal 85 * 86 * @private 87 * @const 88 * @enum {String} 89 */ 90 var STATES = { 91 CLOSING: 'closing', 92 CLOSED: 'closed', 93 OPENING: 'opening', 94 OPENED: 'opened' 95 }; 96 97 /** 98 * Reasons of the state change. 99 * 100 * @private 101 * @const 102 * @enum {String} 103 */ 104 var STATE_CHANGE_REASONS = { 105 CONFIRMATION: 'confirmation', 106 CANCELLATION: 'cancellation' 107 }; 108 109 /** 110 * Is animation supported? 111 * 112 * @private 113 * @const 114 * @type {Boolean} 115 */ 116 var IS_ANIMATION = (function() { 117 var style = document.createElement( 'div' ).style; 118 119 return style.animationName !== undefined || 120 style.WebkitAnimationName !== undefined || 121 style.MozAnimationName !== undefined || 122 style.msAnimationName !== undefined || 123 style.OAnimationName !== undefined; 124 })(); 125 126 /** 127 * Is iOS? 128 * 129 * @private 130 * @const 131 * @type {Boolean} 132 */ 133 var IS_IOS = /iPad|iPhone|iPod/.test( navigator.platform ); 134 135 /** 136 * Current modal 137 * 138 * @private 139 * @type {Remodal} 140 */ 141 var current; 142 143 /** 144 * Scrollbar position 145 * 146 * @private 147 * @type {Number} 148 */ 149 var scrollTop; 150 151 /** 152 * Returns an animation duration 153 * 154 * @private 155 * @param {jQuery} $elem 156 * @returns {Number} 157 */ 158 function getAnimationDuration($elem) { 159 if ( 160 IS_ANIMATION && 161 $elem.css( 'animation-name' ) === 'none' && 162 $elem.css( '-webkit-animation-name' ) === 'none' && 163 $elem.css( '-moz-animation-name' ) === 'none' && 164 $elem.css( '-o-animation-name' ) === 'none' && 165 $elem.css( '-ms-animation-name' ) === 'none' 166 ) { 167 return 0; 168 } 169 170 var duration = $elem.css( 'animation-duration' ) || 171 $elem.css( '-webkit-animation-duration' ) || 172 $elem.css( '-moz-animation-duration' ) || 173 $elem.css( '-o-animation-duration' ) || 174 $elem.css( '-ms-animation-duration' ) || 175 '0s'; 176 177 var delay = $elem.css( 'animation-delay' ) || 178 $elem.css( '-webkit-animation-delay' ) || 179 $elem.css( '-moz-animation-delay' ) || 180 $elem.css( '-o-animation-delay' ) || 181 $elem.css( '-ms-animation-delay' ) || 182 '0s'; 183 184 var iterationCount = $elem.css( 'animation-iteration-count' ) || 185 $elem.css( '-webkit-animation-iteration-count' ) || 186 $elem.css( '-moz-animation-iteration-count' ) || 187 $elem.css( '-o-animation-iteration-count' ) || 188 $elem.css( '-ms-animation-iteration-count' ) || 189 '1'; 190 191 var max; 192 var len; 193 var num; 194 var i; 195 196 duration = duration.split( ', ' ); 197 delay = delay.split( ', ' ); 198 iterationCount = iterationCount.split( ', ' ); 199 200 // The 'duration' size is the same as the 'delay' size 201 for (i = 0, len = duration.length, max = Number.NEGATIVE_INFINITY; i < len; i++) { 202 num = parseFloat( duration[i] ) * parseInt( iterationCount[i], 10 ) + parseFloat( delay[i] ); 203 204 if (num > max) { 205 max = num; 206 } 207 } 208 209 return max; 210 } 211 212 /** 213 * Returns a scrollbar width 214 * 215 * @private 216 * @returns {Number} 217 */ 218 function getScrollbarWidth() { 219 if ($( document ).height() <= $( window ).height()) { 220 return 0; 221 } 222 223 var outer = document.createElement( 'div' ); 224 var inner = document.createElement( 'div' ); 225 var widthNoScroll; 226 var widthWithScroll; 227 228 outer.style.visibility = 'hidden'; 229 outer.style.width = '100px'; 230 document.body.appendChild( outer ); 231 232 widthNoScroll = outer.offsetWidth; 233 234 // Force scrollbars 235 outer.style.overflow = 'scroll'; 236 237 // Add inner div 238 inner.style.width = '100%'; 239 outer.appendChild( inner ); 240 241 widthWithScroll = inner.offsetWidth; 242 243 // Remove divs 244 outer.parentNode.removeChild( outer ); 245 246 return widthNoScroll - widthWithScroll; 247 } 248 249 /** 250 * Locks the screen 251 * 252 * @private 253 */ 254 function lockScreen() { 255 if (IS_IOS) { 256 return; 257 } 258 259 var $html = $( 'html' ); 260 var lockedClass = namespacify( 'is-locked' ); 261 var paddingRight; 262 var $body; 263 264 if ( ! $html.hasClass( lockedClass )) { 265 $body = $( document.body ); 266 267 // Zepto does not support '-=', '+=' in the `css` method 268 paddingRight = parseInt( $body.css( 'padding-right' ), 10 ) + getScrollbarWidth(); 269 270 $body.css( 'padding-right', paddingRight + 'px' ); 271 $html.addClass( lockedClass ); 272 } 273 } 274 275 /** 276 * Unlocks the screen 277 * 278 * @private 279 */ 280 function unlockScreen() { 281 if (IS_IOS) { 282 return; 283 } 284 285 var $html = $( 'html' ); 286 var lockedClass = namespacify( 'is-locked' ); 287 var paddingRight; 288 var $body; 289 290 if ($html.hasClass( lockedClass )) { 291 $body = $( document.body ); 292 293 // Zepto does not support '-=', '+=' in the `css` method 294 paddingRight = parseInt( $body.css( 'padding-right' ), 10 ) - getScrollbarWidth(); 295 296 $body.css( 'padding-right', paddingRight + 'px' ); 297 $html.removeClass( lockedClass ); 298 } 299 } 300 301 /** 302 * Sets a state for an instance 303 * 304 * @private 305 * @param {Remodal} instance 306 * @param {STATES} state 307 * @param {Boolean} isSilent If true, Remodal does not trigger events 308 * @param {String} Reason of a state change. 309 */ 310 function setState(instance, state, isSilent, reason) { 311 312 var newState = namespacify( 'is', state ); 313 var allStates = [namespacify( 'is', STATES.CLOSING ), 314 namespacify( 'is', STATES.OPENING ), 315 namespacify( 'is', STATES.CLOSED ), 316 namespacify( 'is', STATES.OPENED )].join( ' ' ); 317 318 instance.$bg 319 .removeClass( allStates ) 320 .addClass( newState ); 321 322 instance.$overlay 323 .removeClass( allStates ) 324 .addClass( newState ); 325 326 instance.$wrapper 327 .removeClass( allStates ) 328 .addClass( newState ); 329 330 instance.$modal 331 .removeClass( allStates ) 332 .addClass( newState ); 333 334 instance.state = state; 335 ! isSilent && instance.$modal.trigger( 336 { 337 type: state, 338 reason: reason 339 }, [{ reason: reason }] 340 ); 341 } 342 343 /** 344 * Synchronizes with the animation 345 * 346 * @param {Function} doBeforeAnimation 347 * @param {Function} doAfterAnimation 348 * @param {Remodal} instance 349 */ 350 function syncWithAnimation(doBeforeAnimation, doAfterAnimation, instance) { 351 var runningAnimationsCount = 0; 352 353 var handleAnimationStart = function(e) { 354 if (e.target !== this) { 355 return; 356 } 357 358 runningAnimationsCount++; 359 }; 360 361 var handleAnimationEnd = function(e) { 362 if (e.target !== this) { 363 return; 364 } 365 366 if (--runningAnimationsCount === 0) { 367 368 // Remove event listeners 369 $.each( 370 ['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) { 371 instance[elemName].off( ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS ); 372 } 373 ); 374 375 doAfterAnimation(); 376 } 377 }; 378 379 $.each( 380 ['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) { 381 instance[elemName] 382 .on( ANIMATIONSTART_EVENTS, handleAnimationStart ) 383 .on( ANIMATIONEND_EVENTS, handleAnimationEnd ); 384 } 385 ); 386 387 doBeforeAnimation(); 388 389 // If the animation is not supported by a browser or its duration is 0 390 if ( 391 getAnimationDuration( instance.$bg ) === 0 && 392 getAnimationDuration( instance.$overlay ) === 0 && 393 getAnimationDuration( instance.$wrapper ) === 0 && 394 getAnimationDuration( instance.$modal ) === 0 395 ) { 396 397 // Remove event listeners 398 $.each( 399 ['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) { 400 instance[elemName].off( ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS ); 401 } 402 ); 403 404 doAfterAnimation(); 405 } 406 } 407 408 /** 409 * Closes immediately 410 * 411 * @private 412 * @param {Remodal} instance 413 */ 414 function halt(instance) { 415 if (instance.state === STATES.CLOSED) { 416 return; 417 } 418 419 $.each( 420 ['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) { 421 instance[elemName].off( ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS ); 422 } 423 ); 424 425 instance.$bg.removeClass( instance.settings.modifier ); 426 instance.$overlay.removeClass( instance.settings.modifier ).hide(); 427 instance.$wrapper.hide(); 428 unlockScreen(); 429 setState( instance, STATES.CLOSED, true ); 430 } 431 432 /** 433 * Parses a string with options 434 * 435 * @private 436 * @param str 437 * @returns {Object} 438 */ 439 function parseOptions(str) { 440 var obj = {}; 441 var arr; 442 var len; 443 var val; 444 var i; 445 446 // Remove spaces before and after delimiters 447 str = str.replace( /\s*:\s*/g, ':' ).replace( /\s*,\s*/g, ',' ); 448 449 // Parse a string 450 arr = str.split( ',' ); 451 for (i = 0, len = arr.length; i < len; i++) { 452 arr[i] = arr[i].split( ':' ); 453 val = arr[i][1]; 454 455 // Convert a string value if it is like a boolean 456 if (typeof val === 'string' || val instanceof String) { 457 val = val === 'true' || (val === 'false' ? false : val); 458 } 459 460 // Convert a string value if it is like a number 461 if (typeof val === 'string' || val instanceof String) { 462 val = ! isNaN( val ) ? +val : val; 463 } 464 465 obj[arr[i][0]] = val; 466 } 467 468 return obj; 469 } 470 471 /** 472 * Generates a string separated by dashes and prefixed with NAMESPACE 473 * 474 * @private 475 * @param {...String} 476 * @returns {String} 477 */ 478 function namespacify() { 479 var result = NAMESPACE; 480 481 for (var i = 0; i < arguments.length; ++i) { 482 result += '-' + arguments[i]; 483 } 484 485 return result; 486 } 487 488 /** 489 * Handles the hashchange event 490 * 491 * @private 492 * @listens hashchange 493 */ 494 function handleHashChangeEvent() { 495 var id = location.hash.replace( '#', '' ); 496 var instance; 497 var $elem; 498 499 if ( ! id) { 500 501 // Check if we have currently opened modal and animation was completed 502 if (current && current.state === STATES.OPENED && current.settings.hashTracking) { 503 current.close(); 504 } 505 } else { 506 507 // Catch syntax error if your hash is bad 508 try { 509 $elem = $( 510 '[data-' + PLUGIN_NAME + '-id="' + id + '"]' 511 ); 512 } catch (err) { 513 } 514 515 if ($elem && $elem.length) { 516 instance = $[PLUGIN_NAME].lookup[$elem.data( PLUGIN_NAME )]; 517 518 if (instance && instance.settings.hashTracking) { 519 instance.open(); 520 } 521 } 522 523 } 524 } 525 526 /** 527 * Remodal constructor 528 * 529 * @constructor 530 * @param {jQuery} $modal 531 * @param {Object} options 532 */ 533 function Remodal($modal, options) { 534 var $body = $( document.body ); 535 var $appendTo = $body; 536 var remodal = this; 537 538 remodal.settings = $.extend( {}, DEFAULTS, options ); 539 remodal.index = $[PLUGIN_NAME].lookup.push( remodal ) - 1; 540 remodal.state = STATES.CLOSED; 541 542 remodal.$overlay = $( '.' + namespacify( 'overlay' ) ); 543 544 if (remodal.settings.appendTo !== null && remodal.settings.appendTo.length) { 545 $appendTo = $( remodal.settings.appendTo ); 546 } 547 548 if ( ! remodal.$overlay.length) { 549 remodal.$overlay = $( '<div>' ).addClass( namespacify( 'overlay' ) + ' ' + namespacify( 'is', STATES.CLOSED ) ).hide(); 550 $appendTo.append( remodal.$overlay ); 551 } 552 553 remodal.$bg = $( '.' + namespacify( 'bg' ) ).addClass( namespacify( 'is', STATES.CLOSED ) ); 554 555 remodal.$modal = $modal 556 .addClass( 557 NAMESPACE + ' ' + 558 namespacify( 'is-initialized' ) + ' ' + 559 remodal.settings.modifier + ' ' + 560 namespacify( 'is', STATES.CLOSED ) 561 ) 562 .attr( 'tabindex', '-1' ); 563 564 remodal.$wrapper = $( '<div>' ) 565 .addClass( 566 namespacify( 'wrapper' ) + ' ' + 567 remodal.settings.modifier + ' ' + 568 namespacify( 'is', STATES.CLOSED ) 569 ) 570 .hide() 571 .append( remodal.$modal ); 572 $appendTo.append( remodal.$wrapper ); 573 574 // Add the event listener for the close button 575 remodal.$wrapper.on( 576 'click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="close"]', function(e) { 577 e.preventDefault(); 578 579 remodal.close(); 580 } 581 ); 582 583 // Add the event listener for the cancel button 584 remodal.$wrapper.on( 585 'click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="cancel"]', function(e) { 586 e.preventDefault(); 587 588 remodal.$modal.trigger( STATE_CHANGE_REASONS.CANCELLATION ); 589 590 if (remodal.settings.closeOnCancel) { 591 remodal.close( STATE_CHANGE_REASONS.CANCELLATION ); 592 } 593 } 594 ); 595 596 // Add the event listener for the confirm button 597 remodal.$wrapper.on( 598 'click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="confirm"]', function(e) { 599 e.preventDefault(); 600 601 remodal.$modal.trigger( STATE_CHANGE_REASONS.CONFIRMATION ); 602 603 if (remodal.settings.closeOnConfirm) { 604 remodal.close( STATE_CHANGE_REASONS.CONFIRMATION ); 605 } 606 } 607 ); 608 609 // Add the event listener for the overlay 610 remodal.$wrapper.on( 611 'click.' + NAMESPACE, function(e) { 612 var $target = $( e.target ); 613 614 if ( ! $target.hasClass( namespacify( 'wrapper' ) )) { 615 return; 616 } 617 618 if (remodal.settings.closeOnOutsideClick) { 619 remodal.close(); 620 } 621 } 622 ); 623 } 624 625 /** 626 * Opens a modal window 627 * 628 * @public 629 */ 630 Remodal.prototype.open = function() { 631 var remodal = this; 632 var id; 633 634 // Check if the animation was completed 635 if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING) { 636 return; 637 } 638 639 id = remodal.$modal.attr( 'data-' + PLUGIN_NAME + '-id' ); 640 641 if (id && remodal.settings.hashTracking) { 642 scrollTop = $( window ).scrollTop(); 643 location.hash = id; 644 } 645 646 if (current && current !== remodal) { 647 halt( current ); 648 } 649 650 current = remodal; 651 lockScreen(); 652 remodal.$bg.addClass( remodal.settings.modifier ); 653 remodal.$overlay.addClass( remodal.settings.modifier ).show(); 654 remodal.$wrapper.show().scrollTop( 0 ); 655 remodal.$modal.focus(); 656 657 syncWithAnimation( 658 function() { 659 setState( remodal, STATES.OPENING ); 660 }, 661 function() { 662 setState( remodal, STATES.OPENED ); 663 }, 664 remodal 665 ); 666 }; 667 668 /** 669 * Closes a modal window 670 * 671 * @public 672 * @param {String} reason 673 */ 674 Remodal.prototype.close = function(reason) { 675 var remodal = this; 676 677 // Check if the animation was completed 678 if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING || remodal.state === STATES.CLOSED) { 679 return; 680 } 681 682 if ( 683 remodal.settings.hashTracking && 684 remodal.$modal.attr( 'data-' + PLUGIN_NAME + '-id' ) === location.hash.substr( 1 ) 685 ) { 686 location.hash = ''; 687 $( window ).scrollTop( scrollTop ); 688 } 689 690 syncWithAnimation( 691 function() { 692 setState( remodal, STATES.CLOSING, false, reason ); 693 }, 694 function() { 695 remodal.$bg.removeClass( remodal.settings.modifier ); 696 remodal.$overlay.removeClass( remodal.settings.modifier ).hide(); 697 remodal.$wrapper.hide(); 698 unlockScreen(); 699 700 setState( remodal, STATES.CLOSED, false, reason ); 701 }, 702 remodal 703 ); 704 }; 705 706 /** 707 * Returns a current state of a modal 708 * 709 * @public 710 * @returns {STATES} 711 */ 712 Remodal.prototype.getState = function() { 713 return this.state; 714 }; 715 716 /** 717 * Destroys a modal 718 * 719 * @public 720 */ 721 Remodal.prototype.destroy = function() { 722 var lookup = $[PLUGIN_NAME].lookup; 723 var instanceCount; 724 725 halt( this ); 726 this.$wrapper.remove(); 727 728 delete lookup[this.index]; 729 instanceCount = $.grep( 730 lookup, function(instance) { 731 return ! ! instance; 732 } 733 ).length; 734 735 if (instanceCount === 0) { 736 this.$overlay.remove(); 737 this.$bg.removeClass( 738 namespacify( 'is', STATES.CLOSING ) + ' ' + 739 namespacify( 'is', STATES.OPENING ) + ' ' + 740 namespacify( 'is', STATES.CLOSED ) + ' ' + 741 namespacify( 'is', STATES.OPENED ) 742 ); 743 } 744 }; 745 746 /** 747 * Special plugin object for instances 748 * 749 * @public 750 * @type {Object} 751 */ 752 $[PLUGIN_NAME] = { 753 lookup: [] 754 }; 755 756 /** 757 * Plugin constructor 758 * 759 * @constructor 760 * @param {Object} options 761 * @returns {JQuery} 762 */ 763 $.fn[PLUGIN_NAME] = function(opts) { 764 var instance; 765 var $elem; 766 767 this.each( 768 function(index, elem) { 769 $elem = $( elem ); 770 771 if ($elem.data( PLUGIN_NAME ) == null) { 772 instance = new Remodal( $elem, opts ); 773 $elem.data( PLUGIN_NAME, instance.index ); 774 775 if ( 776 instance.settings.hashTracking && 777 $elem.attr( 'data-' + PLUGIN_NAME + '-id' ) === location.hash.substr( 1 ) 778 ) { 779 instance.open(); 780 } 781 } else { 782 instance = $[PLUGIN_NAME].lookup[$elem.data( PLUGIN_NAME )]; 783 } 784 } 785 ); 786 787 return instance; 788 }; 789 790 $( document ).ready( 791 function() { 792 793 // data-remodal-target opens a modal window with the special Id 794 $( document ).on( 795 'click', '[data-' + PLUGIN_NAME + '-target]', function(e) { 796 e.preventDefault(); 797 798 var elem = e.currentTarget; 799 var id = elem.getAttribute( 'data-' + PLUGIN_NAME + '-target' ); 800 var $target = $( '[data-' + PLUGIN_NAME + '-id="' + id + '"]' ); 801 802 $[PLUGIN_NAME].lookup[$target.data( PLUGIN_NAME )].open(); 803 } 804 ); 805 806 // Auto initialization of modal windows 807 // They should have the 'remodal' class attribute 808 // Also you can write the `data-remodal-options` attribute to pass params into the modal 809 $( document ).find( '.' + NAMESPACE ).each( 810 function(i, container) { 811 var $container = $( container ); 812 var options = $container.data( PLUGIN_NAME + '-options' ); 813 814 if ( ! options) { 815 options = {}; 816 } else if (typeof options === 'string' || options instanceof String) { 817 options = parseOptions( options ); 818 } 819 820 $container[PLUGIN_NAME](options); 821 } 822 ); 823 824 // Handles the keydown event 825 $( document ).on( 826 'keydown.' + NAMESPACE, function(e) { 827 if (current && current.settings.closeOnEscape && current.state === STATES.OPENED && e.keyCode === 27) { 828 current.close(); 829 } 830 } 831 ); 832 833 // Handles the hashchange event 834 $( window ).on( 'hashchange.' + NAMESPACE, handleHashChangeEvent ); 835 } 836 ); 837 } 838);
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.