/build/alertify.js
https://github.com/alihalabyah/AlertifyJS · JavaScript · 2769 lines · 1651 code · 209 blank · 909 comment · 247 complexity · e326dcdfa9255ae037b3488a86d91c19 MD5 · raw file
Large files are truncated click here to view the full 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 u…