vendor: 118,817 bytes, lines 1-3058
1/** 2 * alertifyjs 1.10.0 http://alertifyjs.com 3 * AlertifyJS is a javascript framework for developing pretty browser dialogs and notifications. 4 * Copyright 2017 Mohammad Younes <[email protected]> (http://alertifyjs.com) 5 * Licensed under GPL 3 <https://opensource.org/licenses/gpl-3.0>*/ 6( function ( window ) { 7 'use strict'; 8 9 /** 10 * Keys enum 11 * @type {Object} 12 */ 13 var keys = { 14 ENTER: 13, 15 ESC: 27, 16 F1: 112, 17 F12: 123, 18 LEFT: 37, 19 RIGHT: 39 20 }; 21 /** 22 * Default options 23 * @type {Object} 24 */ 25 var defaults = { 26 autoReset:true, 27 basic:false, 28 closable:true, 29 closableByDimmer:true, 30 frameless:false, 31 maintainFocus:true, //global default not per instance, applies to all dialogs 32 maximizable:true, 33 modal:true, 34 movable:true, 35 moveBounded:false, 36 overflow:true, 37 padding: true, 38 pinnable:true, 39 pinned:true, 40 preventBodyShift:false, //global default not per instance, applies to all dialogs 41 resizable:true, 42 startMaximized:false, 43 transition:'pulse', 44 notifier:{ 45 delay:5, 46 position:'bottom-right', 47 closeButton:false 48 }, 49 glossary:{ 50 title:'AlertifyJS', 51 ok: 'OK', 52 cancel: 'Cancel', 53 acccpt: 'Accept', 54 deny: 'Deny', 55 confirm: 'Confirm', 56 decline: 'Decline', 57 close: 'Close', 58 maximize: 'Maximize', 59 restore: 'Restore', 60 }, 61 theme:{ 62 input:'ajs-input', 63 ok:'ajs-ok', 64 cancel:'ajs-cancel', 65 } 66 }; 67 68 //holds open dialogs instances 69 var openDialogs = []; 70 71 /** 72 * [Helper] Adds the specified class(es) to the element. 73 * 74 * @element {node} The element 75 * @className {string} One or more space-separated classes to be added to the class attribute of the element. 76 * 77 * @return {undefined} 78 */ 79 function addClass(element,classNames){ 80 element.className += ' ' + classNames; 81 } 82 83 /** 84 * [Helper] Removes the specified class(es) from the element. 85 * 86 * @element {node} The element 87 * @className {string} One or more space-separated classes to be removed from the class attribute of the element. 88 * 89 * @return {undefined} 90 */ 91 function removeClass(element, classNames) { 92 var original = element.className.split(' '); 93 var toBeRemoved = classNames.split(' '); 94 for (var x = 0; x < toBeRemoved.length; x += 1) { 95 var index = original.indexOf(toBeRemoved[x]); 96 if (index > -1){ 97 original.splice(index,1); 98 } 99 } 100 element.className = original.join(' '); 101 } 102 103 /** 104 * [Helper] Checks if the document is RTL 105 * 106 * @return {Boolean} True if the document is RTL, false otherwise. 107 */ 108 function isRightToLeft(){ 109 return window.getComputedStyle(document.body).direction === 'rtl'; 110 } 111 /** 112 * [Helper] Get the document current scrollTop 113 * 114 * @return {Number} current document scrollTop value 115 */ 116 function getScrollTop(){ 117 return ((document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop); 118 } 119 120 /** 121 * [Helper] Get the document current scrollLeft 122 * 123 * @return {Number} current document scrollLeft value 124 */ 125 function getScrollLeft(){ 126 return ((document.documentElement && document.documentElement.scrollLeft) || document.body.scrollLeft); 127 } 128 129 /** 130 * Helper: clear contents 131 * 132 */ 133 function clearContents(element){ 134 while (element.lastChild) { 135 element.removeChild(element.lastChild); 136 } 137 } 138 /** 139 * Extends a given prototype by merging properties from base into sub. 140 * 141 * @sub {Object} sub The prototype being overwritten. 142 * @base {Object} base The prototype being written. 143 * 144 * @return {Object} The extended prototype. 145 */ 146 function copy(src) { 147 if(null === src){ 148 return src; 149 } 150 var cpy; 151 if(Array.isArray(src)){ 152 cpy = []; 153 for(var x=0;x<src.length;x+=1){ 154 cpy.push(copy(src[x])); 155 } 156 return cpy; 157 } 158 159 if(src instanceof Date){ 160 return new Date(src.getTime()); 161 } 162 163 if(src instanceof RegExp){ 164 cpy = new RegExp(src.source); 165 cpy.global = src.global; 166 cpy.ignoreCase = src.ignoreCase; 167 cpy.multiline = src.multiline; 168 cpy.lastIndex = src.lastIndex; 169 return cpy; 170 } 171 172 if(typeof src === 'object'){ 173 cpy = {}; 174 // copy dialog pototype over definition. 175 for (var prop in src) { 176 if (src.hasOwnProperty(prop)) { 177 cpy[prop] = copy(src[prop]); 178 } 179 } 180 return cpy; 181 } 182 return src; 183 } 184 /** 185 * Helper: destruct the dialog 186 * 187 */ 188 function destruct(instance, initialize){ 189 //delete the dom and it's references. 190 var root = instance.elements.root; 191 root.parentNode.removeChild(root); 192 delete instance.elements; 193 //copy back initial settings. 194 instance.settings = copy(instance.__settings); 195 //re-reference init function. 196 instance.__init = initialize; 197 //delete __internal variable to allow re-initialization. 198 delete instance.__internal; 199 } 200 201 /** 202 * Use a closure to return proper event listener method. Try to use 203 * `addEventListener` by default but fallback to `attachEvent` for 204 * unsupported browser. The closure simply ensures that the test doesn't 205 * happen every time the method is called. 206 * 207 * @param {Node} el Node element 208 * @param {String} event Event type 209 * @param {Function} fn Callback of event 210 * @return {Function} 211 */ 212 var on = (function () { 213 if (document.addEventListener) { 214 return function (el, event, fn, useCapture) { 215 el.addEventListener(event, fn, useCapture === true); 216 }; 217 } else if (document.attachEvent) { 218 return function (el, event, fn) { 219 el.attachEvent('on' + event, fn); 220 }; 221 } 222 }()); 223 224 /** 225 * Use a closure to return proper event listener method. Try to use 226 * `removeEventListener` by default but fallback to `detachEvent` for 227 * unsupported browser. The closure simply ensures that the test doesn't 228 * happen every time the method is called. 229 * 230 * @param {Node} el Node element 231 * @param {String} event Event type 232 * @param {Function} fn Callback of event 233 * @return {Function} 234 */ 235 var off = (function () { 236 if (document.removeEventListener) { 237 return function (el, event, fn, useCapture) { 238 el.removeEventListener(event, fn, useCapture === true); 239 }; 240 } else if (document.detachEvent) { 241 return function (el, event, fn) { 242 el.detachEvent('on' + event, fn); 243 }; 244 } 245 }()); 246 247 /** 248 * Prevent default event from firing 249 * 250 * @param {Event} event Event object 251 * @return {undefined} 252 253 function prevent ( event ) { 254 if ( event ) { 255 if ( event.preventDefault ) { 256 event.preventDefault(); 257 } else { 258 event.returnValue = false; 259 } 260 } 261 } 262 */ 263 var transition = (function () { 264 var t, type; 265 var supported = false; 266 var transitions = { 267 'animation' : 'animationend', 268 'OAnimation' : 'oAnimationEnd oanimationend', 269 'msAnimation' : 'MSAnimationEnd', 270 'MozAnimation' : 'animationend', 271 'WebkitAnimation' : 'webkitAnimationEnd' 272 }; 273 274 for (t in transitions) { 275 if (document.documentElement.style[t] !== undefined) { 276 type = transitions[t]; 277 supported = true; 278 break; 279 } 280 } 281 282 return { 283 type: type, 284 supported: supported 285 }; 286 }()); 287 288 /** 289 * Creates event handler delegate that sends the instance as last argument. 290 * 291 * @return {Function} a function wrapper which sends the instance as last argument. 292 */ 293 function delegate(context, method) { 294 return function () { 295 if (arguments.length > 0) { 296 var args = []; 297 for (var x = 0; x < arguments.length; x += 1) { 298 args.push(arguments[x]); 299 } 300 args.push(context); 301 return method.apply(context, args); 302 } 303 return method.apply(context, [null, context]); 304 }; 305 } 306 /** 307 * Helper for creating a dialog close event. 308 * 309 * @return {object} 310 */ 311 function createCloseEvent(index, button) { 312 return { 313 index: index, 314 button: button, 315 cancel: false 316 }; 317 } 318 /** 319 * Helper for dispatching events. 320 * 321 * @param {string} evenType The type of the event to disptach. 322 * @param {object} instance The dialog instance disptaching the event. 323 * 324 * @return {any} The result of the invoked function. 325 */ 326 function dispatchEvent(eventType, instance) { 327 if ( typeof instance.get(eventType) === 'function' ) { 328 return instance.get(eventType).call(instance); 329 } 330 } 331 332 333 /** 334 * Super class for all dialogs 335 * 336 * @return {Object} base dialog prototype 337 */ 338 var dialog = (function () { 339 var //holds the list of used keys. 340 usedKeys = [], 341 //dummy variable, used to trigger dom reflow. 342 reflow = null, 343 //condition for detecting safari 344 isSafari = window.navigator.userAgent.indexOf('Safari') > -1 && window.navigator.userAgent.indexOf('Chrome') < 0, 345 //dialog building blocks 346 templates = { 347 dimmer:'<div class="ajs-dimmer"></div>', 348 /*tab index required to fire click event before body focus*/ 349 modal: '<div class="ajs-modal" tabindex="0"></div>', 350 dialog: '<div class="ajs-dialog" tabindex="0"></div>', 351 reset: '<button class="ajs-reset"></button>', 352 commands: '<div class="ajs-commands"><button class="ajs-pin"></button><button class="ajs-maximize"></button><button class="ajs-close"></button></div>', 353 header: '<div class="ajs-header"></div>', 354 body: '<div class="ajs-body"></div>', 355 content: '<div class="ajs-content"></div>', 356 footer: '<div class="ajs-footer"></div>', 357 buttons: { primary: '<div class="ajs-primary ajs-buttons"></div>', auxiliary: '<div class="ajs-auxiliary ajs-buttons"></div>' }, 358 button: '<button class="ajs-button"></button>', 359 resizeHandle: '<div class="ajs-handle"></div>', 360 }, 361 //common class names 362 classes = { 363 animationIn: 'ajs-in', 364 animationOut: 'ajs-out', 365 base: 'alertify', 366 basic:'ajs-basic', 367 capture: 'ajs-capture', 368 closable:'ajs-closable', 369 fixed: 'ajs-fixed', 370 frameless:'ajs-frameless', 371 hidden: 'ajs-hidden', 372 maximize: 'ajs-maximize', 373 maximized: 'ajs-maximized', 374 maximizable:'ajs-maximizable', 375 modeless: 'ajs-modeless', 376 movable: 'ajs-movable', 377 noSelection: 'ajs-no-selection', 378 noOverflow: 'ajs-no-overflow', 379 noPadding:'ajs-no-padding', 380 pin:'ajs-pin', 381 pinnable:'ajs-pinnable', 382 prefix: 'ajs-', 383 resizable: 'ajs-resizable', 384 restore: 'ajs-restore', 385 shake:'ajs-shake', 386 unpinned:'ajs-unpinned', 387 }; 388 389 /** 390 * Helper: initializes the dialog instance 391 * 392 * @return {Number} The total count of currently open modals. 393 */ 394 function initialize(instance){ 395 396 if(!instance.__internal){ 397 398 //no need to expose init after this. 399 delete instance.__init; 400 401 //keep a copy of initial dialog settings 402 if(!instance.__settings){ 403 instance.__settings = copy(instance.settings); 404 } 405 //in case the script was included before body. 406 //after first dialog gets initialized, it won't be null anymore! 407 if(null === reflow){ 408 // set tabindex attribute on body element this allows script to give it 409 // focus after the dialog is closed 410 document.body.setAttribute( 'tabindex', '0' ); 411 } 412 413 //get dialog buttons/focus setup 414 var setup; 415 if(typeof instance.setup === 'function'){ 416 setup = instance.setup(); 417 setup.options = setup.options || {}; 418 setup.focus = setup.focus || {}; 419 }else{ 420 setup = { 421 buttons:[], 422 focus:{ 423 element:null, 424 select:false 425 }, 426 options:{ 427 } 428 }; 429 } 430 431 //initialize hooks object. 432 if(typeof instance.hooks !== 'object'){ 433 instance.hooks = {}; 434 } 435 436 //copy buttons defintion 437 var buttonsDefinition = []; 438 if(Array.isArray(setup.buttons)){ 439 for(var b=0;b<setup.buttons.length;b+=1){ 440 var ref = setup.buttons[b], 441 cpy = {}; 442 for (var i in ref) { 443 if (ref.hasOwnProperty(i)) { 444 cpy[i] = ref[i]; 445 } 446 } 447 buttonsDefinition.push(cpy); 448 } 449 } 450 451 var internal = instance.__internal = { 452 /** 453 * Flag holding the open state of the dialog 454 * 455 * @type {Boolean} 456 */ 457 isOpen:false, 458 /** 459 * Active element is the element that will receive focus after 460 * closing the dialog. It defaults as the body tag, but gets updated 461 * to the last focused element before the dialog was opened. 462 * 463 * @type {Node} 464 */ 465 activeElement:document.body, 466 timerIn:undefined, 467 timerOut:undefined, 468 buttons: buttonsDefinition, 469 focus: setup.focus, 470 options: { 471 title: undefined, 472 modal: undefined, 473 basic:undefined, 474 frameless:undefined, 475 pinned: undefined, 476 movable: undefined, 477 moveBounded:undefined, 478 resizable: undefined, 479 autoReset: undefined, 480 closable: undefined, 481 closableByDimmer: undefined, 482 maximizable: undefined, 483 startMaximized: undefined, 484 pinnable: undefined, 485 transition: undefined, 486 padding:undefined, 487 overflow:undefined, 488 onshow:undefined, 489 onclosing:undefined, 490 onclose:undefined, 491 onfocus:undefined, 492 onmove:undefined, 493 onmoved:undefined, 494 onresize:undefined, 495 onresized:undefined, 496 onmaximize:undefined, 497 onmaximized:undefined, 498 onrestore:undefined, 499 onrestored:undefined 500 }, 501 resetHandler:undefined, 502 beginMoveHandler:undefined, 503 beginResizeHandler:undefined, 504 bringToFrontHandler:undefined, 505 modalClickHandler:undefined, 506 buttonsClickHandler:undefined, 507 commandsClickHandler:undefined, 508 transitionInHandler:undefined, 509 transitionOutHandler:undefined, 510 destroy:undefined 511 }; 512 513 var elements = {}; 514 //root node 515 elements.root = document.createElement('div'); 516 517 elements.root.className = classes.base + ' ' + classes.hidden + ' '; 518 519 elements.root.innerHTML = templates.dimmer + templates.modal; 520 521 //dimmer 522 elements.dimmer = elements.root.firstChild; 523 524 //dialog 525 elements.modal = elements.root.lastChild; 526 elements.modal.innerHTML = templates.dialog; 527 elements.dialog = elements.modal.firstChild; 528 elements.dialog.innerHTML = templates.reset + templates.commands + templates.header + templates.body + templates.footer + templates.resizeHandle + templates.reset; 529 530 //reset links 531 elements.reset = []; 532 elements.reset.push(elements.dialog.firstChild); 533 elements.reset.push(elements.dialog.lastChild); 534 535 //commands 536 elements.commands = {}; 537 elements.commands.container = elements.reset[0].nextSibling; 538 elements.commands.pin = elements.commands.container.firstChild; 539 elements.commands.maximize = elements.commands.pin.nextSibling; 540 elements.commands.close = elements.commands.maximize.nextSibling; 541 542 //header 543 elements.header = elements.commands.container.nextSibling; 544 545 //body 546 elements.body = elements.header.nextSibling; 547 elements.body.innerHTML = templates.content; 548 elements.content = elements.body.firstChild; 549 550 //footer 551 elements.footer = elements.body.nextSibling; 552 elements.footer.innerHTML = templates.buttons.auxiliary + templates.buttons.primary; 553 554 //resize handle 555 elements.resizeHandle = elements.footer.nextSibling; 556 557 //buttons 558 elements.buttons = {}; 559 elements.buttons.auxiliary = elements.footer.firstChild; 560 elements.buttons.primary = elements.buttons.auxiliary.nextSibling; 561 elements.buttons.primary.innerHTML = templates.button; 562 elements.buttonTemplate = elements.buttons.primary.firstChild; 563 //remove button template 564 elements.buttons.primary.removeChild(elements.buttonTemplate); 565 566 for(var x=0; x < instance.__internal.buttons.length; x+=1) { 567 var button = instance.__internal.buttons[x]; 568 569 // add to the list of used keys. 570 if(usedKeys.indexOf(button.key) < 0){ 571 usedKeys.push(button.key); 572 } 573 574 button.element = elements.buttonTemplate.cloneNode(); 575 button.element.innerHTML = button.text; 576 if(typeof button.className === 'string' && button.className !== ''){ 577 addClass(button.element, button.className); 578 } 579 for(var key in button.attrs){ 580 if(key !== 'className' && button.attrs.hasOwnProperty(key)){ 581 button.element.setAttribute(key, button.attrs[key]); 582 } 583 } 584 if(button.scope === 'auxiliary'){ 585 elements.buttons.auxiliary.appendChild(button.element); 586 }else{ 587 elements.buttons.primary.appendChild(button.element); 588 } 589 } 590 //make elements pubic 591 instance.elements = elements; 592 593 //save event handlers delegates 594 internal.resetHandler = delegate(instance, onReset); 595 internal.beginMoveHandler = delegate(instance, beginMove); 596 internal.beginResizeHandler = delegate(instance, beginResize); 597 internal.bringToFrontHandler = delegate(instance, bringToFront); 598 internal.modalClickHandler = delegate(instance, modalClickHandler); 599 internal.buttonsClickHandler = delegate(instance, buttonsClickHandler); 600 internal.commandsClickHandler = delegate(instance, commandsClickHandler); 601 internal.transitionInHandler = delegate(instance, handleTransitionInEvent); 602 internal.transitionOutHandler = delegate(instance, handleTransitionOutEvent); 603 604 //settings 605 for(var opKey in internal.options){ 606 if(setup.options[opKey] !== undefined){ 607 // if found in user options 608 instance.set(opKey, setup.options[opKey]); 609 }else if(alertify.defaults.hasOwnProperty(opKey)) { 610 // else if found in defaults options 611 instance.set(opKey, alertify.defaults[opKey]); 612 }else if(opKey === 'title' ) { 613 // else if title key, use alertify.defaults.glossary 614 instance.set(opKey, alertify.defaults.glossary[opKey]); 615 } 616 } 617 618 // allow dom customization 619 if(typeof instance.build === 'function'){ 620 instance.build(); 621 } 622 } 623 624 //add to the end of the DOM tree. 625 document.body.appendChild(instance.elements.root); 626 } 627 628 /** 629 * Helper: maintains scroll position 630 * 631 */ 632 var scrollX, scrollY; 633 function saveScrollPosition(){ 634 scrollX = getScrollLeft(); 635 scrollY = getScrollTop(); 636 } 637 function restoreScrollPosition(){ 638 window.scrollTo(scrollX, scrollY); 639 } 640 641 /** 642 * Helper: adds/removes no-overflow class from body 643 * 644 */ 645 function ensureNoOverflow(){ 646 var requiresNoOverflow = 0; 647 for(var x=0;x<openDialogs.length;x+=1){ 648 var instance = openDialogs[x]; 649 if(instance.isModal() || instance.isMaximized()){ 650 requiresNoOverflow+=1; 651 } 652 } 653 if(requiresNoOverflow === 0 && document.body.className.indexOf(classes.noOverflow) >= 0){ 654 //last open modal or last maximized one 655 removeClass(document.body, classes.noOverflow); 656 preventBodyShift(false); 657 }else if(requiresNoOverflow > 0 && document.body.className.indexOf(classes.noOverflow) < 0){ 658 //first open modal or first maximized one 659 preventBodyShift(true); 660 addClass(document.body, classes.noOverflow); 661 } 662 } 663 var top = '', topScroll = 0; 664 /** 665 * Helper: prevents body shift. 666 * 667 */ 668 function preventBodyShift(add){ 669 if(alertify.defaults.preventBodyShift && document.documentElement.scrollHeight > document.documentElement.clientHeight){ 670 if(add ){//&& openDialogs[openDialogs.length-1].elements.dialog.clientHeight <= document.documentElement.clientHeight){ 671 topScroll = scrollY; 672 top = window.getComputedStyle(document.body).top; 673 addClass(document.body, classes.fixed); 674 document.body.style.top = -scrollY + 'px'; 675 } else { 676 scrollY = topScroll; 677 document.body.style.top = top; 678 removeClass(document.body, classes.fixed); 679 restoreScrollPosition(); 680 } 681 } 682 } 683 684 /** 685 * Sets the name of the transition used to show/hide the dialog 686 * 687 * @param {Object} instance The dilog instance. 688 * 689 */ 690 function updateTransition(instance, value, oldValue){ 691 if(typeof oldValue === 'string'){ 692 removeClass(instance.elements.root,classes.prefix + oldValue); 693 } 694 addClass(instance.elements.root, classes.prefix + value); 695 reflow = instance.elements.root.offsetWidth; 696 } 697 698 /** 699 * Toggles the dialog display mode 700 * 701 * @param {Object} instance The dilog instance. 702 * 703 * @return {undefined} 704 */ 705 function updateDisplayMode(instance){ 706 if(instance.get('modal')){ 707 708 //make modal 709 removeClass(instance.elements.root, classes.modeless); 710 711 //only if open 712 if(instance.isOpen()){ 713 unbindModelessEvents(instance); 714 715 //in case a pinned modless dialog was made modal while open. 716 updateAbsPositionFix(instance); 717 718 ensureNoOverflow(); 719 } 720 }else{ 721 //make modelss 722 addClass(instance.elements.root, classes.modeless); 723 724 //only if open 725 if(instance.isOpen()){ 726 bindModelessEvents(instance); 727 728 //in case pin/unpin was called while a modal is open 729 updateAbsPositionFix(instance); 730 731 ensureNoOverflow(); 732 } 733 } 734 } 735 736 /** 737 * Toggles the dialog basic view mode 738 * 739 * @param {Object} instance The dilog instance. 740 * 741 * @return {undefined} 742 */ 743 function updateBasicMode(instance){ 744 if (instance.get('basic')) { 745 // add class 746 addClass(instance.elements.root, classes.basic); 747 } else { 748 // remove class 749 removeClass(instance.elements.root, classes.basic); 750 } 751 } 752 753 /** 754 * Toggles the dialog frameless view mode 755 * 756 * @param {Object} instance The dilog instance. 757 * 758 * @return {undefined} 759 */ 760 function updateFramelessMode(instance){ 761 if (instance.get('frameless')) { 762 // add class 763 addClass(instance.elements.root, classes.frameless); 764 } else { 765 // remove class 766 removeClass(instance.elements.root, classes.frameless); 767 } 768 } 769 770 /** 771 * Helper: Brings the modeless dialog to front, attached to modeless dialogs. 772 * 773 * @param {Event} event Focus event 774 * @param {Object} instance The dilog instance. 775 * 776 * @return {undefined} 777 */ 778 function bringToFront(event, instance){ 779 780 // Do not bring to front if preceeded by an open modal 781 var index = openDialogs.indexOf(instance); 782 for(var x=index+1;x<openDialogs.length;x+=1){ 783 if(openDialogs[x].isModal()){ 784 return; 785 } 786 } 787 788 // Bring to front by making it the last child. 789 if(document.body.lastChild !== instance.elements.root){ 790 document.body.appendChild(instance.elements.root); 791 //also make sure its at the end of the list 792 openDialogs.splice(openDialogs.indexOf(instance),1); 793 openDialogs.push(instance); 794 setFocus(instance); 795 } 796 797 return false; 798 } 799 800 /** 801 * Helper: reflects dialogs options updates 802 * 803 * @param {Object} instance The dilog instance. 804 * @param {String} option The updated option name. 805 * 806 * @return {undefined} 807 */ 808 function optionUpdated(instance, option, oldValue, newValue){ 809 switch(option){ 810 case 'title': 811 instance.setHeader(newValue); 812 break; 813 case 'modal': 814 updateDisplayMode(instance); 815 break; 816 case 'basic': 817 updateBasicMode(instance); 818 break; 819 case 'frameless': 820 updateFramelessMode(instance); 821 break; 822 case 'pinned': 823 updatePinned(instance); 824 break; 825 case 'closable': 826 updateClosable(instance); 827 break; 828 case 'maximizable': 829 updateMaximizable(instance); 830 break; 831 case 'pinnable': 832 updatePinnable(instance); 833 break; 834 case 'movable': 835 updateMovable(instance); 836 break; 837 case 'resizable': 838 updateResizable(instance); 839 break; 840 case 'transition': 841 updateTransition(instance,newValue, oldValue); 842 break; 843 case 'padding': 844 if(newValue){ 845 removeClass(instance.elements.root, classes.noPadding); 846 }else if(instance.elements.root.className.indexOf(classes.noPadding) < 0){ 847 addClass(instance.elements.root, classes.noPadding); 848 } 849 break; 850 case 'overflow': 851 if(newValue){ 852 removeClass(instance.elements.root, classes.noOverflow); 853 }else if(instance.elements.root.className.indexOf(classes.noOverflow) < 0){ 854 addClass(instance.elements.root, classes.noOverflow); 855 } 856 break; 857 case 'transition': 858 updateTransition(instance,newValue, oldValue); 859 break; 860 } 861 862 // internal on option updated event 863 if(typeof instance.hooks.onupdate === 'function'){ 864 instance.hooks.onupdate.call(instance, option, oldValue, newValue); 865 } 866 } 867 868 /** 869 * Helper: reflects dialogs options updates 870 * 871 * @param {Object} instance The dilog instance. 872 * @param {Object} obj The object to set/get a value on/from. 873 * @param {Function} callback The callback function to call if the key was found. 874 * @param {String|Object} key A string specifying a propery name or a collection of key value pairs. 875 * @param {Object} value Optional, the value associated with the key (in case it was a string). 876 * @param {String} option The updated option name. 877 * 878 * @return {Object} result object 879 * The result objects has an 'op' property, indicating of this is a SET or GET operation. 880 * GET: 881 * - found: a flag indicating if the key was found or not. 882 * - value: the property value. 883 * SET: 884 * - items: a list of key value pairs of the properties being set. 885 * each contains: 886 * - found: a flag indicating if the key was found or not. 887 * - key: the property key. 888 * - value: the property value. 889 */ 890 function update(instance, obj, callback, key, value){ 891 var result = {op:undefined, items: [] }; 892 if(typeof value === 'undefined' && typeof key === 'string') { 893 //get 894 result.op = 'get'; 895 if(obj.hasOwnProperty(key)){ 896 result.found = true; 897 result.value = obj[key]; 898 }else{ 899 result.found = false; 900 result.value = undefined; 901 } 902 } 903 else 904 { 905 var old; 906 //set 907 result.op = 'set'; 908 if(typeof key === 'object'){ 909 //set multiple 910 var args = key; 911 for (var prop in args) { 912 if (obj.hasOwnProperty(prop)) { 913 if(obj[prop] !== args[prop]){ 914 old = obj[prop]; 915 obj[prop] = args[prop]; 916 callback.call(instance,prop, old, args[prop]); 917 } 918 result.items.push({ 'key': prop, 'value': args[prop], 'found':true}); 919 }else{ 920 result.items.push({ 'key': prop, 'value': args[prop], 'found':false}); 921 } 922 } 923 } else if (typeof key === 'string'){ 924 //set single 925 if (obj.hasOwnProperty(key)) { 926 if(obj[key] !== value){ 927 old = obj[key]; 928 obj[key] = value; 929 callback.call(instance,key, old, value); 930 } 931 result.items.push({'key': key, 'value': value , 'found':true}); 932 933 }else{ 934 result.items.push({'key': key, 'value': value , 'found':false}); 935 } 936 } else { 937 //invalid params 938 throw new Error('args must be a string or object'); 939 } 940 } 941 return result; 942 } 943 944 945 /** 946 * Triggers a close event. 947 * 948 * @param {Object} instance The dilog instance. 949 * 950 * @return {undefined} 951 */ 952 function triggerClose(instance) { 953 var found; 954 triggerCallback(instance, function (button) { 955 return found = (button.invokeOnClose === true); 956 }); 957 //none of the buttons registered as onclose callback 958 //close the dialog 959 if (!found && instance.isOpen()) { 960 instance.close(); 961 } 962 } 963 964 /** 965 * Dialogs commands event handler, attached to the dialog commands element. 966 * 967 * @param {Event} event DOM event object. 968 * @param {Object} instance The dilog instance. 969 * 970 * @return {undefined} 971 */ 972 function commandsClickHandler(event, instance) { 973 var target = event.srcElement || event.target; 974 switch (target) { 975 case instance.elements.commands.pin: 976 if (!instance.isPinned()) { 977 pin(instance); 978 } else { 979 unpin(instance); 980 } 981 break; 982 case instance.elements.commands.maximize: 983 if (!instance.isMaximized()) { 984 maximize(instance); 985 } else { 986 restore(instance); 987 } 988 break; 989 case instance.elements.commands.close: 990 triggerClose(instance); 991 break; 992 } 993 return false; 994 } 995 996 /** 997 * Helper: pins the modeless dialog. 998 * 999 * @param {Object} instance The dialog instance. 1000 * 1001 * @return {undefined} 1002 */ 1003 function pin(instance) { 1004 //pin the dialog 1005 instance.set('pinned', true); 1006 } 1007 1008 /** 1009 * Helper: unpins the modeless dialog. 1010 * 1011 * @param {Object} instance The dilog instance. 1012 * 1013 * @return {undefined} 1014 */ 1015 function unpin(instance) { 1016 //unpin the dialog 1017 instance.set('pinned', false); 1018 } 1019 1020 1021 /** 1022 * Helper: enlarges the dialog to fill the entire screen. 1023 * 1024 * @param {Object} instance The dilog instance. 1025 * 1026 * @return {undefined} 1027 */ 1028 function maximize(instance) { 1029 // allow custom `onmaximize` method 1030 dispatchEvent('onmaximize', instance); 1031 //maximize the dialog 1032 addClass(instance.elements.root, classes.maximized); 1033 if (instance.isOpen()) { 1034 ensureNoOverflow(); 1035 } 1036 // allow custom `onmaximized` method 1037 dispatchEvent('onmaximized', instance); 1038 } 1039 1040 /** 1041 * Helper: returns the dialog to its former size. 1042 * 1043 * @param {Object} instance The dilog instance. 1044 * 1045 * @return {undefined} 1046 */ 1047 function restore(instance) { 1048 // allow custom `onrestore` method 1049 dispatchEvent('onrestore', instance); 1050 //maximize the dialog 1051 removeClass(instance.elements.root, classes.maximized); 1052 if (instance.isOpen()) { 1053 ensureNoOverflow(); 1054 } 1055 // allow custom `onrestored` method 1056 dispatchEvent('onrestored', instance); 1057 } 1058 1059 /** 1060 * Show or hide the maximize box. 1061 * 1062 * @param {Object} instance The dilog instance. 1063 * @param {Boolean} on True to add the behavior, removes it otherwise. 1064 * 1065 * @return {undefined} 1066 */ 1067 function updatePinnable(instance) { 1068 if (instance.get('pinnable')) { 1069 // add class 1070 addClass(instance.elements.root, classes.pinnable); 1071 } else { 1072 // remove class 1073 removeClass(instance.elements.root, classes.pinnable); 1074 } 1075 } 1076 1077 /** 1078 * Helper: Fixes the absolutly positioned modal div position. 1079 * 1080 * @param {Object} instance The dialog instance. 1081 * 1082 * @return {undefined} 1083 */ 1084 function addAbsPositionFix(instance) { 1085 var scrollLeft = getScrollLeft(); 1086 instance.elements.modal.style.marginTop = getScrollTop() + 'px'; 1087 instance.elements.modal.style.marginLeft = scrollLeft + 'px'; 1088 instance.elements.modal.style.marginRight = (-scrollLeft) + 'px'; 1089 } 1090 1091 /** 1092 * Helper: Removes the absolutly positioned modal div position fix. 1093 * 1094 * @param {Object} instance The dialog instance. 1095 * 1096 * @return {undefined} 1097 */ 1098 function removeAbsPositionFix(instance) { 1099 var marginTop = parseInt(instance.elements.modal.style.marginTop, 10); 1100 var marginLeft = parseInt(instance.elements.modal.style.marginLeft, 10); 1101 instance.elements.modal.style.marginTop = ''; 1102 instance.elements.modal.style.marginLeft = ''; 1103 instance.elements.modal.style.marginRight = ''; 1104 1105 if (instance.isOpen()) { 1106 var top = 0, 1107 left = 0 1108 ; 1109 if (instance.elements.dialog.style.top !== '') { 1110 top = parseInt(instance.elements.dialog.style.top, 10); 1111 } 1112 instance.elements.dialog.style.top = (top + (marginTop - getScrollTop())) + 'px'; 1113 1114 if (instance.elements.dialog.style.left !== '') { 1115 left = parseInt(instance.elements.dialog.style.left, 10); 1116 } 1117 instance.elements.dialog.style.left = (left + (marginLeft - getScrollLeft())) + 'px'; 1118 } 1119 } 1120 /** 1121 * Helper: Adds/Removes the absolutly positioned modal div position fix based on its pinned setting. 1122 * 1123 * @param {Object} instance The dialog instance. 1124 * 1125 * @return {undefined} 1126 */ 1127 function updateAbsPositionFix(instance) { 1128 // if modeless and unpinned add fix 1129 if (!instance.get('modal') && !instance.get('pinned')) { 1130 addAbsPositionFix(instance); 1131 } else { 1132 removeAbsPositionFix(instance); 1133 } 1134 } 1135 /** 1136 * Toggles the dialog position lock | modeless only. 1137 * 1138 * @param {Object} instance The dilog instance. 1139 * @param {Boolean} on True to make it modal, false otherwise. 1140 * 1141 * @return {undefined} 1142 */ 1143 function updatePinned(instance) { 1144 if (instance.get('pinned')) { 1145 removeClass(instance.elements.root, classes.unpinned); 1146 if (instance.isOpen()) { 1147 removeAbsPositionFix(instance); 1148 } 1149 } else { 1150 addClass(instance.elements.root, classes.unpinned); 1151 if (instance.isOpen() && !instance.isModal()) { 1152 addAbsPositionFix(instance); 1153 } 1154 } 1155 } 1156 1157 /** 1158 * Show or hide the maximize box. 1159 * 1160 * @param {Object} instance The dilog instance. 1161 * @param {Boolean} on True to add the behavior, removes it otherwise. 1162 * 1163 * @return {undefined} 1164 */ 1165 function updateMaximizable(instance) { 1166 if (instance.get('maximizable')) { 1167 // add class 1168 addClass(instance.elements.root, classes.maximizable); 1169 } else { 1170 // remove class 1171 removeClass(instance.elements.root, classes.maximizable); 1172 } 1173 } 1174 1175 /** 1176 * Show or hide the close box. 1177 * 1178 * @param {Object} instance The dilog instance. 1179 * @param {Boolean} on True to add the behavior, removes it otherwise. 1180 * 1181 * @return {undefined} 1182 */ 1183 function updateClosable(instance) { 1184 if (instance.get('closable')) { 1185 // add class 1186 addClass(instance.elements.root, classes.closable); 1187 bindClosableEvents(instance); 1188 } else { 1189 // remove class 1190 removeClass(instance.elements.root, classes.closable); 1191 unbindClosableEvents(instance); 1192 } 1193 } 1194 1195 // flag to cancel click event if already handled by end resize event (the mousedown, mousemove, mouseup sequence fires a click event.). 1196 var cancelClick = false; 1197 1198 /** 1199 * Helper: closes the modal dialog when clicking the modal 1200 * 1201 * @param {Event} event DOM event object. 1202 * @param {Object} instance The dilog instance. 1203 * 1204 * @return {undefined} 1205 */ 1206 function modalClickHandler(event, instance) { 1207 var target = event.srcElement || event.target; 1208 if (!cancelClick && target === instance.elements.modal && instance.get('closableByDimmer') === true) { 1209 triggerClose(instance); 1210 } 1211 cancelClick = false; 1212 return false; 1213 } 1214 1215 // flag to cancel keyup event if already handled by click event (pressing Enter on a focusted button). 1216 var cancelKeyup = false; 1217 /** 1218 * Helper: triggers a button callback 1219 * 1220 * @param {Object} The dilog instance. 1221 * @param {Function} Callback to check which button triggered the event. 1222 * 1223 * @return {undefined} 1224 */ 1225 function triggerCallback(instance, check) { 1226 for (var idx = 0; idx < instance.__internal.buttons.length; idx += 1) { 1227 var button = instance.__internal.buttons[idx]; 1228 if (!button.element.disabled && check(button)) { 1229 var closeEvent = createCloseEvent(idx, button); 1230 if (typeof instance.callback === 'function') { 1231 instance.callback.apply(instance, [closeEvent]); 1232 } 1233 //close the dialog only if not canceled. 1234 if (closeEvent.cancel === false) { 1235 instance.close(); 1236 } 1237 break; 1238 } 1239 } 1240 } 1241 1242 /** 1243 * Clicks event handler, attached to the dialog footer. 1244 * 1245 * @param {Event} DOM event object. 1246 * @param {Object} The dilog instance. 1247 * 1248 * @return {undefined} 1249 */ 1250 function buttonsClickHandler(event, instance) { 1251 var target = event.srcElement || event.target; 1252 triggerCallback(instance, function (button) { 1253 // if this button caused the click, cancel keyup event 1254 return button.element === target && (cancelKeyup = true); 1255 }); 1256 } 1257 1258 /** 1259 * Keyup event handler, attached to the document.body 1260 * 1261 * @param {Event} DOM event object. 1262 * @param {Object} The dilog instance. 1263 * 1264 * @return {undefined} 1265 */ 1266 function keyupHandler(event) { 1267 //hitting enter while button has focus will trigger keyup too. 1268 //ignore if handled by clickHandler 1269 if (cancelKeyup) { 1270 cancelKeyup = false; 1271 return; 1272 } 1273 var instance = openDialogs[openDialogs.length - 1]; 1274 var keyCode = event.keyCode; 1275 if (instance.__internal.buttons.length === 0 && keyCode === keys.ESC && instance.get('closable') === true) { 1276 triggerClose(instance); 1277 return false; 1278 }else if (usedKeys.indexOf(keyCode) > -1) { 1279 triggerCallback(instance, function (button) { 1280 return button.key === keyCode; 1281 }); 1282 return false; 1283 } 1284 } 1285 /** 1286 * Keydown event handler, attached to the document.body 1287 * 1288 * @param {Event} DOM event object. 1289 * @param {Object} The dilog instance. 1290 * 1291 * @return {undefined} 1292 */ 1293 function keydownHandler(event) { 1294 var instance = openDialogs[openDialogs.length - 1]; 1295 var keyCode = event.keyCode; 1296 if (keyCode === keys.LEFT || keyCode === keys.RIGHT) { 1297 var buttons = instance.__internal.buttons; 1298 for (var x = 0; x < buttons.length; x += 1) { 1299 if (document.activeElement === buttons[x].element) { 1300 switch (keyCode) { 1301 case keys.LEFT: 1302 buttons[(x || buttons.length) - 1].element.focus(); 1303 return; 1304 case keys.RIGHT: 1305 buttons[(x + 1) % buttons.length].element.focus(); 1306 return; 1307 } 1308 } 1309 } 1310 }else if (keyCode < keys.F12 + 1 && keyCode > keys.F1 - 1 && usedKeys.indexOf(keyCode) > -1) { 1311 event.preventDefault(); 1312 event.stopPropagation(); 1313 triggerCallback(instance, function (button) { 1314 return button.key === keyCode; 1315 }); 1316 return false; 1317 } 1318 } 1319 1320 1321 /** 1322 * Sets focus to proper dialog element 1323 * 1324 * @param {Object} instance The dilog instance. 1325 * @param {Node} [resetTarget=undefined] DOM element to reset focus to. 1326 * 1327 * @return {undefined} 1328 */ 1329 function setFocus(instance, resetTarget) { 1330 // reset target has already been determined. 1331 if (resetTarget) { 1332 resetTarget.focus(); 1333 } else { 1334 // current instance focus settings 1335 var focus = instance.__internal.focus; 1336 // the focus element. 1337 var element = focus.element; 1338 1339 switch (typeof focus.element) { 1340 // a number means a button index 1341 case 'number': 1342 if (instance.__internal.buttons.length > focus.element) { 1343 //in basic view, skip focusing the buttons. 1344 if (instance.get('basic') === true) { 1345 element = instance.elements.reset[0]; 1346 } else { 1347 element = instance.__internal.buttons[focus.element].element; 1348 } 1349 } 1350 break; 1351 // a string means querySelector to select from dialog body contents. 1352 case 'string': 1353 element = instance.elements.body.querySelector(focus.element); 1354 break; 1355 // a function should return the focus element. 1356 case 'function': 1357 element = focus.element.call(instance); 1358 break; 1359 } 1360 1361 // if no focus element, default to first reset element. 1362 if ((typeof element === 'undefined' || element === null) && instance.__internal.buttons.length === 0) { 1363 element = instance.elements.reset[0]; 1364 } 1365 // focus 1366 if (element && element.focus) { 1367 element.focus(); 1368 // if selectable 1369 if (focus.select && element.select) { 1370 element.select(); 1371 } 1372 } 1373 } 1374 } 1375 1376 /** 1377 * Focus event handler, attached to document.body and dialogs own reset links. 1378 * handles the focus for modal dialogs only. 1379 * 1380 * @param {Event} event DOM focus event object. 1381 * @param {Object} instance The dilog instance. 1382 * 1383 * @return {undefined} 1384 */ 1385 function onReset(event, instance) { 1386 1387 // should work on last modal if triggered from document.body 1388 if (!instance) { 1389 for (var x = openDialogs.length - 1; x > -1; x -= 1) { 1390 if (openDialogs[x].isModal()) { 1391 instance = openDialogs[x]; 1392 break; 1393 } 1394 } 1395 } 1396 // if modal 1397 if (instance && instance.isModal()) { 1398 // determine reset target to enable forward/backward tab cycle. 1399 var resetTarget, target = event.srcElement || event.target; 1400 var lastResetElement = target === instance.elements.reset[1] || (instance.__internal.buttons.length === 0 && target === document.body); 1401 1402 // if last reset link, then go to maximize or close 1403 if (lastResetElement) { 1404 if (instance.get('maximizable')) { 1405 resetTarget = instance.elements.commands.maximize; 1406 } else if (instance.get('closable')) { 1407 resetTarget = instance.elements.commands.close; 1408 } 1409 } 1410 // if no reset target found, try finding the best button 1411 if (resetTarget === undefined) { 1412 if (typeof instance.__internal.focus.element === 'number') { 1413 // button focus element, go to first available button 1414 if (target === instance.elements.reset[0]) { 1415 resetTarget = instance.elements.buttons.auxiliary.firstChild || instance.elements.buttons.primary.firstChild; 1416 } else if (lastResetElement) { 1417 //restart the cycle by going to first reset link 1418 resetTarget = instance.elements.reset[0]; 1419 } 1420 } else { 1421 // will reach here when tapping backwards, so go to last child 1422 // The focus element SHOULD NOT be a button (logically!). 1423 if (target === instance.elements.reset[0]) { 1424 resetTarget = instance.elements.buttons.primary.lastChild || instance.elements.buttons.auxiliary.lastChild; 1425 } 1426 } 1427 } 1428 // focus 1429 setFocus(instance, resetTarget); 1430 } 1431 } 1432 /** 1433 * Transition in transitionend event handler. 1434 * 1435 * @param {Event} TransitionEnd event object. 1436 * @param {Object} The dilog instance. 1437 * 1438 * @return {undefined} 1439 */ 1440 function handleTransitionInEvent(event, instance) { 1441 // clear the timer 1442 clearTimeout(instance.__internal.timerIn); 1443 1444 // once transition is complete, set focus 1445 setFocus(instance); 1446 1447 //restore scroll to prevent document jump 1448 restoreScrollPosition(); 1449 1450 // allow handling key up after transition ended. 1451 cancelKeyup = false; 1452 1453 // allow custom `onfocus` method 1454 dispatchEvent('onfocus', instance); 1455 1456 // unbind the event 1457 off(instance.elements.dialog, transition.type, instance.__internal.transitionInHandler); 1458 1459 removeClass(instance.elements.root, classes.animationIn); 1460 } 1461 1462 /** 1463 * Transition out transitionend event handler. 1464 * 1465 * @param {Event} TransitionEnd event object. 1466 * @param {Object} The dilog instance. 1467 * 1468 * @return {undefined} 1469 */ 1470 function handleTransitionOutEvent(event, instance) { 1471 // clear the timer 1472 clearTimeout(instance.__internal.timerOut); 1473 // unbind the event 1474 off(instance.elements.dialog, transition.type, instance.__internal.transitionOutHandler); 1475 1476 // reset move updates 1477 resetMove(instance); 1478 // reset resize updates 1479 resetResize(instance); 1480 1481 // restore if maximized 1482 if (instance.isMaximized() && !instance.get('startMaximized')) { 1483 restore(instance); 1484 } 1485 1486 // return focus to the last active element 1487 if (alertify.defaults.maintainFocus && instance.__internal.activeElement) { 1488 instance.__internal.activeElement.focus(); 1489 instance.__internal.activeElement = null; 1490 } 1491 1492 //destory the instance 1493 if (typeof instance.__internal.destroy === 'function') { 1494 instance.__internal.destroy.apply(instance); 1495 } 1496 } 1497 /* Controls moving a dialog around */ 1498 //holde the current moving instance 1499 var movable = null, 1500 //holds the current X offset when move starts 1501 offsetX = 0, 1502 //holds the current Y offset when move starts 1503 offsetY = 0, 1504 xProp = 'pageX', 1505 yProp = 'pageY', 1506 bounds = null, 1507 refreshTop = false, 1508 moveDelegate = null 1509 ; 1510 1511 /** 1512 * Helper: sets the element top/left coordinates 1513 * 1514 * @param {Event} event DOM event object. 1515 * @param {Node} element The element being moved. 1516 * 1517 * @return {undefined} 1518 */ 1519 function moveElement(event, element) { 1520 var left = (event[xProp] - offsetX), 1521 top = (event[yProp] - offsetY); 1522 1523 if(refreshTop){ 1524 top -= document.body.scrollTop; 1525 } 1526 1527 element.style.left = left + 'px'; 1528 element.style.top = top + 'px'; 1529 1530 } 1531 /** 1532 * Helper: sets the element top/left coordinates within screen bounds 1533 * 1534 * @param {Event} event DOM event object. 1535 * @param {Node} element The element being moved. 1536 * 1537 * @return {undefined} 1538 */ 1539 function moveElementBounded(event, element) { 1540 var left = (event[xProp] - offsetX), 1541 top = (event[yProp] - offsetY); 1542 1543 if(refreshTop){ 1544 top -= document.body.scrollTop; 1545 } 1546 1547 element.style.left = Math.min(bounds.maxLeft, Math.max(bounds.minLeft, left)) + 'px'; 1548 if(refreshTop){ 1549 element.style.top = Math.min(bounds.maxTop, Math.max(bounds.minTop, top)) + 'px'; 1550 }else{ 1551 element.style.top = Math.max(bounds.minTop, top) + 'px'; 1552 } 1553 } 1554 1555 1556 /** 1557 * Triggers the start of a move event, attached to the header element mouse down event. 1558 * Adds no-selection class to the body, disabling selection while moving. 1559 * 1560 * @param {Event} event DOM event object. 1561 * @param {Object} instance The dilog instance. 1562 * 1563 * @return {Boolean} false 1564 */ 1565 function beginMove(event, instance) { 1566 if (resizable === null && !instance.isMaximized() && instance.get('movable')) { 1567 var eventSrc, left=0, top=0; 1568 if (event.type === 'touchstart') { 1569 event.preventDefault(); 1570 eventSrc = event.targetTouches[0]; 1571 xProp = 'clientX'; 1572 yProp = 'clientY'; 1573 } else if (event.button === 0) { 1574 eventSrc = event; 1575 } 1576 1577 if (eventSrc) { 1578 1579 var element = instance.elements.dialog; 1580 addClass(element, classes.capture); 1581 1582 if (element.style.left) { 1583 left = parseInt(element.style.left, 10); 1584 } 1585 1586 if (element.style.top) { 1587 top = parseInt(element.style.top, 10); 1588 } 1589 1590 offsetX = eventSrc[xProp] - left; 1591 offsetY = eventSrc[yProp] - top; 1592 1593 if(instance.isModal()){ 1594 offsetY += instance.elements.modal.scrollTop; 1595 }else if(instance.isPinned()){ 1596 offsetY -= document.body.scrollTop; 1597 } 1598 1599 if(instance.get('moveBounded')){ 1600 var current = element, 1601 offsetLeft = -left, 1602 offsetTop = -top; 1603 1604 //calc offset 1605 do { 1606 offsetLeft += current.offsetLeft; 1607 offsetTop += current.offsetTop; 1608 } while (current = current.offsetParent); 1609 1610 bounds = { 1611 maxLeft : offsetLeft, 1612 minLeft : -offsetLeft, 1613 maxTop : document.documentElement.clientHeight - element.clientHeight - offsetTop, 1614 minTop : -offsetTop 1615 }; 1616 moveDelegate = moveElementBounded; 1617 }else{ 1618 bounds = null; 1619 moveDelegate = moveElement; 1620 } 1621 1622 // allow custom `onmove` method 1623 dispatchEvent('onmove', instance); 1624 1625 refreshTop = !instance.isModal() && instance.isPinned(); 1626 movable = instance; 1627 moveDelegate(eventSrc, element); 1628 addClass(document.body, classes.noSelection); 1629 return false; 1630 } 1631 } 1632 } 1633 1634 /** 1635 * The actual move handler, attached to document.body mousemove event. 1636 * 1637 * @param {Event} event DOM event object. 1638 * 1639 * @return {undefined} 1640 */ 1641 function move(event) { 1642 if (movable) { 1643 var eventSrc; 1644 if (event.type === 'touchmove') { 1645 event.preventDefault(); 1646 eventSrc = event.targetTouches[0]; 1647 } else if (event.button === 0) { 1648 eventSrc = event; 1649 } 1650 if (eventSrc) { 1651 moveDelegate(eventSrc, movable.elements.dialog); 1652 } 1653 } 1654 } 1655 1656 /** 1657 * Triggers the end of a move event, attached to document.body mouseup event. 1658 * Removes no-selection class from document.body, allowing selection. 1659 * 1660 * @return {undefined} 1661 */ 1662 function endMove() { 1663 if (movable) { 1664 var instance = movable; 1665 movable = bounds = null; 1666 removeClass(document.body, classes.noSelection); 1667 removeClass(instance.elements.dialog, classes.capture); 1668 // allow custom `onmoved` method 1669 dispatchEvent('onmoved', instance); 1670 } 1671 } 1672 1673 /** 1674 * Resets any changes made by moving the element to its original state, 1675 * 1676 * @param {Object} instance The dilog instance. 1677 * 1678 * @return {undefined} 1679 */ 1680 function resetMove(instance) { 1681 movable = null; 1682 var element = instance.elements.dialog; 1683 element.style.left = element.style.top = ''; 1684 } 1685 1686 /** 1687 * Updates the dialog move behavior. 1688 * 1689 * @param {Object} instance The dilog instance. 1690 * @param {Boolean} on True to add the behavior, removes it otherwise. 1691 * 1692 * @return {undefined} 1693 */ 1694 function updateMovable(instance) { 1695 if (instance.get('movable')) { 1696 // add class 1697 addClass(instance.elements.root, classes.movable); 1698 if (instance.isOpen()) { 1699 bindMovableEvents(instance); 1700 } 1701 } else { 1702 1703 //reset 1704 resetMove(instance); 1705 // remove class 1706 removeClass(instance.elements.root, classes.movable); 1707 if (instance.isOpen()) { 1708 unbindMovableEvents(instance); 1709 } 1710 } 1711 } 1712 1713 /* Controls moving a dialog around */ 1714 //holde the current instance being resized 1715 var resizable = null, 1716 //holds the staring left offset when resize starts. 1717 startingLeft = Number.Nan, 1718 //holds the staring width when resize starts. 1719 startingWidth = 0, 1720 //holds the initial width when resized for the first time. 1721 minWidth = 0, 1722 //holds the offset of the resize handle. 1723 handleOffset = 0 1724 ; 1725 1726 /** 1727 * Helper: sets the element width/height and updates left coordinate if neccessary. 1728 * 1729 * @param {Event} event DOM mousemove event object. 1730 * @param {Node} element The element being moved. 1731 * @param {Boolean} pinned A flag indicating if the element being resized is pinned to the screen. 1732 * 1733 * @return {undefined} 1734 */ 1735 function resizeElement(event, element, pageRelative) { 1736 1737 //calculate offsets from 0,0 1738 var current = element; 1739 var offsetLeft = 0; 1740 var offsetTop = 0; 1741 do { 1742 offsetLeft += current.offsetLeft; 1743 offsetTop += current.offsetTop; 1744 } while (current = current.offsetParent); 1745 1746 // determine X,Y coordinates. 1747 var X, Y; 1748 if (pageRelative === true) { 1749 X = event.pageX; 1750 Y = event.pageY; 1751 } else { 1752 X = event.clientX; 1753 Y = event.clientY; 1754 } 1755 // rtl handling 1756 var isRTL = isRightToLeft(); 1757 if (isRTL) { 1758 // reverse X 1759 X = document.body.offsetWidth - X; 1760 // if has a starting left, calculate offsetRight 1761 if (!isNaN(startingLeft)) { 1762 offsetLeft = document.body.offsetWidth - offsetLeft - element.offsetWidth; 1763 } 1764 } 1765 1766 // set width/height 1767 element.style.height = (Y - offsetTop + handleOffset) + 'px'; 1768 element.style.width = (X - offsetLeft + handleOffset) + 'px'; 1769 1770 // if the element being resized has a starting left, maintain it. 1771 // the dialog is centered, divide by half the offset to maintain the margins. 1772 if (!isNaN(startingLeft)) { 1773 var diff = Math.abs(element.offsetWidth - startingWidth) * 0.5; 1774 if (isRTL) { 1775 //negate the diff, why? 1776 //when growing it should decrease left 1777 //when shrinking it should increase left 1778 diff *= -1; 1779 } 1780 if (element.offsetWidth > startingWidth) { 1781 //growing 1782 element.style.left = (startingLeft + diff) + 'px'; 1783 } else if (element.offsetWidth >= minWidth) { 1784 //shrinking 1785 element.style.left = (startingLeft - diff) + 'px'; 1786 } 1787 } 1788 } 1789 1790 /** 1791 * Triggers the start of a resize event, attached to the resize handle element mouse down event. 1792 * Adds no-selection class to the body, disabling selection while moving. 1793 * 1794 * @param {Event} event DOM event object. 1795 * @param {Object} instance The dilog instance. 1796 * 1797 * @return {Boolean} false 1798 */ 1799 function beginResize(event, instance) { 1800 if (!instance.isMaximized()) { 1801 var eventSrc; 1802 if (event.type === 'touchstart') { 1803 event.preventDefault(); 1804 eventSrc = event.targetTouches[0]; 1805 } else if (event.button === 0) { 1806 eventSrc = event; 1807 } 1808 if (eventSrc) { 1809 // allow custom `onresize` method 1810 dispatchEvent('onresize', instance); 1811 1812 resizable = instance; 1813 handleOffset = instance.elements.resizeHandle.offsetHeight / 2; 1814 var element = instance.elements.dialog; 1815 addClass(element, classes.capture); 1816 startingLeft = parseInt(element.style.left, 10); 1817 element.style.height = element.offsetHeight + 'px'; 1818 element.style.minHeight = instance.elements.header.offsetHeight + instance.elements.footer.offsetHeight + 'px'; 1819 element.style.width = (startingWidth = element.offsetWidth) + 'px'; 1820 1821 if (element.style.maxWidth !== 'none') { 1822 element.style.minWidth = (minWidth = element.offsetWidth) + 'px'; 1823 } 1824 element.style.maxWidth = 'none'; 1825 addClass(document.body, classes.noSelection); 1826 return false; 1827 } 1828 } 1829 } 1830 1831 /** 1832 * The actual resize handler, attached to document.body mousemove event. 1833 * 1834 * @param {Event} event DOM event object. 1835 * 1836 * @return {undefined} 1837 */ 1838 function resize(event) { 1839 if (resizable) { 1840 var eventSrc; 1841 if (event.type === 'touchmove') { 1842 event.preventDefault(); 1843 eventSrc = event.targetTouches[0]; 1844 } else if (event.button === 0) { 1845 eventSrc = event; 1846 } 1847 if (eventSrc) { 1848 resizeElement(eventSrc, resizable.elements.dialog, !resizable.get('modal') && !resizable.get('pinned')); 1849 } 1850 } 1851 } 1852 1853 /** 1854 * Triggers the end of a resize event, attached to document.body mouseup event. 1855 * Removes no-selection class from document.body, allowing selection. 1856 * 1857 * @return {undefined} 1858 */ 1859 function endResize() { 1860 if (resizable) { 1861 var instance = resizable; 1862 resizable = null; 1863 removeClass(document.body, classes.noSelection); 1864 removeClass(instance.elements.dialog, classes.capture); 1865 cancelClick = true; 1866 // allow custom `onresized` method 1867 dispatchEvent('onresized', instance); 1868 } 1869 } 1870 1871 /** 1872 * Resets any changes made by resizing the element to its original state. 1873 * 1874 * @param {Object} instance The dilog instance. 1875 * 1876 * @return {undefined} 1877 */ 1878 function resetResize(instance) { 1879 resizable = null; 1880 var element = instance.elements.dialog; 1881 if (element.style.maxWidth === 'none') { 1882 //clear inline styles. 1883 element.style.maxWidth = element.style.minWidth = element.style.width = element.style.height = element.style.minHeight = element.style.left = ''; 1884 //reset variables. 1885 startingLeft = Number.Nan; 1886 startingWidth = minWidth = handleOffset = 0; 1887 } 1888 } 1889 1890 1891 /** 1892 * Updates the dialog move behavior. 1893 * 1894 * @param {Object} instance The dilog instance. 1895 * @param {Boolean} on True to add the behavior, removes it otherwise. 1896 * 1897 * @return {undefined} 1898 */ 1899 function updateResizable(instance) { 1900 if (instance.get('resizable')) { 1901 // add class 1902 addClass(instance.elements.root, classes.resizable); 1903 if (instance.isOpen()) { 1904 bindResizableEvents(instance); 1905 } 1906 } else { 1907 //reset 1908 resetResize(instance); 1909 // remove class 1910 removeClass(instance.elements.root, classes.resizable); 1911 if (instance.isOpen()) { 1912 unbindResizableEvents(instance); 1913 } 1914 } 1915 } 1916 1917 /** 1918 * Reset move/resize on window resize. 1919 * 1920 * @param {Event} event window resize event object. 1921 * 1922 * @return {undefined} 1923 */ 1924 function windowResize(/*event*/) { 1925 for (var x = 0; x < openDialogs.length; x += 1) { 1926 var instance = openDialogs[x]; 1927 if (instance.get('autoReset')) { 1928 resetMove(instance); 1929 resetResize(instance); 1930 } 1931 } 1932 } 1933 /** 1934 * Bind dialogs events 1935 * 1936 * @param {Object} instance The dilog instance. 1937 * 1938 * @return {undefined} 1939 */ 1940 function bindEvents(instance) { 1941 // if first dialog, hook global handlers 1942 if (openDialogs.length === 1) { 1943 //global 1944 on(window, 'resize', windowResize); 1945 on(document.body, 'keyup', keyupHandler); 1946 on(document.body, 'keydown', keydownHandler); 1947 on(document.body, 'focus', onReset); 1948 1949 //move 1950 on(document.documentElement, 'mousemove', move); 1951 on(document.documentElement, 'touchmove', move); 1952 on(document.documentElement, 'mouseup', endMove); 1953 on(document.documentElement, 'touchend', endMove); 1954 //resize 1955 on(document.documentElement, 'mousemove', resize); 1956 on(document.documentElement, 'touchmove', resize); 1957 on(document.documentElement, 'mouseup', endResize); 1958 on(document.documentElement, 'touchend', endResize); 1959 } 1960 1961 // common events 1962 on(instance.elements.commands.container, 'click', instance.__internal.commandsClickHandler); 1963 on(instance.elements.footer, 'click', instance.__internal.buttonsClickHandler); 1964 on(instance.elements.reset[0], 'focus', instance.__internal.resetHandler); 1965 on(instance.elements.reset[1], 'focus', instance.__internal.resetHandler); 1966 1967 //prevent handling key up when dialog is being opened by a key stroke. 1968 cancelKeyup = true; 1969 // hook in transition handler 1970 on(instance.elements.dialog, transition.type, instance.__internal.transitionInHandler); 1971 1972 // modelss only events 1973 if (!instance.get('modal')) { 1974 bindModelessEvents(instance); 1975 } 1976 1977 // resizable 1978 if (instance.get('resizable')) { 1979 bindResizableEvents(instance); 1980 } 1981 1982 // movable 1983 if (instance.get('movable')) { 1984 bindMovableEvents(instance); 1985 } 1986 } 1987 1988 /** 1989 * Unbind dialogs events 1990 * 1991 * @param {Object} instance The dilog instance. 1992 * 1993 * @return {undefined} 1994 */ 1995 function unbindEvents(instance) { 1996 // if last dialog, remove global handlers 1997 if (openDialogs.length === 1) { 1998 //global 1999 off(window, 'resize', windowResize); 2000 off(document.body, 'keyup', keyupHandler); 2001 off(document.body, 'keydown', keydownHandler); 2002 off(document.body, 'focus', onReset); 2003 //move 2004 off(document.documentElement, 'mousemove', move); 2005 off(document.documentElement, 'mouseup', endMove); 2006 //resize 2007 off(document.documentElement, 'mousemove', resize); 2008 off(document.documentElement, 'mouseup', endResize); 2009 } 2010 2011 // common events 2012 off(instance.elements.commands.container, 'click', instance.__internal.commandsClickHandler); 2013 off(instance.elements.footer, 'click', instance.__internal.buttonsClickHandler); 2014 off(instance.elements.reset[0], 'focus', instance.__internal.resetHandler); 2015 off(instance.elements.reset[1], 'focus', instance.__internal.resetHandler); 2016 2017 // hook out transition handler 2018 on(instance.elements.dialog, transition.type, instance.__internal.transitionOutHandler); 2019 2020 // modelss only events 2021 if (!instance.get('modal')) { 2022 unbindModelessEvents(instance); 2023 } 2024 2025 // movable 2026 if (instance.get('movable')) { 2027 unbindMovableEvents(instance); 2028 } 2029 2030 // resizable 2031 if (instance.get('resizable')) { 2032 unbindResizableEvents(instance); 2033 } 2034 2035 } 2036 2037 /** 2038 * Bind modeless specific events 2039 * 2040 * @param {Object} instance The dilog instance. 2041 * 2042 * @return {undefined} 2043 */ 2044 function bindModelessEvents(instance) { 2045 on(instance.elements.dialog, 'focus', instance.__internal.bringToFrontHandler, true); 2046 } 2047 2048 /** 2049 * Unbind modeless specific events 2050 * 2051 * @param {Object} instance The dilog instance. 2052 * 2053 * @return {undefined} 2054 */ 2055 function unbindModelessEvents(instance) { 2056 off(instance.elements.dialog, 'focus', instance.__internal.bringToFrontHandler, true); 2057 } 2058 2059 2060 2061 /** 2062 * Bind movable specific events 2063 * 2064 * @param {Object} instance The dilog instance. 2065 * 2066 * @return {undefined} 2067 */ 2068 function bindMovableEvents(instance) { 2069 on(instance.elements.header, 'mousedown', instance.__internal.beginMoveHandler); 2070 on(instance.elements.header, 'touchstart', instance.__internal.beginMoveHandler); 2071 } 2072 2073 /** 2074 * Unbind movable specific events 2075 * 2076 * @param {Object} instance The dilog instance. 2077 * 2078 * @return {undefined} 2079 */ 2080 function unbindMovableEvents(instance) { 2081 off(instance.elements.header, 'mousedown', instance.__internal.beginMoveHandler); 2082 off(instance.elements.header, 'touchstart', instance.__internal.beginMoveHandler); 2083 } 2084 2085 2086 2087 /** 2088 * Bind resizable specific events 2089 * 2090 * @param {Object} instance The dilog instance. 2091 * 2092 * @return {undefined} 2093 */ 2094 function bindResizableEvents(instance) { 2095 on(instance.elements.resizeHandle, 'mousedown', instance.__internal.beginResizeHandler); 2096 on(instance.elements.resizeHandle, 'touchstart', instance.__internal.beginResizeHandler); 2097 } 2098 2099 /** 2100 * Unbind resizable specific events 2101 * 2102 * @param {Object} instance The dilog instance. 2103 * 2104 * @return {undefined} 2105 */ 2106 function unbindResizableEvents(instance) { 2107 off(instance.elements.resizeHandle, 'mousedown', instance.__internal.beginResizeHandler); 2108 off(instance.elements.resizeHandle, 'touchstart', instance.__internal.beginResizeHandler); 2109 } 2110 2111 /** 2112 * Bind closable events 2113 * 2114 * @param {Object} instance The dilog instance. 2115 * 2116 * @return {undefined} 2117 */ 2118 function bindClosableEvents(instance) { 2119 on(instance.elements.modal, 'click', instance.__internal.modalClickHandler); 2120 } 2121 2122 /** 2123 * Unbind closable specific events 2124 * 2125 * @param {Object} instance The dilog instance. 2126 * 2127 * @return {undefined} 2128 */ 2129 function unbindClosableEvents(instance) { 2130 off(instance.elements.modal, 'click', instance.__internal.modalClickHandler); 2131 } 2132 // dialog API 2133 return { 2134 __init:initialize, 2135 /** 2136 * Check if dialog is currently open 2137 * 2138 * @return {Boolean} 2139 */ 2140 isOpen: function () { 2141 return this.__internal.isOpen; 2142 }, 2143 isModal: function (){ 2144 return this.elements.root.className.indexOf(classes.modeless) < 0; 2145 }, 2146 isMaximized:function(){ 2147 return this.elements.root.className.indexOf(classes.maximized) > -1; 2148 }, 2149 isPinned:function(){ 2150 return this.elements.root.className.indexOf(classes.unpinned) < 0; 2151 }, 2152 maximize:function(){ 2153 if(!this.isMaximized()){ 2154 maximize(this); 2155 } 2156 return this; 2157 }, 2158 restore:function(){ 2159 if(this.isMaximized()){ 2160 restore(this); 2161 } 2162 return this; 2163 }, 2164 pin:function(){ 2165 if(!this.isPinned()){ 2166 pin(this); 2167 } 2168 return this; 2169 }, 2170 unpin:function(){ 2171 if(this.isPinned()){ 2172 unpin(this); 2173 } 2174 return this; 2175 }, 2176 bringToFront:function(){ 2177 bringToFront(null, this); 2178 return this; 2179 }, 2180 /** 2181 * Move the dialog to a specific x/y coordinates 2182 * 2183 * @param {Number} x The new dialog x coordinate in pixels. 2184 * @param {Number} y The new dialog y coordinate in pixels. 2185 * 2186 * @return {Object} The dialog instance. 2187 */ 2188 moveTo:function(x,y){ 2189 if(!isNaN(x) && !isNaN(y)){ 2190 // allow custom `onmove` method 2191 dispatchEvent('onmove', this); 2192 2193 var element = this.elements.dialog, 2194 current = element, 2195 offsetLeft = 0, 2196 offsetTop = 0; 2197 2198 //subtract existing left,top 2199 if (element.style.left) { 2200 offsetLeft -= parseInt(element.style.left, 10); 2201 } 2202 if (element.style.top) { 2203 offsetTop -= parseInt(element.style.top, 10); 2204 } 2205 //calc offset 2206 do { 2207 offsetLeft += current.offsetLeft; 2208 offsetTop += current.offsetTop; 2209 } while (current = current.offsetParent); 2210 2211 //calc left, top 2212 var left = (x - offsetLeft); 2213 var top = (y - offsetTop); 2214 2215 //// rtl handling 2216 if (isRightToLeft()) { 2217 left *= -1; 2218 } 2219 2220 element.style.left = left + 'px'; 2221 element.style.top = top + 'px'; 2222 2223 // allow custom `onmoved` method 2224 dispatchEvent('onmoved', this); 2225 } 2226 return this; 2227 }, 2228 /** 2229 * Resize the dialog to a specific width/height (the dialog must be 'resizable'). 2230 * The dialog can be resized to: 2231 * A minimum width equal to the initial display width 2232 * A minimum height equal to the sum of header/footer heights. 2233 * 2234 * 2235 * @param {Number or String} width The new dialog width in pixels or in percent. 2236 * @param {Number or String} height The new dialog height in pixels or in percent. 2237 * 2238 * @return {Object} The dialog instance. 2239 */ 2240 resizeTo:function(width,height){ 2241 var w = parseFloat(width), 2242 h = parseFloat(height), 2243 regex = /(\d*\.\d+|\d+)%/ 2244 ; 2245 2246 if(!isNaN(w) && !isNaN(h) && this.get('resizable') === true){ 2247 2248 // allow custom `onresize` method 2249 dispatchEvent('onresize', this); 2250 2251 if(('' + width).match(regex)){ 2252 w = w / 100 * document.documentElement.clientWidth ; 2253 } 2254 2255 if(('' + height).match(regex)){ 2256 h = h / 100 * document.documentElement.clientHeight; 2257 } 2258 2259 var element = this.elements.dialog; 2260 if (element.style.maxWidth !== 'none') { 2261 element.style.minWidth = (minWidth = element.offsetWidth) + 'px'; 2262 } 2263 element.style.maxWidth = 'none'; 2264 element.style.minHeight = this.elements.header.offsetHeight + this.elements.footer.offsetHeight + 'px'; 2265 element.style.width = w + 'px'; 2266 element.style.height = h + 'px'; 2267 2268 // allow custom `onresized` method 2269 dispatchEvent('onresized', this); 2270 } 2271 return this; 2272 }, 2273 /** 2274 * Gets or Sets dialog settings/options 2275 * 2276 * @param {String|Object} key A string specifying a propery name or a collection of key/value pairs. 2277 * @param {Object} value Optional, the value associated with the key (in case it was a string). 2278 * 2279 * @return {undefined} 2280 */ 2281 setting : function (key, value) { 2282 var self = this; 2283 var result = update(this, this.__internal.options, function(k,o,n){ optionUpdated(self,k,o,n); }, key, value); 2284 if(result.op === 'get'){ 2285 if(result.found){ 2286 return result.value; 2287 }else if(typeof this.settings !== 'undefined'){ 2288 return update(this, this.settings, this.settingUpdated || function(){}, key, value).value; 2289 }else{ 2290 return undefined; 2291 } 2292 }else if(result.op === 'set'){ 2293 if(result.items.length > 0){ 2294 var callback = this.settingUpdated || function(){}; 2295 for(var x=0;x<result.items.length;x+=1){ 2296 var item = result.items[x]; 2297 if(!item.found && typeof this.settings !== 'undefined'){ 2298 update(this, this.settings, callback, item.key, item.value); 2299 } 2300 } 2301 } 2302 return this; 2303 } 2304 }, 2305 /** 2306 * [Alias] Sets dialog settings/options 2307 */ 2308 set:function(key, value){ 2309 this.setting(key,value); 2310 return this; 2311 }, 2312 /** 2313 * [Alias] Gets dialog settings/options 2314 */ 2315 get:function(key){ 2316 return this.setting(key); 2317 }, 2318 /** 2319 * Sets dialog header 2320 * @content {string or element} 2321 * 2322 * @return {undefined} 2323 */ 2324 setHeader:function(content){ 2325 if(typeof content === 'string'){ 2326 clearContents(this.elements.header); 2327 this.elements.header.innerHTML = content; 2328 }else if (content instanceof window.HTMLElement && this.elements.header.firstChild !== content){ 2329 clearContents(this.elements.header); 2330 this.elements.header.appendChild(content); 2331 } 2332 return this; 2333 }, 2334 /** 2335 * Sets dialog contents 2336 * @content {string or element} 2337 * 2338 * @return {undefined} 2339 */ 2340 setContent:function(content){ 2341 if(typeof content === 'string'){ 2342 clearContents(this.elements.content); 2343 this.elements.content.innerHTML = content; 2344 }else if (content instanceof window.HTMLElement && this.elements.content.firstChild !== content){ 2345 clearContents(this.elements.content); 2346 this.elements.content.appendChild(content); 2347 } 2348 return this; 2349 }, 2350 /** 2351 * Show the dialog as modal 2352 * 2353 * @return {Object} the dialog instance. 2354 */ 2355 showModal: function(className){ 2356 return this.show(true, className); 2357 }, 2358 /** 2359 * Show the dialog 2360 * 2361 * @return {Object} the dialog instance. 2362 */ 2363 show: function (modal, className) { 2364 2365 // ensure initialization 2366 initialize(this); 2367 2368 if ( !this.__internal.isOpen ) { 2369 2370 // add to open dialogs 2371 this.__internal.isOpen = true; 2372 openDialogs.push(this); 2373 2374 // save last focused element 2375 if(alertify.defaults.maintainFocus){ 2376 this.__internal.activeElement = document.activeElement; 2377 } 2378 2379 //allow custom dom manipulation updates before showing the dialog. 2380 if(typeof this.prepare === 'function'){ 2381 this.prepare(); 2382 } 2383 2384 bindEvents(this); 2385 2386 if(modal !== undefined){ 2387 this.set('modal', modal); 2388 } 2389 2390 //save scroll to prevent document jump 2391 saveScrollPosition(); 2392 2393 ensureNoOverflow(); 2394 2395 // allow custom dialog class on show 2396 if(typeof className === 'string' && className !== ''){ 2397 this.__internal.className = className; 2398 addClass(this.elements.root, className); 2399 } 2400 2401 // maximize if start maximized 2402 if ( this.get('startMaximized')) { 2403 this.maximize(); 2404 }else if(this.isMaximized()){ 2405 restore(this); 2406 } 2407 2408 updateAbsPositionFix(this); 2409 2410 removeClass(this.elements.root, classes.animationOut); 2411 addClass(this.elements.root, classes.animationIn); 2412 2413 // set 1s fallback in case transition event doesn't fire 2414 clearTimeout( this.__internal.timerIn); 2415 this.__internal.timerIn = setTimeout( this.__internal.transitionInHandler, transition.supported ? 1000 : 100 ); 2416 2417 if(isSafari){ 2418 // force desktop safari reflow 2419 var root = this.elements.root; 2420 root.style.display = 'none'; 2421 setTimeout(function(){root.style.display = 'block';}, 0); 2422 } 2423 2424 //reflow 2425 reflow = this.elements.root.offsetWidth; 2426 2427 // show dialog 2428 removeClass(this.elements.root, classes.hidden); 2429 2430 // internal on show event 2431 if(typeof this.hooks.onshow === 'function'){ 2432 this.hooks.onshow.call(this); 2433 } 2434 2435 // allow custom `onshow` method 2436 dispatchEvent('onshow', this); 2437 2438 }else{ 2439 // reset move updates 2440 resetMove(this); 2441 // reset resize updates 2442 resetResize(this); 2443 // shake the dialog to indicate its already open 2444 addClass(this.elements.dialog, classes.shake); 2445 var self = this; 2446 setTimeout(function(){ 2447 removeClass(self.elements.dialog, classes.shake); 2448 },200); 2449 } 2450 return this; 2451 }, 2452 /** 2453 * Close the dialog 2454 * 2455 * @return {Object} The dialog instance 2456 */ 2457 close: function () { 2458 if (this.__internal.isOpen ) { 2459 // custom `onclosing` event 2460 if(dispatchEvent('onclosing', this) !== false){ 2461 2462 unbindEvents(this); 2463 2464 removeClass(this.elements.root, classes.animationIn); 2465 addClass(this.elements.root, classes.animationOut); 2466 2467 // set 1s fallback in case transition event doesn't fire 2468 clearTimeout( this.__internal.timerOut ); 2469 this.__internal.timerOut = setTimeout( this.__internal.transitionOutHandler, transition.supported ? 1000 : 100 ); 2470 // hide dialog 2471 addClass(this.elements.root, classes.hidden); 2472 //reflow 2473 reflow = this.elements.modal.offsetWidth; 2474 2475 // remove custom dialog class on hide 2476 if (typeof this.__internal.className !== 'undefined' && this.__internal.className !== '') { 2477 removeClass(this.elements.root, this.__internal.className); 2478 } 2479 2480 // internal on close event 2481 if(typeof this.hooks.onclose === 'function'){ 2482 this.hooks.onclose.call(this); 2483 } 2484 2485 // allow custom `onclose` method 2486 dispatchEvent('onclose', this); 2487 2488 //remove from open dialogs 2489 openDialogs.splice(openDialogs.indexOf(this),1); 2490 this.__internal.isOpen = false; 2491 2492 ensureNoOverflow(); 2493 } 2494 2495 } 2496 return this; 2497 }, 2498 /** 2499 * Close all open dialogs except this. 2500 * 2501 * @return {undefined} 2502 */ 2503 closeOthers:function(){ 2504 alertify.closeAll(this); 2505 return this; 2506 }, 2507 /** 2508 * Destroys this dialog instance 2509 * 2510 * @return {undefined} 2511 */ 2512 destroy:function(){ 2513 if (this.__internal.isOpen ) { 2514 //mark dialog for destruction, this will be called on tranistionOut event. 2515 this.__internal.destroy = function(){ 2516 destruct(this, initialize); 2517 }; 2518 //close the dialog to unbind all events. 2519 this.close(); 2520 }else{ 2521 destruct(this, initialize); 2522 } 2523 return this; 2524 }, 2525 }; 2526 } () ); 2527 var notifier = (function () { 2528 var reflow, 2529 element, 2530 openInstances = [], 2531 classes = { 2532 base: 'alertify-notifier', 2533 message: 'ajs-message', 2534 top: 'ajs-top', 2535 right: 'ajs-right', 2536 bottom: 'ajs-bottom', 2537 left: 'ajs-left', 2538 visible: 'ajs-visible', 2539 hidden: 'ajs-hidden', 2540 close: 'ajs-close' 2541 }; 2542 /** 2543 * Helper: initializes the notifier instance 2544 * 2545 */ 2546 function initialize(instance) { 2547 2548 if (!instance.__internal) { 2549 instance.__internal = { 2550 position: alertify.defaults.notifier.position, 2551 delay: alertify.defaults.notifier.delay, 2552 }; 2553 2554 element = document.createElement('DIV'); 2555 2556 updatePosition(instance); 2557 } 2558 2559 //add to DOM tree. 2560 if (element.parentNode !== document.body) { 2561 document.body.appendChild(element); 2562 } 2563 } 2564 2565 function pushInstance(instance) { 2566 instance.__internal.pushed = true; 2567 openInstances.push(instance); 2568 } 2569 function popInstance(instance) { 2570 openInstances.splice(openInstances.indexOf(instance), 1); 2571 instance.__internal.pushed = false; 2572 } 2573 /** 2574 * Helper: update the notifier instance position 2575 * 2576 */ 2577 function updatePosition(instance) { 2578 element.className = classes.base; 2579 switch (instance.__internal.position) { 2580 case 'top-right': 2581 addClass(element, classes.top + ' ' + classes.right); 2582 break; 2583 case 'top-left': 2584 addClass(element, classes.top + ' ' + classes.left); 2585 break; 2586 case 'bottom-left': 2587 addClass(element, classes.bottom + ' ' + classes.left); 2588 break; 2589 2590 default: 2591 case 'bottom-right': 2592 addClass(element, classes.bottom + ' ' + classes.right); 2593 break; 2594 } 2595 } 2596 2597 /** 2598 * creates a new notification message 2599 * 2600 * @param {DOMElement} message The notifier message element 2601 * @param {Number} wait Time (in ms) to wait before the message is dismissed, a value of 0 means keep open till clicked. 2602 * @param {Function} callback A callback function to be invoked when the message is dismissed. 2603 * 2604 * @return {undefined} 2605 */ 2606 function create(div, callback) { 2607 2608 function clickDelegate(event, instance) { 2609 if(!instance.__internal.closeButton || event.target.getAttribute('data-close') === 'true'){ 2610 instance.dismiss(true); 2611 } 2612 } 2613 2614 function transitionDone(event, instance) { 2615 // unbind event 2616 off(instance.element, transition.type, transitionDone); 2617 // remove the message 2618 element.removeChild(instance.element); 2619 } 2620 2621 function initialize(instance) { 2622 if (!instance.__internal) { 2623 instance.__internal = { 2624 pushed: false, 2625 delay : undefined, 2626 timer: undefined, 2627 clickHandler: undefined, 2628 transitionEndHandler: undefined, 2629 transitionTimeout: undefined 2630 }; 2631 instance.__internal.clickHandler = delegate(instance, clickDelegate); 2632 instance.__internal.transitionEndHandler = delegate(instance, transitionDone); 2633 } 2634 return instance; 2635 } 2636 function clearTimers(instance) { 2637 clearTimeout(instance.__internal.timer); 2638 clearTimeout(instance.__internal.transitionTimeout); 2639 } 2640 return initialize({ 2641 /* notification DOM element*/ 2642 element: div, 2643 /* 2644 * Pushes a notification message 2645 * @param {string or DOMElement} content The notification message content 2646 * @param {Number} wait The time (in seconds) to wait before the message is dismissed, a value of 0 means keep open till clicked. 2647 * 2648 */ 2649 push: function (_content, _wait) { 2650 if (!this.__internal.pushed) { 2651 2652 pushInstance(this); 2653 clearTimers(this); 2654 2655 var content, wait; 2656 switch (arguments.length) { 2657 case 0: 2658 wait = this.__internal.delay; 2659 break; 2660 case 1: 2661 if (typeof (_content) === 'number') { 2662 wait = _content; 2663 } else { 2664 content = _content; 2665 wait = this.__internal.delay; 2666 } 2667 break; 2668 case 2: 2669 content = _content; 2670 wait = _wait; 2671 break; 2672 } 2673 this.__internal.closeButton = alertify.defaults.notifier.closeButton; 2674 // set contents 2675 if (typeof content !== 'undefined') { 2676 this.setContent(content); 2677 } 2678 // append or insert 2679 if (notifier.__internal.position.indexOf('top') < 0) { 2680 element.appendChild(this.element); 2681 } else { 2682 element.insertBefore(this.element, element.firstChild); 2683 } 2684 reflow = this.element.offsetWidth; 2685 addClass(this.element, classes.visible); 2686 // attach click event 2687 on(this.element, 'click', this.__internal.clickHandler); 2688 return this.delay(wait); 2689 } 2690 return this; 2691 }, 2692 /* 2693 * {Function} callback function to be invoked before dismissing the notification message. 2694 * Remarks: A return value === 'false' will cancel the dismissal 2695 * 2696 */ 2697 ondismiss: function () { }, 2698 /* 2699 * {Function} callback function to be invoked when the message is dismissed. 2700 * 2701 */ 2702 callback: callback, 2703 /* 2704 * Dismisses the notification message 2705 * @param {Boolean} clicked A flag indicating if the dismissal was caused by a click. 2706 * 2707 */ 2708 dismiss: function (clicked) { 2709 if (this.__internal.pushed) { 2710 clearTimers(this); 2711 if (!(typeof this.ondismiss === 'function' && this.ondismiss.call(this) === false)) { 2712 //detach click event 2713 off(this.element, 'click', this.__internal.clickHandler); 2714 // ensure element exists 2715 if (typeof this.element !== 'undefined' && this.element.parentNode === element) { 2716 //transition end or fallback 2717 this.__internal.transitionTimeout = setTimeout(this.__internal.transitionEndHandler, transition.supported ? 1000 : 100); 2718 removeClass(this.element, classes.visible); 2719 2720 // custom callback on dismiss 2721 if (typeof this.callback === 'function') { 2722 this.callback.call(this, clicked); 2723 } 2724 } 2725 popInstance(this); 2726 } 2727 } 2728 return this; 2729 }, 2730 /* 2731 * Delays the notification message dismissal 2732 * @param {Number} wait The time (in seconds) to wait before the message is dismissed, a value of 0 means keep open till clicked. 2733 * 2734 */ 2735 delay: function (wait) { 2736 clearTimers(this); 2737 this.__internal.delay = typeof wait !== 'undefined' && !isNaN(+wait) ? +wait : notifier.__internal.delay; 2738 if (this.__internal.delay > 0) { 2739 var self = this; 2740 this.__internal.timer = setTimeout(function () { self.dismiss(); }, this.__internal.delay * 1000); 2741 } 2742 return this; 2743 }, 2744 /* 2745 * Sets the notification message contents 2746 * @param {string or DOMElement} content The notification message content 2747 * 2748 */ 2749 setContent: function (content) { 2750 if (typeof content === 'string') { 2751 clearContents(this.element); 2752 this.element.innerHTML = content; 2753 } else if (content instanceof window.HTMLElement && this.element.firstChild !== content) { 2754 clearContents(this.element); 2755 this.element.appendChild(content); 2756 } 2757 if(this.__internal.closeButton){ 2758 var close = document.createElement('span'); 2759 addClass(close, classes.close); 2760 close.setAttribute('data-close', true); 2761 this.element.appendChild(close); 2762 } 2763 return this; 2764 }, 2765 /* 2766 * Dismisses all open notifications except this. 2767 * 2768 */ 2769 dismissOthers: function () { 2770 notifier.dismissAll(this); 2771 return this; 2772 } 2773 }); 2774 } 2775 2776 //notifier api 2777 return { 2778 /** 2779 * Gets or Sets notifier settings. 2780 * 2781 * @param {string} key The setting name 2782 * @param {Variant} value The setting value. 2783 * 2784 * @return {Object} if the called as a setter, return the notifier instance. 2785 */ 2786 setting: function (key, value) { 2787 //ensure init 2788 initialize(this); 2789 2790 if (typeof value === 'undefined') { 2791 //get 2792 return this.__internal[key]; 2793 } else { 2794 //set 2795 switch (key) { 2796 case 'position': 2797 this.__internal.position = value; 2798 updatePosition(this); 2799 break; 2800 case 'delay': 2801 this.__internal.delay = value; 2802 break; 2803 } 2804 } 2805 return this; 2806 }, 2807 /** 2808 * [Alias] Sets dialog settings/options 2809 */ 2810 set:function(key,value){ 2811 this.setting(key,value); 2812 return this; 2813 }, 2814 /** 2815 * [Alias] Gets dialog settings/options 2816 */ 2817 get:function(key){ 2818 return this.setting(key); 2819 }, 2820 /** 2821 * Creates a new notification message 2822 * 2823 * @param {string} type The type of notification message (simply a CSS class name 'ajs-{type}' to be added). 2824 * @param {Function} callback A callback function to be invoked when the message is dismissed. 2825 * 2826 * @return {undefined} 2827 */ 2828 create: function (type, callback) { 2829 //ensure notifier init 2830 initialize(this); 2831 //create new notification message 2832 var div = document.createElement('div'); 2833 div.className = classes.message + ((typeof type === 'string' && type !== '') ? ' ajs-' + type : ''); 2834 return create(div, callback); 2835 }, 2836 /** 2837 * Dismisses all open notifications. 2838 * 2839 * @param {Object} excpet [optional] The notification object to exclude from dismissal. 2840 * 2841 */ 2842 dismissAll: function (except) { 2843 var clone = openInstances.slice(0); 2844 for (var x = 0; x < clone.length; x += 1) { 2845 var instance = clone[x]; 2846 if (except === undefined || except !== instance) { 2847 instance.dismiss(); 2848 } 2849 } 2850 } 2851 }; 2852 })(); 2853 /** 2854 * Alertify public API 2855 * This contains everything that is exposed through the alertify object. 2856 * 2857 * @return {Object} 2858 */ 2859 function Alertify() { 2860 2861 // holds a references of created dialogs 2862 var dialogs = {}; 2863 2864 /** 2865 * Extends a given prototype by merging properties from base into sub. 2866 * 2867 * @sub {Object} sub The prototype being overwritten. 2868 * @base {Object} base The prototype being written. 2869 * 2870 * @return {Object} The extended prototype. 2871 */ 2872 function extend(sub, base) { 2873 // copy dialog pototype over definition. 2874 for (var prop in base) { 2875 if (base.hasOwnProperty(prop)) { 2876 sub[prop] = base[prop]; 2877 } 2878 } 2879 return sub; 2880 } 2881 2882 2883 /** 2884 * Helper: returns a dialog instance from saved dialogs. 2885 * and initializes the dialog if its not already initialized. 2886 * 2887 * @name {String} name The dialog name. 2888 * 2889 * @return {Object} The dialog instance. 2890 */ 2891 function get_dialog(name) { 2892 var dialog = dialogs[name].dialog; 2893 //initialize the dialog if its not already initialized. 2894 if (dialog && typeof dialog.__init === 'function') { 2895 dialog.__init(dialog); 2896 } 2897 return dialog; 2898 } 2899 2900 /** 2901 * Helper: registers a new dialog definition. 2902 * 2903 * @name {String} name The dialog name. 2904 * @Factory {Function} Factory a function resposible for creating dialog prototype. 2905 * @transient {Boolean} transient True to create a new dialog instance each time the dialog is invoked, false otherwise. 2906 * @base {String} base the name of another dialog to inherit from. 2907 * 2908 * @return {Object} The dialog definition. 2909 */ 2910 function register(name, Factory, transient, base) { 2911 var definition = { 2912 dialog: null, 2913 factory: Factory 2914 }; 2915 2916 //if this is based on an existing dialog, create a new definition 2917 //by applying the new protoype over the existing one. 2918 if (base !== undefined) { 2919 definition.factory = function () { 2920 return extend(new dialogs[base].factory(), new Factory()); 2921 }; 2922 } 2923 2924 if (!transient) { 2925 //create a new definition based on dialog 2926 definition.dialog = extend(new definition.factory(), dialog); 2927 } 2928 return dialogs[name] = definition; 2929 } 2930 2931 return { 2932 /** 2933 * Alertify defaults 2934 * 2935 * @type {Object} 2936 */ 2937 defaults: defaults, 2938 /** 2939 * Dialogs factory 2940 * 2941 * @param {string} Dialog name. 2942 * @param {Function} A Dialog factory function. 2943 * @param {Boolean} Indicates whether to create a singleton or transient dialog. 2944 * @param {String} The name of the base type to inherit from. 2945 */ 2946 dialog: function (name, Factory, transient, base) { 2947 2948 // get request, create a new instance and return it. 2949 if (typeof Factory !== 'function') { 2950 return get_dialog(name); 2951 } 2952 2953 if (this.hasOwnProperty(name)) { 2954 throw new Error('alertify.dialog: name already exists'); 2955 } 2956 2957 // register the dialog 2958 var definition = register(name, Factory, transient, base); 2959 2960 if (transient) { 2961 2962 // make it public 2963 this[name] = function () { 2964 //if passed with no params, consider it a get request 2965 if (arguments.length === 0) { 2966 return definition.dialog; 2967 } else { 2968 var instance = extend(new definition.factory(), dialog); 2969 //ensure init 2970 if (instance && typeof instance.__init === 'function') { 2971 instance.__init(instance); 2972 } 2973 instance['main'].apply(instance, arguments); 2974 return instance['show'].apply(instance); 2975 } 2976 }; 2977 } else { 2978 // make it public 2979 this[name] = function () { 2980 //ensure init 2981 if (definition.dialog && typeof definition.dialog.__init === 'function') { 2982 definition.dialog.__init(definition.dialog); 2983 } 2984 //if passed with no params, consider it a get request 2985 if (arguments.length === 0) { 2986 return definition.dialog; 2987 } else { 2988 var dialog = definition.dialog; 2989 dialog['main'].apply(definition.dialog, arguments); 2990 return dialog['show'].apply(definition.dialog); 2991 } 2992 }; 2993 } 2994 }, 2995 /** 2996 * Close all open dialogs. 2997 * 2998 * @param {Object} excpet [optional] The dialog object to exclude from closing. 2999 * 3000 * @return {undefined} 3001 */ 3002 closeAll: function (except) { 3003 var clone = openDialogs.slice(0); 3004 for (var x = 0; x < clone.length; x += 1) { 3005 var instance = clone[x]; 3006 if (except === undefined || except !== instance) { 3007 instance.close(); 3008 } 3009 } 3010 }, 3011 /** 3012 * Gets or Sets dialog settings/options. if the dialog is transient, this call does nothing. 3013 * 3014 * @param {string} name The dialog name. 3015 * @param {String|Object} key A string specifying a propery name or a collection of key/value pairs. 3016 * @param {Variant} value Optional, the value associated with the key (in case it was a string). 3017 * 3018 * @return {undefined} 3019 */ 3020 setting: function (name, key, value) { 3021 3022 if (name === 'notifier') { 3023 return notifier.setting(key, value); 3024 } 3025 3026 var dialog = get_dialog(name); 3027 if (dialog) { 3028 return dialog.setting(key, value); 3029 } 3030 }, 3031 /** 3032 * [Alias] Sets dialog settings/options 3033 */ 3034 set: function(name,key,value){ 3035 return this.setting(name, key,value); 3036 }, 3037 /** 3038 * [Alias] Gets dialog settings/options 3039 */ 3040 get: function(name, key){ 3041 return this.setting(name, key); 3042 }, 3043 /** 3044 * Creates a new notification message. 3045 * If a type is passed, a class name "ajs-{type}" will be added. 3046 * This allows for custom look and feel for various types of notifications. 3047 * 3048 * @param {String | DOMElement} [message=undefined] Message text 3049 * @param {String} [type=''] Type of log message 3050 * @param {String} [wait=''] Time (in seconds) to wait before auto-close 3051 * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed. 3052 * 3053 * @return {Object} Notification object. 3054 */ 3055 notify: function (message, type, wait, callback) { 3056 3057 return notifier.create(type, callback).push(message, wait); 3058
vendor: 16,491 bytes, lines 3058-3485
3058 3059 }, 3060 /** 3061 * Creates a new notification message. 3062 * 3063 * @param {String} [message=undefined] Message text 3064 * @param {String} [wait=''] Time (in seconds) to wait before auto-close 3065 * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed. 3066 * 3067 * @return {Object} Notification object. 3068 */ 3069 message: function (message, wait, callback) { 3070 return notifier.create(null, callback).push(message, wait); 3071 }, 3072 /** 3073 * Creates a new notification message of type 'success'. 3074 * 3075 * @param {String} [message=undefined] Message text 3076 * @param {String} [wait=''] Time (in seconds) to wait before auto-close 3077 * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed. 3078 * 3079 * @return {Object} Notification object. 3080 */ 3081 success: function (message, wait, callback) { 3082 return notifier.create('success', callback).push(message, wait); 3083 }, 3084 /** 3085 * Creates a new notification message of type 'error'. 3086 * 3087 * @param {String} [message=undefined] Message text 3088 * @param {String} [wait=''] Time (in seconds) to wait before auto-close 3089 * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed. 3090 * 3091 * @return {Object} Notification object. 3092 */ 3093 error: function (message, wait, callback) { 3094 return notifier.create('error', callback).push(message, wait); 3095 }, 3096 /** 3097 * Creates a new notification message of type 'warning'. 3098 * 3099 * @param {String} [message=undefined] Message text 3100 * @param {String} [wait=''] Time (in seconds) to wait before auto-close 3101 * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed. 3102 * 3103 * @return {Object} Notification object. 3104 */ 3105 warning: function (message, wait, callback) { 3106 return notifier.create('warning', callback).push(message, wait); 3107 }, 3108 /** 3109 * Dismisses all open notifications 3110 * 3111 * @return {undefined} 3112 */ 3113 dismissAll: function () { 3114 notifier.dismissAll(); 3115 } 3116 }; 3117 } 3118 var alertify = new Alertify(); 3119 3120 /** 3121 * Alert dialog definition 3122 * 3123 * invoked by: 3124 * alertify.alert(message); 3125 * alertify.alert(title, message); 3126 * alertify.alert(message, onok); 3127 * alertify.alert(title, message, onok); 3128 */ 3129 alertify.dialog('alert', function () { 3130 return { 3131 main: function (_title, _message, _onok) { 3132 var title, message, onok; 3133 switch (arguments.length) { 3134 case 1: 3135 message = _title; 3136 break; 3137 case 2: 3138 if (typeof _message === 'function') { 3139 message = _title; 3140 onok = _message; 3141 } else { 3142 title = _title; 3143 message = _message; 3144 } 3145 break; 3146 case 3: 3147 title = _title; 3148 message = _message; 3149 onok = _onok; 3150 break; 3151 } 3152 this.set('title', title); 3153 this.set('message', message); 3154 this.set('onok', onok); 3155 return this; 3156 }, 3157 setup: function () { 3158 return { 3159 buttons: [ 3160 { 3161 text: alertify.defaults.glossary.ok, 3162 key: keys.ESC, 3163 invokeOnClose: true, 3164 className: alertify.defaults.theme.ok, 3165 } 3166 ], 3167 focus: { 3168 element: 0, 3169 select: false 3170 }, 3171 options: { 3172 maximizable: false, 3173 resizable: false 3174 } 3175 }; 3176 }, 3177 build: function () { 3178 // nothing 3179 }, 3180 prepare: function () { 3181 //nothing 3182 }, 3183 setMessage: function (message) { 3184 this.setContent(message); 3185 }, 3186 settings: { 3187 message: undefined, 3188 onok: undefined, 3189 label: undefined, 3190 }, 3191 settingUpdated: function (key, oldValue, newValue) { 3192 switch (key) { 3193 case 'message': 3194 this.setMessage(newValue); 3195 break; 3196 case 'label': 3197 if (this.__internal.buttons[0].element) { 3198 this.__internal.buttons[0].element.innerHTML = newValue; 3199 } 3200 break; 3201 } 3202 }, 3203 callback: function (closeEvent) { 3204 if (typeof this.get('onok') === 'function') { 3205 var returnValue = this.get('onok').call(this, closeEvent); 3206 if (typeof returnValue !== 'undefined') { 3207 closeEvent.cancel = !returnValue; 3208 } 3209 } 3210 } 3211 }; 3212 }); 3213 /** 3214 * Confirm dialog object 3215 * 3216 * alertify.confirm(message); 3217 * alertify.confirm(message, onok); 3218 * alertify.confirm(message, onok, oncancel); 3219 * alertify.confirm(title, message, onok, oncancel); 3220 */ 3221 alertify.dialog('confirm', function () { 3222 3223 var autoConfirm = { 3224 timer: null, 3225 index: null, 3226 text: null, 3227 duration: null, 3228 task: function (event, self) { 3229 if (self.isOpen()) { 3230 self.__internal.buttons[autoConfirm.index].element.innerHTML = autoConfirm.text + ' (‏' + autoConfirm.duration + '‏) '; 3231 autoConfirm.duration -= 1; 3232 if (autoConfirm.duration === -1) { 3233 clearAutoConfirm(self); 3234 var button = self.__internal.buttons[autoConfirm.index]; 3235 var closeEvent = createCloseEvent(autoConfirm.index, button); 3236 3237 if (typeof self.callback === 'function') { 3238 self.callback.apply(self, [closeEvent]); 3239 } 3240 //close the dialog. 3241 if (closeEvent.close !== false) { 3242 self.close(); 3243 } 3244 } 3245 } else { 3246 clearAutoConfirm(self); 3247 } 3248 } 3249 }; 3250 3251 function clearAutoConfirm(self) { 3252 if (autoConfirm.timer !== null) { 3253 clearInterval(autoConfirm.timer); 3254 autoConfirm.timer = null; 3255 self.__internal.buttons[autoConfirm.index].element.innerHTML = autoConfirm.text; 3256 } 3257 } 3258 3259 function startAutoConfirm(self, index, duration) { 3260 clearAutoConfirm(self); 3261 autoConfirm.duration = duration; 3262 autoConfirm.index = index; 3263 autoConfirm.text = self.__internal.buttons[index].element.innerHTML; 3264 autoConfirm.timer = setInterval(delegate(self, autoConfirm.task), 1000); 3265 autoConfirm.task(null, self); 3266 } 3267 3268 3269 return { 3270 main: function (_title, _message, _onok, _oncancel) { 3271 var title, message, onok, oncancel; 3272 switch (arguments.length) { 3273 case 1: 3274 message = _title; 3275 break; 3276 case 2: 3277 message = _title; 3278 onok = _message; 3279 break; 3280 case 3: 3281 message = _title; 3282 onok = _message; 3283 oncancel = _onok; 3284 break; 3285 case 4: 3286 title = _title; 3287 message = _message; 3288 onok = _onok; 3289 oncancel = _oncancel; 3290 break; 3291 } 3292 this.set('title', title); 3293 this.set('message', message); 3294 this.set('onok', onok); 3295 this.set('oncancel', oncancel); 3296 return this; 3297 }, 3298 setup: function () { 3299 return { 3300 buttons: [ 3301 { 3302 text: alertify.defaults.glossary.ok, 3303 key: keys.ENTER, 3304 className: alertify.defaults.theme.ok, 3305 }, 3306 { 3307 text: alertify.defaults.glossary.cancel, 3308 key: keys.ESC, 3309 invokeOnClose: true, 3310 className: alertify.defaults.theme.cancel, 3311 } 3312 ], 3313 focus: { 3314 element: 0, 3315 select: false 3316 }, 3317 options: { 3318 maximizable: false, 3319 resizable: false 3320 } 3321 }; 3322 }, 3323 build: function () { 3324 //nothing 3325 }, 3326 prepare: function () { 3327 //nothing 3328 }, 3329 setMessage: function (message) { 3330 this.setContent(message); 3331 }, 3332 settings: { 3333 message: null, 3334 labels: null, 3335 onok: null, 3336 oncancel: null, 3337 defaultFocus: null, 3338 reverseButtons: null, 3339 }, 3340 settingUpdated: function (key, oldValue, newValue) { 3341 switch (key) { 3342 case 'message': 3343 this.setMessage(newValue); 3344 break; 3345 case 'labels': 3346 if ('ok' in newValue && this.__internal.buttons[0].element) { 3347 this.__internal.buttons[0].text = newValue.ok; 3348 this.__internal.buttons[0].element.innerHTML = newValue.ok; 3349 } 3350 if ('cancel' in newValue && this.__internal.buttons[1].element) { 3351 this.__internal.buttons[1].text = newValue.cancel; 3352 this.__internal.buttons[1].element.innerHTML = newValue.cancel; 3353 } 3354 break; 3355 case 'reverseButtons': 3356 if (newValue === true) { 3357 this.elements.buttons.primary.appendChild(this.__internal.buttons[0].element); 3358 } else { 3359 this.elements.buttons.primary.appendChild(this.__internal.buttons[1].element); 3360 } 3361 break; 3362 case 'defaultFocus': 3363 this.__internal.focus.element = newValue === 'ok' ? 0 : 1; 3364 break; 3365 } 3366 }, 3367 callback: function (closeEvent) { 3368 clearAutoConfirm(this); 3369 var returnValue; 3370 switch (closeEvent.index) { 3371 case 0: 3372 if (typeof this.get('onok') === 'function') { 3373 returnValue = this.get('onok').call(this, closeEvent); 3374 if (typeof returnValue !== 'undefined') { 3375 closeEvent.cancel = !returnValue; 3376 } 3377 } 3378 break; 3379 case 1: 3380 if (typeof this.get('oncancel') === 'function') { 3381 returnValue = this.get('oncancel').call(this, closeEvent); 3382 if (typeof returnValue !== 'undefined') { 3383 closeEvent.cancel = !returnValue; 3384 } 3385 } 3386 break; 3387 } 3388 }, 3389 autoOk: function (duration) { 3390 startAutoConfirm(this, 0, duration); 3391 return this; 3392 }, 3393 autoCancel: function (duration) { 3394 startAutoConfirm(this, 1, duration); 3395 return this; 3396 } 3397 }; 3398 }); 3399 /** 3400 * Prompt dialog object 3401 * 3402 * invoked by: 3403 * alertify.prompt(message); 3404 * alertify.prompt(message, value); 3405 * alertify.prompt(message, value, onok); 3406 * alertify.prompt(message, value, onok, oncancel); 3407 * alertify.prompt(title, message, value, onok, oncancel); 3408 */ 3409 alertify.dialog('prompt', function () { 3410 var input = document.createElement('INPUT'); 3411 var p = document.createElement('P'); 3412 return { 3413 main: function (_title, _message, _value, _onok, _oncancel) { 3414 var title, message, value, onok, oncancel; 3415 switch (arguments.length) { 3416 case 1: 3417 message = _title; 3418 break; 3419 case 2: 3420 message = _title; 3421 value = _message; 3422 break; 3423 case 3: 3424 message = _title; 3425 value = _message; 3426 onok = _value; 3427 break; 3428 case 4: 3429 message = _title; 3430 value = _message; 3431 onok = _value; 3432 oncancel = _onok; 3433 break; 3434 case 5: 3435 title = _title; 3436 message = _message; 3437 value = _value; 3438 onok = _onok; 3439 oncancel = _oncancel; 3440 break; 3441 } 3442 this.set('title', title); 3443 this.set('message', message); 3444 this.set('value', value); 3445 this.set('onok', onok); 3446 this.set('oncancel', oncancel); 3447 return this; 3448 }, 3449 setup: function () { 3450 return { 3451 buttons: [ 3452 { 3453 text: alertify.defaults.glossary.ok, 3454 key: keys.ENTER, 3455 className: alertify.defaults.theme.ok, 3456 }, 3457 { 3458 text: alertify.defaults.glossary.cancel, 3459 key: keys.ESC, 3460 invokeOnClose: true, 3461 className: alertify.defaults.theme.cancel, 3462 } 3463 ], 3464 focus: { 3465 element: input, 3466 select: true 3467 }, 3468 options: { 3469 maximizable: false, 3470 resizable: false 3471 } 3472 }; 3473 }, 3474 build: function () { 3475 input.className = alertify.defaults.theme.input; 3476 input.setAttribute('type', 'text'); 3477 input.value = this.get('value'); 3478 this.elements.content.appendChild(p); 3479 this.elements.content.appendChild(input); 3480 }, 3481 prepare: function () { 3482 //nothing 3483 }, 3484 setMessage: function (message) { 3485 if (
3485typeof message === 'string'
vendor: 4,088 bytes, lines 3485-3589
3485) { 3486 clearContents(p); 3487 p.innerHTML = message; 3488 } else if (message instanceof window.HTMLElement && p.firstChild !== message) { 3489 clearContents(p); 3490 p.appendChild(message); 3491 } 3492 }, 3493 settings: { 3494 message: undefined, 3495 labels: undefined, 3496 onok: undefined, 3497 oncancel: undefined, 3498 value: '', 3499 type:'text', 3500 reverseButtons: undefined, 3501 }, 3502 settingUpdated: function (key, oldValue, newValue) { 3503 switch (key) { 3504 case 'message': 3505 this.setMessage(newValue); 3506 break; 3507 case 'value': 3508 input.value = newValue; 3509 break; 3510 case 'type': 3511 switch (newValue) { 3512 case 'text': 3513 case 'color': 3514 case 'date': 3515 case 'datetime-local': 3516 case 'email': 3517 case 'month': 3518 case 'number': 3519 case 'password': 3520 case 'search': 3521 case 'tel': 3522 case 'time': 3523 case 'week': 3524 input.type = newValue; 3525 break; 3526 default: 3527 input.type = 'text'; 3528 break; 3529 } 3530 break; 3531 case 'labels': 3532 if (newValue.ok && this.__internal.buttons[0].element) { 3533 this.__internal.buttons[0].element.innerHTML = newValue.ok; 3534 } 3535 if (newValue.cancel && this.__internal.buttons[1].element) { 3536 this.__internal.buttons[1].element.innerHTML = newValue.cancel; 3537 } 3538 break; 3539 case 'reverseButtons': 3540 if (newValue === true) { 3541 this.elements.buttons.primary.appendChild(this.__internal.buttons[0].element); 3542 } else { 3543 this.elements.buttons.primary.appendChild(this.__internal.buttons[1].element); 3544 } 3545 break; 3546 } 3547 }, 3548 callback: function (closeEvent) { 3549 var returnValue; 3550 switch (closeEvent.index) { 3551 case 0: 3552 this.settings.value = input.value; 3553 if (typeof this.get('onok') === 'function') { 3554 returnValue = this.get('onok').call(this, closeEvent, this.settings.value); 3555 if (typeof returnValue !== 'undefined') { 3556 closeEvent.cancel = !returnValue; 3557 } 3558 } 3559 break; 3560 case 1: 3561 if (typeof this.get('oncancel') === 'function') { 3562 returnValue = this.get('oncancel').call(this, closeEvent); 3563 if (typeof returnValue !== 'undefined') { 3564 closeEvent.cancel = !returnValue; 3565 } 3566 } 3567 if(!closeEvent.cancel){ 3568 input.value = this.settings.value; 3569 } 3570 break; 3571 } 3572 } 3573 }; 3574 }); 3575 3576 // CommonJS 3577 if ( typeof module === 'object' && typeof module.exports === 'object' ) { 3578 module.exports = alertify; 3579 // AMD 3580 } else if ( typeof define === 'function' && define.amd) { 3581 define( [], function () { 3582 return alertify; 3583 } ); 3584 // window 3585 } else if ( !window.alertify ) { 3586 window.alertify = alertify; 3587 } 3588 3589} ( typeof window !== 'undefined' ? window : this ) );
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.