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