/build/alertify.js
https://github.com/alihalabyah/AlertifyJS · JavaScript · 2769 lines · 1651 code · 209 blank · 909 comment · 247 complexity · e326dcdfa9255ae037b3488a86d91c19 MD5 · raw file
- /**
- * AlertifyJS
- * AlertifyJS is a javascript framework for developing pretty browser dialogs and notifications.
- *
- * @author Mohammad Younes <Mohammad@alertifyjs.com> (http://alertifyjs.com)
- * @copyright 2014
- * @license MIT <http://opensource.org/licenses/mit-license.php>
- * @link http://alertifyjs.com
- * @module AlertifyJS
- * @version 0.0.0
- */
- ( function ( window ) {
- 'use strict';
-
- /**
- * Keys enum
- * @type {Object}
- */
- var keys = {
- ENTER: 13,
- ESC: 27
- };
- /**
- * Default options
- * @type {Object}
- */
- var defaults = {
- modal:true,
- movable:true,
- resizable:true,
- closable:true,
- maximizable:true,
- pinnable:true,
- pinned:true,
- transition:'pulse',
- padding: true,
- overflow: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',
- }
- };
-
- /**
- * [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 classes = classNames.split(' ');
- for(var x=0;x<classes.length;x+=1){
- element.className = element.className.replace(' ' + classes[x], '');
- }
- }
- /**
- * [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);
- }
- /**
- * 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 el = document.createElement( 'fakeelement' );
- var transitions = {
- 'WebkitTransition': 'webkitTransitionEnd',
- 'MozTransition': 'transitionend',
- 'OTransition': 'otransitionend',
- 'transition': 'transitionend'
- };
- for ( t in transitions ) {
- if ( el.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
- };
- }
-
- /**
- * Super class for all dialogs
- *
- * @return {Object} base dialog prototype
- */
- var dialog = (function () {
- //holds open dialogs instances
- var openInstances = [],
- //dummy variable, used to trigger dom reflow.
- reflow = null,
- //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: '<a class="ajs-reset" href="#"></a>',
- 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',
- 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'
- };
-
- /**
- * 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;
-
- //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:{
- }
- };
- }
- 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,
- buttons: setup.buttons,
- focus: setup.focus,
- options: {
- title: undefined,
- modal: undefined,
- pinned: undefined,
- movable: undefined,
- resizable: undefined,
- closable: undefined,
- maximizable: undefined,
- pinnable: undefined,
- transition: undefined,
- padding:undefined,
- overflow:undefined,
- onshow:undefined,
- onclose:undefined
- },
- resetHandler:undefined,
- beginMoveHandler:undefined,
- beginResizeHandler:undefined,
- bringToFrontHandler:undefined,
- modalClickHandler:undefined,
- buttonsClickHandler:undefined,
- commandsClickHandler:undefined,
- transitionInHandler:undefined,
- transitionOutHandler: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.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 + templates.resizeHandle;
- elements.resizeHandle = elements.footer.lastChild;
- //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];
- 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.setting('title', setup.options.title === undefined ? alertify.defaults.glossary.title : setup.options.title);
-
- instance.setting('modal', setup.options.modal === undefined ? alertify.defaults.modal : setup.options.modal);
-
- instance.setting('movable', setup.options.movable === undefined ? alertify.defaults.movable : setup.options.movable);
- instance.setting('resizable', setup.options.resizable === undefined ? alertify.defaults.resizable : setup.options.resizable);
-
- instance.setting('closable', setup.options.closable === undefined ? alertify.defaults.closable : setup.options.closable);
- instance.setting('maximizable', setup.options.maximizable === undefined ? alertify.defaults.maximizable : setup.options.maximizable);
-
- instance.setting('pinnable', setup.options.pinnable === undefined ? alertify.defaults.pinnable : setup.options.pinnable);
- instance.setting('pinned', setup.options.pinned === undefined ? alertify.defaults.pinned : setup.options.pinned);
-
- instance.setting('transition', setup.options.transition === undefined ? alertify.defaults.transition : setup.options.transition);
- instance.setting('padding', setup.options.padding === undefined ? alertify.defaults.padding : setup.options.padding);
- instance.setting('overflow', setup.options.overflow === undefined ? alertify.defaults.overflow : setup.options.overflow);
-
- // allow dom customization
- if(typeof instance.build === 'function'){
- instance.build();
- }
-
- }
-
- //add to DOM tree.
- document.body.appendChild(instance.elements.root);
- }
- /**
- * Helper: adds/removes no-overflow class from body
- *
- */
- function ensureNoOverflow(){
- var requiresNoOverflow = 0;
- for(var x=0;x<openInstances.length;x+=1){
- var instance = openInstances[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.
- * @param {Boolean} on True to make it modal, false otherwise.
- *
- * @return {undefined}
- */
- function updateDisplayMode(instance){
- if(instance.setting('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();
- }
- }
- }
-
- /**
- * 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 = openInstances.indexOf(instance);
- for(var x=index+1;x<openInstances.length;x+=1){
- if(openInstances[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);
- 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 '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;
- }
- }
-
- /**
- * 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.setting('pinned', true);
- }
- /**
- * Helper: unpins the modeless dialog.
- *
- * @param {Object} instance The dilog instance.
- *
- * @return {undefined}
- */
- function unpin(instance){
- //unpin the dialog
- instance.setting('pinned', false);
- }
- /**
- * Helper: enlarges the dialog to fill the entire screen.
- *
- * @param {Object} instance The dilog instance.
- *
- * @return {undefined}
- */
- function maximize(instance){
- //maximize the dialog
- addClass(instance.elements.root, classes.maximized);
- if(instance.isOpen()){
- ensureNoOverflow();
- }
- }
- /**
- * Helper: returns the dialog to its former size.
- *
- * @param {Object} instance The dilog instance.
- *
- * @return {undefined}
- */
- function restore(instance){
- //maximize the dialog
- removeClass(instance.elements.root, classes.maximized);
- if(instance.isOpen()){
- ensureNoOverflow();
- }
- }
- /**
- * 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.setting('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.setting('modal') && !instance.setting('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.setting('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.setting('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.setting('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){
- 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 = openInstances[openInstances.length-1];
- var keyCode = event.keyCode;
- if (keyCode === keys.ENTER || keyCode === keys.ESC) {
- 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;
- // a number means a button index
- if (typeof focus.element === 'number') {
- element = instance.__internal.buttons[focus.element].element;
- }
- // 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=openInstances.length-1;x>-1;x-=1){
- if(openInstances[x].isModal()){
- instance = openInstances[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 lastResetLink = target === instance.elements.reset[1];
-
- // if last reset link, then go to maximize or close
- if(lastResetLink){
- if(instance.setting('maximizable')){
- resetTarget = instance.elements.commands.maximize;
- }else if(instance.setting('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(lastResetLink){
- //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);
- }
- }
- //animation timers
- var transitionInTimeout,transitionOutTimeout;
- /**
- * 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( transitionInTimeout );
-
- // once transition is complete, set focus
- setFocus(instance);
-
- // allow handling key up after transition ended.
- cancelKeyup = false;
- // allow custom `onfocus` method
- if ( typeof instance.onfocus === 'function' ) {
- instance.onfocus();
- }
-
- // unbind the event
- off( instance.elements.dialog, transition.type, instance.__internal.transitionInHandler);
- }
-
- /**
- * 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( transitionOutTimeout );
- // 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()){
- restore(instance);
- }
-
- // return focus to the last active element
- instance.__internal.activeElement.focus();
- }
- /* 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
- ;
- /**
- * 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){
- element.style.left = (event.pageX - offsetX) + 'px';
- element.style.top = (event.pageY - offsetY) + '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(event.button === 0 && !instance.isMaximized() && instance.setting('movable')){
- movable = instance;
- offsetX = event.pageX;
- offsetY = event.pageY;
- var element = instance.elements.dialog;
-
- if(element.style.left){
- offsetX -= parseInt(element.style.left,10);
- }
-
- if(element.style.top){
- offsetY -= parseInt(element.style.top,10);
- }
- moveElement(event, 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){
- moveElement(event, 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){
- movable = null;
- removeClass(document.body, classes.noSelection);
- }
- }
-
- /**
- * Resets any changes made by moving the element to its original state,
- *
- * @param {Object} instance The dilog instance.
- *
- * @return {undefined}
- */
- function resetMove(instance){
- 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.setting('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(event.button === 0 && !instance.isMaximized()){
- resizable = instance;
- handleOffset = instance.elements.resizeHandle.offsetHeight/2;
- var element = instance.elements.dialog;
- 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){
- resizeElement(event, resizable.elements.dialog, !resizable.setting('modal') && !resizable.setting('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){
- resizable = null;
- removeClass(document.body, classes.noSelection);
- cancelClick = true;
- }
- }
- /**
- * Resets any changes made by resizing the element to its original state.
- *
- * @param {Object} instance The dilog instance.
- *
- * @return {undefined}
- */
- function resetResize(instance){
- 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.setting('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<openInstances.length;x+=1){
- var instance = openInstances[x];
- resetMove(instance);
- resetResize(instance);
- }
- }
- /**
- * Bind dialogs events
- *
- * @param {Object} instance The dilog instance.
- *
- * @return {undefined}
- */
- function bindEvents(instance){
- // if first dialog, hook body handlers
- if(openInstances.length === 1){
- //global
- on( window, 'resize', windowResize);
- on( document.body, 'keyup', keyupHandler);
- on( document.body, 'focus', onReset);
-
- //move
- on( document.body, 'mousemove', move);
- on( document.body, 'mouseup', endMove);
- //resize
- on( document.body, 'mousemove', resize);
- on( document.body, 'mouseup', 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.setting('modal')){
- bindModelessEvents(instance);
- }
-
- // resizable
- if(instance.setting('resizable')){
- bindResizableEvents(instance);
- }
-
- // movable
- if(instance.setting('movable')){
- bindMovableEvents(instance);
- }
- }
- /**
- * Unbind dialogs events
- *
- * @param {Object} instance The dilog instance.
- *
- * @return {undefined}
- */
- function unbindEvents(instance){
- // if last dialog, remove body handlers
- if(openInstances.length === 1){
- //global
- off( window, 'resize', windowResize);
- off( document.body, 'keyup', keyupHandler);
- off( document.body, 'focus', onReset);
- //move
- off( document.body, 'mousemove', move);
- off( document.body, 'mouseup', endMove);
- //resize
- off( document.body, 'mousemove', resize);
- off( document.body, '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.setting('modal')){
- unbindModelessEvents(instance);
- }
-
- // movable
- if(instance.setting('movable')){
- unbindMovableEvents(instance);
- }
-
- // resizable
- if(instance.setting('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);
- }
- /**
- * Unbind movable specific events
- *
- * @param {Object} instance The dilog instance.
- *
- * @return {undefined}
- */
- function unbindMovableEvents(instance){
- off(instance.elements.header, 'mousedown', 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);
- }
- /**
- * Unbind resizable specific events
- *
- * @param {Object} instance The dilog instance.
- *
- * @return {undefined}
- */
- function unbindResizableEvents(instance){
- off( instance.elements.resizeHandle, 'mousedown', 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;
- },
- /**
- * 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;
- }
- },
- /**
- * Sets dialog header
- * @content {string or element}
- *
- * @return {undefined}
- */
- setHeader:function(content){
- if(typeof content === 'string'){
- this.elements.header.innerHTML = content;
- }else if (content instanceof window.HTMLElement && this.elements.header.firstChild !== content){
- this.elements.header.innerHTML = '';
- this.elements.header.appendChild(content);
- }
- return this;
- },
- /**
- * Sets dialog contents
- * @content {string or element}
- *
- * @return {undefined}
- */
- setContent:function(content){
- if(typeof content === 'string'){
- this.elements.content.innerHTML = content;
- }else if (content instanceof window.HTMLElement && this.elements.content.firstChild !== content){
- this.elements.content.innerHTML = '';
- 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;
- openInstances.push(this);
- // save last focused element
- 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.setting('modal', modal);
- }
-
- ensureNoOverflow();
-
- // allow custom dialog class on show
- if(typeof className === 'string' && className !== ''){
- this.__internal.className = className;
- addClass(this.elements.root, className);
- }
-
- 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( transitionInTimeout );
- transitionInTimeout = setTimeout( this.__internal.transitionInHandler, transition.supported ? 1000 : 100 );
- //reflow
- reflow = this.elements.modal.offsetWidth;
- // show dialog
- removeClass(this.elements.root, classes.hidden);
- // allow custom `onshow` method
- if ( typeof this.setting('onshow') === 'function' ) {
- this.setting('onshow')();
- }
- }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 {undefined}
- */
- 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( transitionOutTimeout );
- transitionOutTimeout = 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);
- }
- // allow custom `onclose` method
- if ( typeof this.setting('onclose') === 'function' ) {
- this.setting('onclose')();
- }
-
- //remove from open dialogs
- openInstances.splice(openInstances.indexOf(this),1);
- this.__internal.isOpen = false;
-
- ensureNoOverflow();
-
- }
- return this;
- },
- };
- } () );
- var notifier = (function () {
- var reflow,
- element,
- 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.
- document.body.appendChild(element);
- }
- }
-
- /**
- * 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(message, wait, callback) {
- var clickDelegate, hideElement, transitionDone;
- clickDelegate = function () {
- off(message, 'click', clickDelegate);
- hideElement(message,true);
- };
-
- transitionDone = function () {
- // unbind event
- off(message, transition.type, transitionDone);
- // remove the message
- element.removeChild(message);
- };
-
- // set click event on messages
- on(message, 'click', clickDelegate);
-
- // this sets the hide class to transition out
- // or removes the child if css transitions aren't supported
- hideElement = function (message,clicked) {
- // ensure element exists
- if (typeof message !== 'undefined' && message.parentNode === element) {
- // whether CSS transition exists
- if (transition.supported) {
- on(message, transition.type, transitionDone);
- removeClass(message, classes.visible);
- } else {
- element.removeChild(message);
- }
- // custom callback on hide
- if(typeof callback === 'function'){
- callback.call(undefined,clicked);
- }
- }
- };
-
- // never close (until click) if wait is set to 0
- if (wait === 0) {
- return;
- }
- // set timeout to auto close the notifier message
- setTimeout(function () { hideElement(message); }, wait);
- }
-
- //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;
- },
- /**
- * Creates a new notification message
- *
- * @param {string or DOMElement} content The message content
- * @param {string} type The type of notification message (simply a CSS class name 'ajs-{type}' to be added).
- * @param {Number} wait The time (in seconds) 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}
- */
- notify:function(content, type, wait, callback){
-
- //ensure init
- initialize(this);
-
- var message = document.createElement('div');
- message.className = classes.message + ((typeof type === 'string' && type !== '') ? ' ajs-' + type : '');
-
- //html or dom
- if(typeof content === 'string'){
- message.innerHTML = content;
- }else{
- message.appendChild(content);
- }
-
- // append or insert
- if(this.__internal.position.indexOf('top') < 0){
- element.appendChild(message);
- }else{
- element.insertBefore(message,element.firstChild);
- }
-
- reflow = message.offsetWidth;
- addClass(message, classes.visible);
- var delay = typeof wait !== 'undefined' && !isNaN(+wait) ? +wait : this.__internal.delay;
- create(message, delay * 1000, callback);
- }
- };
- }) ();
- /**
- * 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.
- * @type {Object}
- */
- 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);
- }
- };
- }
- },
- /**
- * 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);
- }
- },
- /**
- * 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} [message=undefined] Message text
- * @param {String} [type=''] Type of log message
- * @param {String} [value=''] Time (in ms) to wait before auto-close
- * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
- *
- * @return {undefined}
- */
- notify: function ( message, type, wait, callback ) {
- notifier.notify(message, type, wait, callback);
- },
- /**
- * Creates a new notification message.
- *
- * @param {String} [message=undefined] Message text
- * @param {String} [type=''] Type of log message
- * @param {String} [value=''] Time (in ms) to wait before auto-close
- * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
- *
- * @return {undefined}
- */
- message: function ( message, wait, callback ) {
- notifier.notify(message, null, wait, callback);
- },
- /**
- * Creates a new notification message of type 'success'.
- *
- * @param {String} [message=undefined] Message text
- * @param {String} [type=''] Type of log message
- * @param {String} [value=''] Time (in ms) to wait before auto-close
- * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
- *
- * @return {undefined}
- */
- success: function ( message, wait, callback ) {
- notifier.notify(message, 'success', wait, callback);
- },
- /**
- * Creates a new notification message of type 'error'.
- *
- * @param {String} [message=undefined] Message text
- * @param {String} [type=''] Type of log message
- * @param {String} [value=''] Time (in ms) to wait before auto-close
- * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
- *
- * @return {undefined}
- */
- error: function ( message, wait, callback ) {
- notifier.notify(message, 'error', wait, callback);
- },
- /**
- * Creates a new notification message of type 'warning'.
- *
- * @param {String} [message=undefined] Message text
- * @param {String} [type=''] Type of log message
- * @param {String} [value=''] Time (in ms) to wait before auto-close
- * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
- *
- * @return {undefined}
- */
- warning: function ( message, wait, callback ) {
- notifier.notify(message, 'warning', wait, callback);
- }
- };
- }
- 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.setting('title', title);
- this.setting('message', message);
- this.setting('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.settings.onok === 'function'){
- var returnValue = this.settings.onok.call(undefined, 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,
- duratuin: null,
- task: function(event,self){
- if(self.isOpen()){
- self.__internal.buttons[autoConfirm.index].element.innerHTML = autoConfirm.text + ' (‏' + autoConfirm.duration +'‏) ';
- 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.setting('title', title);
- this.setting('message', message);
- this.setting('onok', onok);
- this.setting('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.settings.onok === 'function'){
- returnValue = this.settings.onok.call(undefined, closeEvent);
- if(typeof returnValue !== 'undefined'){
- closeEvent.cancel = !returnValue;
- }
- }
- break;
- case 1:
- if(typeof this.settings.oncancel === 'function'){
- returnValue = this.settings.oncancel.call(undefined, 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 4:
- title = _title;
- message = _message;
- value = _value;
- onok = _onok;
- oncancel= _oncancel;
- break;
- }
- this.setting('title', title);
- this.setting('message', message);
- this.setting('value', value);
- this.setting('onok', onok);
- this.setting('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.settings.value;
- this.elements.content.appendChild(p);
- this.elements.content.appendChild(input);
- },
- prepare:function(){
- //nothing
- },
- setMessage:function(message){
- if(typeof message === 'string'){
- p.innerHTML = message;
- }else if (message instanceof window.HTMLElement && p.firstChild !== message){
- p.innerHTML = '';
- p.appendChild(message);
- }
- },
- settings:{
- message:undefined,
- labels:undefined,
- onok:undefined,
- oncancel:undefined,
- value:'',
- reverseButtons:undefined,
- },
- settingUpdated:function(key, oldValue, newValue){
- switch(key){
- case 'message':
- this.setMessage(newValue);
- break;
- case 'value':
- input.value = newValue;
- 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.value = input.value;
- if(typeof this.settings.onok === 'function'){
- returnValue = this.settings.onok.call(undefined, closeEvent, this.value);
- if(typeof returnValue !== 'undefined'){
- closeEvent.cancel = !returnValue;
- }
- }
- break;
- case 1:
- if(typeof this.settings.oncancel === 'function'){
- returnValue = this.settings.oncancel.call(undefined, closeEvent);
- if(typeof returnValue !== 'undefined'){
- closeEvent.cancel = !returnValue;
- }
- }
- break;
- }
- }
- };
- });
- // AMD and window support
- if ( typeof define === 'function' ) {
- define( [], function () {
- return alertify;
- } );
- } else if ( !window.alertify ) {
- window.alertify = alertify;
- }
- } ( this ) );