Files
pgadmin4/web/pgadmin/static/js/alertifyjs/alertify.js
T
Ashesh Vashi 95c79e3a4c Upgraded Alertify to v1.7.1
This version includes the feature request about invoking the callbacks
for different resize events. It will allows us to adjust the layout of
the dialogs based on its width, just like we've in the panels.
2016-06-20 11:45:45 +05:30

3564 lines
132 KiB
JavaScript

/**
* alertifyjs 1.7.1 http://alertifyjs.com
* AlertifyJS is a javascript framework for developing pretty browser dialogs and notifications.
* Copyright 2016 Mohammad Younes <Mohammad@alertifyjs.com> (http://alertifyjs.com)
* Licensed under MIT <http://opensource.org/licenses/mit-license.php>*/
( 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 = {
modal:true,
basic:false,
frameless:false,
movable:true,
moveBounded:false,
resizable:true,
closable:true,
closableByDimmer:true,
maximizable:true,
startMaximized:false,
pinnable:true,
pinned:true,
padding: true,
overflow:true,
maintainFocus:true,
transition:'pulse',
autoReset:true,
notifier:{
delay:5,
position:'bottom-right'
},
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){
//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 {object}
*/
function dispatchEvent(eventType, instance) {
if ( typeof instance.get(eventType) === 'function' ) {
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,
//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 = {
base: 'alertify',
prefix: 'ajs-',
hidden: 'ajs-hidden',
noSelection: 'ajs-no-selection',
noOverflow: 'ajs-no-overflow',
noPadding:'ajs-no-padding',
modeless: 'ajs-modeless',
movable: 'ajs-movable',
resizable: 'ajs-resizable',
capture: 'ajs-capture',
fixed: 'ajs-fixed',
closable:'ajs-closable',
maximizable:'ajs-maximizable',
maximize: 'ajs-maximize',
restore: 'ajs-restore',
pinnable:'ajs-pinnable',
unpinned:'ajs-unpinned',
pin:'ajs-pin',
maximized: 'ajs-maximized',
animationIn: 'ajs-in',
animationOut: 'ajs-out',
shake:'ajs-shake',
basic:'ajs-basic',
frameless:'ajs-frameless'
};
/**
* 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);
}
//in case the script was included before body.
//after first dialog gets initialized, it won't be null anymore!
if(null === reflow){
// set tabindex attribute on body element this allows script to give it
// focus after the dialog is closed
document.body.setAttribute( 'tabindex', '0' );
}
//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,
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');
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
instance.set('title', setup.options.title === undefined ? alertify.defaults.glossary.title : setup.options.title);
instance.set('modal', setup.options.modal === undefined ? alertify.defaults.modal : setup.options.modal);
instance.set('basic', setup.options.basic === undefined ? alertify.defaults.basic : setup.options.basic);
instance.set('frameless', setup.options.frameless === undefined ? alertify.defaults.frameless : setup.options.frameless);
instance.set('movable', setup.options.movable === undefined ? alertify.defaults.movable : setup.options.movable);
instance.set('moveBounded', setup.options.moveBounded === undefined ? alertify.defaults.moveBounded : setup.options.moveBounded);
instance.set('resizable', setup.options.resizable === undefined ? alertify.defaults.resizable : setup.options.resizable);
instance.set('autoReset', setup.options.autoReset === undefined ? alertify.defaults.autoReset : setup.options.autoReset);
instance.set('closable', setup.options.closable === undefined ? alertify.defaults.closable : setup.options.closable);
instance.set('closableByDimmer', setup.options.closableByDimmer === undefined ? alertify.defaults.closableByDimmer : setup.options.closableByDimmer);
instance.set('maximizable', setup.options.maximizable === undefined ? alertify.defaults.maximizable : setup.options.maximizable);
instance.set('startMaximized', setup.options.startMaximized === undefined ? alertify.defaults.startMaximized : setup.options.startMaximized);
instance.set('pinnable', setup.options.pinnable === undefined ? alertify.defaults.pinnable : setup.options.pinnable);
instance.set('pinned', setup.options.pinned === undefined ? alertify.defaults.pinned : setup.options.pinned);
instance.set('transition', setup.options.transition === undefined ? alertify.defaults.transition : setup.options.transition);
instance.set('padding', setup.options.padding === undefined ? alertify.defaults.padding : setup.options.padding);
instance.set('overflow', setup.options.overflow === undefined ? alertify.defaults.overflow : setup.options.overflow);
// 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){
//last open modal or last maximized one
removeClass(document.body, classes.noOverflow);
}else if(requiresNoOverflow > 0 && document.body.className.indexOf(classes.noOverflow) < 0){
//first open modal or first maximized one
addClass(document.body, classes.noOverflow);
}
}
/**
* 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 'transition':
updateTransition(instance,newValue, oldValue);
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);
}
}
// flag to cancel click event if already handled by end resize event (the mousedown, mousemove, mouseup sequence fires a click event.).
var cancelClick = false;
/**
* 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) {
var target = event.srcElement || event.target;
if (!cancelClick && target === instance.elements.modal && instance.get('closableByDimmer') === true) {
triggerClose(instance);
}
cancelClick = false;
return false;
}
// 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) {
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;
}
//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);
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 ) {
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();
}
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.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{
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',
visible: 'ajs-visible',
hidden: 'ajs-hidden'
};
/**
* 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 'bottom-left':
addClass(element, classes.bottom + ' ' + classes.left);
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) {
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;
}
// 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);
}
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();
}
}
},
/**
* 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,
},
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;
}
},
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 ) );