big-head
Version:
A control panel generator for APIed-Piper
1,327 lines (1,236 loc) • 140 kB
JavaScript
/**
* alertifyjs 1.11.2 http://alertifyjs.com
* AlertifyJS is a javascript framework for developing pretty browser dialogs and notifications.
* Copyright 2018 Mohammad Younes <Mohammad@alertifyjs.com> (http://alertifyjs.com)
* Licensed under GPL 3 <https://opensource.org/licenses/gpl-3.0>*/
( function ( window ) {
'use strict';
/**
* Keys enum
* @type {Object}
*/
var keys = {
ENTER: 13,
ESC: 27,
F1: 112,
F12: 123,
LEFT: 37,
RIGHT: 39
};
/**
* Default options
* @type {Object}
*/
var defaults = {
autoReset:true,
basic:false,
closable:true,
closableByDimmer:true,
frameless:false,
maintainFocus:true, //global default not per instance, applies to all dialogs
maximizable:true,
modal:true,
movable:true,
moveBounded:false,
overflow:true,
padding: true,
pinnable:true,
pinned:true,
preventBodyShift:false, //global default not per instance, applies to all dialogs
resizable:true,
startMaximized:false,
transition:'pulse',
notifier:{
delay:5,
position:'bottom-right',
closeButton:false
},
glossary:{
title:'AlertifyJS',
ok: 'OK',
cancel: 'Cancel',
acccpt: 'Accept',
deny: 'Deny',
confirm: 'Confirm',
decline: 'Decline',
close: 'Close',
maximize: 'Maximize',
restore: 'Restore',
},
theme:{
input:'ajs-input',
ok:'ajs-ok',
cancel:'ajs-cancel',
}
};
//holds open dialogs instances
var openDialogs = [];
/**
* [Helper] Adds the specified class(es) to the element.
*
* @element {node} The element
* @className {string} One or more space-separated classes to be added to the class attribute of the element.
*
* @return {undefined}
*/
function addClass(element,classNames){
element.className += ' ' + classNames;
}
/**
* [Helper] Removes the specified class(es) from the element.
*
* @element {node} The element
* @className {string} One or more space-separated classes to be removed from the class attribute of the element.
*
* @return {undefined}
*/
function removeClass(element, classNames) {
var original = element.className.split(' ');
var toBeRemoved = classNames.split(' ');
for (var x = 0; x < toBeRemoved.length; x += 1) {
var index = original.indexOf(toBeRemoved[x]);
if (index > -1){
original.splice(index,1);
}
}
element.className = original.join(' ');
}
/**
* [Helper] Checks if the document is RTL
*
* @return {Boolean} True if the document is RTL, false otherwise.
*/
function isRightToLeft(){
return window.getComputedStyle(document.body).direction === 'rtl';
}
/**
* [Helper] Get the document current scrollTop
*
* @return {Number} current document scrollTop value
*/
function getScrollTop(){
return ((document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop);
}
/**
* [Helper] Get the document current scrollLeft
*
* @return {Number} current document scrollLeft value
*/
function getScrollLeft(){
return ((document.documentElement && document.documentElement.scrollLeft) || document.body.scrollLeft);
}
/**
* Helper: clear contents
*
*/
function clearContents(element){
while (element.lastChild) {
element.removeChild(element.lastChild);
}
}
/**
* Extends a given prototype by merging properties from base into sub.
*
* @sub {Object} sub The prototype being overwritten.
* @base {Object} base The prototype being written.
*
* @return {Object} The extended prototype.
*/
function copy(src) {
if(null === src){
return src;
}
var cpy;
if(Array.isArray(src)){
cpy = [];
for(var x=0;x<src.length;x+=1){
cpy.push(copy(src[x]));
}
return cpy;
}
if(src instanceof Date){
return new Date(src.getTime());
}
if(src instanceof RegExp){
cpy = new RegExp(src.source);
cpy.global = src.global;
cpy.ignoreCase = src.ignoreCase;
cpy.multiline = src.multiline;
cpy.lastIndex = src.lastIndex;
return cpy;
}
if(typeof src === 'object'){
cpy = {};
// copy dialog pototype over definition.
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
cpy[prop] = copy(src[prop]);
}
}
return cpy;
}
return src;
}
/**
* Helper: destruct the dialog
*
*/
function destruct(instance, initialize){
if(instance.elements){
//delete the dom and it's references.
var root = instance.elements.root;
root.parentNode.removeChild(root);
delete instance.elements;
//copy back initial settings.
instance.settings = copy(instance.__settings);
//re-reference init function.
instance.__init = initialize;
//delete __internal variable to allow re-initialization.
delete instance.__internal;
}
}
/**
* Use a closure to return proper event listener method. Try to use
* `addEventListener` by default but fallback to `attachEvent` for
* unsupported browser. The closure simply ensures that the test doesn't
* happen every time the method is called.
*
* @param {Node} el Node element
* @param {String} event Event type
* @param {Function} fn Callback of event
* @return {Function}
*/
var on = (function () {
if (document.addEventListener) {
return function (el, event, fn, useCapture) {
el.addEventListener(event, fn, useCapture === true);
};
} else if (document.attachEvent) {
return function (el, event, fn) {
el.attachEvent('on' + event, fn);
};
}
}());
/**
* Use a closure to return proper event listener method. Try to use
* `removeEventListener` by default but fallback to `detachEvent` for
* unsupported browser. The closure simply ensures that the test doesn't
* happen every time the method is called.
*
* @param {Node} el Node element
* @param {String} event Event type
* @param {Function} fn Callback of event
* @return {Function}
*/
var off = (function () {
if (document.removeEventListener) {
return function (el, event, fn, useCapture) {
el.removeEventListener(event, fn, useCapture === true);
};
} else if (document.detachEvent) {
return function (el, event, fn) {
el.detachEvent('on' + event, fn);
};
}
}());
/**
* Prevent default event from firing
*
* @param {Event} event Event object
* @return {undefined}
function prevent ( event ) {
if ( event ) {
if ( event.preventDefault ) {
event.preventDefault();
} else {
event.returnValue = false;
}
}
}
*/
var transition = (function () {
var t, type;
var supported = false;
var transitions = {
'animation' : 'animationend',
'OAnimation' : 'oAnimationEnd oanimationend',
'msAnimation' : 'MSAnimationEnd',
'MozAnimation' : 'animationend',
'WebkitAnimation' : 'webkitAnimationEnd'
};
for (t in transitions) {
if (document.documentElement.style[t] !== undefined) {
type = transitions[t];
supported = true;
break;
}
}
return {
type: type,
supported: supported
};
}());
/**
* Creates event handler delegate that sends the instance as last argument.
*
* @return {Function} a function wrapper which sends the instance as last argument.
*/
function delegate(context, method) {
return function () {
if (arguments.length > 0) {
var args = [];
for (var x = 0; x < arguments.length; x += 1) {
args.push(arguments[x]);
}
args.push(context);
return method.apply(context, args);
}
return method.apply(context, [null, context]);
};
}
/**
* Helper for creating a dialog close event.
*
* @return {object}
*/
function createCloseEvent(index, button) {
return {
index: index,
button: button,
cancel: false
};
}
/**
* Helper for dispatching events.
*
* @param {string} evenType The type of the event to disptach.
* @param {object} instance The dialog instance disptaching the event.
*
* @return {any} The result of the invoked function.
*/
function dispatchEvent(eventType, instance) {
if ( typeof instance.get(eventType) === 'function' ) {
return instance.get(eventType).call(instance);
}
}
/**
* Super class for all dialogs
*
* @return {Object} base dialog prototype
*/
var dialog = (function () {
var //holds the list of used keys.
usedKeys = [],
//dummy variable, used to trigger dom reflow.
reflow = null,
//holds body tab index in case it has any.
tabindex = false,
//condition for detecting safari
isSafari = window.navigator.userAgent.indexOf('Safari') > -1 && window.navigator.userAgent.indexOf('Chrome') < 0,
//dialog building blocks
templates = {
dimmer:'<div class="ajs-dimmer"></div>',
/*tab index required to fire click event before body focus*/
modal: '<div class="ajs-modal" tabindex="0"></div>',
dialog: '<div class="ajs-dialog" tabindex="0"></div>',
reset: '<button class="ajs-reset"></button>',
commands: '<div class="ajs-commands"><button class="ajs-pin"></button><button class="ajs-maximize"></button><button class="ajs-close"></button></div>',
header: '<div class="ajs-header"></div>',
body: '<div class="ajs-body"></div>',
content: '<div class="ajs-content"></div>',
footer: '<div class="ajs-footer"></div>',
buttons: { primary: '<div class="ajs-primary ajs-buttons"></div>', auxiliary: '<div class="ajs-auxiliary ajs-buttons"></div>' },
button: '<button class="ajs-button"></button>',
resizeHandle: '<div class="ajs-handle"></div>',
},
//common class names
classes = {
animationIn: 'ajs-in',
animationOut: 'ajs-out',
base: 'alertify',
basic:'ajs-basic',
capture: 'ajs-capture',
closable:'ajs-closable',
fixed: 'ajs-fixed',
frameless:'ajs-frameless',
hidden: 'ajs-hidden',
maximize: 'ajs-maximize',
maximized: 'ajs-maximized',
maximizable:'ajs-maximizable',
modeless: 'ajs-modeless',
movable: 'ajs-movable',
noSelection: 'ajs-no-selection',
noOverflow: 'ajs-no-overflow',
noPadding:'ajs-no-padding',
pin:'ajs-pin',
pinnable:'ajs-pinnable',
prefix: 'ajs-',
resizable: 'ajs-resizable',
restore: 'ajs-restore',
shake:'ajs-shake',
unpinned:'ajs-unpinned',
};
/**
* Helper: initializes the dialog instance
*
* @return {Number} The total count of currently open modals.
*/
function initialize(instance){
if(!instance.__internal){
//no need to expose init after this.
delete instance.__init;
//keep a copy of initial dialog settings
if(!instance.__settings){
instance.__settings = copy(instance.settings);
}
//get dialog buttons/focus setup
var setup;
if(typeof instance.setup === 'function'){
setup = instance.setup();
setup.options = setup.options || {};
setup.focus = setup.focus || {};
}else{
setup = {
buttons:[],
focus:{
element:null,
select:false
},
options:{
}
};
}
//initialize hooks object.
if(typeof instance.hooks !== 'object'){
instance.hooks = {};
}
//copy buttons defintion
var buttonsDefinition = [];
if(Array.isArray(setup.buttons)){
for(var b=0;b<setup.buttons.length;b+=1){
var ref = setup.buttons[b],
cpy = {};
for (var i in ref) {
if (ref.hasOwnProperty(i)) {
cpy[i] = ref[i];
}
}
buttonsDefinition.push(cpy);
}
}
var internal = instance.__internal = {
/**
* Flag holding the open state of the dialog
*
* @type {Boolean}
*/
isOpen:false,
/**
* Active element is the element that will receive focus after
* closing the dialog. It defaults as the body tag, but gets updated
* to the last focused element before the dialog was opened.
*
* @type {Node}
*/
activeElement:document.body,
timerIn:undefined,
timerOut:undefined,
buttons: buttonsDefinition,
focus: setup.focus,
options: {
title: undefined,
modal: undefined,
basic:undefined,
frameless:undefined,
pinned: undefined,
movable: undefined,
moveBounded:undefined,
resizable: undefined,
autoReset: undefined,
closable: undefined,
closableByDimmer: undefined,
maximizable: undefined,
startMaximized: undefined,
pinnable: undefined,
transition: undefined,
padding:undefined,
overflow:undefined,
onshow:undefined,
onclosing:undefined,
onclose:undefined,
onfocus:undefined,
onmove:undefined,
onmoved:undefined,
onresize:undefined,
onresized:undefined,
onmaximize:undefined,
onmaximized:undefined,
onrestore:undefined,
onrestored:undefined
},
resetHandler:undefined,
beginMoveHandler:undefined,
beginResizeHandler:undefined,
bringToFrontHandler:undefined,
modalClickHandler:undefined,
buttonsClickHandler:undefined,
commandsClickHandler:undefined,
transitionInHandler:undefined,
transitionOutHandler:undefined,
destroy:undefined
};
var elements = {};
//root node
elements.root = document.createElement('div');
elements.root.className = classes.base + ' ' + classes.hidden + ' ';
elements.root.innerHTML = templates.dimmer + templates.modal;
//dimmer
elements.dimmer = elements.root.firstChild;
//dialog
elements.modal = elements.root.lastChild;
elements.modal.innerHTML = templates.dialog;
elements.dialog = elements.modal.firstChild;
elements.dialog.innerHTML = templates.reset + templates.commands + templates.header + templates.body + templates.footer + templates.resizeHandle + templates.reset;
//reset links
elements.reset = [];
elements.reset.push(elements.dialog.firstChild);
elements.reset.push(elements.dialog.lastChild);
//commands
elements.commands = {};
elements.commands.container = elements.reset[0].nextSibling;
elements.commands.pin = elements.commands.container.firstChild;
elements.commands.maximize = elements.commands.pin.nextSibling;
elements.commands.close = elements.commands.maximize.nextSibling;
//header
elements.header = elements.commands.container.nextSibling;
//body
elements.body = elements.header.nextSibling;
elements.body.innerHTML = templates.content;
elements.content = elements.body.firstChild;
//footer
elements.footer = elements.body.nextSibling;
elements.footer.innerHTML = templates.buttons.auxiliary + templates.buttons.primary;
//resize handle
elements.resizeHandle = elements.footer.nextSibling;
//buttons
elements.buttons = {};
elements.buttons.auxiliary = elements.footer.firstChild;
elements.buttons.primary = elements.buttons.auxiliary.nextSibling;
elements.buttons.primary.innerHTML = templates.button;
elements.buttonTemplate = elements.buttons.primary.firstChild;
//remove button template
elements.buttons.primary.removeChild(elements.buttonTemplate);
for(var x=0; x < instance.__internal.buttons.length; x+=1) {
var button = instance.__internal.buttons[x];
// add to the list of used keys.
if(usedKeys.indexOf(button.key) < 0){
usedKeys.push(button.key);
}
button.element = elements.buttonTemplate.cloneNode();
button.element.innerHTML = button.text;
if(typeof button.className === 'string' && button.className !== ''){
addClass(button.element, button.className);
}
for(var key in button.attrs){
if(key !== 'className' && button.attrs.hasOwnProperty(key)){
button.element.setAttribute(key, button.attrs[key]);
}
}
if(button.scope === 'auxiliary'){
elements.buttons.auxiliary.appendChild(button.element);
}else{
elements.buttons.primary.appendChild(button.element);
}
}
//make elements pubic
instance.elements = elements;
//save event handlers delegates
internal.resetHandler = delegate(instance, onReset);
internal.beginMoveHandler = delegate(instance, beginMove);
internal.beginResizeHandler = delegate(instance, beginResize);
internal.bringToFrontHandler = delegate(instance, bringToFront);
internal.modalClickHandler = delegate(instance, modalClickHandler);
internal.buttonsClickHandler = delegate(instance, buttonsClickHandler);
internal.commandsClickHandler = delegate(instance, commandsClickHandler);
internal.transitionInHandler = delegate(instance, handleTransitionInEvent);
internal.transitionOutHandler = delegate(instance, handleTransitionOutEvent);
//settings
for(var opKey in internal.options){
if(setup.options[opKey] !== undefined){
// if found in user options
instance.set(opKey, setup.options[opKey]);
}else if(alertify.defaults.hasOwnProperty(opKey)) {
// else if found in defaults options
instance.set(opKey, alertify.defaults[opKey]);
}else if(opKey === 'title' ) {
// else if title key, use alertify.defaults.glossary
instance.set(opKey, alertify.defaults.glossary[opKey]);
}
}
// allow dom customization
if(typeof instance.build === 'function'){
instance.build();
}
}
//add to the end of the DOM tree.
document.body.appendChild(instance.elements.root);
}
/**
* Helper: maintains scroll position
*
*/
var scrollX, scrollY;
function saveScrollPosition(){
scrollX = getScrollLeft();
scrollY = getScrollTop();
}
function restoreScrollPosition(){
window.scrollTo(scrollX, scrollY);
}
/**
* Helper: adds/removes no-overflow class from body
*
*/
function ensureNoOverflow(){
var requiresNoOverflow = 0;
for(var x=0;x<openDialogs.length;x+=1){
var instance = openDialogs[x];
if(instance.isModal() || instance.isMaximized()){
requiresNoOverflow+=1;
}
}
if(requiresNoOverflow === 0 && document.body.className.indexOf(classes.noOverflow) >= 0){
//last open modal or last maximized one
removeClass(document.body, classes.noOverflow);
preventBodyShift(false);
}else if(requiresNoOverflow > 0 && document.body.className.indexOf(classes.noOverflow) < 0){
//first open modal or first maximized one
preventBodyShift(true);
addClass(document.body, classes.noOverflow);
}
}
var top = '', topScroll = 0;
/**
* Helper: prevents body shift.
*
*/
function preventBodyShift(add){
if(alertify.defaults.preventBodyShift && document.documentElement.scrollHeight > document.documentElement.clientHeight){
if(add ){//&& openDialogs[openDialogs.length-1].elements.dialog.clientHeight <= document.documentElement.clientHeight){
topScroll = scrollY;
top = window.getComputedStyle(document.body).top;
addClass(document.body, classes.fixed);
document.body.style.top = -scrollY + 'px';
} else {
scrollY = topScroll;
document.body.style.top = top;
removeClass(document.body, classes.fixed);
restoreScrollPosition();
}
}
}
/**
* Sets the name of the transition used to show/hide the dialog
*
* @param {Object} instance The dilog instance.
*
*/
function updateTransition(instance, value, oldValue){
if(typeof oldValue === 'string'){
removeClass(instance.elements.root,classes.prefix + oldValue);
}
addClass(instance.elements.root, classes.prefix + value);
reflow = instance.elements.root.offsetWidth;
}
/**
* Toggles the dialog display mode
*
* @param {Object} instance The dilog instance.
*
* @return {undefined}
*/
function updateDisplayMode(instance){
if(instance.get('modal')){
//make modal
removeClass(instance.elements.root, classes.modeless);
//only if open
if(instance.isOpen()){
unbindModelessEvents(instance);
//in case a pinned modless dialog was made modal while open.
updateAbsPositionFix(instance);
ensureNoOverflow();
}
}else{
//make modelss
addClass(instance.elements.root, classes.modeless);
//only if open
if(instance.isOpen()){
bindModelessEvents(instance);
//in case pin/unpin was called while a modal is open
updateAbsPositionFix(instance);
ensureNoOverflow();
}
}
}
/**
* Toggles the dialog basic view mode
*
* @param {Object} instance The dilog instance.
*
* @return {undefined}
*/
function updateBasicMode(instance){
if (instance.get('basic')) {
// add class
addClass(instance.elements.root, classes.basic);
} else {
// remove class
removeClass(instance.elements.root, classes.basic);
}
}
/**
* Toggles the dialog frameless view mode
*
* @param {Object} instance The dilog instance.
*
* @return {undefined}
*/
function updateFramelessMode(instance){
if (instance.get('frameless')) {
// add class
addClass(instance.elements.root, classes.frameless);
} else {
// remove class
removeClass(instance.elements.root, classes.frameless);
}
}
/**
* Helper: Brings the modeless dialog to front, attached to modeless dialogs.
*
* @param {Event} event Focus event
* @param {Object} instance The dilog instance.
*
* @return {undefined}
*/
function bringToFront(event, instance){
// Do not bring to front if preceeded by an open modal
var index = openDialogs.indexOf(instance);
for(var x=index+1;x<openDialogs.length;x+=1){
if(openDialogs[x].isModal()){
return;
}
}
// Bring to front by making it the last child.
if(document.body.lastChild !== instance.elements.root){
document.body.appendChild(instance.elements.root);
//also make sure its at the end of the list
openDialogs.splice(openDialogs.indexOf(instance),1);
openDialogs.push(instance);
setFocus(instance);
}
return false;
}
/**
* Helper: reflects dialogs options updates
*
* @param {Object} instance The dilog instance.
* @param {String} option The updated option name.
*
* @return {undefined}
*/
function optionUpdated(instance, option, oldValue, newValue){
switch(option){
case 'title':
instance.setHeader(newValue);
break;
case 'modal':
updateDisplayMode(instance);
break;
case 'basic':
updateBasicMode(instance);
break;
case 'frameless':
updateFramelessMode(instance);
break;
case 'pinned':
updatePinned(instance);
break;
case 'closable':
updateClosable(instance);
break;
case 'maximizable':
updateMaximizable(instance);
break;
case 'pinnable':
updatePinnable(instance);
break;
case 'movable':
updateMovable(instance);
break;
case 'resizable':
updateResizable(instance);
break;
case 'padding':
if(newValue){
removeClass(instance.elements.root, classes.noPadding);
}else if(instance.elements.root.className.indexOf(classes.noPadding) < 0){
addClass(instance.elements.root, classes.noPadding);
}
break;
case 'overflow':
if(newValue){
removeClass(instance.elements.root, classes.noOverflow);
}else if(instance.elements.root.className.indexOf(classes.noOverflow) < 0){
addClass(instance.elements.root, classes.noOverflow);
}
break;
case 'transition':
updateTransition(instance,newValue, oldValue);
break;
}
// internal on option updated event
if(typeof instance.hooks.onupdate === 'function'){
instance.hooks.onupdate.call(instance, option, oldValue, newValue);
}
}
/**
* Helper: reflects dialogs options updates
*
* @param {Object} instance The dilog instance.
* @param {Object} obj The object to set/get a value on/from.
* @param {Function} callback The callback function to call if the key was found.
* @param {String|Object} key A string specifying a propery name or a collection of key value pairs.
* @param {Object} value Optional, the value associated with the key (in case it was a string).
* @param {String} option The updated option name.
*
* @return {Object} result object
* The result objects has an 'op' property, indicating of this is a SET or GET operation.
* GET:
* - found: a flag indicating if the key was found or not.
* - value: the property value.
* SET:
* - items: a list of key value pairs of the properties being set.
* each contains:
* - found: a flag indicating if the key was found or not.
* - key: the property key.
* - value: the property value.
*/
function update(instance, obj, callback, key, value){
var result = {op:undefined, items: [] };
if(typeof value === 'undefined' && typeof key === 'string') {
//get
result.op = 'get';
if(obj.hasOwnProperty(key)){
result.found = true;
result.value = obj[key];
}else{
result.found = false;
result.value = undefined;
}
}
else
{
var old;
//set
result.op = 'set';
if(typeof key === 'object'){
//set multiple
var args = key;
for (var prop in args) {
if (obj.hasOwnProperty(prop)) {
if(obj[prop] !== args[prop]){
old = obj[prop];
obj[prop] = args[prop];
callback.call(instance,prop, old, args[prop]);
}
result.items.push({ 'key': prop, 'value': args[prop], 'found':true});
}else{
result.items.push({ 'key': prop, 'value': args[prop], 'found':false});
}
}
} else if (typeof key === 'string'){
//set single
if (obj.hasOwnProperty(key)) {
if(obj[key] !== value){
old = obj[key];
obj[key] = value;
callback.call(instance,key, old, value);
}
result.items.push({'key': key, 'value': value , 'found':true});
}else{
result.items.push({'key': key, 'value': value , 'found':false});
}
} else {
//invalid params
throw new Error('args must be a string or object');
}
}
return result;
}
/**
* Triggers a close event.
*
* @param {Object} instance The dilog instance.
*
* @return {undefined}
*/
function triggerClose(instance) {
var found;
triggerCallback(instance, function (button) {
return found = (button.invokeOnClose === true);
});
//none of the buttons registered as onclose callback
//close the dialog
if (!found && instance.isOpen()) {
instance.close();
}
}
/**
* Dialogs commands event handler, attached to the dialog commands element.
*
* @param {Event} event DOM event object.
* @param {Object} instance The dilog instance.
*
* @return {undefined}
*/
function commandsClickHandler(event, instance) {
var target = event.srcElement || event.target;
switch (target) {
case instance.elements.commands.pin:
if (!instance.isPinned()) {
pin(instance);
} else {
unpin(instance);
}
break;
case instance.elements.commands.maximize:
if (!instance.isMaximized()) {
maximize(instance);
} else {
restore(instance);
}
break;
case instance.elements.commands.close:
triggerClose(instance);
break;
}
return false;
}
/**
* Helper: pins the modeless dialog.
*
* @param {Object} instance The dialog instance.
*
* @return {undefined}
*/
function pin(instance) {
//pin the dialog
instance.set('pinned', true);
}
/**
* Helper: unpins the modeless dialog.
*
* @param {Object} instance The dilog instance.
*
* @return {undefined}
*/
function unpin(instance) {
//unpin the dialog
instance.set('pinned', false);
}
/**
* Helper: enlarges the dialog to fill the entire screen.
*
* @param {Object} instance The dilog instance.
*
* @return {undefined}
*/
function maximize(instance) {
// allow custom `onmaximize` method
dispatchEvent('onmaximize', instance);
//maximize the dialog
addClass(instance.elements.root, classes.maximized);
if (instance.isOpen()) {
ensureNoOverflow();
}
// allow custom `onmaximized` method
dispatchEvent('onmaximized', instance);
}
/**
* Helper: returns the dialog to its former size.
*
* @param {Object} instance The dilog instance.
*
* @return {undefined}
*/
function restore(instance) {
// allow custom `onrestore` method
dispatchEvent('onrestore', instance);
//maximize the dialog
removeClass(instance.elements.root, classes.maximized);
if (instance.isOpen()) {
ensureNoOverflow();
}
// allow custom `onrestored` method
dispatchEvent('onrestored', instance);
}
/**
* Show or hide the maximize box.
*
* @param {Object} instance The dilog instance.
* @param {Boolean} on True to add the behavior, removes it otherwise.
*
* @return {undefined}
*/
function updatePinnable(instance) {
if (instance.get('pinnable')) {
// add class
addClass(instance.elements.root, classes.pinnable);
} else {
// remove class
removeClass(instance.elements.root, classes.pinnable);
}
}
/**
* Helper: Fixes the absolutly positioned modal div position.
*
* @param {Object} instance The dialog instance.
*
* @return {undefined}
*/
function addAbsPositionFix(instance) {
var scrollLeft = getScrollLeft();
instance.elements.modal.style.marginTop = getScrollTop() + 'px';
instance.elements.modal.style.marginLeft = scrollLeft + 'px';
instance.elements.modal.style.marginRight = (-scrollLeft) + 'px';
}
/**
* Helper: Removes the absolutly positioned modal div position fix.
*
* @param {Object} instance The dialog instance.
*
* @return {undefined}
*/
function removeAbsPositionFix(instance) {
var marginTop = parseInt(instance.elements.modal.style.marginTop, 10);
var marginLeft = parseInt(instance.elements.modal.style.marginLeft, 10);
instance.elements.modal.style.marginTop = '';
instance.elements.modal.style.marginLeft = '';
instance.elements.modal.style.marginRight = '';
if (instance.isOpen()) {
var top = 0,
left = 0
;
if (instance.elements.dialog.style.top !== '') {
top = parseInt(instance.elements.dialog.style.top, 10);
}
instance.elements.dialog.style.top = (top + (marginTop - getScrollTop())) + 'px';
if (instance.elements.dialog.style.left !== '') {
left = parseInt(instance.elements.dialog.style.left, 10);
}
instance.elements.dialog.style.left = (left + (marginLeft - getScrollLeft())) + 'px';
}
}
/**
* Helper: Adds/Removes the absolutly positioned modal div position fix based on its pinned setting.
*
* @param {Object} instance The dialog instance.
*
* @return {undefined}
*/
function updateAbsPositionFix(instance) {
// if modeless and unpinned add fix
if (!instance.get('modal') && !instance.get('pinned')) {
addAbsPositionFix(instance);
} else {
removeAbsPositionFix(instance);
}
}
/**
* Toggles the dialog position lock | modeless only.
*
* @param {Object} instance The dilog instance.
* @param {Boolean} on True to make it modal, false otherwise.
*
* @return {undefined}
*/
function updatePinned(instance) {
if (instance.get('pinned')) {
removeClass(instance.elements.root, classes.unpinned);
if (instance.isOpen()) {
removeAbsPositionFix(instance);
}
} else {
addClass(instance.elements.root, classes.unpinned);
if (instance.isOpen() && !instance.isModal()) {
addAbsPositionFix(instance);
}
}
}
/**
* Show or hide the maximize box.
*
* @param {Object} instance The dilog instance.
* @param {Boolean} on True to add the behavior, removes it otherwise.
*
* @return {undefined}
*/
function updateMaximizable(instance) {
if (instance.get('maximizable')) {
// add class
addClass(instance.elements.root, classes.maximizable);
} else {
// remove class
removeClass(instance.elements.root, classes.maximizable);
}
}
/**
* Show or hide the close box.
*
* @param {Object} instance The dilog instance.
* @param {Boolean} on True to add the behavior, removes it otherwise.
*
* @return {undefined}
*/
function updateClosable(instance) {
if (instance.get('closable')) {
// add class
addClass(instance.elements.root, classes.closable);
bindClosableEvents(instance);
} else {
// remove class
removeClass(instance.elements.root, classes.closable);
unbindClosableEvents(instance);
}
}
var cancelClick = false,// flag to cancel click event if already handled by end resize event (the mousedown, mousemove, mouseup sequence fires a click event.).
modalClickHandlerTS=0 // stores last click timestamp to prevent executing the handler twice on double click.
;
/**
* Helper: closes the modal dialog when clicking the modal
*
* @param {Event} event DOM event object.
* @param {Object} instance The dilog instance.
*
* @return {undefined}
*/
function modalClickHandler(event, instance) {
if(event.timeStamp - modalClickHandlerTS > 200 && (modalClickHandlerTS = event.timeStamp) && !cancelClick){
var target = event.srcElement || event.target;
if (instance.get('closableByDimmer') === true && target === instance.elements.modal) {
triggerClose(instance);
}
cancelClick = false;
return false;
}
}
// stores last call timestamp to prevent triggering the callback twice.
var callbackTS = 0;
// flag to cancel keyup event if already handled by click event (pressing Enter on a focusted button).
var cancelKeyup = false;
/**
* 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) {
if(Date.now() - callbackTS > 200 && (callbackTS = Date.now())){
for (var idx = 0; idx < instance.__internal.buttons.length; idx += 1) {
var button = instance.__internal.buttons[idx];
if (!button.element.disabled && check(button)) {
var closeEvent = createCloseEvent(idx, button);
if (typeof instance.callback === 'function') {
instance.callback.apply(instance, [closeEvent]);
}
//close the dialog only if not canceled.
if (closeEvent.cancel === false) {
instance.close();
}
break;
}
}
}
}
/**
* Clicks event handler, attached to the dialog footer.
*
* @param {Event} DOM event object.
* @param {Object} The dilog instance.
*
* @return {undefined}
*/
function buttonsClickHandler(event, instance) {
var target = event.srcElement || event.target;
triggerCallback(instance, function (button) {
// if this button caused the click, cancel keyup event
return button.element === target && (cancelKeyup = true);
});
}
/**
* Keyup event handler, attached to the document.body
*
* @param {Event} DOM event object.
* @param {Object} The dilog instance.
*
* @return {undefined}
*/
function keyupHandler(event) {
//hitting enter while button has focus will trigger keyup too.
//ignore if handled by clickHandler
if (cancelKeyup) {
cancelKeyup = false;
return;
}
var instance = openDialogs[openDialogs.length - 1];
var keyCode = event.keyCode;
if (instance.__internal.buttons.length === 0 && keyCode === keys.ESC && instance.get('closable') === true) {
triggerClose(instance);
return false;
}else if (usedKeys.indexOf(keyCode) > -1) {
triggerCallback(instance, function (button) {
return button.key === keyCode;
});
return false;
}
}
/**
* Keydown event handler, attached to the document.body
*
* @param {Event} DOM event object.
* @param {Object} The dilog instance.
*
* @return {undefined}
*/
function keydownHandler(event) {
var instance = openDialogs[openDialogs.length - 1];
var keyCode = event.keyCode;
if (keyCode === keys.LEFT || keyCode === keys.RIGHT) {
var buttons = instance.__internal.buttons;
for (var x = 0; x < buttons.length; x += 1) {
if (document.activeElement === buttons[x].element) {
switch (keyCode) {
case keys.LEFT:
buttons[(x || buttons.length) - 1].element.focus();
return;
case keys.RIGHT:
buttons[(x + 1) % buttons.length].element.focus();
return;
}
}
}
}else if (keyCode < keys.F12 + 1 && keyCode > keys.F1 - 1 && usedKeys.indexOf(keyCode) > -1) {
event.preventDefault();
event.stopPropagation();
triggerCallback(instance, function (button) {
return button.key === keyCode;
});
return false;
}
}
/**
* Sets focus to proper dialog element
*
* @param {Object} instance The dilog instance.