alertifyjs
Version:
AlertifyJS is a javascript framework for developing pretty browser dialogs and notifications.
415 lines (371 loc) • 16.9 kB
JavaScript
// 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(isString(content)){
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(isString(content)){
clearContents(this.elements.content);
this.elements.content.innerHTML = content;
}else if (content instanceof window.HTMLElement && this.elements.content.firstChild !== content){
clearContents(this.elements.content);
this.elements.content.appendChild(content);
}
return this;
},
/**
* Show the dialog as modal
*
* @return {Object} the dialog instance.
*/
showModal: function(className){
return this.show(true, className);
},
/**
* Show the dialog
*
* @return {Object} the dialog instance.
*/
show: function (modal, className) {
// ensure initialization
initialize(this);
if ( !this.__internal.isOpen ) {
// add to open dialogs
this.__internal.isOpen = true;
openDialogs.push(this);
// save last focused element
if(alertify.defaults.maintainFocus){
this.__internal.activeElement = document.activeElement;
}
// set tabindex attribute on body element this allows script to give it focusable
if(!document.body.hasAttribute('tabindex')) {
document.body.setAttribute( 'tabindex', tabindex = '0');
}
//allow custom dom manipulation updates before showing the dialog.
if(typeof this.prepare === 'function'){
this.prepare();
}
bindEvents(this);
if(modal !== undefined){
this.set('modal', modal);
}
//save scroll to prevent document jump
saveScrollPosition();
ensureNoOverflow();
// allow custom dialog class on show
if(typeof className === 'string' && className !== ''){
this.__internal.className = className;
addClass(this.elements.root, className);
}
// maximize if start maximized
if ( this.get('startMaximized')) {
this.maximize();
}else if(this.isMaximized()){
restore(this);
}
updateAbsPositionFix(this);
this.elements.root.removeAttribute('style');
removeClass(this.elements.root, classes.animationOut);
addClass(this.elements.root, classes.animationIn);
// set 1s fallback in case transition event doesn't fire
clearTimeout( this.__internal.timerIn);
this.__internal.timerIn = setTimeout( this.__internal.transitionInHandler, transition.supported ? 1000 : 100 );
if(isSafari){
// force desktop safari reflow
var root = this.elements.root;
root.style.display = 'none';
setTimeout(function(){root.style.display = 'block';}, 0);
}
//reflow
reflow = this.elements.root.offsetWidth;
// show dialog
removeClass(this.elements.root, classes.hidden);
//restore scroll to prevent document jump
restoreScrollPosition();
// internal on show event
if(typeof this.hooks.onshow === 'function'){
this.hooks.onshow.call(this);
}
// allow custom `onshow` method
dispatchEvent('onshow', this);
}else{
// reset move updates
resetMove(this);
// reset resize updates
resetResize(this);
// shake the dialog to indicate its already open
addClass(this.elements.dialog, classes.shake);
var self = this;
setTimeout(function(){
removeClass(self.elements.dialog, classes.shake);
},200);
}
return this;
},
/**
* Close the dialog
*
* @return {Object} The dialog instance
*/
close: function () {
if (this.__internal.isOpen ) {
// custom `onclosing` event
if(dispatchEvent('onclosing', this) !== false){
unbindEvents(this);
removeClass(this.elements.root, classes.animationIn);
addClass(this.elements.root, classes.animationOut);
// set 1s fallback in case transition event doesn't fire
clearTimeout( this.__internal.timerOut );
this.__internal.timerOut = setTimeout( this.__internal.transitionOutHandler, transition.supported ? 1000 : 100 );
// hide dialog
addClass(this.elements.root, classes.hidden);
//reflow
reflow = this.elements.modal.offsetWidth;
// return focus to the last active element
if (alertify.defaults.maintainFocus && this.__internal.activeElement) {
this.__internal.activeElement.focus();
this.__internal.activeElement = null;
}
// remove custom dialog class on hide
if (typeof this.__internal.className !== 'undefined' && this.__internal.className !== '') {
removeClass(this.elements.root, this.__internal.className);
}
// internal on close event
if(typeof this.hooks.onclose === 'function'){
this.hooks.onclose.call(this);
}
// allow custom `onclose` method
dispatchEvent('onclose', this);
//remove from open dialogs
openDialogs.splice(openDialogs.indexOf(this),1);
this.__internal.isOpen = false;
ensureNoOverflow();
}
}
// last dialog and tab index was set by us, remove it.
if(!openDialogs.length && tabindex === '0'){
document.body.removeAttribute('tabindex');
}
return this;
},
/**
* Close all open dialogs except this.
*
* @return {undefined}
*/
closeOthers:function(){
alertify.closeAll(this);
return this;
},
/**
* Destroys this dialog instance
*
* @return {undefined}
*/
destroy:function(){
if(this.__internal) {
if (this.__internal.isOpen ) {
//mark dialog for destruction, this will be called on tranistionOut event.
this.__internal.destroy = function(){
destruct(this, initialize);
};
//close the dialog to unbind all events.
this.close();
}else if(!this.__internal.destroy){
destruct(this, initialize);
}
}
return this;
},
};