PluginProbe
Embed Plus for YouTube Gallery, Livestream and Lazy Loading with Facades / trunk
Embed Plus for YouTube Gallery, Livestream and Lazy Loading with Facades vtrunk
trunk 1.0 1.1 10.0 10.1 10.2 10.3 10.4 10.5 10.6 10.7 10.8 10.9 11.0 11.0.1 11.1 11.2 11.3 11.3.1 11.4 11.5 11.6 11.7 11.7.1 11.8 All 143 releases
youtube-embed-plus / scripts / alertify / alertify.js

alertify.js in Embed Plus for YouTube Gallery, Livestream and Lazy Loading with Facades trunk, at scripts/alertify/alertify.js

3,596 lines 136.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * alertifyjs 1.11.0 http://alertifyjs.com
3 * AlertifyJS is a javascript framework for developing pretty browser dialogs and notifications.
4 * Copyright 2017 Mohammad Younes <Mohammad@alertifyjs.com> (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 center: 'ajs-center',
2539 visible: 'ajs-visible',
2540 hidden: 'ajs-hidden',
2541 close: 'ajs-close'
2542 };
2543 /**
2544 * Helper: initializes the notifier instance
2545 *
2546 */
2547 function initialize(instance) {
2548
2549 if (!instance.__internal) {
2550 instance.__internal = {
2551 position: alertify.defaults.notifier.position,
2552 delay: alertify.defaults.notifier.delay,
2553 };
2554
2555 element = document.createElement('DIV');
2556
2557 updatePosition(instance);
2558 }
2559
2560 //add to DOM tree.
2561 if (element.parentNode !== document.body) {
2562 document.body.appendChild(element);
2563 }
2564 }
2565
2566 function pushInstance(instance) {
2567 instance.__internal.pushed = true;
2568 openInstances.push(instance);
2569 }
2570 function popInstance(instance) {
2571 openInstances.splice(openInstances.indexOf(instance), 1);
2572 instance.__internal.pushed = false;
2573 }
2574 /**
2575 * Helper: update the notifier instance position
2576 *
2577 */
2578 function updatePosition(instance) {
2579 element.className = classes.base;
2580 switch (instance.__internal.position) {
2581 case 'top-right':
2582 addClass(element, classes.top + ' ' + classes.right);
2583 break;
2584 case 'top-left':
2585 addClass(element, classes.top + ' ' + classes.left);
2586 break;
2587 case 'top-center':
2588 addClass(element, classes.top + ' ' + classes.center);
2589 break;
2590 case 'bottom-left':
2591 addClass(element, classes.bottom + ' ' + classes.left);
2592 break;
2593 case 'bottom-center':
2594 addClass(element, classes.bottom + ' ' + classes.center);
2595 break;
2596
2597 default:
2598 case 'bottom-right':
2599 addClass(element, classes.bottom + ' ' + classes.right);
2600 break;
2601 }
2602 }
2603
2604 /**
2605 * creates a new notification message
2606 *
2607 * @param {DOMElement} message The notifier message element
2608 * @param {Number} wait Time (in ms) to wait before the message is dismissed, a value of 0 means keep open till clicked.
2609 * @param {Function} callback A callback function to be invoked when the message is dismissed.
2610 *
2611 * @return {undefined}
2612 */
2613 function create(div, callback) {
2614
2615 function clickDelegate(event, instance) {
2616 if(!instance.__internal.closeButton || event.target.getAttribute('data-close') === 'true'){
2617 instance.dismiss(true);
2618 }
2619 }
2620
2621 function transitionDone(event, instance) {
2622 // unbind event
2623 off(instance.element, transition.type, transitionDone);
2624 // remove the message
2625 element.removeChild(instance.element);
2626 }
2627
2628 function initialize(instance) {
2629 if (!instance.__internal) {
2630 instance.__internal = {
2631 pushed: false,
2632 delay : undefined,
2633 timer: undefined,
2634 clickHandler: undefined,
2635 transitionEndHandler: undefined,
2636 transitionTimeout: undefined
2637 };
2638 instance.__internal.clickHandler = delegate(instance, clickDelegate);
2639 instance.__internal.transitionEndHandler = delegate(instance, transitionDone);
2640 }
2641 return instance;
2642 }
2643 function clearTimers(instance) {
2644 clearTimeout(instance.__internal.timer);
2645 clearTimeout(instance.__internal.transitionTimeout);
2646 }
2647 return initialize({
2648 /* notification DOM element*/
2649 element: div,
2650 /*
2651 * Pushes a notification message
2652 * @param {string or DOMElement} content The notification message content
2653 * @param {Number} wait The time (in seconds) to wait before the message is dismissed, a value of 0 means keep open till clicked.
2654 *
2655 */
2656 push: function (_content, _wait) {
2657 if (!this.__internal.pushed) {
2658
2659 pushInstance(this);
2660 clearTimers(this);
2661
2662 var content, wait;
2663 switch (arguments.length) {
2664 case 0:
2665 wait = this.__internal.delay;
2666 break;
2667 case 1:
2668 if (typeof (_content) === 'number') {
2669 wait = _content;
2670 } else {
2671 content = _content;
2672 wait = this.__internal.delay;
2673 }
2674 break;
2675 case 2:
2676 content = _content;
2677 wait = _wait;
2678 break;
2679 }
2680 this.__internal.closeButton = alertify.defaults.notifier.closeButton;
2681 // set contents
2682 if (typeof content !== 'undefined') {
2683 this.setContent(content);
2684 }
2685 // append or insert
2686 if (notifier.__internal.position.indexOf('top') < 0) {
2687 element.appendChild(this.element);
2688 } else {
2689 element.insertBefore(this.element, element.firstChild);
2690 }
2691 reflow = this.element.offsetWidth;
2692 addClass(this.element, classes.visible);
2693 // attach click event
2694 on(this.element, 'click', this.__internal.clickHandler);
2695 return this.delay(wait);
2696 }
2697 return this;
2698 },
2699 /*
2700 * {Function} callback function to be invoked before dismissing the notification message.
2701 * Remarks: A return value === 'false' will cancel the dismissal
2702 *
2703 */
2704 ondismiss: function () { },
2705 /*
2706 * {Function} callback function to be invoked when the message is dismissed.
2707 *
2708 */
2709 callback: callback,
2710 /*
2711 * Dismisses the notification message
2712 * @param {Boolean} clicked A flag indicating if the dismissal was caused by a click.
2713 *
2714 */
2715 dismiss: function (clicked) {
2716 if (this.__internal.pushed) {
2717 clearTimers(this);
2718 if (!(typeof this.ondismiss === 'function' && this.ondismiss.call(this) === false)) {
2719 //detach click event
2720 off(this.element, 'click', this.__internal.clickHandler);
2721 // ensure element exists
2722 if (typeof this.element !== 'undefined' && this.element.parentNode === element) {
2723 //transition end or fallback
2724 this.__internal.transitionTimeout = setTimeout(this.__internal.transitionEndHandler, transition.supported ? 1000 : 100);
2725 removeClass(this.element, classes.visible);
2726
2727 // custom callback on dismiss
2728 if (typeof this.callback === 'function') {
2729 this.callback.call(this, clicked);
2730 }
2731 }
2732 popInstance(this);
2733 }
2734 }
2735 return this;
2736 },
2737 /*
2738 * Delays the notification message dismissal
2739 * @param {Number} wait The time (in seconds) to wait before the message is dismissed, a value of 0 means keep open till clicked.
2740 *
2741 */
2742 delay: function (wait) {
2743 clearTimers(this);
2744 this.__internal.delay = typeof wait !== 'undefined' && !isNaN(+wait) ? +wait : notifier.__internal.delay;
2745 if (this.__internal.delay > 0) {
2746 var self = this;
2747 this.__internal.timer = setTimeout(function () { self.dismiss(); }, this.__internal.delay * 1000);
2748 }
2749 return this;
2750 },
2751 /*
2752 * Sets the notification message contents
2753 * @param {string or DOMElement} content The notification message content
2754 *
2755 */
2756 setContent: function (content) {
2757 if (typeof content === 'string') {
2758 clearContents(this.element);
2759 this.element.innerHTML = content;
2760 } else if (content instanceof window.HTMLElement && this.element.firstChild !== content) {
2761 clearContents(this.element);
2762 this.element.appendChild(content);
2763 }
2764 if(this.__internal.closeButton){
2765 var close = document.createElement('span');
2766 addClass(close, classes.close);
2767 close.setAttribute('data-close', true);
2768 this.element.appendChild(close);
2769 }
2770 return this;
2771 },
2772 /*
2773 * Dismisses all open notifications except this.
2774 *
2775 */
2776 dismissOthers: function () {
2777 notifier.dismissAll(this);
2778 return this;
2779 }
2780 });
2781 }
2782
2783 //notifier api
2784 return {
2785 /**
2786 * Gets or Sets notifier settings.
2787 *
2788 * @param {string} key The setting name
2789 * @param {Variant} value The setting value.
2790 *
2791 * @return {Object} if the called as a setter, return the notifier instance.
2792 */
2793 setting: function (key, value) {
2794 //ensure init
2795 initialize(this);
2796
2797 if (typeof value === 'undefined') {
2798 //get
2799 return this.__internal[key];
2800 } else {
2801 //set
2802 switch (key) {
2803 case 'position':
2804 this.__internal.position = value;
2805 updatePosition(this);
2806 break;
2807 case 'delay':
2808 this.__internal.delay = value;
2809 break;
2810 }
2811 }
2812 return this;
2813 },
2814 /**
2815 * [Alias] Sets dialog settings/options
2816 */
2817 set:function(key,value){
2818 this.setting(key,value);
2819 return this;
2820 },
2821 /**
2822 * [Alias] Gets dialog settings/options
2823 */
2824 get:function(key){
2825 return this.setting(key);
2826 },
2827 /**
2828 * Creates a new notification message
2829 *
2830 * @param {string} type The type of notification message (simply a CSS class name 'ajs-{type}' to be added).
2831 * @param {Function} callback A callback function to be invoked when the message is dismissed.
2832 *
2833 * @return {undefined}
2834 */
2835 create: function (type, callback) {
2836 //ensure notifier init
2837 initialize(this);
2838 //create new notification message
2839 var div = document.createElement('div');
2840 div.className = classes.message + ((typeof type === 'string' && type !== '') ? ' ajs-' + type : '');
2841 return create(div, callback);
2842 },
2843 /**
2844 * Dismisses all open notifications.
2845 *
2846 * @param {Object} excpet [optional] The notification object to exclude from dismissal.
2847 *
2848 */
2849 dismissAll: function (except) {
2850 var clone = openInstances.slice(0);
2851 for (var x = 0; x < clone.length; x += 1) {
2852 var instance = clone[x];
2853 if (except === undefined || except !== instance) {
2854 instance.dismiss();
2855 }
2856 }
2857 }
2858 };
2859 })();
2860
2861 /**
2862 * Alertify public API
2863 * This contains everything that is exposed through the alertify object.
2864 *
2865 * @return {Object}
2866 */
2867 function Alertify() {
2868
2869 // holds a references of created dialogs
2870 var dialogs = {};
2871
2872 /**
2873 * Extends a given prototype by merging properties from base into sub.
2874 *
2875 * @sub {Object} sub The prototype being overwritten.
2876 * @base {Object} base The prototype being written.
2877 *
2878 * @return {Object} The extended prototype.
2879 */
2880 function extend(sub, base) {
2881 // copy dialog pototype over definition.
2882 for (var prop in base) {
2883 if (base.hasOwnProperty(prop)) {
2884 sub[prop] = base[prop];
2885 }
2886 }
2887 return sub;
2888 }
2889
2890
2891 /**
2892 * Helper: returns a dialog instance from saved dialogs.
2893 * and initializes the dialog if its not already initialized.
2894 *
2895 * @name {String} name The dialog name.
2896 *
2897 * @return {Object} The dialog instance.
2898 */
2899 function get_dialog(name) {
2900 var dialog = dialogs[name].dialog;
2901 //initialize the dialog if its not already initialized.
2902 if (dialog && typeof dialog.__init === 'function') {
2903 dialog.__init(dialog);
2904 }
2905 return dialog;
2906 }
2907
2908 /**
2909 * Helper: registers a new dialog definition.
2910 *
2911 * @name {String} name The dialog name.
2912 * @Factory {Function} Factory a function resposible for creating dialog prototype.
2913 * @transient {Boolean} transient True to create a new dialog instance each time the dialog is invoked, false otherwise.
2914 * @base {String} base the name of another dialog to inherit from.
2915 *
2916 * @return {Object} The dialog definition.
2917 */
2918 function register(name, Factory, transient, base) {
2919 var definition = {
2920 dialog: null,
2921 factory: Factory
2922 };
2923
2924 //if this is based on an existing dialog, create a new definition
2925 //by applying the new protoype over the existing one.
2926 if (base !== undefined) {
2927 definition.factory = function () {
2928 return extend(new dialogs[base].factory(), new Factory());
2929 };
2930 }
2931
2932 if (!transient) {
2933 //create a new definition based on dialog
2934 definition.dialog = extend(new definition.factory(), dialog);
2935 }
2936 return dialogs[name] = definition;
2937 }
2938
2939 return {
2940 /**
2941 * Alertify defaults
2942 *
2943 * @type {Object}
2944 */
2945 defaults: defaults,
2946 /**
2947 * Dialogs factory
2948 *
2949 * @param {string} Dialog name.
2950 * @param {Function} A Dialog factory function.
2951 * @param {Boolean} Indicates whether to create a singleton or transient dialog.
2952 * @param {String} The name of the base type to inherit from.
2953 */
2954 dialog: function (name, Factory, transient, base) {
2955
2956 // get request, create a new instance and return it.
2957 if (typeof Factory !== 'function') {
2958 return get_dialog(name);
2959 }
2960
2961 if (this.hasOwnProperty(name)) {
2962 throw new Error('alertify.dialog: name already exists');
2963 }
2964
2965 // register the dialog
2966 var definition = register(name, Factory, transient, base);
2967
2968 if (transient) {
2969
2970 // make it public
2971 this[name] = function () {
2972 //if passed with no params, consider it a get request
2973 if (arguments.length === 0) {
2974 return definition.dialog;
2975 } else {
2976 var instance = extend(new definition.factory(), dialog);
2977 //ensure init
2978 if (instance && typeof instance.__init === 'function') {
2979 instance.__init(instance);
2980 }
2981 instance['main'].apply(instance, arguments);
2982 return instance['show'].apply(instance);
2983 }
2984 };
2985 } else {
2986 // make it public
2987 this[name] = function () {
2988 //ensure init
2989 if (definition.dialog && typeof definition.dialog.__init === 'function') {
2990 definition.dialog.__init(definition.dialog);
2991 }
2992 //if passed with no params, consider it a get request
2993 if (arguments.length === 0) {
2994 return definition.dialog;
2995 } else {
2996 var dialog = definition.dialog;
2997 dialog['main'].apply(definition.dialog, arguments);
2998 return dialog['show'].apply(definition.dialog);
2999 }
3000 };
3001 }
3002 },
3003 /**
3004 * Close all open dialogs.
3005 *
3006 * @param {Object} excpet [optional] The dialog object to exclude from closing.
3007 *
3008 * @return {undefined}
3009 */
3010 closeAll: function (except) {
3011 var clone = openDialogs.slice(0);
3012 for (var x = 0; x < clone.length; x += 1) {
3013 var instance = clone[x];
3014 if (except === undefined || except !== instance) {
3015 instance.close();
3016 }
3017 }
3018 },
3019 /**
3020 * Gets or Sets dialog settings/options. if the dialog is transient, this call does nothing.
3021 *
3022 * @param {string} name The dialog name.
3023 * @param {String|Object} key A string specifying a propery name or a collection of key/value pairs.
3024 * @param {Variant} value Optional, the value associated with the key (in case it was a string).
3025 *
3026 * @return {undefined}
3027 */
3028 setting: function (name, key, value) {
3029
3030 if (name === 'notifier') {
3031 return notifier.setting(key, value);
3032 }
3033
3034 var dialog = get_dialog(name);
3035 if (dialog) {
3036 return dialog.setting(key, value);
3037 }
3038 },
3039 /**
3040 * [Alias] Sets dialog settings/options
3041 */
3042 set: function(name,key,value){
3043 return this.setting(name, key,value);
3044 },
3045 /**
3046 * [Alias] Gets dialog settings/options
3047 */
3048 get: function(name, key){
3049 return this.setting(name, key);
3050 },
3051 /**
3052 * Creates a new notification message.
3053 * If a type is passed, a class name "ajs-{type}" will be added.
3054 * This allows for custom look and feel for various types of notifications.
3055 *
3056 * @param {String | DOMElement} [message=undefined] Message text
3057 * @param {String} [type=''] Type of log message
3058 * @param {String} [wait=''] Time (in seconds) to wait before auto-close
3059 * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
3060 *
3061 * @return {Object} Notification object.
3062 */
3063 notify: function (message, type, wait, callback) {
3064 return notifier.create(type, callback).push(message, wait);
3065 },
3066 /**
3067 * Creates a new notification message.
3068 *
3069 * @param {String} [message=undefined] Message text
3070 * @param {String} [wait=''] Time (in seconds) to wait before auto-close
3071 * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
3072 *
3073 * @return {Object} Notification object.
3074 */
3075 message: function (message, wait, callback) {
3076 return notifier.create(null, callback).push(message, wait);
3077 },
3078 /**
3079 * Creates a new notification message of type 'success'.
3080 *
3081 * @param {String} [message=undefined] Message text
3082 * @param {String} [wait=''] Time (in seconds) to wait before auto-close
3083 * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
3084 *
3085 * @return {Object} Notification object.
3086 */
3087 success: function (message, wait, callback) {
3088 return notifier.create('success', callback).push(message, wait);
3089 },
3090 /**
3091 * Creates a new notification message of type 'error'.
3092 *
3093 * @param {String} [message=undefined] Message text
3094 * @param {String} [wait=''] Time (in seconds) to wait before auto-close
3095 * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
3096 *
3097 * @return {Object} Notification object.
3098 */
3099 error: function (message, wait, callback) {
3100 return notifier.create('error', callback).push(message, wait);
3101 },
3102 /**
3103 * Creates a new notification message of type 'warning'.
3104 *
3105 * @param {String} [message=undefined] Message text
3106 * @param {String} [wait=''] Time (in seconds) to wait before auto-close
3107 * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
3108 *
3109 * @return {Object} Notification object.
3110 */
3111 warning: function (message, wait, callback) {
3112 return notifier.create('warning', callback).push(message, wait);
3113 },
3114 /**
3115 * Dismisses all open notifications
3116 *
3117 * @return {undefined}
3118 */
3119 dismissAll: function () {
3120 notifier.dismissAll();
3121 }
3122 };
3123 }
3124 var alertify = new Alertify();
3125
3126 /**
3127 * Alert dialog definition
3128 *
3129 * invoked by:
3130 * alertify.alert(message);
3131 * alertify.alert(title, message);
3132 * alertify.alert(message, onok);
3133 * alertify.alert(title, message, onok);
3134 */
3135 alertify.dialog('alert', function () {
3136 return {
3137 main: function (_title, _message, _onok) {
3138 var title, message, onok;
3139 switch (arguments.length) {
3140 case 1:
3141 message = _title;
3142 break;
3143 case 2:
3144 if (typeof _message === 'function') {
3145 message = _title;
3146 onok = _message;
3147 } else {
3148 title = _title;
3149 message = _message;
3150 }
3151 break;
3152 case 3:
3153 title = _title;
3154 message = _message;
3155 onok = _onok;
3156 break;
3157 }
3158 this.set('title', title);
3159 this.set('message', message);
3160 this.set('onok', onok);
3161 return this;
3162 },
3163 setup: function () {
3164 return {
3165 buttons: [
3166 {
3167 text: alertify.defaults.glossary.ok,
3168 key: keys.ESC,
3169 invokeOnClose: true,
3170 className: alertify.defaults.theme.ok,
3171 }
3172 ],
3173 focus: {
3174 element: 0,
3175 select: false
3176 },
3177 options: {
3178 maximizable: false,
3179 resizable: false
3180 }
3181 };
3182 },
3183 build: function () {
3184 // nothing
3185 },
3186 prepare: function () {
3187 //nothing
3188 },
3189 setMessage: function (message) {
3190 this.setContent(message);
3191 },
3192 settings: {
3193 message: undefined,
3194 onok: undefined,
3195 label: undefined,
3196 },
3197 settingUpdated: function (key, oldValue, newValue) {
3198 switch (key) {
3199 case 'message':
3200 this.setMessage(newValue);
3201 break;
3202 case 'label':
3203 if (this.__internal.buttons[0].element) {
3204 this.__internal.buttons[0].element.innerHTML = newValue;
3205 }
3206 break;
3207 }
3208 },
3209 callback: function (closeEvent) {
3210 if (typeof this.get('onok') === 'function') {
3211 var returnValue = this.get('onok').call(this, closeEvent);
3212 if (typeof returnValue !== 'undefined') {
3213 closeEvent.cancel = !returnValue;
3214 }
3215 }
3216 }
3217 };
3218 });
3219 /**
3220 * Confirm dialog object
3221 *
3222 * alertify.confirm(message);
3223 * alertify.confirm(message, onok);
3224 * alertify.confirm(message, onok, oncancel);
3225 * alertify.confirm(title, message, onok, oncancel);
3226 */
3227 alertify.dialog('confirm', function () {
3228
3229 var autoConfirm = {
3230 timer: null,
3231 index: null,
3232 text: null,
3233 duration: null,
3234 task: function (event, self) {
3235 if (self.isOpen()) {
3236 self.__internal.buttons[autoConfirm.index].element.innerHTML = autoConfirm.text + ' (&#8207;' + autoConfirm.duration + '&#8207;) ';
3237 autoConfirm.duration -= 1;
3238 if (autoConfirm.duration === -1) {
3239 clearAutoConfirm(self);
3240 var button = self.__internal.buttons[autoConfirm.index];
3241 var closeEvent = createCloseEvent(autoConfirm.index, button);
3242
3243 if (typeof self.callback === 'function') {
3244 self.callback.apply(self, [closeEvent]);
3245 }
3246 //close the dialog.
3247 if (closeEvent.close !== false) {
3248 self.close();
3249 }
3250 }
3251 } else {
3252 clearAutoConfirm(self);
3253 }
3254 }
3255 };
3256
3257 function clearAutoConfirm(self) {
3258 if (autoConfirm.timer !== null) {
3259 clearInterval(autoConfirm.timer);
3260 autoConfirm.timer = null;
3261 self.__internal.buttons[autoConfirm.index].element.innerHTML = autoConfirm.text;
3262 }
3263 }
3264
3265 function startAutoConfirm(self, index, duration) {
3266 clearAutoConfirm(self);
3267 autoConfirm.duration = duration;
3268 autoConfirm.index = index;
3269 autoConfirm.text = self.__internal.buttons[index].element.innerHTML;
3270 autoConfirm.timer = setInterval(delegate(self, autoConfirm.task), 1000);
3271 autoConfirm.task(null, self);
3272 }
3273
3274
3275 return {
3276 main: function (_title, _message, _onok, _oncancel) {
3277 var title, message, onok, oncancel;
3278 switch (arguments.length) {
3279 case 1:
3280 message = _title;
3281 break;
3282 case 2:
3283 message = _title;
3284 onok = _message;
3285 break;
3286 case 3:
3287 message = _title;
3288 onok = _message;
3289 oncancel = _onok;
3290 break;
3291 case 4:
3292 title = _title;
3293 message = _message;
3294 onok = _onok;
3295 oncancel = _oncancel;
3296 break;
3297 }
3298 this.set('title', title);
3299 this.set('message', message);
3300 this.set('onok', onok);
3301 this.set('oncancel', oncancel);
3302 return this;
3303 },
3304 setup: function () {
3305 return {
3306 buttons: [
3307 {
3308 text: alertify.defaults.glossary.ok,
3309 key: keys.ENTER,
3310 className: alertify.defaults.theme.ok,
3311 },
3312 {
3313 text: alertify.defaults.glossary.cancel,
3314 key: keys.ESC,
3315 invokeOnClose: true,
3316 className: alertify.defaults.theme.cancel,
3317 }
3318 ],
3319 focus: {
3320 element: 0,
3321 select: false
3322 },
3323 options: {
3324 maximizable: false,
3325 resizable: false
3326 }
3327 };
3328 },
3329 build: function () {
3330 //nothing
3331 },
3332 prepare: function () {
3333 //nothing
3334 },
3335 setMessage: function (message) {
3336 this.setContent(message);
3337 },
3338 settings: {
3339 message: null,
3340 labels: null,
3341 onok: null,
3342 oncancel: null,
3343 defaultFocus: null,
3344 reverseButtons: null,
3345 },
3346 settingUpdated: function (key, oldValue, newValue) {
3347 switch (key) {
3348 case 'message':
3349 this.setMessage(newValue);
3350 break;
3351 case 'labels':
3352 if ('ok' in newValue && this.__internal.buttons[0].element) {
3353 this.__internal.buttons[0].text = newValue.ok;
3354 this.__internal.buttons[0].element.innerHTML = newValue.ok;
3355 }
3356 if ('cancel' in newValue && this.__internal.buttons[1].element) {
3357 this.__internal.buttons[1].text = newValue.cancel;
3358 this.__internal.buttons[1].element.innerHTML = newValue.cancel;
3359 }
3360 break;
3361 case 'reverseButtons':
3362 if (newValue === true) {
3363 this.elements.buttons.primary.appendChild(this.__internal.buttons[0].element);
3364 } else {
3365 this.elements.buttons.primary.appendChild(this.__internal.buttons[1].element);
3366 }
3367 break;
3368 case 'defaultFocus':
3369 this.__internal.focus.element = newValue === 'ok' ? 0 : 1;
3370 break;
3371 }
3372 },
3373 callback: function (closeEvent) {
3374 clearAutoConfirm(this);
3375 var returnValue;
3376 switch (closeEvent.index) {
3377 case 0:
3378 if (typeof this.get('onok') === 'function') {
3379 returnValue = this.get('onok').call(this, closeEvent);
3380 if (typeof returnValue !== 'undefined') {
3381 closeEvent.cancel = !returnValue;
3382 }
3383 }
3384 break;
3385 case 1:
3386 if (typeof this.get('oncancel') === 'function') {
3387 returnValue = this.get('oncancel').call(this, closeEvent);
3388 if (typeof returnValue !== 'undefined') {
3389 closeEvent.cancel = !returnValue;
3390 }
3391 }
3392 break;
3393 }
3394 },
3395 autoOk: function (duration) {
3396 startAutoConfirm(this, 0, duration);
3397 return this;
3398 },
3399 autoCancel: function (duration) {
3400 startAutoConfirm(this, 1, duration);
3401 return this;
3402 }
3403 };
3404 });
3405 /**
3406 * Prompt dialog object
3407 *
3408 * invoked by:
3409 * alertify.prompt(message);
3410 * alertify.prompt(message, value);
3411 * alertify.prompt(message, value, onok);
3412 * alertify.prompt(message, value, onok, oncancel);
3413 * alertify.prompt(title, message, value, onok, oncancel);
3414 */
3415 alertify.dialog('prompt', function () {
3416 var input = document.createElement('INPUT');
3417 var p = document.createElement('P');
3418 return {
3419 main: function (_title, _message, _value, _onok, _oncancel) {
3420 var title, message, value, onok, oncancel;
3421 switch (arguments.length) {
3422 case 1:
3423 message = _title;
3424 break;
3425 case 2:
3426 message = _title;
3427 value = _message;
3428 break;
3429 case 3:
3430 message = _title;
3431 value = _message;
3432 onok = _value;
3433 break;
3434 case 4:
3435 message = _title;
3436 value = _message;
3437 onok = _value;
3438 oncancel = _onok;
3439 break;
3440 case 5:
3441 title = _title;
3442 message = _message;
3443 value = _value;
3444 onok = _onok;
3445 oncancel = _oncancel;
3446 break;
3447 }
3448 this.set('title', title);
3449 this.set('message', message);
3450 this.set('value', value);
3451 this.set('onok', onok);
3452 this.set('oncancel', oncancel);
3453 return this;
3454 },
3455 setup: function () {
3456 return {
3457 buttons: [
3458 {
3459 text: alertify.defaults.glossary.ok,
3460 key: keys.ENTER,
3461 className: alertify.defaults.theme.ok,
3462 },
3463 {
3464 text: alertify.defaults.glossary.cancel,
3465 key: keys.ESC,
3466 invokeOnClose: true,
3467 className: alertify.defaults.theme.cancel,
3468 }
3469 ],
3470 focus: {
3471 element: input,
3472 select: true
3473 },
3474 options: {
3475 maximizable: false,
3476 resizable: false
3477 }
3478 };
3479 },
3480 build: function () {
3481 input.className = alertify.defaults.theme.input;
3482 input.setAttribute('type', 'text');
3483 input.value = this.get('value');
3484 this.elements.content.appendChild(p);
3485 this.elements.content.appendChild(input);
3486 },
3487 prepare: function () {
3488 //nothing
3489 },
3490 setMessage: function (message) {
3491 if (typeof message === 'string') {
3492 clearContents(p);
3493 p.innerHTML = message;
3494 } else if (message instanceof window.HTMLElement && p.firstChild !== message) {
3495 clearContents(p);
3496 p.appendChild(message);
3497 }
3498 },
3499 settings: {
3500 message: undefined,
3501 labels: undefined,
3502 onok: undefined,
3503 oncancel: undefined,
3504 value: '',
3505 type:'text',
3506 reverseButtons: undefined,
3507 },
3508 settingUpdated: function (key, oldValue, newValue) {
3509 switch (key) {
3510 case 'message':
3511 this.setMessage(newValue);
3512 break;
3513 case 'value':
3514 input.value = newValue;
3515 break;
3516 case 'type':
3517 switch (newValue) {
3518 case 'text':
3519 case 'color':
3520 case 'date':
3521 case 'datetime-local':
3522 case 'email':
3523 case 'month':
3524 case 'number':
3525 case 'password':
3526 case 'search':
3527 case 'tel':
3528 case 'time':
3529 case 'week':
3530 input.type = newValue;
3531 break;
3532 default:
3533 input.type = 'text';
3534 break;
3535 }
3536 break;
3537 case 'labels':
3538 if (newValue.ok && this.__internal.buttons[0].element) {
3539 this.__internal.buttons[0].element.innerHTML = newValue.ok;
3540 }
3541 if (newValue.cancel && this.__internal.buttons[1].element) {
3542 this.__internal.buttons[1].element.innerHTML = newValue.cancel;
3543 }
3544 break;
3545 case 'reverseButtons':
3546 if (newValue === true) {
3547 this.elements.buttons.primary.appendChild(this.__internal.buttons[0].element);
3548 } else {
3549 this.elements.buttons.primary.appendChild(this.__internal.buttons[1].element);
3550 }
3551 break;
3552 }
3553 },
3554 callback: function (closeEvent) {
3555 var returnValue;
3556 switch (closeEvent.index) {
3557 case 0:
3558 this.settings.value = input.value;
3559 if (typeof this.get('onok') === 'function') {
3560 returnValue = this.get('onok').call(this, closeEvent, this.settings.value);
3561 if (typeof returnValue !== 'undefined') {
3562 closeEvent.cancel = !returnValue;
3563 }
3564 }
3565 break;
3566 case 1:
3567 if (typeof this.get('oncancel') === 'function') {
3568 returnValue = this.get('oncancel').call(this, closeEvent);
3569 if (typeof returnValue !== 'undefined') {
3570 closeEvent.cancel = !returnValue;
3571 }
3572 }
3573 if(!closeEvent.cancel){
3574 input.value = this.settings.value;
3575 }
3576 break;
3577 }
3578 }
3579 };
3580 });
3581
3582 // CommonJS
3583 if ( typeof module === 'object' && typeof module.exports === 'object' ) {
3584 module.exports = alertify;
3585 // AMD
3586 } else if ( typeof define === 'function' && define.amd) {
3587 define( [], function () {
3588 return alertify;
3589 } );
3590 // window
3591 } else if ( !window.alertify ) {
3592 window.alertify = alertify;
3593 }
3594
3595 } ( typeof window !== 'undefined' ? window : this ) );
3596