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