/**

 * IMPORTANT!
 * MODIFICATION: SEE the destroyAll method!
 * MODIFICATION: SEE the buttonClasses addition

 * alertifyjs 1.11.4 http://alertifyjs.com
 * AlertifyJS is a javascript framework for developing pretty browser dialogs and notifications.
 * Copyright 2019 Mohammad Younes <Mohammad@alertifyjs.com> (http://alertifyjs.com) 
 * Licensed under a commercial usage license from the author */
( function ( window ) {
		'use strict';

		/**
		 * Keys enum
		 * @type {Object}
		 */
		var keys = {
				ENTER: 13,
				ESC: 27,
				F1: 112,
				F12: 123,
				LEFT: 37,
				RIGHT: 39
		};
		/**
		 * Default options
		 * @type {Object}
		 */
		var defaults = {
				autoReset:true,
				basic:false,
				closable:true,
				closableByDimmer:true,
				frameless:false,
				maintainFocus:true, //global default not per instance, applies to all dialogs
				maximizable:true,
				modal:true,
				movable:true,
				moveBounded:false,
				overflow:true,
				padding: true,
				pinnable:true,
				pinned:true,
				preventBodyShift:false, //global default not per instance, applies to all dialogs
				resizable:true,
				startMaximized:false,
				transition:'pulse',
				notifier:{
						delay:5,
						position:'bottom-right',
						closeButton:false
				},
				glossary:{
						title:'AlertifyJS',
						ok: 'OK',
						cancel: 'Cancel',
						acccpt: 'Accept',
						deny: 'Deny',
						confirm: 'Confirm',
						decline: 'Decline',
						close: 'Close',
						maximize: 'Maximize',
						restore: 'Restore',
				},
				theme:{
						input:'ajs-input',
						ok:'ajs-ok',
						cancel:'ajs-cancel',
				}
		};

		//holds open dialogs instances
		var openDialogs = [];

		/**
		 * [Helper]	Adds the specified class(es) to the element.
		 *
		 * @element {node}			The element
		 * @className {string}	One or more space-separated classes to be added to the class attribute of the element.
		 *
		 * @return {undefined}
		 */
		function addClass(element,classNames){
				element.className += ' ' + classNames;
		}

		/**
		 * [Helper]	Removes the specified class(es) from the element.
		 *
		 * @element {node}			The element
		 * @className {string}	One or more space-separated classes to be removed from the class attribute of the element.
		 *
		 * @return {undefined}
		 */
		function removeClass(element, classNames) {
				var original = element.className.split(' ');
				var toBeRemoved = classNames.split(' ');
				for (var x = 0; x < toBeRemoved.length; x += 1) {
						var index = original.indexOf(toBeRemoved[x]);
						if (index > -1){
								original.splice(index,1);
						}
				}
				element.className = original.join(' ');
		}

		/**
		 * [Helper]	Checks if the document is RTL
		 *
		 * @return {Boolean} True if the document is RTL, false otherwise.
		 */
		function isRightToLeft(){
				return window.getComputedStyle(document.body).direction === 'rtl';
		}
		/**
		 * [Helper]	Get the document current scrollTop
		 *
		 * @return {Number} current document scrollTop value
		 */
		function getScrollTop(){
				return ((document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop);
		}

		/**
		 * [Helper]	Get the document current scrollLeft
		 *
		 * @return {Number} current document scrollLeft value
		 */
		function getScrollLeft(){
				return ((document.documentElement && document.documentElement.scrollLeft) || document.body.scrollLeft);
		}

		/**
		* Helper: clear contents
		*
		*/
		function clearContents(element){
				while (element.lastChild) {
						element.removeChild(element.lastChild);
				}
		}
		/**
		 * Extends a given prototype by merging properties from base into sub.
		 *
		 * @sub {Object} sub The prototype being overwritten.
		 * @base {Object} base The prototype being written.
		 *
		 * @return {Object} The extended prototype.
		 */
		function copy(src) {
				if(null === src){
						return src;
				}
				var cpy;
				if(Array.isArray(src)){
						cpy = [];
						for(var x=0;x<src.length;x+=1){
								cpy.push(copy(src[x]));
						}
						return cpy;
				}

				if(src instanceof Date){
						return new Date(src.getTime());
				}

				if(src instanceof RegExp){
						cpy = new RegExp(src.source);
						cpy.global = src.global;
						cpy.ignoreCase = src.ignoreCase;
						cpy.multiline = src.multiline;
						cpy.lastIndex = src.lastIndex;
						return cpy;
				}

				if(typeof src === 'object'){
						cpy = {};
						// copy dialog pototype over definition.
						for (var prop in src) {
								if (src.hasOwnProperty(prop)) {
										cpy[prop] = copy(src[prop]);
								}
						}
						return cpy;
				}
				return src;
		}
		/**
			* Helper: destruct the dialog
			*
			*/
		function destruct(instance, initialize){
				if(instance.elements){
						//delete the dom and it's references.
						var root = instance.elements.root;
						root.parentNode.removeChild(root);
						delete instance.elements;
						//copy back initial settings.
						instance.settings = copy(instance.__settings);
						//re-reference init function.
						instance.__init = initialize;
						//delete __internal variable to allow re-initialization.
						delete instance.__internal;
				}
		}

		/**
		 * Use a closure to return proper event listener method. Try to use
		 * `addEventListener` by default but fallback to `attachEvent` for
		 * unsupported browser. The closure simply ensures that the test doesn't
		 * happen every time the method is called.
		 *
		 * @param		{Node}		 el		Node element
		 * @param		{String}	 event Event type
		 * @param		{Function} fn		Callback of event
		 * @return	 {Function}
		 */
		var on = (function () {
				if (document.addEventListener) {
						return function (el, event, fn, useCapture) {
								el.addEventListener(event, fn, useCapture === true);
						};
				} else if (document.attachEvent) {
						return function (el, event, fn) {
								el.attachEvent('on' + event, fn);
						};
				}
		}());

		/**
		 * Use a closure to return proper event listener method. Try to use
		 * `removeEventListener` by default but fallback to `detachEvent` for
		 * unsupported browser. The closure simply ensures that the test doesn't
		 * happen every time the method is called.
		 *
		 * @param		{Node}		 el		Node element
		 * @param		{String}	 event Event type
		 * @param		{Function} fn		Callback of event
		 * @return	 {Function}
		 */
		var off = (function () {
				if (document.removeEventListener) {
						return function (el, event, fn, useCapture) {
								el.removeEventListener(event, fn, useCapture === true);
						};
				} else if (document.detachEvent) {
						return function (el, event, fn) {
								el.detachEvent('on' + event, fn);
						};
				}
		}());

		/**
		 * Prevent default event from firing
		 *
		 * @param	{Event} event Event object
		 * @return {undefined}

		function prevent ( event ) {
				if ( event ) {
						if ( event.preventDefault ) {
								event.preventDefault();
						} else {
								event.returnValue = false;
						}
				}
		}
		*/
		var transition = (function () {
				var t, type;
				var supported = false;
				var transitions = {
						'animation'				: 'animationend',
						'OAnimation'			 : 'oAnimationEnd oanimationend',
						'msAnimation'			: 'MSAnimationEnd',
						'MozAnimation'		 : 'animationend',
						'WebkitAnimation'	: 'webkitAnimationEnd'
				};

				for (t in transitions) {
						if (document.documentElement.style[t] !== undefined) {
								type = transitions[t];
								supported = true;
								break;
						}
				}

				return {
						type: type,
						supported: supported
				};
		}());

		/**
		* Creates event handler delegate that sends the instance as last argument.
		*
		* @return {Function}		a function wrapper which sends the instance as last argument.
		*/
		function delegate(context, method) {
				return function () {
						if (arguments.length > 0) {
								var args = [];
								for (var x = 0; x < arguments.length; x += 1) {
										args.push(arguments[x]);
								}
								args.push(context);
								return method.apply(context, args);
						}
						return method.apply(context, [null, context]);
				};
		}
		/**
		* Helper for creating a dialog close event.
		*
		* @return {object}
		*/
		function createCloseEvent(index, button) {
				return {
						index: index,
						button: button,
						cancel: false
				};
		}
		/**
		* Helper for dispatching events.
		*
		* @param	{string} evenType The type of the event to disptach.
		* @param	{object} instance The dialog instance disptaching the event.
		*
		* @return	 {any}	 The result of the invoked function.
		*/
		function dispatchEvent(eventType, instance) {
				if ( typeof instance.get(eventType) === 'function' ) {
						return instance.get(eventType).call(instance);
				}
		}


		/**
		 * Super class for all dialogs
		 *
		 * @return {Object}		 base dialog prototype
		 */
		var dialog = (function () {
				var //holds the list of used keys.
						usedKeys = [],
						//dummy variable, used to trigger dom reflow.
						reflow = null,
						//holds body tab index in case it has any.
						tabindex = false,
						//condition for detecting safari
						isSafari = window.navigator.userAgent.indexOf('Safari') > -1 && window.navigator.userAgent.indexOf('Chrome') < 0,
						//dialog building blocks
						templates = {
								dimmer:'<div class="ajs-dimmer"></div>',
								/*tab index required to fire click event before body focus*/
								modal: '<div class="ajs-modal" tabindex="0"></div>',
								dialog: '<div class="ajs-dialog" tabindex="0"></div>',
								reset: '<button class="ajs-reset"></button>',
								commands: '<div class="ajs-commands"><button class="ajs-pin"></button><button class="ajs-maximize"></button><button class="ajs-close"></button></div>',
								header: '<div class="ajs-header"></div>',
								body: '<div class="ajs-body"></div>',
								content: '<div class="ajs-content"></div>',
								footer: '<div class="ajs-footer"></div>',
								buttons: { primary: '<div class="ajs-primary ajs-buttons"></div>', auxiliary: '<div class="ajs-auxiliary ajs-buttons"></div>' },
								button: '<button class="ajs-button"></button>',
								resizeHandle: '<div class="ajs-handle"></div>',
						},
						//common class names
						classes = {
								animationIn: 'ajs-in',
								animationOut: 'ajs-out',
								base: 'alertify',
								basic:'ajs-basic',
								capture: 'ajs-capture',
								closable:'ajs-closable',
								fixed: 'ajs-fixed',
								frameless:'ajs-frameless',
								hidden: 'ajs-hidden',
								maximize: 'ajs-maximize',
								maximized: 'ajs-maximized',
								maximizable:'ajs-maximizable',
								modeless: 'ajs-modeless',
								movable: 'ajs-movable',
								noSelection: 'ajs-no-selection',
								noOverflow: 'ajs-no-overflow',
								noPadding:'ajs-no-padding',
								pin:'ajs-pin',
								pinnable:'ajs-pinnable',
								prefix: 'ajs-',
								resizable: 'ajs-resizable',
								restore: 'ajs-restore',
								shake:'ajs-shake',
								unpinned:'ajs-unpinned',
						};

				/**
				 * Helper: initializes the dialog instance
				 *
				 * @return	{Number}		The total count of currently open modals.
				 */
				function initialize(instance){

						if(!instance.__internal){

								//no need to expose init after this.
								delete instance.__init;

								//keep a copy of initial dialog settings
								if(!instance.__settings){
										instance.__settings = copy(instance.settings);
								}

								//get dialog buttons/focus setup
								var setup;
								if(typeof instance.setup === 'function'){
										setup = instance.setup();
										setup.options = setup.options	|| {};
										setup.focus = setup.focus	|| {};
								}else{
										setup = {
												buttons:[],
												focus:{
														element:null,
														select:false
												},
												options:{
												}
										};
								}

								//initialize hooks object.
								if(typeof instance.hooks !== 'object'){
										instance.hooks = {};
								}

								//copy buttons defintion
								var buttonsDefinition = [];
								if(Array.isArray(setup.buttons)){
										for(var b=0;b<setup.buttons.length;b+=1){
												var ref	= setup.buttons[b],
														cpy = {};
												for (var i in ref) {
														if (ref.hasOwnProperty(i)) {
																cpy[i] = ref[i];
														}
												}
												buttonsDefinition.push(cpy);
										}
								}

								var internal = instance.__internal = {
										/**
										 * Flag holding the open state of the dialog
										 *
										 * @type {Boolean}
										 */
										isOpen:false,
										/**
										 * Active element is the element that will receive focus after
										 * closing the dialog. It defaults as the body tag, but gets updated
										 * to the last focused element before the dialog was opened.
										 *
										 * @type {Node}
										 */
										activeElement:document.body,
										timerIn:undefined,
										timerOut:undefined,
										buttons: buttonsDefinition,
										focus: setup.focus,
										options: {
												title: undefined,
												modal: undefined,
												basic:undefined,
												frameless:undefined,
												pinned: undefined,
												movable: undefined,
												moveBounded:undefined,
												resizable: undefined,
												autoReset: undefined,
												closable: undefined,
												closableByDimmer: undefined,
												maximizable: undefined,
												startMaximized: undefined,
												pinnable: undefined,
												transition: undefined,
												padding:undefined,
												overflow:undefined,
												onshow:undefined,
												onclosing:undefined,
												onclose:undefined,
												onfocus:undefined,
												onmove:undefined,
												onmoved:undefined,
												onresize:undefined,
												onresized:undefined,
												onmaximize:undefined,
												onmaximized:undefined,
												onrestore:undefined,
												onrestored:undefined
										},
										resetHandler:undefined,
										beginMoveHandler:undefined,
										beginResizeHandler:undefined,
										bringToFrontHandler:undefined,
										modalClickHandler:undefined,
										buttonsClickHandler:undefined,
										commandsClickHandler:undefined,
										transitionInHandler:undefined,
										transitionOutHandler:undefined,
										destroy:undefined
								};

								var elements = {};
								//root node
								elements.root = document.createElement('div');
								//prevent FOUC in case of async styles loading.
								elements.root.style.display = 'none';
								elements.root.className = classes.base + ' ' + classes.hidden + ' ';

								elements.root.innerHTML = templates.dimmer + templates.modal;

								//dimmer
								elements.dimmer = elements.root.firstChild;

								//dialog
								elements.modal = elements.root.lastChild;
								elements.modal.innerHTML = templates.dialog;
								elements.dialog = elements.modal.firstChild;
								elements.dialog.innerHTML = templates.reset + templates.commands + templates.header + templates.body + templates.footer + templates.resizeHandle + templates.reset;

								//reset links
								elements.reset = [];
								elements.reset.push(elements.dialog.firstChild);
								elements.reset.push(elements.dialog.lastChild);

								//commands
								elements.commands = {};
								elements.commands.container = elements.reset[0].nextSibling;
								elements.commands.pin = elements.commands.container.firstChild;
								elements.commands.maximize = elements.commands.pin.nextSibling;
								elements.commands.close = elements.commands.maximize.nextSibling;

								//header
								elements.header = elements.commands.container.nextSibling;

								//body
								elements.body = elements.header.nextSibling;
								elements.body.innerHTML = templates.content;
								elements.content = elements.body.firstChild;

								//footer
								elements.footer = elements.body.nextSibling;
								elements.footer.innerHTML = templates.buttons.auxiliary + templates.buttons.primary;

								//resize handle
								elements.resizeHandle = elements.footer.nextSibling;

								//buttons
								elements.buttons = {};
								elements.buttons.auxiliary = elements.footer.firstChild;
								elements.buttons.primary = elements.buttons.auxiliary.nextSibling;
								elements.buttons.primary.innerHTML = templates.button;
								elements.buttonTemplate = elements.buttons.primary.firstChild;
								//remove button template
								elements.buttons.primary.removeChild(elements.buttonTemplate);

								for(var x=0; x < instance.__internal.buttons.length; x+=1) {
										var button = instance.__internal.buttons[x];

										// add to the list of used keys.
										if(usedKeys.indexOf(button.key) < 0){
												usedKeys.push(button.key);
										}

										button.element = elements.buttonTemplate.cloneNode();
										button.element.innerHTML = button.text;
										if(typeof button.className === 'string' &&	button.className !== ''){
												addClass(button.element, button.className);
										}
										for(var key in button.attrs){
												if(key !== 'className' && button.attrs.hasOwnProperty(key)){
														button.element.setAttribute(key, button.attrs[key]);
												}
										}
										if(button.scope === 'auxiliary'){
												elements.buttons.auxiliary.appendChild(button.element);
										}else{
												elements.buttons.primary.appendChild(button.element);
										}
								}
								//make elements pubic
								instance.elements = elements;

								//save event handlers delegates
								internal.resetHandler = delegate(instance, onReset);
								internal.beginMoveHandler = delegate(instance, beginMove);
								internal.beginResizeHandler = delegate(instance, beginResize);
								internal.bringToFrontHandler = delegate(instance, bringToFront);
								internal.modalClickHandler = delegate(instance, modalClickHandler);
								internal.buttonsClickHandler = delegate(instance, buttonsClickHandler);
								internal.commandsClickHandler = delegate(instance, commandsClickHandler);
								internal.transitionInHandler = delegate(instance, handleTransitionInEvent);
								internal.transitionOutHandler = delegate(instance, handleTransitionOutEvent);

								//settings
								for(var opKey in internal.options){
										if(setup.options[opKey] !== undefined){
												// if found in user options
												instance.set(opKey, setup.options[opKey]);
										}else if(alertify.defaults.hasOwnProperty(opKey)) {
												// else if found in defaults options
												instance.set(opKey, alertify.defaults[opKey]);
										}else if(opKey === 'title' ) {
												// else if title key, use alertify.defaults.glossary
												instance.set(opKey, alertify.defaults.glossary[opKey]);
										}
								}

								// allow dom customization
								if(typeof instance.build === 'function'){
										instance.build();
								}
						}

						//add to the end of the DOM tree.
						document.body.appendChild(instance.elements.root);
				}

				/**
				 * Helper: maintains scroll position
				 *
				 */
				var scrollX, scrollY;
				function saveScrollPosition(){
						scrollX = getScrollLeft();
						scrollY = getScrollTop();
				}
				function restoreScrollPosition(){
						window.scrollTo(scrollX, scrollY);
				}

				/**
				 * Helper: adds/removes no-overflow class from body
				 *
				 */
				function ensureNoOverflow(){
						var requiresNoOverflow = 0;
						for(var x=0;x<openDialogs.length;x+=1){
								var instance = openDialogs[x];
								if(instance.isModal() || instance.isMaximized()){
										requiresNoOverflow+=1;
								}
						}
						if(requiresNoOverflow === 0 && document.body.className.indexOf(classes.noOverflow) >= 0){
								//last open modal or last maximized one
								removeClass(document.body, classes.noOverflow);
								preventBodyShift(false);
						}else if(requiresNoOverflow > 0 && document.body.className.indexOf(classes.noOverflow) < 0){
								//first open modal or first maximized one
								preventBodyShift(true);
								addClass(document.body, classes.noOverflow);
						}
				}
				var top = '', topScroll = 0;
				/**
				 * Helper: prevents body shift.
				 *
				 */
				function preventBodyShift(add){
						if(alertify.defaults.preventBodyShift){
								if(add && document.documentElement.scrollHeight > document.documentElement.clientHeight ){//&& openDialogs[openDialogs.length-1].elements.dialog.clientHeight <= document.documentElement.clientHeight){
										topScroll = scrollY;
										top = window.getComputedStyle(document.body).top;
										addClass(document.body, classes.fixed);
										document.body.style.top = -scrollY + 'px';
								} else if(!add) {
										scrollY = topScroll;
										document.body.style.top = top;
										removeClass(document.body, classes.fixed);
										restoreScrollPosition();
								}
						}
				}

				/**
				 * Sets the name of the transition used to show/hide the dialog
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 */
				function updateTransition(instance, value, oldValue){
						if(typeof oldValue === 'string'){
								removeClass(instance.elements.root,classes.prefix +	oldValue);
						}
						addClass(instance.elements.root, classes.prefix + value);
						reflow = instance.elements.root.offsetWidth;
				}

				/**
				 * Toggles the dialog display mode
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function updateDisplayMode(instance){
						if(instance.get('modal')){

								//make modal
								removeClass(instance.elements.root, classes.modeless);

								//only if open
								if(instance.isOpen()){
										unbindModelessEvents(instance);

										//in case a pinned modless dialog was made modal while open.
										updateAbsPositionFix(instance);

										ensureNoOverflow();
								}
						}else{
								//make modelss
								addClass(instance.elements.root, classes.modeless);

								//only if open
								if(instance.isOpen()){
										bindModelessEvents(instance);

										//in case pin/unpin was called while a modal is open
										updateAbsPositionFix(instance);

										ensureNoOverflow();
								}
						}
				}

				/**
				 * Toggles the dialog basic view mode
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function updateBasicMode(instance){
						if (instance.get('basic')) {
								// add class
								addClass(instance.elements.root, classes.basic);
						} else {
								// remove class
								removeClass(instance.elements.root, classes.basic);
						}
				}

				/**
				 * Toggles the dialog frameless view mode
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function updateFramelessMode(instance){
						if (instance.get('frameless')) {
								// add class
								addClass(instance.elements.root, classes.frameless);
						} else {
								// remove class
								removeClass(instance.elements.root, classes.frameless);
						}
				}

				/**
				 * Helper: Brings the modeless dialog to front, attached to modeless dialogs.
				 *
				 * @param {Event} event Focus event
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function bringToFront(event, instance){

						// Do not bring to front if preceeded by an open modal
						var index = openDialogs.indexOf(instance);
						for(var x=index+1;x<openDialogs.length;x+=1){
								if(openDialogs[x].isModal()){
										return;
								}
						}

						// Bring to front by making it the last child.
						if(document.body.lastChild !== instance.elements.root){
								document.body.appendChild(instance.elements.root);
								//also make sure its at the end of the list
								openDialogs.splice(openDialogs.indexOf(instance),1);
								openDialogs.push(instance);
								setFocus(instance);
						}

						return false;
				}

				/**
				 * Helper: reflects dialogs options updates
				 *
				 * @param {Object} instance The dilog instance.
				 * @param {String} option The updated option name.
				 *
				 * @return	{undefined}
				 */
				function optionUpdated(instance, option, oldValue, newValue){
						switch(option){
						case 'title':
								instance.setHeader(newValue);
								break;
						case 'modal':
								updateDisplayMode(instance);
								break;
						case 'basic':
								updateBasicMode(instance);
								break;
						case 'frameless':
								updateFramelessMode(instance);
								break;
						case 'pinned':
								updatePinned(instance);
								break;
						case 'closable':
								updateClosable(instance);
								break;
						case 'maximizable':
								updateMaximizable(instance);
								break;
						case 'pinnable':
								updatePinnable(instance);
								break;
						case 'movable':
								updateMovable(instance);
								break;
						case 'resizable':
								updateResizable(instance);
								break;
						case 'padding':
								if(newValue){
										removeClass(instance.elements.root, classes.noPadding);
								}else if(instance.elements.root.className.indexOf(classes.noPadding) < 0){
										addClass(instance.elements.root, classes.noPadding);
								}
								break;
						case 'overflow':
								if(newValue){
										removeClass(instance.elements.root, classes.noOverflow);
								}else if(instance.elements.root.className.indexOf(classes.noOverflow) < 0){
										addClass(instance.elements.root, classes.noOverflow);
								}
								break;
						case 'transition':
								updateTransition(instance,newValue, oldValue);
								break;
						}

						// internal on option updated event
						if(typeof instance.hooks.onupdate === 'function'){
								instance.hooks.onupdate.call(instance, option, oldValue, newValue);
						}
				}

				/**
				 * Helper: reflects dialogs options updates
				 *
				 * @param {Object} instance The dilog instance.
				 * @param {Object} obj The object to set/get a value on/from.
				 * @param {Function} callback The callback function to call if the key was found.
				 * @param {String|Object} key A string specifying a propery name or a collection of key value pairs.
				 * @param {Object} value Optional, the value associated with the key (in case it was a string).
				 * @param {String} option The updated option name.
				 *
				 * @return	{Object} result object
				 *	The result objects has an 'op' property, indicating of this is a SET or GET operation.
				 *			GET:
				 *			- found: a flag indicating if the key was found or not.
				 *			- value: the property value.
				 *			SET:
				 *			- items: a list of key value pairs of the properties being set.
				 *							each contains:
				 *									- found: a flag indicating if the key was found or not.
				 *									- key: the property key.
				 *									- value: the property value.
				 */
				function update(instance, obj, callback, key, value){
						var result = {op:undefined, items: [] };
						if(typeof value === 'undefined' && typeof key === 'string') {
								//get
								result.op = 'get';
								if(obj.hasOwnProperty(key)){
										result.found = true;
										result.value = obj[key];
								}else{
										result.found = false;
										result.value = undefined;
								}
						}
						else
						{
								var old;
								//set
								result.op = 'set';
								if(typeof key === 'object'){
										//set multiple
										var args = key;
										for (var prop in args) {
												if (obj.hasOwnProperty(prop)) {
														if(obj[prop] !== args[prop]){
																old = obj[prop];
																obj[prop] = args[prop];
																callback.call(instance,prop, old, args[prop]);
														}
														result.items.push({ 'key': prop, 'value': args[prop], 'found':true});
												}else{
														result.items.push({ 'key': prop, 'value': args[prop], 'found':false});
												}
										}
								} else if (typeof key === 'string'){
										//set single
										if (obj.hasOwnProperty(key)) {
												if(obj[key] !== value){
														old	= obj[key];
														obj[key] = value;
														callback.call(instance,key, old, value);
												}
												result.items.push({'key': key, 'value': value , 'found':true});

										}else{
												result.items.push({'key': key, 'value': value , 'found':false});
										}
								} else {
										//invalid params
										throw new Error('args must be a string or object');
								}
						}
						return result;
				}


				/**
				 * Triggers a close event.
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function triggerClose(instance) {
						var found;
						triggerCallback(instance, function (button) {
								return found = (button.invokeOnClose === true);
						});
						//none of the buttons registered as onclose callback
						//close the dialog
						if (!found && instance.isOpen()) {
								instance.close();
						}
				}

				/**
				 * Dialogs commands event handler, attached to the dialog commands element.
				 *
				 * @param {Event} event DOM event object.
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function commandsClickHandler(event, instance) {
						var target = event.srcElement || event.target;
						switch (target) {
						case instance.elements.commands.pin:
								if (!instance.isPinned()) {
										pin(instance);
								} else {
										unpin(instance);
								}
								break;
						case instance.elements.commands.maximize:
								if (!instance.isMaximized()) {
										maximize(instance);
								} else {
										restore(instance);
								}
								break;
						case instance.elements.commands.close:
								triggerClose(instance);
								break;
						}
						return false;
				}

				/**
				 * Helper: pins the modeless dialog.
				 *
				 * @param {Object} instance The dialog instance.
				 *
				 * @return {undefined}
				 */
				function pin(instance) {
						//pin the dialog
						instance.set('pinned', true);
				}

				/**
				 * Helper: unpins the modeless dialog.
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function unpin(instance) {
						//unpin the dialog
						instance.set('pinned', false);
				}


				/**
				 * Helper: enlarges the dialog to fill the entire screen.
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function maximize(instance) {
						// allow custom `onmaximize` method
						dispatchEvent('onmaximize', instance);
						//maximize the dialog
						addClass(instance.elements.root, classes.maximized);
						if (instance.isOpen()) {
								ensureNoOverflow();
						}
						// allow custom `onmaximized` method
						dispatchEvent('onmaximized', instance);
				}

				/**
				 * Helper: returns the dialog to its former size.
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function restore(instance) {
						// allow custom `onrestore` method
						dispatchEvent('onrestore', instance);
						//maximize the dialog
						removeClass(instance.elements.root, classes.maximized);
						if (instance.isOpen()) {
								ensureNoOverflow();
						}
						// allow custom `onrestored` method
						dispatchEvent('onrestored', instance);
				}

				/**
				 * Show or hide the maximize box.
				 *
				 * @param {Object} instance The dilog instance.
				 * @param {Boolean} on True to add the behavior, removes it otherwise.
				 *
				 * @return {undefined}
				 */
				function updatePinnable(instance) {
						if (instance.get('pinnable')) {
								// add class
								addClass(instance.elements.root, classes.pinnable);
						} else {
								// remove class
								removeClass(instance.elements.root, classes.pinnable);
						}
				}

				/**
				 * Helper: Fixes the absolutly positioned modal div position.
				 *
				 * @param {Object} instance The dialog instance.
				 *
				 * @return {undefined}
				 */
				function addAbsPositionFix(instance) {
						var scrollLeft = getScrollLeft();
						instance.elements.modal.style.marginTop = getScrollTop() + 'px';
						instance.elements.modal.style.marginLeft = scrollLeft + 'px';
						instance.elements.modal.style.marginRight = (-scrollLeft) + 'px';
				}

				/**
				 * Helper: Removes the absolutly positioned modal div position fix.
				 *
				 * @param {Object} instance The dialog instance.
				 *
				 * @return {undefined}
				 */
				function removeAbsPositionFix(instance) {
						var marginTop = parseInt(instance.elements.modal.style.marginTop, 10);
						var marginLeft = parseInt(instance.elements.modal.style.marginLeft, 10);
						instance.elements.modal.style.marginTop = '';
						instance.elements.modal.style.marginLeft = '';
						instance.elements.modal.style.marginRight = '';

						if (instance.isOpen()) {
								var top = 0,
										left = 0
								;
								if (instance.elements.dialog.style.top !== '') {
										top = parseInt(instance.elements.dialog.style.top, 10);
								}
								instance.elements.dialog.style.top = (top + (marginTop - getScrollTop())) + 'px';

								if (instance.elements.dialog.style.left !== '') {
										left = parseInt(instance.elements.dialog.style.left, 10);
								}
								instance.elements.dialog.style.left = (left + (marginLeft - getScrollLeft())) + 'px';
						}
				}
				/**
				 * Helper: Adds/Removes the absolutly positioned modal div position fix based on its pinned setting.
				 *
				 * @param {Object} instance The dialog instance.
				 *
				 * @return {undefined}
				 */
				function updateAbsPositionFix(instance) {
						// if modeless and unpinned add fix
						if (!instance.get('modal') && !instance.get('pinned')) {
								addAbsPositionFix(instance);
						} else {
								removeAbsPositionFix(instance);
						}
				}
				/**
				 * Toggles the dialog position lock | modeless only.
				 *
				 * @param {Object} instance The dilog instance.
				 * @param {Boolean} on True to make it modal, false otherwise.
				 *
				 * @return {undefined}
				 */
				function updatePinned(instance) {
						if (instance.get('pinned')) {
								removeClass(instance.elements.root, classes.unpinned);
								if (instance.isOpen()) {
										removeAbsPositionFix(instance);
								}
						} else {
								addClass(instance.elements.root, classes.unpinned);
								if (instance.isOpen() && !instance.isModal()) {
										addAbsPositionFix(instance);
								}
						}
				}

				/**
				 * Show or hide the maximize box.
				 *
				 * @param {Object} instance The dilog instance.
				 * @param {Boolean} on True to add the behavior, removes it otherwise.
				 *
				 * @return {undefined}
				 */
				function updateMaximizable(instance) {
						if (instance.get('maximizable')) {
								// add class
								addClass(instance.elements.root, classes.maximizable);
						} else {
								// remove class
								removeClass(instance.elements.root, classes.maximizable);
						}
				}

				/**
				 * Show or hide the close box.
				 *
				 * @param {Object} instance The dilog instance.
				 * @param {Boolean} on True to add the behavior, removes it otherwise.
				 *
				 * @return {undefined}
				 */
				function updateClosable(instance) {
						if (instance.get('closable')) {
								// add class
								addClass(instance.elements.root, classes.closable);
								bindClosableEvents(instance);
						} else {
								// remove class
								removeClass(instance.elements.root, classes.closable);
								unbindClosableEvents(instance);
						}
				}


				var cancelClick = false,// flag to cancel click event if already handled by end resize event (the mousedown, mousemove, mouseup sequence fires a click event.).
						modalClickHandlerTS=0 // stores last click timestamp to prevent executing the handler twice on double click.
						;

				/**
				 * Helper: closes the modal dialog when clicking the modal
				 *
				 * @param {Event} event DOM event object.
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function modalClickHandler(event, instance) {
						if(event.timeStamp - modalClickHandlerTS > 200 && (modalClickHandlerTS = event.timeStamp) && !cancelClick){
								var target = event.srcElement || event.target;
								if (instance.get('closableByDimmer') === true && target === instance.elements.modal) {
										triggerClose(instance);
								}
								cancelClick = false;
								return false;
						}
				}

				// stores last call timestamp to prevent triggering the callback twice.
				var callbackTS = 0;
				// flag to cancel keyup event if already handled by click event (pressing Enter on a focusted button).
				var cancelKeyup = false;
				/**
				 * Helper: triggers a button callback
				 *
				 * @param {Object}			The dilog instance.
				 * @param {Function}		Callback to check which button triggered the event.
				 *
				 * @return {undefined}
				 */
				function triggerCallback(instance, check) {
						if(Date.now() - callbackTS > 200 && (callbackTS = Date.now())){
								for (var idx = 0; idx < instance.__internal.buttons.length; idx += 1) {
										var button = instance.__internal.buttons[idx];
										if (!button.element.disabled && check(button)) {
												var closeEvent = createCloseEvent(idx, button);
												if (typeof instance.callback === 'function') {
														instance.callback.apply(instance, [closeEvent]);
												}
												//close the dialog only if not canceled.
												if (closeEvent.cancel === false) {
														instance.close();
												}
												break;
										}
								}
						}
				}

				/**
				 * Clicks event handler, attached to the dialog footer.
				 *
				 * @param {Event}			 DOM event object.
				 * @param {Object}			The dilog instance.
				 *
				 * @return {undefined}
				 */
				function buttonsClickHandler(event, instance) {
						var target = event.srcElement || event.target;
						triggerCallback(instance, function (button) {
								// if this button caused the click, cancel keyup event
								return button.element === target && (cancelKeyup = true);
						});
				}

				/**
				 * Keyup event handler, attached to the document.body
				 *
				 * @param {Event}			 DOM event object.
				 * @param {Object}			The dilog instance.
				 *
				 * @return {undefined}
				 */
				function keyupHandler(event) {
						//hitting enter while button has focus will trigger keyup too.
						//ignore if handled by clickHandler
						if (cancelKeyup) {
								cancelKeyup = false;
								return;
						}
						var instance = openDialogs[openDialogs.length - 1];
						var keyCode = event.keyCode;
						if (instance.__internal.buttons.length === 0 && keyCode === keys.ESC && instance.get('closable') === true) {
								triggerClose(instance);
								return false;
						}else if (usedKeys.indexOf(keyCode) > -1) {
								triggerCallback(instance, function (button) {
										return button.key === keyCode;
								});
								return false;
						}
				}
				/**
				* Keydown event handler, attached to the document.body
				*
				* @param {Event}				DOM event object.
				* @param {Object}			 The dilog instance.
				*
				* @return {undefined}
				*/
				function keydownHandler(event) {
						var instance = openDialogs[openDialogs.length - 1];
						var keyCode = event.keyCode;
						if (keyCode === keys.LEFT || keyCode === keys.RIGHT) {
								var buttons = instance.__internal.buttons;
								for (var x = 0; x < buttons.length; x += 1) {
										if (document.activeElement === buttons[x].element) {
												switch (keyCode) {
												case keys.LEFT:
														buttons[(x || buttons.length) - 1].element.focus();
														return;
												case keys.RIGHT:
														buttons[(x + 1) % buttons.length].element.focus();
														return;
												}
										}
								}
						}else if (keyCode < keys.F12 + 1 && keyCode > keys.F1 - 1 && usedKeys.indexOf(keyCode) > -1) {
								event.preventDefault();
								event.stopPropagation();
								triggerCallback(instance, function (button) {
										return button.key === keyCode;
								});
								return false;
						}
				}


				/**
				 * Sets focus to proper dialog element
				 *
				 * @param {Object} instance The dilog instance.
				 * @param {Node} [resetTarget=undefined] DOM element to reset focus to.
				 *
				 * @return {undefined}
				 */
				function setFocus(instance, resetTarget) {
						// reset target has already been determined.
						if (resetTarget) {
								resetTarget.focus();
						} else {
								// current instance focus settings
								var focus = instance.__internal.focus;
								// the focus element.
								var element = focus.element;

								switch (typeof focus.element) {
								// a number means a button index
								case 'number':
										if (instance.__internal.buttons.length > focus.element) {
												//in basic view, skip focusing the buttons.
												if (instance.get('basic') === true) {
														element = instance.elements.reset[0];
												} else {
														element = instance.__internal.buttons[focus.element].element;
												}
										}
										break;
								// a string means querySelector to select from dialog body contents.
								case 'string':
										element = instance.elements.body.querySelector(focus.element);
										break;
								// a function should return the focus element.
								case 'function':
										element = focus.element.call(instance);
										break;
								}

								// if no focus element, default to first reset element.
								if ((typeof element === 'undefined' || element === null) && instance.__internal.buttons.length === 0) {
										element = instance.elements.reset[0];
								}
								// focus
								if (element && element.focus) {
										element.focus();
										// if selectable
										if (focus.select && element.select) {
												element.select();
										}
								}
						}
				}

				/**
				 * Focus event handler, attached to document.body and dialogs own reset links.
				 * handles the focus for modal dialogs only.
				 *
				 * @param {Event} event DOM focus event object.
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function onReset(event, instance) {

						// should work on last modal if triggered from document.body
						if (!instance) {
								for (var x = openDialogs.length - 1; x > -1; x -= 1) {
										if (openDialogs[x].isModal()) {
												instance = openDialogs[x];
												break;
										}
								}
						}
						// if modal
						if (instance && instance.isModal()) {
								// determine reset target to enable forward/backward tab cycle.
								var resetTarget, target = event.srcElement || event.target;
								var lastResetElement = target === instance.elements.reset[1] || (instance.__internal.buttons.length === 0 && target === document.body);

								// if last reset link, then go to maximize or close
								if (lastResetElement) {
										if (instance.get('maximizable')) {
												resetTarget = instance.elements.commands.maximize;
										} else if (instance.get('closable')) {
												resetTarget = instance.elements.commands.close;
										}
								}
								// if no reset target found, try finding the best button
								if (resetTarget === undefined) {
										if (typeof instance.__internal.focus.element === 'number') {
												// button focus element, go to first available button
												if (target === instance.elements.reset[0]) {
														resetTarget = instance.elements.buttons.auxiliary.firstChild || instance.elements.buttons.primary.firstChild;
												} else if (lastResetElement) {
														//restart the cycle by going to first reset link
														resetTarget = instance.elements.reset[0];
												}
										} else {
												// will reach here when tapping backwards, so go to last child
												// The focus element SHOULD NOT be a button (logically!).
												if (target === instance.elements.reset[0]) {
														resetTarget = instance.elements.buttons.primary.lastChild || instance.elements.buttons.auxiliary.lastChild;
												}
										}
								}
								// focus
								setFocus(instance, resetTarget);
						}
				}
				/**
				 * Transition in transitionend event handler.
				 *
				 * @param {Event}			 TransitionEnd event object.
				 * @param {Object}			The dilog instance.
				 *
				 * @return {undefined}
				 */
				function handleTransitionInEvent(event, instance) {
						// clear the timer
						clearTimeout(instance.__internal.timerIn);

						// once transition is complete, set focus
						setFocus(instance);

						//restore scroll to prevent document jump
						restoreScrollPosition();

						// allow handling key up after transition ended.
						cancelKeyup = false;

						// allow custom `onfocus` method
						dispatchEvent('onfocus', instance);

						// unbind the event
						off(instance.elements.dialog, transition.type, instance.__internal.transitionInHandler);

						removeClass(instance.elements.root, classes.animationIn);
				}

				/**
				 * Transition out transitionend event handler.
				 *
				 * @param {Event}			 TransitionEnd event object.
				 * @param {Object}			The dilog instance.
				 *
				 * @return {undefined}
				 */
				function handleTransitionOutEvent(event, instance) {
						// clear the timer
						clearTimeout(instance.__internal.timerOut);
						// unbind the event
						off(instance.elements.dialog, transition.type, instance.__internal.transitionOutHandler);

						// reset move updates
						resetMove(instance);
						// reset resize updates
						resetResize(instance);

						// restore if maximized
						if (instance.isMaximized() && !instance.get('startMaximized')) {
								restore(instance);
						}

						// return focus to the last active element
						if (alertify.defaults.maintainFocus && instance.__internal.activeElement) {
								instance.__internal.activeElement.focus();
								instance.__internal.activeElement = null;
						}

						//destory the instance
						if (typeof instance.__internal.destroy === 'function') {
								instance.__internal.destroy.apply(instance);
						}
				}
				/* Controls moving a dialog around */
				//holde the current moving instance
				var movable = null,
						//holds the current X offset when move starts
						offsetX = 0,
						//holds the current Y offset when move starts
						offsetY = 0,
						xProp = 'pageX',
						yProp = 'pageY',
						bounds = null,
						refreshTop = false,
						moveDelegate = null
				;

				/**
				 * Helper: sets the element top/left coordinates
				 *
				 * @param {Event} event DOM event object.
				 * @param {Node} element The element being moved.
				 *
				 * @return {undefined}
				 */
				function moveElement(event, element) {
						var left = (event[xProp] - offsetX),
								top	= (event[yProp] - offsetY);

						if(refreshTop){
								top -= document.body.scrollTop;
						}

						element.style.left = left + 'px';
						element.style.top = top + 'px';

				}
				/**
				 * Helper: sets the element top/left coordinates within screen bounds
				 *
				 * @param {Event} event DOM event object.
				 * @param {Node} element The element being moved.
				 *
				 * @return {undefined}
				 */
				function moveElementBounded(event, element) {
						var left = (event[xProp] - offsetX),
								top	= (event[yProp] - offsetY);

						if(refreshTop){
								top -= document.body.scrollTop;
						}

						element.style.left = Math.min(bounds.maxLeft, Math.max(bounds.minLeft, left)) + 'px';
						if(refreshTop){
								element.style.top = Math.min(bounds.maxTop, Math.max(bounds.minTop, top)) + 'px';
						}else{
								element.style.top = Math.max(bounds.minTop, top) + 'px';
						}
				}


				/**
				 * Triggers the start of a move event, attached to the header element mouse down event.
				 * Adds no-selection class to the body, disabling selection while moving.
				 *
				 * @param {Event} event DOM event object.
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {Boolean} false
				 */
				function beginMove(event, instance) {
						if (resizable === null && !instance.isMaximized() && instance.get('movable')) {
								var eventSrc, left=0, top=0;
								if (event.type === 'touchstart') {
										event.preventDefault();
										eventSrc = event.targetTouches[0];
										xProp = 'clientX';
										yProp = 'clientY';
								} else if (event.button === 0) {
										eventSrc = event;
								}

								if (eventSrc) {

										var element = instance.elements.dialog;
										addClass(element, classes.capture);

										if (element.style.left) {
												left = parseInt(element.style.left, 10);
										}

										if (element.style.top) {
												top = parseInt(element.style.top, 10);
										}

										offsetX = eventSrc[xProp] - left;
										offsetY = eventSrc[yProp] - top;

										if(instance.isModal()){
												offsetY += instance.elements.modal.scrollTop;
										}else if(instance.isPinned()){
												offsetY -= document.body.scrollTop;
										}

										if(instance.get('moveBounded')){
												var current = element,
														offsetLeft = -left,
														offsetTop = -top;

												//calc offset
												do {
														offsetLeft += current.offsetLeft;
														offsetTop += current.offsetTop;
												} while (current = current.offsetParent);

												bounds = {
														maxLeft : offsetLeft,
														minLeft : -offsetLeft,
														maxTop	: document.documentElement.clientHeight - element.clientHeight - offsetTop,
														minTop	: -offsetTop
												};
												moveDelegate = moveElementBounded;
										}else{
												bounds = null;
												moveDelegate = moveElement;
										}

										// allow custom `onmove` method
										dispatchEvent('onmove', instance);

										refreshTop = !instance.isModal() && instance.isPinned();
										movable = instance;
										moveDelegate(eventSrc, element);
										addClass(document.body, classes.noSelection);
										return false;
								}
						}
				}

				/**
				 * The actual move handler,	attached to document.body mousemove event.
				 *
				 * @param {Event} event DOM event object.
				 *
				 * @return {undefined}
				 */
				function move(event) {
						if (movable) {
								var eventSrc;
								if (event.type === 'touchmove') {
										event.preventDefault();
										eventSrc = event.targetTouches[0];
								} else if (event.button === 0) {
										eventSrc = event;
								}
								if (eventSrc) {
										moveDelegate(eventSrc, movable.elements.dialog);
								}
						}
				}

				/**
				 * Triggers the end of a move event,	attached to document.body mouseup event.
				 * Removes no-selection class from document.body, allowing selection.
				 *
				 * @return {undefined}
				 */
				function endMove() {
						if (movable) {
								var instance = movable;
								movable = bounds = null;
								removeClass(document.body, classes.noSelection);
								removeClass(instance.elements.dialog, classes.capture);
								// allow custom `onmoved` method
								dispatchEvent('onmoved', instance);
						}
				}

				/**
				 * Resets any changes made by moving the element to its original state,
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function resetMove(instance) {
						movable = null;
						var element = instance.elements.dialog;
						element.style.left = element.style.top = '';
				}

				/**
				 * Updates the dialog move behavior.
				 *
				 * @param {Object} instance The dilog instance.
				 * @param {Boolean} on True to add the behavior, removes it otherwise.
				 *
				 * @return {undefined}
				 */
				function updateMovable(instance) {
						if (instance.get('movable')) {
								// add class
								addClass(instance.elements.root, classes.movable);
								if (instance.isOpen()) {
										bindMovableEvents(instance);
								}
						} else {

								//reset
								resetMove(instance);
								// remove class
								removeClass(instance.elements.root, classes.movable);
								if (instance.isOpen()) {
										unbindMovableEvents(instance);
								}
						}
				}

				/* Controls moving a dialog around */
				//holde the current instance being resized
				var resizable = null,
						//holds the staring left offset when resize starts.
						startingLeft = Number.Nan,
						//holds the staring width when resize starts.
						startingWidth = 0,
						//holds the initial width when resized for the first time.
						minWidth = 0,
						//holds the offset of the resize handle.
						handleOffset = 0
				;

				/**
				 * Helper: sets the element width/height and updates left coordinate if neccessary.
				 *
				 * @param {Event} event DOM mousemove event object.
				 * @param {Node} element The element being moved.
				 * @param {Boolean} pinned A flag indicating if the element being resized is pinned to the screen.
				 *
				 * @return {undefined}
				 */
				function resizeElement(event, element, pageRelative) {

						//calculate offsets from 0,0
						var current = element;
						var offsetLeft = 0;
						var offsetTop = 0;
						do {
								offsetLeft += current.offsetLeft;
								offsetTop += current.offsetTop;
						} while (current = current.offsetParent);

						// determine X,Y coordinates.
						var X, Y;
						if (pageRelative === true) {
								X = event.pageX;
								Y = event.pageY;
						} else {
								X = event.clientX;
								Y = event.clientY;
						}
						// rtl handling
						var isRTL = isRightToLeft();
						if (isRTL) {
								// reverse X
								X = document.body.offsetWidth - X;
								// if has a starting left, calculate offsetRight
								if (!isNaN(startingLeft)) {
										offsetLeft = document.body.offsetWidth - offsetLeft - element.offsetWidth;
								}
						}

						// set width/height
						element.style.height = (Y - offsetTop + handleOffset) + 'px';
						element.style.width = (X - offsetLeft + handleOffset) + 'px';

						// if the element being resized has a starting left, maintain it.
						// the dialog is centered, divide by half the offset to maintain the margins.
						if (!isNaN(startingLeft)) {
								var diff = Math.abs(element.offsetWidth - startingWidth) * 0.5;
								if (isRTL) {
										//negate the diff, why?
										//when growing it should decrease left
										//when shrinking it should increase left
										diff *= -1;
								}
								if (element.offsetWidth > startingWidth) {
										//growing
										element.style.left = (startingLeft + diff) + 'px';
								} else if (element.offsetWidth >= minWidth) {
										//shrinking
										element.style.left = (startingLeft - diff) + 'px';
								}
						}
				}

				/**
				 * Triggers the start of a resize event, attached to the resize handle element mouse down event.
				 * Adds no-selection class to the body, disabling selection while moving.
				 *
				 * @param {Event} event DOM event object.
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {Boolean} false
				 */
				function beginResize(event, instance) {
						if (!instance.isMaximized()) {
								var eventSrc;
								if (event.type === 'touchstart') {
										event.preventDefault();
										eventSrc = event.targetTouches[0];
								} else if (event.button === 0) {
										eventSrc = event;
								}
								if (eventSrc) {
										// allow custom `onresize` method
										dispatchEvent('onresize', instance);

										resizable = instance;
										handleOffset = instance.elements.resizeHandle.offsetHeight / 2;
										var element = instance.elements.dialog;
										addClass(element, classes.capture);
										startingLeft = parseInt(element.style.left, 10);
										element.style.height = element.offsetHeight + 'px';
										element.style.minHeight = instance.elements.header.offsetHeight + instance.elements.footer.offsetHeight + 'px';
										element.style.width = (startingWidth = element.offsetWidth) + 'px';

										if (element.style.maxWidth !== 'none') {
												element.style.minWidth = (minWidth = element.offsetWidth) + 'px';
										}
										element.style.maxWidth = 'none';
										addClass(document.body, classes.noSelection);
										return false;
								}
						}
				}

				/**
				 * The actual resize handler,	attached to document.body mousemove event.
				 *
				 * @param {Event} event DOM event object.
				 *
				 * @return {undefined}
				 */
				function resize(event) {
						if (resizable) {
								var eventSrc;
								if (event.type === 'touchmove') {
										event.preventDefault();
										eventSrc = event.targetTouches[0];
								} else if (event.button === 0) {
										eventSrc = event;
								}
								if (eventSrc) {
										resizeElement(eventSrc, resizable.elements.dialog, !resizable.get('modal') && !resizable.get('pinned'));
								}
						}
				}

				/**
				 * Triggers the end of a resize event,	attached to document.body mouseup event.
				 * Removes no-selection class from document.body, allowing selection.
				 *
				 * @return {undefined}
				 */
				function endResize() {
						if (resizable) {
								var instance = resizable;
								resizable = null;
								removeClass(document.body, classes.noSelection);
								removeClass(instance.elements.dialog, classes.capture);
								cancelClick = true;
								// allow custom `onresized` method
								dispatchEvent('onresized', instance);
						}
				}

				/**
				 * Resets any changes made by resizing the element to its original state.
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function resetResize(instance) {
						resizable = null;
						var element = instance.elements.dialog;
						if (element.style.maxWidth === 'none') {
								//clear inline styles.
								element.style.maxWidth = element.style.minWidth = element.style.width = element.style.height = element.style.minHeight = element.style.left = '';
								//reset variables.
								startingLeft = Number.Nan;
								startingWidth = minWidth = handleOffset = 0;
						}
				}


				/**
				 * Updates the dialog move behavior.
				 *
				 * @param {Object} instance The dilog instance.
				 * @param {Boolean} on True to add the behavior, removes it otherwise.
				 *
				 * @return {undefined}
				 */
				function updateResizable(instance) {
						if (instance.get('resizable')) {
								// add class
								addClass(instance.elements.root, classes.resizable);
								if (instance.isOpen()) {
										bindResizableEvents(instance);
								}
						} else {
								//reset
								resetResize(instance);
								// remove class
								removeClass(instance.elements.root, classes.resizable);
								if (instance.isOpen()) {
										unbindResizableEvents(instance);
								}
						}
				}

				/**
				 * Reset move/resize on window resize.
				 *
				 * @param {Event} event window resize event object.
				 *
				 * @return {undefined}
				 */
				function windowResize(/*event*/) {
						for (var x = 0; x < openDialogs.length; x += 1) {
								var instance = openDialogs[x];
								if (instance.get('autoReset')) {
										resetMove(instance);
										resetResize(instance);
								}
						}
				}
				/**
				 * Bind dialogs events
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function bindEvents(instance) {
						// if first dialog, hook global handlers
						if (openDialogs.length === 1) {
								//global
								on(window, 'resize', windowResize);
								on(document.body, 'keyup', keyupHandler);
								on(document.body, 'keydown', keydownHandler);
								on(document.body, 'focus', onReset);

								//move
								on(document.documentElement, 'mousemove', move);
								on(document.documentElement, 'touchmove', move);
								on(document.documentElement, 'mouseup', endMove);
								on(document.documentElement, 'touchend', endMove);
								//resize
								on(document.documentElement, 'mousemove', resize);
								on(document.documentElement, 'touchmove', resize);
								on(document.documentElement, 'mouseup', endResize);
								on(document.documentElement, 'touchend', endResize);
						}

						// common events
						on(instance.elements.commands.container, 'click', instance.__internal.commandsClickHandler);
						on(instance.elements.footer, 'click', instance.__internal.buttonsClickHandler);
						on(instance.elements.reset[0], 'focus', instance.__internal.resetHandler);
						on(instance.elements.reset[1], 'focus', instance.__internal.resetHandler);

						//prevent handling key up when dialog is being opened by a key stroke.
						cancelKeyup = true;
						// hook in transition handler
						on(instance.elements.dialog, transition.type, instance.__internal.transitionInHandler);

						// modelss only events
						if (!instance.get('modal')) {
								bindModelessEvents(instance);
						}

						// resizable
						if (instance.get('resizable')) {
								bindResizableEvents(instance);
						}

						// movable
						if (instance.get('movable')) {
								bindMovableEvents(instance);
						}
				}

				/**
				 * Unbind dialogs events
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function unbindEvents(instance) {
						// if last dialog, remove global handlers
						if (openDialogs.length === 1) {
								//global
								off(window, 'resize', windowResize);
								off(document.body, 'keyup', keyupHandler);
								off(document.body, 'keydown', keydownHandler);
								off(document.body, 'focus', onReset);
								//move
								off(document.documentElement, 'mousemove', move);
								off(document.documentElement, 'mouseup', endMove);
								//resize
								off(document.documentElement, 'mousemove', resize);
								off(document.documentElement, 'mouseup', endResize);
						}

						// common events
						off(instance.elements.commands.container, 'click', instance.__internal.commandsClickHandler);
						off(instance.elements.footer, 'click', instance.__internal.buttonsClickHandler);
						off(instance.elements.reset[0], 'focus', instance.__internal.resetHandler);
						off(instance.elements.reset[1], 'focus', instance.__internal.resetHandler);

						// hook out transition handler
						on(instance.elements.dialog, transition.type, instance.__internal.transitionOutHandler);

						// modelss only events
						if (!instance.get('modal')) {
								unbindModelessEvents(instance);
						}

						// movable
						if (instance.get('movable')) {
								unbindMovableEvents(instance);
						}

						// resizable
						if (instance.get('resizable')) {
								unbindResizableEvents(instance);
						}

				}

				/**
				 * Bind modeless specific events
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function bindModelessEvents(instance) {
						on(instance.elements.dialog, 'focus', instance.__internal.bringToFrontHandler, true);
				}

				/**
				 * Unbind modeless specific events
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function unbindModelessEvents(instance) {
						off(instance.elements.dialog, 'focus', instance.__internal.bringToFrontHandler, true);
				}



				/**
				 * Bind movable specific events
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function bindMovableEvents(instance) {
						on(instance.elements.header, 'mousedown', instance.__internal.beginMoveHandler);
						on(instance.elements.header, 'touchstart', instance.__internal.beginMoveHandler);
				}

				/**
				 * Unbind movable specific events
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function unbindMovableEvents(instance) {
						off(instance.elements.header, 'mousedown', instance.__internal.beginMoveHandler);
						off(instance.elements.header, 'touchstart', instance.__internal.beginMoveHandler);
				}



				/**
				 * Bind resizable specific events
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function bindResizableEvents(instance) {
						on(instance.elements.resizeHandle, 'mousedown', instance.__internal.beginResizeHandler);
						on(instance.elements.resizeHandle, 'touchstart', instance.__internal.beginResizeHandler);
				}

				/**
				 * Unbind resizable specific events
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function unbindResizableEvents(instance) {
						off(instance.elements.resizeHandle, 'mousedown', instance.__internal.beginResizeHandler);
						off(instance.elements.resizeHandle, 'touchstart', instance.__internal.beginResizeHandler);
				}

				/**
				 * Bind closable events
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function bindClosableEvents(instance) {
						on(instance.elements.modal, 'click', instance.__internal.modalClickHandler);
				}

				/**
				 * Unbind closable specific events
				 *
				 * @param {Object} instance The dilog instance.
				 *
				 * @return {undefined}
				 */
				function unbindClosableEvents(instance) {
						off(instance.elements.modal, 'click', instance.__internal.modalClickHandler);
				}
				// dialog API
				return {
						__init:initialize,
						/**
						 * Check if dialog is currently open
						 *
						 * @return {Boolean}
						 */
						isOpen: function () {
								return this.__internal.isOpen;
						},
						isModal: function (){
								return this.elements.root.className.indexOf(classes.modeless) < 0;
						},
						isMaximized:function(){
								return this.elements.root.className.indexOf(classes.maximized) > -1;
						},
						isPinned:function(){
								return this.elements.root.className.indexOf(classes.unpinned) < 0;
						},
						maximize:function(){
								if(!this.isMaximized()){
										maximize(this);
								}
								return this;
						},
						restore:function(){
								if(this.isMaximized()){
										restore(this);
								}
								return this;
						},
						pin:function(){
								if(!this.isPinned()){
										pin(this);
								}
								return this;
						},
						unpin:function(){
								if(this.isPinned()){
										unpin(this);
								}
								return this;
						},
						bringToFront:function(){
								bringToFront(null, this);
								return this;
						},
						/**
						 * Move the dialog to a specific x/y coordinates
						 *
						 * @param {Number} x		The new dialog x coordinate in pixels.
						 * @param {Number} y		The new dialog y coordinate in pixels.
						 *
						 * @return {Object} The dialog instance.
						 */
						moveTo:function(x,y){
								if(!isNaN(x) && !isNaN(y)){
										// allow custom `onmove` method
										dispatchEvent('onmove', this);

										var element = this.elements.dialog,
												current = element,
												offsetLeft = 0,
												offsetTop = 0;

										//subtract existing left,top
										if (element.style.left) {
												offsetLeft -= parseInt(element.style.left, 10);
										}
										if (element.style.top) {
												offsetTop -= parseInt(element.style.top, 10);
										}
										//calc offset
										do {
												offsetLeft += current.offsetLeft;
												offsetTop += current.offsetTop;
										} while (current = current.offsetParent);

										//calc left, top
										var left = (x - offsetLeft);
										var top	= (y - offsetTop);

										//// rtl handling
										if (isRightToLeft()) {
												left *= -1;
										}

										element.style.left = left + 'px';
										element.style.top = top + 'px';

										// allow custom `onmoved` method
										dispatchEvent('onmoved', this);
								}
								return this;
						},
						/**
						 * Resize the dialog to a specific width/height (the dialog must be 'resizable').
						 * The dialog can be resized to:
						 *	A minimum width equal to the initial display width
						 *	A minimum height equal to the sum of header/footer heights.
						 *
						 *
						 * @param {Number or String} width		The new dialog width in pixels or in percent.
						 * @param {Number or String} height	 The new dialog height in pixels or in percent.
						 *
						 * @return {Object} The dialog instance.
						 */
						resizeTo:function(width,height){
								var w = parseFloat(width),
										h = parseFloat(height),
										regex = /(\d*\.\d+|\d+)%/
								;

								if(!isNaN(w) && !isNaN(h) && this.get('resizable') === true){

										// allow custom `onresize` method
										dispatchEvent('onresize', this);

										if(('' + width).match(regex)){
												w = w / 100 * document.documentElement.clientWidth ;
										}

										if(('' + height).match(regex)){
												h = h / 100 * document.documentElement.clientHeight;
										}

										var element = this.elements.dialog;
										if (element.style.maxWidth !== 'none') {
												element.style.minWidth = (minWidth = element.offsetWidth) + 'px';
										}
										element.style.maxWidth = 'none';
										element.style.minHeight = this.elements.header.offsetHeight + this.elements.footer.offsetHeight + 'px';
										element.style.width = w + 'px';
										element.style.height = h + 'px';

										// allow custom `onresized` method
										dispatchEvent('onresized', this);
								}
								return this;
						},
						/**
						 * Gets or Sets dialog settings/options
						 *
						 * @param {String|Object} key A string specifying a propery name or a collection of key/value pairs.
						 * @param {Object} value Optional, the value associated with the key (in case it was a string).
						 *
						 * @return {undefined}
						 */
						setting : function (key, value) {
								var self = this;
								var result = update(this, this.__internal.options, function(k,o,n){ optionUpdated(self,k,o,n); }, key, value);
								if(result.op === 'get'){
										if(result.found){
												return result.value;
										}else if(typeof this.settings !== 'undefined'){
												return update(this, this.settings, this.settingUpdated || function(){}, key, value).value;
										}else{
												return undefined;
										}
								}else if(result.op === 'set'){
										if(result.items.length > 0){
												var callback = this.settingUpdated || function(){};
												for(var x=0;x<result.items.length;x+=1){
														var item = result.items[x];
														if(!item.found && typeof this.settings !== 'undefined'){
																update(this, this.settings, callback, item.key, item.value);
														}
												}
										}
										return this;
								}
						},
						/**
						 * [Alias] Sets dialog settings/options
						 */
						set:function(key, value){
								this.setting(key,value);
								return this;
						},
						/**
						 * [Alias] Gets dialog settings/options
						 */
						get:function(key){
								return this.setting(key);
						},
						/**
						* Sets dialog header
						* @content {string or element}
						*
						* @return {undefined}
						*/
						setHeader:function(content){
								if(typeof content === 'string'){
										clearContents(this.elements.header);
										this.elements.header.innerHTML = content;
								}else if (content instanceof window.HTMLElement && this.elements.header.firstChild !== content){
										clearContents(this.elements.header);
										this.elements.header.appendChild(content);
								}
								return this;
						},
						/**
						* Sets dialog contents
						* @content {string or element}
						*
						* @return {undefined}
						*/
						setContent:function(content){
								if(typeof content === 'string'){
										clearContents(this.elements.content);
										this.elements.content.innerHTML = content;
								}else if (content instanceof window.HTMLElement && this.elements.content.firstChild !== content){
										clearContents(this.elements.content);
										this.elements.content.appendChild(content);
								}
								return this;
						},
						/**
						 * Show the dialog as modal
						 *
						 * @return {Object} the dialog instance.
						 */
						showModal: function(className){
								return this.show(true, className);
						},
						/**
						 * Show the dialog
						 *
						 * @return {Object} the dialog instance.
						 */
						show: function (modal, className) {

								// ensure initialization
								initialize(this);

								if ( !this.__internal.isOpen ) {

										// add to open dialogs
										this.__internal.isOpen = true;
										openDialogs.push(this);

										// save last focused element
										if(alertify.defaults.maintainFocus){
												this.__internal.activeElement = document.activeElement;
										}

										// set tabindex attribute on body element this allows script to give it focusable
										if(!document.body.hasAttribute('tabindex')) {
												document.body.setAttribute( 'tabindex', tabindex = '0');
										}

										//allow custom dom manipulation updates before showing the dialog.
										if(typeof this.prepare === 'function'){
												this.prepare();
										}

										bindEvents(this);

										if(modal !== undefined){
												this.set('modal', modal);
										}

										//save scroll to prevent document jump
										saveScrollPosition();

										ensureNoOverflow();

										// allow custom dialog class on show
										if(typeof className === 'string' && className !== ''){
												this.__internal.className = className;
												addClass(this.elements.root, className);
										}

										// maximize if start maximized
										if ( this.get('startMaximized')) {
												this.maximize();
										}else if(this.isMaximized()){
												restore(this);
										}

										updateAbsPositionFix(this);
										this.elements.root.removeAttribute('style');
										removeClass(this.elements.root, classes.animationOut);
										addClass(this.elements.root, classes.animationIn);

										// set 1s fallback in case transition event doesn't fire
										clearTimeout( this.__internal.timerIn);
										this.__internal.timerIn = setTimeout( this.__internal.transitionInHandler, transition.supported ? 1000 : 100 );

										if(isSafari){
												// force desktop safari reflow
												var root = this.elements.root;
												root.style.display	= 'none';
												setTimeout(function(){root.style.display	= 'block';}, 0);
										}

										//reflow
										reflow = this.elements.root.offsetWidth;

										// show dialog
										removeClass(this.elements.root, classes.hidden);

										// internal on show event
										if(typeof this.hooks.onshow === 'function'){
												this.hooks.onshow.call(this);
										}

										// allow custom `onshow` method
										dispatchEvent('onshow', this);

								}else{
										// reset move updates
										resetMove(this);
										// reset resize updates
										resetResize(this);
										// shake the dialog to indicate its already open
										addClass(this.elements.dialog, classes.shake);
										var self = this;
										setTimeout(function(){
												removeClass(self.elements.dialog, classes.shake);
										},200);
								}
								return this;
						},
						/**
						 * Close the dialog
						 *
						 * @return {Object} The dialog instance
						 */
						close: function () {
								if (this.__internal.isOpen ) {
										// custom `onclosing` event
										if(dispatchEvent('onclosing', this) !== false){

												unbindEvents(this);

												removeClass(this.elements.root, classes.animationIn);
												addClass(this.elements.root, classes.animationOut);

												// set 1s fallback in case transition event doesn't fire
												clearTimeout( this.__internal.timerOut );
												this.__internal.timerOut = setTimeout( this.__internal.transitionOutHandler, transition.supported ? 1000 : 100 );
												// hide dialog
												addClass(this.elements.root, classes.hidden);
												//reflow
												reflow = this.elements.modal.offsetWidth;

												// remove custom dialog class on hide
												if (typeof this.__internal.className !== 'undefined' && this.__internal.className !== '') {
														removeClass(this.elements.root, this.__internal.className);
												}

												// internal on close event
												if(typeof this.hooks.onclose === 'function'){
														this.hooks.onclose.call(this);
												}

												// allow custom `onclose` method
												dispatchEvent('onclose', this);

												//remove from open dialogs
												openDialogs.splice(openDialogs.indexOf(this),1);
												this.__internal.isOpen = false;

												ensureNoOverflow();
										}

								}
								// last dialog and tab index was set by us, remove it.
								if(!openDialogs.length && tabindex === '0'){
										document.body.removeAttribute('tabindex');
								}
								return this;
						},
						/**
						 * Close all open dialogs except this.
						 *
						 * @return {undefined}
						 */
						closeOthers:function(){
								alertify.closeAll(this);
								return this;
						},
						/**
						 * Destroys this dialog instance
						 *
						 * @return {undefined}
						 */
						destroy:function(){
								if(this.__internal) {
										if (this.__internal.isOpen ) {
												//mark dialog for destruction, this will be called on tranistionOut event.
												this.__internal.destroy = function(){
														destruct(this, initialize);
												};
												//close the dialog to unbind all events.
												this.close();
										}else if(!this.__internal.destroy){
												destruct(this, initialize);
										}
								}
								return this;
						},
				};
		} () );
		var notifier = (function () {
				var reflow,
						element,
						openInstances = [],
						classes = {
								base: 'alertify-notifier',
								message: 'ajs-message',
								top: 'ajs-top',
								right: 'ajs-right',
								bottom: 'ajs-bottom',
								left: 'ajs-left',
								center: 'ajs-center',
								visible: 'ajs-visible',
								hidden: 'ajs-hidden',
								close: 'ajs-close'
						};
				/**
				 * Helper: initializes the notifier instance
				 *
				 */
				function initialize(instance) {

						if (!instance.__internal) {
								instance.__internal = {
										position: alertify.defaults.notifier.position,
										delay: alertify.defaults.notifier.delay,
								};

								element = document.createElement('DIV');

								updatePosition(instance);
						}

						//add to DOM tree.
						if (element.parentNode !== document.body) {
								document.body.appendChild(element);
						}
				}

				function pushInstance(instance) {
						instance.__internal.pushed = true;
						openInstances.push(instance);
				}
				function popInstance(instance) {
						openInstances.splice(openInstances.indexOf(instance), 1);
						instance.__internal.pushed = false;
				}
				/**
				 * Helper: update the notifier instance position
				 *
				 */
				function updatePosition(instance) {
						element.className = classes.base;
						switch (instance.__internal.position) {
						case 'top-right':
								addClass(element, classes.top + ' ' + classes.right);
								break;
						case 'top-left':
								addClass(element, classes.top + ' ' + classes.left);
								break;
						case 'top-center':
								addClass(element, classes.top + ' ' + classes.center);
								break;
						case 'bottom-left':
								addClass(element, classes.bottom + ' ' + classes.left);
								break;
						case 'bottom-center':
								addClass(element, classes.bottom + ' ' + classes.center);
								break;

						default:
						case 'bottom-right':
								addClass(element, classes.bottom + ' ' + classes.right);
								break;
						}
				}

				/**
				* creates a new notification message
				*
				* @param	{DOMElement} message	The notifier message element
				* @param	{Number} wait	 Time (in ms) to wait before the message is dismissed, a value of 0 means keep open till clicked.
				* @param	{Function} callback A callback function to be invoked when the message is dismissed.
				*
				* @return {undefined}
				*/
				function create(div, callback) {

						function clickDelegate(event, instance) {
								if(!instance.__internal.closeButton || event.target.getAttribute('data-close') === 'true'){
										instance.dismiss(true);
								}
						}

						function transitionDone(event, instance) {
								// unbind event
								off(instance.element, transition.type, transitionDone);
								// remove the message
								element.removeChild(instance.element);
						}

						function initialize(instance) {
								if (!instance.__internal) {
										instance.__internal = {
												pushed: false,
												delay : undefined,
												timer: undefined,
												clickHandler: undefined,
												transitionEndHandler: undefined,
												transitionTimeout: undefined
										};
										instance.__internal.clickHandler = delegate(instance, clickDelegate);
										instance.__internal.transitionEndHandler = delegate(instance, transitionDone);
								}
								return instance;
						}
						function clearTimers(instance) {
								clearTimeout(instance.__internal.timer);
								clearTimeout(instance.__internal.transitionTimeout);
						}
						return initialize({
								/* notification DOM element*/
								element: div,
								/*
								 * Pushes a notification message
								 * @param {string or DOMElement} content The notification message content
								 * @param {Number} wait The time (in seconds) to wait before the message is dismissed, a value of 0 means keep open till clicked.
								 *
								 */
								push: function (_content, _wait) {
										if (!this.__internal.pushed) {

												pushInstance(this);
												clearTimers(this);

												var content, wait;
												switch (arguments.length) {
												case 0:
														wait = this.__internal.delay;
														break;
												case 1:
														if (typeof (_content) === 'number') {
																wait = _content;
														} else {
																content = _content;
																wait = this.__internal.delay;
														}
														break;
												case 2:
														content = _content;
														wait = _wait;
														break;
												}
												this.__internal.closeButton = alertify.defaults.notifier.closeButton;
												// set contents
												if (typeof content !== 'undefined') {
														this.setContent(content);
												}
												// append or insert
												if (notifier.__internal.position.indexOf('top') < 0) {
														element.appendChild(this.element);
												} else {
														element.insertBefore(this.element, element.firstChild);
												}
												reflow = this.element.offsetWidth;
												addClass(this.element, classes.visible);
												// attach click event
												on(this.element, 'click', this.__internal.clickHandler);
												return this.delay(wait);
										}
										return this;
								},
								/*
								 * {Function} callback function to be invoked before dismissing the notification message.
								 * Remarks: A return value === 'false' will cancel the dismissal
								 *
								 */
								ondismiss: function () { },
								/*
								 * {Function} callback function to be invoked when the message is dismissed.
								 *
								 */
								callback: callback,
								/*
								 * Dismisses the notification message
								 * @param {Boolean} clicked A flag indicating if the dismissal was caused by a click.
								 *
								 */
								dismiss: function (clicked) {
										if (this.__internal.pushed) {
												clearTimers(this);
												if (!(typeof this.ondismiss === 'function' && this.ondismiss.call(this) === false)) {
														//detach click event
														off(this.element, 'click', this.__internal.clickHandler);
														// ensure element exists
														if (typeof this.element !== 'undefined' && this.element.parentNode === element) {
																//transition end or fallback
																this.__internal.transitionTimeout = setTimeout(this.__internal.transitionEndHandler, transition.supported ? 1000 : 100);
																removeClass(this.element, classes.visible);

																// custom callback on dismiss
																if (typeof this.callback === 'function') {
																		this.callback.call(this, clicked);
																}
														}
														popInstance(this);
												}
										}
										return this;
								},
								/*
								 * Delays the notification message dismissal
								 * @param {Number} wait The time (in seconds) to wait before the message is dismissed, a value of 0 means keep open till clicked.
								 *
								 */
								delay: function (wait) {
										clearTimers(this);
										this.__internal.delay = typeof wait !== 'undefined' && !isNaN(+wait) ? +wait : notifier.__internal.delay;
										if (this.__internal.delay > 0) {
												var	self = this;
												this.__internal.timer = setTimeout(function () { self.dismiss(); }, this.__internal.delay * 1000);
										}
										return this;
								},
								/*
								 * Sets the notification message contents
								 * @param {string or DOMElement} content The notification message content
								 *
								 */
								setContent: function (content) {
										if (typeof content === 'string') {
												clearContents(this.element);
												this.element.innerHTML = content;
										} else if (content instanceof window.HTMLElement && this.element.firstChild !== content) {
												clearContents(this.element);
												this.element.appendChild(content);
										}
										if(this.__internal.closeButton){
												var close = document.createElement('span');
												addClass(close, classes.close);
												close.setAttribute('data-close', true);
												this.element.appendChild(close);
										}
										return this;
								},
								/*
								 * Dismisses all open notifications except this.
								 *
								 */
								dismissOthers: function () {
										notifier.dismissAll(this);
										return this;
								}
						});
				}

				//notifier api
				return {
						/**
						 * Gets or Sets notifier settings.
						 *
						 * @param {string} key The setting name
						 * @param {Variant} value The setting value.
						 *
						 * @return {Object} if the called as a setter, return the notifier instance.
						 */
						setting: function (key, value) {
								//ensure init
								initialize(this);

								if (typeof value === 'undefined') {
										//get
										return this.__internal[key];
								} else {
										//set
										switch (key) {
										case 'position':
												this.__internal.position = value;
												updatePosition(this);
												break;
										case 'delay':
												this.__internal.delay = value;
												break;
										}
								}
								return this;
						},
						/**
						 * [Alias] Sets dialog settings/options
						 */
						set:function(key,value){
								this.setting(key,value);
								return this;
						},
						/**
						 * [Alias] Gets dialog settings/options
						 */
						get:function(key){
								return this.setting(key);
						},
						/**
						 * Creates a new notification message
						 *
						 * @param {string} type The type of notification message (simply a CSS class name 'ajs-{type}' to be added).
						 * @param {Function} callback	A callback function to be invoked when the message is dismissed.
						 *
						 * @return {undefined}
						 */
						create: function (type, callback) {
								//ensure notifier init
								initialize(this);
								//create new notification message
								var div = document.createElement('div');
								div.className = classes.message + ((typeof type === 'string' && type !== '') ? ' ajs-' + type : '');
								return create(div, callback);
						},
						/**
						 * Dismisses all open notifications.
						 *
						 * @param {Object} excpet [optional] The notification object to exclude from dismissal.
						 *
						 */
						dismissAll: function (except) {
								var clone = openInstances.slice(0);
								for (var x = 0; x < clone.length; x += 1) {
										var	instance = clone[x];
										if (except === undefined || except !== instance) {
												instance.dismiss();
										}
								}
						}
				};
		})();

		/**
		 * Alertify public API
		 * This contains everything that is exposed through the alertify object.
		 *
		 * @return {Object}
		 */
		function Alertify() {

				// holds a references of created dialogs
				var dialogs = {};

				/**
				 * Extends a given prototype by merging properties from base into sub.
				 *
				 * @sub {Object} sub The prototype being overwritten.
				 * @base {Object} base The prototype being written.
				 *
				 * @return {Object} The extended prototype.
				 */
				function extend(sub, base) {
						// copy dialog pototype over definition.
						for (var prop in base) {
								if (base.hasOwnProperty(prop)) {
										sub[prop] = base[prop];
								}
						}
						return sub;
				}


				/**
				* Helper: returns a dialog instance from saved dialogs.
				* and initializes the dialog if its not already initialized.
				*
				* @name {String} name The dialog name.
				*
				* @return {Object} The dialog instance.
				*/
				function get_dialog(name) {
						var dialog = dialogs[name].dialog;
						//initialize the dialog if its not already initialized.
						if (dialog && typeof dialog.__init === 'function') {
								dialog.__init(dialog);
						}
						return dialog;
				}

				/**
				 * Helper:	registers a new dialog definition.
				 *
				 * @name {String} name The dialog name.
				 * @Factory {Function} Factory a function resposible for creating dialog prototype.
				 * @transient {Boolean} transient True to create a new dialog instance each time the dialog is invoked, false otherwise.
				 * @base {String} base the name of another dialog to inherit from.
				 *
				 * @return {Object} The dialog definition.
				 */
				function register(name, Factory, transient, base) {
						var definition = {
								dialog: null,
								factory: Factory
						};

						//if this is based on an existing dialog, create a new definition
						//by applying the new protoype over the existing one.
						if (base !== undefined) {
								definition.factory = function () {
										return extend(new dialogs[base].factory(), new Factory());
								};
						}

						if (!transient) {
								//create a new definition based on dialog
								definition.dialog = extend(new definition.factory(), dialog);
						}
						return dialogs[name] = definition;
				}

				return {
						/**
						 * Alertify defaults
						 *
						 * @type {Object}
						 */
						defaults: defaults,
						/**
						 * Dialogs factory
						 *
						 * @param {string}			Dialog name.
						 * @param {Function}		A Dialog factory function.
						 * @param {Boolean}		 Indicates whether to create a singleton or transient dialog.
						 * @param {String}			The name of the base type to inherit from.
						 */
						dialog: function (name, Factory, transient, base) {

								// get request, create a new instance and return it.
								if (typeof Factory !== 'function') {
										return get_dialog(name);
								}

								if (this.hasOwnProperty(name)) {
										throw new Error('alertify.dialog: name already exists');
								}

								// register the dialog
								var definition = register(name, Factory, transient, base);

								if (transient) {

										// make it public
										this[name] = function () {
												//if passed with no params, consider it a get request
												if (arguments.length === 0) {
														return definition.dialog;
												} else {
														var instance = extend(new definition.factory(), dialog);
														//ensure init
														if (instance && typeof instance.__init === 'function') {
																instance.__init(instance);
														}
														instance['main'].apply(instance, arguments);
														return instance['show'].apply(instance);
												}
										};
								} else {
										// make it public
										this[name] = function () {
												//ensure init
												if (definition.dialog && typeof definition.dialog.__init === 'function') {
														definition.dialog.__init(definition.dialog);
												}
												//if passed with no params, consider it a get request
												if (arguments.length === 0) {
														return definition.dialog;
												} else {
														var dialog = definition.dialog;
														dialog['main'].apply(definition.dialog, arguments);
														return dialog['show'].apply(definition.dialog);
												}
										};
								}
						},
						/**
						 * Close all open dialogs.
						 *
						 * @param {Object} excpet [optional] The dialog object to exclude from closing.
						 *
						 * @return {undefined}
						 */
						closeAll: function (except) {
								var clone = openDialogs.slice(0);
								for (var x = 0; x < clone.length; x += 1) {
										var instance = clone[x];
										if (except === undefined || except !== instance) {
												instance.close();
										}
								}
						},
						/**
						 * Destroy all open dialogs.
						 *
						 * @param {Object} excpet [optional] The dialog object to exclude from closing.
						 *
						 * @return {undefined}
						 */
						destroyAll: function (except) {
								var clone = openDialogs.slice(0);
								for (var x = 0; x < clone.length; x += 1) {
										var instance = clone[x];
										if (except === undefined || except !== instance) {
												instance.destroy();
										}
								}
						},
						/**
						 * Gets or Sets dialog settings/options. if the dialog is transient, this call does nothing.
						 *
						 * @param {string} name The dialog name.
						 * @param {String|Object} key A string specifying a propery name or a collection of key/value pairs.
						 * @param {Variant} value Optional, the value associated with the key (in case it was a string).
						 *
						 * @return {undefined}
						 */
						setting: function (name, key, value) {

								if (name === 'notifier') {
										return notifier.setting(key, value);
								}

								var dialog = get_dialog(name);
								if (dialog) {
										return dialog.setting(key, value);
								}
						},
						/**
						 * [Alias] Sets dialog settings/options
						 */
						set: function(name,key,value){
								return this.setting(name, key,value);
						},
						/**
						 * [Alias] Gets dialog settings/options
						 */
						get: function(name, key){
								return this.setting(name, key);
						},
						/**
						 * Creates a new notification message.
						 * If a type is passed, a class name "ajs-{type}" will be added.
						 * This allows for custom look and feel for various types of notifications.
						 *
						 * @param	{String | DOMElement}		[message=undefined]		 Message text
						 * @param	{String}								 [type='']							 Type of log message
						 * @param	{String}								 [wait='']							 Time (in seconds) to wait before auto-close
						 * @param	{Function}							 [callback=undefined]		A callback function to be invoked when the log is closed.
						 *
						 * @return {Object} Notification object.
						 */
						notify: function (message, type, wait, callback) {
								return notifier.create(type, callback).push(message, wait);
						},
						/**
						 * Creates a new notification message.
						 *
						 * @param	{String}		 [message=undefined]		 Message text
						 * @param	{String}		 [wait='']							 Time (in seconds) to wait before auto-close
						 * @param	{Function}	 [callback=undefined]		A callback function to be invoked when the log is closed.
						 *
						 * @return {Object} Notification object.
						 */
						message: function (message, wait, callback) {
								return notifier.create(null, callback).push(message, wait);
						},
						/**
						 * Creates a new notification message of type 'success'.
						 *
						 * @param	{String}		 [message=undefined]		 Message text
						 * @param	{String}		 [wait='']							 Time (in seconds) to wait before auto-close
						 * @param	{Function}	 [callback=undefined]		A callback function to be invoked when the log is closed.
						 *
						 * @return {Object} Notification object.
						 */
						success: function (message, wait, callback) {
								return notifier.create('success', callback).push(message, wait);
						},
						/**
						 * Creates a new notification message of type 'error'.
						 *
						 * @param	{String}		 [message=undefined]		 Message text
						 * @param	{String}		 [wait='']							 Time (in seconds) to wait before auto-close
						 * @param	{Function}	 [callback=undefined]		A callback function to be invoked when the log is closed.
						 *
						 * @return {Object} Notification object.
						 */
						error: function (message, wait, callback) {
								return notifier.create('error', callback).push(message, wait);
						},
						/**
						 * Creates a new notification message of type 'warning'.
						 *
						 * @param	{String}		 [message=undefined]		 Message text
						 * @param	{String}		 [wait='']							 Time (in seconds) to wait before auto-close
						 * @param	{Function}	 [callback=undefined]		A callback function to be invoked when the log is closed.
						 *
						 * @return {Object} Notification object.
						 */
						warning: function (message, wait, callback) {
								return notifier.create('warning', callback).push(message, wait);
						},
						/**
						 * Dismisses all open notifications
						 *
						 * @return {undefined}
						 */
						dismissAll: function () {
								notifier.dismissAll();
						}
				};
		}
		var alertify = new Alertify();

		/**
		* Alert dialog definition
		*
		* invoked by:
		*	 alertify.alert(message);
		*	 alertify.alert(title, message);
		*	 alertify.alert(message, onok);
		*	 alertify.alert(title, message, onok);
		 */
		alertify.dialog('alert', function () {
				return {
						main: function (_title, _message, _onok) {
								var title, message, onok;
								switch (arguments.length) {
								case 1:
										message = _title;
										break;
								case 2:
										if (typeof _message === 'function') {
												message = _title;
												onok = _message;
										} else {
												title = _title;
												message = _message;
										}
										break;
								case 3:
										title = _title;
										message = _message;
										onok = _onok;
										break;
								}
								this.set('title', title);
								this.set('message', message);
								this.set('onok', onok);
								return this;
						},
						setup: function () {
								return {
										buttons: [
												{
														text: alertify.defaults.glossary.ok,
														key: keys.ESC,
														invokeOnClose: true,
														className: alertify.defaults.theme.ok,
												}
										],
										focus: {
												element: 0,
												select: false
										},
										options: {
												maximizable: false,
												resizable: false
										}
								};
						},
						build: function () {
								// nothing
						},
						prepare: function () {
								//nothing
						},
						setMessage: function (message) {
								this.setContent(message);
						},
						settings: {
								message: undefined,
								onok: undefined,
								label: undefined,
						},
						settingUpdated: function (key, oldValue, newValue) {
								switch (key) {
								case 'message':
										this.setMessage(newValue);
										break;
								case 'label':
										if (this.__internal.buttons[0].element) {
												this.__internal.buttons[0].element.innerHTML = newValue;
										}
										break;
								}
						},
						callback: function (closeEvent) {
								if (typeof this.get('onok') === 'function') {
										var returnValue = this.get('onok').call(this, closeEvent);
										if (typeof returnValue !== 'undefined') {
												closeEvent.cancel = !returnValue;
										}
								}
						}
				};
		});
		/**
		 * Confirm dialog object
		 *
		 *	alertify.confirm(message);
		 *	alertify.confirm(message, onok);
		 *	alertify.confirm(message, onok, oncancel);
		 *	alertify.confirm(title, message, onok, oncancel);
		 */
		alertify.dialog('confirm', function () {

				var autoConfirm = {
						timer: null,
						index: null,
						text: null,
						duration: null,
						task: function (event, self) {
								if (self.isOpen()) {
										self.__internal.buttons[autoConfirm.index].element.innerHTML = autoConfirm.text + ' (&#8207;' + autoConfirm.duration + '&#8207;) ';
										autoConfirm.duration -= 1;
										if (autoConfirm.duration === -1) {
												clearAutoConfirm(self);
												var button = self.__internal.buttons[autoConfirm.index];
												var closeEvent = createCloseEvent(autoConfirm.index, button);

												if (typeof self.callback === 'function') {
														self.callback.apply(self, [closeEvent]);
												}
												//close the dialog.
												if (closeEvent.close !== false) {
														self.close();
												}
										}
								} else {
										clearAutoConfirm(self);
								}
						}
				};

				function clearAutoConfirm(self) {
						if (autoConfirm.timer !== null) {
								clearInterval(autoConfirm.timer);
								autoConfirm.timer = null;
								self.__internal.buttons[autoConfirm.index].element.innerHTML = autoConfirm.text;
						}
				}

				function startAutoConfirm(self, index, duration) {
						clearAutoConfirm(self);
						autoConfirm.duration = duration;
						autoConfirm.index = index;
						autoConfirm.text = self.__internal.buttons[index].element.innerHTML;
						autoConfirm.timer = setInterval(delegate(self, autoConfirm.task), 1000);
						autoConfirm.task(null, self);
				}


				return {
						main: function (_title, _message, _onok, _oncancel) {
								var title, message, onok, oncancel;
								switch (arguments.length) {
								case 1:
										message = _title;
										break;
								case 2:
										message = _title;
										onok = _message;
										break;
								case 3:
										message = _title;
										onok = _message;
										oncancel = _onok;
										break;
								case 4:
										title = _title;
										message = _message;
										onok = _onok;
										oncancel = _oncancel;
										break;
								}
								this.set('title', title);
								this.set('message', message);
								this.set('onok', onok);
								this.set('oncancel', oncancel);
								return this;
						},
						setup: function () {
								return {
										buttons: [
												{
														text: alertify.defaults.glossary.ok,
														key: keys.ENTER,
														className: alertify.defaults.theme.ok,
												},
												{
														text: alertify.defaults.glossary.cancel,
														key: keys.ESC,
														invokeOnClose: true,
														className: alertify.defaults.theme.cancel,
												}
										],
										focus: {
												element: 0,
												select: false
										},
										options: {
												maximizable: false,
												resizable: false
										}
								};
						},
						build: function () {
								//nothing
						},
						prepare: function () {
								//nothing
						},
						setMessage: function (message) {
								this.setContent(message);
						},
						settings: {
								message: null,
								labels: null,
								onok: null,
								oncancel: null,
								defaultFocus: null,
								reverseButtons: null,
								buttonClasses: null
						},
						settingUpdated: function (key, oldValue, newValue) {
								switch (key) {
								case 'message':
										this.setMessage(newValue);
										break;
								case 'labels':
										if ('ok' in newValue && this.__internal.buttons[0].element) {
												this.__internal.buttons[0].text = newValue.ok;
												this.__internal.buttons[0].element.innerHTML = newValue.ok;
										}
										if ('cancel' in newValue && this.__internal.buttons[1].element) {
												this.__internal.buttons[1].text = newValue.cancel;
												this.__internal.buttons[1].element.innerHTML = newValue.cancel;
										}
										break;
								case 'reverseButtons':
										if (newValue === true) {
												this.elements.buttons.primary.appendChild(this.__internal.buttons[0].element);
										} else {
												this.elements.buttons.primary.appendChild(this.__internal.buttons[1].element);
										}
										break;
								case 'defaultFocus':
										this.__internal.focus.element = newValue === 'ok' ? 0 : 1;
										break;
								case 'buttonClasses':
										if ('ok' in newValue && this.__internal.buttons[0].element) {
												this.__internal.buttons[0].className = newValue.ok;
												this.__internal.buttons[0].element.className = newValue.ok;
										}
										if ('cancel' in newValue && this.__internal.buttons[1].element) {
												this.__internal.buttons[1].className = newValue.cancel;
												this.__internal.buttons[1].element.className = newValue.cancel;
										}
										break;
								}
						},
						callback: function (closeEvent) {
								clearAutoConfirm(this);
								var returnValue;
								switch (closeEvent.index) {
								case 0:
										if (typeof this.get('onok') === 'function') {
												returnValue = this.get('onok').call(this, closeEvent);
												if (typeof returnValue !== 'undefined') {
														closeEvent.cancel = !returnValue;
												}
										}
										break;
								case 1:
										if (typeof this.get('oncancel') === 'function') {
												returnValue = this.get('oncancel').call(this, closeEvent);
												if (typeof returnValue !== 'undefined') {
														closeEvent.cancel = !returnValue;
												}
										}
										break;
								}
						},
						autoOk: function (duration) {
								startAutoConfirm(this, 0, duration);
								return this;
						},
						autoCancel: function (duration) {
								startAutoConfirm(this, 1, duration);
								return this;
						}
				};
		});
		/**
		 * Prompt dialog object
		 *
		 * invoked by:
		 *	alertify.prompt(message);
		 *	alertify.prompt(message, value);
		 *	alertify.prompt(message, value, onok);
		 *	alertify.prompt(message, value, onok, oncancel);
		 *	alertify.prompt(title, message, value, onok, oncancel);
		 */
		alertify.dialog('prompt', function () {
				var input = document.createElement('INPUT');
				var p = document.createElement('P');
				return {
						main: function (_title, _message, _value, _onok, _oncancel) {
								var title, message, value, onok, oncancel;
								switch (arguments.length) {
								case 1:
										message = _title;
										break;
								case 2:
										message = _title;
										value = _message;
										break;
								case 3:
										message = _title;
										value = _message;
										onok = _value;
										break;
								case 4:
										message = _title;
										value = _message;
										onok = _value;
										oncancel = _onok;
										break;
								case 5:
										title = _title;
										message = _message;
										value = _value;
										onok = _onok;
										oncancel = _oncancel;
										break;
								}
								this.set('title', title);
								this.set('message', message);
								this.set('value', value);
								this.set('onok', onok);
								this.set('oncancel', oncancel);
								return this;
						},
						setup: function () {
								return {
										buttons: [
												{
														text: alertify.defaults.glossary.ok,
														key: keys.ENTER,
														className: alertify.defaults.theme.ok,
												},
												{
														text: alertify.defaults.glossary.cancel,
														key: keys.ESC,
														invokeOnClose: true,
														className: alertify.defaults.theme.cancel,
												}
										],
										focus: {
												element: input,
												select: true
										},
										options: {
												maximizable: false,
												resizable: false
										}
								};
						},
						build: function () {
								input.className = alertify.defaults.theme.input;
								input.setAttribute('type', 'text');
								input.value = this.get('value');
								this.elements.content.appendChild(p);
								this.elements.content.appendChild(input);
						},
						prepare: function () {
								//nothing
						},
						setMessage: function (message) {
								if (typeof message === 'string') {
										clearContents(p);
										p.innerHTML = message;
								} else if (message instanceof window.HTMLElement && p.firstChild !== message) {
										clearContents(p);
										p.appendChild(message);
								}
						},
						settings: {
								message: undefined,
								labels: undefined,
								onok: undefined,
								oncancel: undefined,
								value: '',
								type:'text',
								reverseButtons: undefined,
						},
						settingUpdated: function (key, oldValue, newValue) {
								switch (key) {
								case 'message':
										this.setMessage(newValue);
										break;
								case 'value':
										input.value = newValue;
										break;
								case 'type':
										switch (newValue) {
										case 'text':
										case 'color':
										case 'date':
										case 'datetime-local':
										case 'email':
										case 'month':
										case 'number':
										case 'password':
										case 'search':
										case 'tel':
										case 'time':
										case 'week':
												input.type = newValue;
												break;
										default:
												input.type = 'text';
												break;
										}
										break;
								case 'labels':
										if (newValue.ok && this.__internal.buttons[0].element) {
												this.__internal.buttons[0].element.innerHTML = newValue.ok;
										}
										if (newValue.cancel && this.__internal.buttons[1].element) {
												this.__internal.buttons[1].element.innerHTML = newValue.cancel;
										}
										break;
								case 'reverseButtons':
										if (newValue === true) {
												this.elements.buttons.primary.appendChild(this.__internal.buttons[0].element);
										} else {
												this.elements.buttons.primary.appendChild(this.__internal.buttons[1].element);
										}
										break;
								}
						},
						callback: function (closeEvent) {
								var returnValue;
								switch (closeEvent.index) {
								case 0:
										this.settings.value = input.value;
										if (typeof this.get('onok') === 'function') {
												returnValue = this.get('onok').call(this, closeEvent, this.settings.value);
												if (typeof returnValue !== 'undefined') {
														closeEvent.cancel = !returnValue;
												}
										}
										break;
								case 1:
										if (typeof this.get('oncancel') === 'function') {
												returnValue = this.get('oncancel').call(this, closeEvent);
												if (typeof returnValue !== 'undefined') {
														closeEvent.cancel = !returnValue;
												}
										}
										if(!closeEvent.cancel){
												input.value = this.settings.value;
										}
										break;
								}
						}
				};
		});

		// CommonJS
		if ( typeof module === 'object' && typeof module.exports === 'object' ) {
				module.exports = alertify;
		// AMD
		} else if ( typeof define === 'function' && define.amd) {
				define( [], function () {
						return alertify;
				} );
		// window
		} else if ( !window.alertify ) {
				window.alertify = alertify;
		}

} ( typeof window !== 'undefined' ? window : this ) );