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