Commit 9b6abe45 authored by bingchuan's avatar bingchuan

Update alertify.js

parent a5f50604
/**
* alertifyjs 1.8.0 http://alertifyjs.com
* alertifyjs 1.14.0 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)
* Copyright 2024 Mohammad Younes <Mohammad@alertifyjs.com> (http://alertifyjs.com)
* Licensed under GPL 3 <https://opensource.org/licenses/gpl-3.0>*/
( function ( window ) {
'use strict';
var NOT_DISABLED_NOT_RESET = ':not(:disabled):not(.ajs-reset)';
/**
* Keys enum
* @type {Object}
......@@ -16,7 +16,8 @@
F1: 112,
F12: 123,
LEFT: 37,
RIGHT: 39
RIGHT: 39,
TAB: 9
};
/**
* Default options
......@@ -27,7 +28,9 @@
basic:false,
closable:true,
closableByDimmer:true,
invokeOnCloseOff:false,
frameless:false,
defaultFocusOff:false,
maintainFocus:true, //global default not per instance, applies to all dialogs
maximizable:true,
modal:true,
......@@ -41,9 +44,25 @@
resizable:true,
startMaximized:false,
transition:'pulse',
transitionOff:false,
tabbable:['button', '[href]', 'input', 'select', 'textarea', '[tabindex]:not([tabindex^="-"])'+NOT_DISABLED_NOT_RESET].join(NOT_DISABLED_NOT_RESET+','),//global
notifier:{
delay:5,
position:'bottom-right'
position:'bottom-right',
closeButton:false,
classes: {
base: 'alertify-notifier',
prefix:'ajs-',
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'
}
},
glossary:{
title:'AlertifyJS',
......@@ -61,6 +80,10 @@
input:'ajs-input',
ok:'ajs-ok',
cancel:'ajs-cancel',
},
hooks:{
preinit:function(){},
postinit:function(){}
}
};
......@@ -134,6 +157,16 @@
element.removeChild(element.lastChild);
}
}
/**
* detects strings, checks for both string and String instances
* this is unlike typeof(x) === 'string' which only accepts primitive strings
*
*/
function isString(thing) {
return Object.prototype.toString.call(thing) === '[object String]';
}
/**
* Extends a given prototype by merging properties from base into sub.
*
......@@ -185,63 +218,59 @@
*
*/
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;
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.
* Test to check if passive event listeners are supported.
*/
var IsPassiveSupported = false;
try {
var options = Object.defineProperty({}, 'passive', {
get: function () {
IsPassiveSupported = true;
}
});
window.addEventListener('test', options, options);
window.removeEventListener('test', options, options);
} catch (e) {}
/**
* Removes an event listener
*
* @param {Node} el Node element
* @param {String} event Event type
* @param {Function} fn Callback of event
* @return {Function}
* @param {HTMLElement} el The EventTarget to register the listenr on.
* @param {string} event The event type to listen for.
* @param {Function} handler The function to handle the event.
* @param {boolean} useCapture Specifices if the event to be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree.
* @param {boolean} passive A Boolean which, if true, indicates that the function specified by listener will never call preventDefault().
*/
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);
};
}
}());
var on = function (el, event, fn, useCapture, passive) {
el.addEventListener(event, fn, IsPassiveSupported ? { capture: useCapture, passive: passive } : useCapture === true);
};
/**
* 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.
* Removes an event listener
*
* @param {Node} el Node element
* @param {String} event Event type
* @param {Function} fn Callback of event
* @return {Function}
* @param {HTMLElement} el The EventTarget to unregister the listenr from.
* @param {string} event The event type to remove.
* @param {Function} fn The event handler to remove.
* @param {boolean} useCapture Specifices if the event to be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree.
* @param {boolean} passive A Boolean which, if true, indicates that the function specified by listener will never call preventDefault().
*/
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);
};
}
}());
var off = function (el, event, fn, useCapture, passive) {
el.removeEventListener(event, fn, IsPassiveSupported ? { capture: useCapture, passive: passive } : useCapture === true);
};
/**
* Prevent default event from firing
......@@ -250,14 +279,14 @@
* @return {undefined}
function prevent ( event ) {
if ( event ) {
if ( event.preventDefault ) {
event.preventDefault();
} else {
event.returnValue = false;
}
}
}
if ( event ) {
if ( event.preventDefault ) {
event.preventDefault();
} else {
event.returnValue = false;
}
}
}
*/
var transition = (function () {
var t, type;
......@@ -320,11 +349,11 @@
* @param {string} evenType The type of the event to disptach.
* @param {object} instance The dialog instance disptaching the event.
*
* @return {object}
* @return {any} The result of the invoked function.
*/
function dispatchEvent(eventType, instance) {
if ( typeof instance.get(eventType) === 'function' ) {
instance.get(eventType).call(instance);
return instance.get(eventType).call(instance);
}
}
......@@ -336,54 +365,57 @@
*/
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 = {
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',
};
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',
noTransition:'ajs-no-transition'
};
/**
* Helper: initializes the dialog instance
......@@ -392,8 +424,9 @@
*/
function initialize(instance){
if(!instance.__internal||instance.settings.type=='text'){
if(!instance.__internal){
//invoke preinit global hook
alertify.defaults.hooks.preinit(instance);
//no need to expose init after this.
delete instance.__init;
......@@ -401,13 +434,6 @@
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;
......@@ -437,7 +463,7 @@
if(Array.isArray(setup.buttons)){
for(var b=0;b<setup.buttons.length;b+=1){
var ref = setup.buttons[b],
cpy = {};
cpy = {};
for (var i in ref) {
if (ref.hasOwnProperty(i)) {
cpy[i] = ref[i];
......@@ -471,6 +497,7 @@
modal: undefined,
basic:undefined,
frameless:undefined,
defaultFocusOff:undefined,
pinned: undefined,
movable: undefined,
moveBounded:undefined,
......@@ -478,13 +505,16 @@
autoReset: undefined,
closable: undefined,
closableByDimmer: undefined,
invokeOnCloseOff:undefined,
maximizable: undefined,
startMaximized: undefined,
pinnable: undefined,
transition: undefined,
transitionOff: undefined,
padding:undefined,
overflow:undefined,
onshow:undefined,
onclosing:undefined,
onclose:undefined,
onfocus:undefined,
onmove:undefined,
......@@ -511,7 +541,8 @@
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;
......@@ -618,6 +649,9 @@
if(typeof instance.build === 'function'){
instance.build();
}
//invoke postinit global hook
alertify.defaults.hooks.postinit(instance);
}
//add to the end of the DOM tree.
......@@ -665,13 +699,13 @@
*
*/
function preventBodyShift(add){
if(alertify.defaults.preventBodyShift && document.documentElement.scrollHeight > document.documentElement.clientHeight){
if(add ){//&& openDialogs[openDialogs.length-1].elements.dialog.clientHeight <= document.documentElement.clientHeight){
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 {
} else if(!add) {
scrollY = topScroll;
document.body.style.top = top;
removeClass(document.body, classes.fixed);
......@@ -687,13 +721,30 @@
*
*/
function updateTransition(instance, value, oldValue){
if(typeof oldValue === 'string'){
if(isString(oldValue)){
removeClass(instance.elements.root,classes.prefix + oldValue);
}
addClass(instance.elements.root, classes.prefix + value);
reflow = instance.elements.root.offsetWidth;
}
/**
* Toggles the dialog no transition
*
* @param {Object} instance The dilog instance.
*
* @return {undefined}
*/
function updateTransitionOff(instance){
if (instance.get('transitionOff')) {
// add class
addClass(instance.elements.root, classes.noTransition);
} else {
// remove class
removeClass(instance.elements.root, classes.noTransition);
}
}
/**
* Toggles the dialog display mode
*
......@@ -836,9 +887,6 @@
case 'resizable':
updateResizable(instance);
break;
case 'transition':
updateTransition(instance,newValue, oldValue);
break;
case 'padding':
if(newValue){
removeClass(instance.elements.root, classes.noPadding);
......@@ -853,7 +901,12 @@
addClass(instance.elements.root, classes.noOverflow);
}
break;
case 'transition':
updateTransition(instance,newValue, oldValue);
break;
case 'transitionOff':
updateTransitionOff(instance);
break;
}
// internal on option updated event
......@@ -949,7 +1002,7 @@
function triggerClose(instance) {
var found;
triggerCallback(instance, function (button) {
return found = (button.invokeOnClose === true);
return found = instance.get('invokeOnCloseOff') !== true && (button.invokeOnClose === true);
});
//none of the buttons registered as onclose callback
//close the dialog
......@@ -1101,7 +1154,7 @@
if (instance.isOpen()) {
var top = 0,
left = 0
left = 0
;
if (instance.elements.dialog.style.top !== '') {
top = parseInt(instance.elements.dialog.style.top, 10);
......@@ -1189,8 +1242,10 @@
}
}
// flag to cancel click event if already handled by end resize event (the mousedown, mousemove, mouseup sequence fires a click event.).
var cancelClick = false;
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
......@@ -1201,14 +1256,17 @@
* @return {undefined}
*/
function modalClickHandler(event, instance) {
var target = event.srcElement || event.target;
if (!cancelClick && target === instance.elements.modal && instance.get('closableByDimmer') === true) {
triggerClose(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;
/**
......@@ -1220,18 +1278,20 @@
* @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();
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;
}
break;
}
}
}
......@@ -1248,7 +1308,7 @@
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);
return button.element.contains(target) && (cancelKeyup = true);
});
}
......@@ -1334,7 +1394,7 @@
var element = focus.element;
switch (typeof focus.element) {
// a number means a button index
// a number means a button index
case 'number':
if (instance.__internal.buttons.length > focus.element) {
//in basic view, skip focusing the buttons.
......@@ -1345,18 +1405,18 @@
}
}
break;
// a string means querySelector to select from dialog body contents.
// 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.
// 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) {
if (instance.get('defaultFocusOff') === true || ((typeof element === 'undefined' || element === null) && instance.__internal.buttons.length === 0)) {
element = instance.elements.reset[0];
}
// focus
......@@ -1390,40 +1450,53 @@
}
}
}
// 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(instance) {
// if modal
if (instance.isModal()) {
// determine reset target to enable forward/backward tab cycle.
var firstReset = instance.elements.reset[0],
lastReset = instance.elements.reset[1],
lastFocusedElement = event.relatedTarget,
within = instance.elements.root.contains(lastFocusedElement),
target = event.srcElement || event.target,
resetTarget;
//if the previous focused element element was outside the modal do nthing
if( /*first show */
(target === firstReset && !within) ||
/*focus cycle */
(target === lastReset && lastFocusedElement === firstReset)){
return;
}else if(target === lastReset || target === document.body){
resetTarget = firstReset;
}else if(target === firstReset && lastFocusedElement === lastReset){
resetTarget = findTabbable(instance);
}else if(target === firstReset && within){
resetTarget = findTabbable(instance, true);
}
// focus
setFocus(instance, resetTarget);
}
// 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;
}
}
}
}
function findTabbable(instance, last){
var tabbables = [].slice.call(instance.elements.dialog.querySelectorAll(defaults.tabbable));
if(last){
tabbables.reverse();
}
for(var x=0;x<tabbables.length;x+=1){
var tabbable = tabbables[x];
//check if visible
if(!!(tabbable.offsetParent || tabbable.offsetWidth || tabbable.offsetHeight || tabbable.getClientRects().length)){
return tabbable;
}
// focus
setFocus(instance, resetTarget);
}
}
function recycleTab(event) {
var instance = openDialogs[openDialogs.length - 1];
if (instance && event.shiftKey && event.keyCode === keys.TAB) {
instance.elements.reset[1].focus();
}
}
/**
......@@ -1441,9 +1514,6 @@
// once transition is complete, set focus
setFocus(instance);
//restore scroll to prevent document jump
restoreScrollPosition();
// allow handling key up after transition ended.
cancelKeyup = false;
......@@ -1480,12 +1550,6 @@
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);
......@@ -1494,15 +1558,15 @@
/* 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
//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
;
/**
......@@ -1515,7 +1579,7 @@
*/
function moveElement(event, element) {
var left = (event[xProp] - offsetX),
top = (event[yProp] - offsetY);
top = (event[yProp] - offsetY);
if(refreshTop){
top -= document.body.scrollTop;
......@@ -1535,7 +1599,7 @@
*/
function moveElementBounded(event, element) {
var left = (event[xProp] - offsetX),
top = (event[yProp] - offsetY);
top = (event[yProp] - offsetY);
if(refreshTop){
top -= document.body.scrollTop;
......@@ -1595,8 +1659,8 @@
if(instance.get('moveBounded')){
var current = element,
offsetLeft = -left,
offsetTop = -top;
offsetLeft = -left,
offsetTop = -top;
//calc offset
do {
......@@ -1710,14 +1774,14 @@
/* 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
//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
;
/**
......@@ -1945,12 +2009,12 @@
//move
on(document.documentElement, 'mousemove', move);
on(document.documentElement, 'touchmove', move);
on(document.documentElement, 'touchmove', move, false, false);
on(document.documentElement, 'mouseup', endMove);
on(document.documentElement, 'touchend', endMove);
//resize
on(document.documentElement, 'mousemove', resize);
on(document.documentElement, 'touchmove', resize);
on(document.documentElement, 'touchmove', resize, false, false);
on(document.documentElement, 'mouseup', endResize);
on(document.documentElement, 'touchend', endResize);
}
......@@ -1958,8 +2022,9 @@
// 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);
on(instance.elements.reset[0], 'focusin', instance.__internal.resetHandler);
on(instance.elements.reset[0], 'keydown', recycleTab);
on(instance.elements.reset[1], 'focusin', instance.__internal.resetHandler);
//prevent handling key up when dialog is being opened by a key stroke.
cancelKeyup = true;
......@@ -2008,8 +2073,9 @@
// 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);
off(instance.elements.reset[0], 'focusin', instance.__internal.resetHandler);
off(instance.elements.reset[0], 'keydown', recycleTab);
off(instance.elements.reset[1], 'focusin', instance.__internal.resetHandler);
// hook out transition handler
on(instance.elements.dialog, transition.type, instance.__internal.transitionOutHandler);
......@@ -2064,7 +2130,7 @@
*/
function bindMovableEvents(instance) {
on(instance.elements.header, 'mousedown', instance.__internal.beginMoveHandler);
on(instance.elements.header, 'touchstart', instance.__internal.beginMoveHandler);
on(instance.elements.header, 'touchstart', instance.__internal.beginMoveHandler, false, false);
}
/**
......@@ -2076,7 +2142,7 @@
*/
function unbindMovableEvents(instance) {
off(instance.elements.header, 'mousedown', instance.__internal.beginMoveHandler);
off(instance.elements.header, 'touchstart', instance.__internal.beginMoveHandler);
off(instance.elements.header, 'touchstart', instance.__internal.beginMoveHandler, false, false);
}
......@@ -2090,7 +2156,7 @@
*/
function bindResizableEvents(instance) {
on(instance.elements.resizeHandle, 'mousedown', instance.__internal.beginResizeHandler);
on(instance.elements.resizeHandle, 'touchstart', instance.__internal.beginResizeHandler);
on(instance.elements.resizeHandle, 'touchstart', instance.__internal.beginResizeHandler, false, false);
}
/**
......@@ -2102,7 +2168,7 @@
*/
function unbindResizableEvents(instance) {
off(instance.elements.resizeHandle, 'mousedown', instance.__internal.beginResizeHandler);
off(instance.elements.resizeHandle, 'touchstart', instance.__internal.beginResizeHandler);
off(instance.elements.resizeHandle, 'touchstart', instance.__internal.beginResizeHandler, false, false);
}
/**
......@@ -2188,9 +2254,9 @@
dispatchEvent('onmove', this);
var element = this.elements.dialog,
current = element,
offsetLeft = 0,
offsetTop = 0;
current = element,
offsetLeft = 0,
offsetTop = 0;
//subtract existing left,top
if (element.style.left) {
......@@ -2236,8 +2302,8 @@
*/
resizeTo:function(width,height){
var w = parseFloat(width),
h = parseFloat(height),
regex = /(\d*\.\d+|\d+)%/
h = parseFloat(height),
regex = /(\d*\.\d+|\d+)%/
;
if(!isNaN(w) && !isNaN(h) && this.get('resizable') === true){
......@@ -2319,7 +2385,7 @@
* @return {undefined}
*/
setHeader:function(content){
if(typeof content === 'string'){
if(isString(content)){
clearContents(this.elements.header);
this.elements.header.innerHTML = content;
}else if (content instanceof window.HTMLElement && this.elements.header.firstChild !== content){
......@@ -2335,7 +2401,7 @@
* @return {undefined}
*/
setContent:function(content){
if(typeof content === 'string'){
if(isString(content)){
clearContents(this.elements.content);
this.elements.content.innerHTML = content;
}else if (content instanceof window.HTMLElement && this.elements.content.firstChild !== content){
......@@ -2373,6 +2439,11 @@
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();
......@@ -2403,7 +2474,7 @@
}
updateAbsPositionFix(this);
this.elements.root.removeAttribute('style');
removeClass(this.elements.root, classes.animationOut);
addClass(this.elements.root, classes.animationIn);
......@@ -2424,6 +2495,9 @@
// show dialog
removeClass(this.elements.root, classes.hidden);
//restore scroll to prevent document jump
restoreScrollPosition();
// internal on show event
if(typeof this.hooks.onshow === 'function'){
this.hooks.onshow.call(this);
......@@ -2453,39 +2527,52 @@
*/
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;
// return focus to the last active element
if (alertify.defaults.maintainFocus && this.__internal.activeElement) {
this.__internal.activeElement.focus();
this.__internal.activeElement = null;
}
unbindEvents(this);
// remove custom dialog class on hide
if (typeof this.__internal.className !== 'undefined' && this.__internal.className !== '') {
removeClass(this.elements.root, this.__internal.className);
}
removeClass(this.elements.root, classes.animationIn);
addClass(this.elements.root, classes.animationOut);
// internal on close event
if(typeof this.hooks.onclose === 'function'){
this.hooks.onclose.call(this);
}
// 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;
// allow custom `onclose` method
dispatchEvent('onclose', this);
// remove custom dialog class on hide
if (typeof this.__internal.className !== 'undefined' && this.__internal.className !== '') {
removeClass(this.elements.root, this.__internal.className);
}
//remove from open dialogs
openDialogs.splice(openDialogs.indexOf(this),1);
this.__internal.isOpen = false;
// internal on close event
if(typeof this.hooks.onclose === 'function'){
this.hooks.onclose.call(this);
ensureNoOverflow();
}
// 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;
},
......@@ -2504,34 +2591,29 @@
* @return {undefined}
*/
destroy:function(){
if (this.__internal.isOpen ) {
//mark dialog for destruction, this will be called on tranistionOut event.
this.__internal.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);
};
//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'
};
element,
openInstances = [],
classes = defaults.notifier.classes,
baseClass = classes.base;
/**
* Helper: initializes the notifier instance
*
......@@ -2545,7 +2627,10 @@
};
element = document.createElement('DIV');
var transitionOff = 'transitionOff' in defaults.notifier ? defaults.notifier.transitionOff : defaults.transitionOff;
if(transitionOff){
baseClass = classes.base + ' ajs-no-transition';
}
updatePosition(instance);
}
......@@ -2568,7 +2653,7 @@
*
*/
function updatePosition(instance) {
element.className = classes.base;
element.className = baseClass;
switch (instance.__internal.position) {
case 'top-right':
addClass(element, classes.top + ' ' + classes.right);
......@@ -2576,9 +2661,15 @@
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':
......@@ -2599,7 +2690,9 @@
function create(div, callback) {
function clickDelegate(event, instance) {
instance.dismiss(true);
if(!instance.__internal.closeButton || event.target.getAttribute('data-close') === 'true'){
instance.dismiss(true);
}
}
function transitionDone(event, instance) {
......@@ -2661,6 +2754,7 @@
wait = _wait;
break;
}
this.__internal.closeButton = alertify.defaults.notifier.closeButton;
// set contents
if (typeof content !== 'undefined') {
this.setContent(content);
......@@ -2737,13 +2831,19 @@
*
*/
setContent: function (content) {
if (typeof content === 'string') {
if (isString(content)) {
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;
},
/*
......@@ -2814,7 +2914,7 @@
initialize(this);
//create new notification message
var div = document.createElement('div');
div.className = classes.message + ((typeof type === 'string' && type !== '') ? ' ajs-' + type : '');
div.className = classes.message + ((typeof type === 'string' && type !== '') ? ' ' + classes.prefix + type : '');
return create(div, callback);
},
/**
......@@ -2834,6 +2934,7 @@
}
};
})();
/**
* Alertify public API
* This contains everything that is exposed through the alertify object.
......@@ -3489,7 +3590,7 @@
//nothing
},
setMessage: function (message) {
if (typeof message === 'string') {
if (isString(message)) {
clearContents(p);
p.innerHTML = message;
} else if (message instanceof window.HTMLElement && p.firstChild !== message) {
......@@ -3593,4 +3694,4 @@
window.alertify = alertify;
}
} ( typeof window !== 'undefined' ? window : this ) );
} ( typeof window !== 'undefined' ? window : this ) );
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment