blob: d8d1f6ef22589d6f1b84475c89ae3ff93e0cdbdf [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Create a new dialog.
*
* If the height, width, and fullscreen breakpoints options are not set this plugin will look for (and use)
* any CSS defined styles for height, min/max-height, width, min/max-width. If no CSS styles are defined then the
* plugin will attempt to calculate these values when the dialog is first opened (based on screen size).
*
* The options are specified in the following format:
*
* {
* header: true,
* footer: true,
* headerText: 'Dialog Header',
* subtitleText: 'Dialog subtitle',
* scrollableContentStyle: 'scrollable',
* buttons: [{
* buttonText: 'Cancel',
* color: {
* base: '#728E9B',
* hover: '#004849',
* text: '#ffffff'
* },
* disabled: isDisabledFunction,
* handler: {
* click: cancelHandler
* }
* }, {
* buttonText: 'Apply',
* color: {
* base: '#E3E8EB',
* hover: '#C7D2D7',
* text: '#004849'
* },
* disabled: isDisabledFunction,
* handler: {
* click: applyHandler
* }
* }],
* handler: {
* close: closeHandler,
* open: openHandler,
* resize: resizeHandler
* },
* height: "55%", //optional. Property can also be set with css (accepts 'px' or '%' values)
* width: "34%", //optional. Property can also be set with css (accepts 'px' or '%' values)
* min-height: "420px", //optional, defaults to 'height'. Property can also be set with css (accepts 'px' values)
* min-width: "470px" //optional, defaults to 'width'. Property can also be set with css (accepts 'px' values)
* responsive: {
* x: "true", //optional, default true
* y: "true", //optional, default true
* fullscreen-height: "420px", //optional, default is original dialog height (accepts 'px' values)
* fullscreen-width: "470px", //optional, default is original dialog width (accepts 'px' values)
* },
* glasspane: "#728E9B" //optional, sets the color of modal glasspane...default if unset is the dialog header color
* }
*
* The content of the dialog MUST be contained in an element with the class `dialog-content`
* directly under the dialog element.
*
* <div id="dialogId">
* <div class="dialog-content">
* //Dialog Content....
* </div>
* </div>
*
* @argument {jQuery} $
*/
(function ($) {
var isUndefined = function (obj) {
return typeof obj === 'undefined';
};
var isNull = function (obj) {
return obj === null;
};
var isDefinedAndNotNull = function (obj) {
return !isUndefined(obj) && !isNull(obj);
};
var isBlank = function (str) {
return isUndefined(str) || isNull(str) || str === '';
};
// private function for adding buttons
var addButtons = function (dialog, buttonModel) {
if (isDefinedAndNotNull(buttonModel)) {
var buttonWrapper = $('<div class="dialog-buttons"></div>');
$.each(buttonModel, function (i, buttonConfig) {
var isDisabled = function () {
return typeof buttonConfig.disabled === 'function' && buttonConfig.disabled.call() === true;
};
// create the button
var button = $('<div class="button"></div>').append($('<span></span>').text(buttonConfig.buttonText));
// add the class if specified
if (isDefinedAndNotNull(buttonConfig.clazz)) {
button.addClass(buttonConfig.clazz);
}
// set the color if specified
if (isDefinedAndNotNull(buttonConfig.color)) {
button.css({
'background': buttonConfig.color.base,
'color': buttonConfig.color.text
});
}
// check if the button should be disabled
if (isDisabled()) {
button.addClass('disabled-button');
} else {
// enable custom hover if specified
if (isDefinedAndNotNull(buttonConfig.color)) {
button.hover(function () {
$(this).css("background-color", buttonConfig.color.hover);
}, function () {
$(this).css("background-color", buttonConfig.color.base);
});
}
button.click(function () {
var handler = $(this).data('handler');
if (isDefinedAndNotNull(handler) && typeof handler.click === 'function') {
handler.click.call(dialog);
}
});
}
// add the button to the wrapper
button.data('handler', buttonConfig.handler).appendTo(buttonWrapper);
});
// store the button model to refresh later
dialog.append(buttonWrapper).data('buttonModel', buttonModel);
}
};
var methods = {
/**
* Initializes the dialog.
*
* @argument {object} options The options for the plugin
*/
init: function (options) {
return this.each(function () {
// get the combo
var dialog = $(this).addClass('dialog cancellable modal');
dialog.css('display', 'none');
var nfDialogData = {};
if (isDefinedAndNotNull(dialog.data('nf-dialog'))) {
nfDialogData = dialog.data('nf-dialog');
}
// ensure the options have been properly specified
if (isDefinedAndNotNull(options)) {
$.extend(nfDialogData, options);
//persist data attribute
dialog.data('nfDialog', nfDialogData);
}
// determine if dialog needs a header
if (!isDefinedAndNotNull(nfDialogData.header) || nfDialogData.header) {
var dialogHeaderText = $('<span class="dialog-header-text"></span>');
var dialogHeader = $('<div class="dialog-header"></div>').prepend(dialogHeaderText);
// determine if the specified header text is null
if (!isBlank(nfDialogData.headerText)) {
dialogHeaderText.text(nfDialogData.headerText);
}
$('<span></span>').addClass("dialog-header-subtitle").text(nfDialogData.subtitleText).appendTo(dialogHeader);
dialog.prepend(dialogHeader);
}
// determine if dialog needs footer/buttons
if (!isDefinedAndNotNull(nfDialogData.footer) || nfDialogData.footer) {
// add the buttons
addButtons(dialog, nfDialogData.buttons);
}
});
},
/**
* Sets the handler that is used when the dialog is closed.
*
* @argument {function} handler The function to call when hiding the dialog
*/
setCloseHandler: function (handler) {
return this.each(function (index, dialog) {
var nfDialogData = {};
if (isDefinedAndNotNull($(this).data('nf-dialog'))) {
nfDialogData = $(dialog).data('nf-dialog');
}
if (!isDefinedAndNotNull(nfDialogData.handler)){
nfDialogData.handler = {};
}
nfDialogData.handler.close = handler;
//persist data attribute
$(dialog).data('nfDialog', nfDialogData);
});
},
/**
* Sets the handler that is used when the dialog is opened.
*
* @argument {function} handler The function to call when showing the dialog
*/
setOpenHandler: function (handler) {
return this.each(function (index, dialog) {
var nfDialogData = {};
if (isDefinedAndNotNull($(this).data('nf-dialog'))) {
nfDialogData = $(dialog).data('nf-dialog');
}
if (!isDefinedAndNotNull(nfDialogData.handler)){
nfDialogData.handler = {};
}
nfDialogData.handler.open = handler;
//persist data attribute
$(dialog).data('nfDialog', nfDialogData);
});
},
/**
* Sets the handler that is used when the dialog is resized.
*
* @argument {function} handler The function to call when resizing the dialog
*/
setResizeHandler: function (handler) {
return this.each(function (index, dialog) {
var nfDialogData = {};
if (isDefinedAndNotNull($(this).data('nf-dialog'))) {
nfDialogData = $(dialog).data('nf-dialog');
}
if (!isDefinedAndNotNull(nfDialogData.handler)){
nfDialogData.handler = {};
}
nfDialogData.handler.resize = handler;
//persist data attribute
$(dialog).data('nfDialog', nfDialogData);
});
},
/**
* Updates the button model for the selected dialog.
*
* @argument {array} buttons The new button model
*/
setButtonModel: function (buttons) {
return this.each(function () {
if (isDefinedAndNotNull(buttons)) {
var dialog = $(this);
// remove the current buttons
dialog.children('.dialog-buttons').remove();
// add the new buttons
addButtons(dialog, buttons);
}
});
},
/**
* Refreshes the buttons with the existing model.
*/
refreshButtons: function () {
return this.each(function () {
var dialog = $(this);
var buttons = dialog.data('buttonModel');
// remove the current buttons
dialog.children('.dialog-buttons').remove();
// add the new buttons
addButtons(dialog, buttons);
});
},
/**
* Sets the header text of the dialog.
*
* @argument {string} text Text to use a as a header
*/
setHeaderText: function (text) {
return this.each(function () {
$(this).find('span.dialog-header-text').text(text);
});
},
setSubtitle: function (text) {
return this.each(function () {
$(this).find('span.dialog-header-subtitle').text(text);
});
},
resize: function () {
var dialog = $(this);
var dialogContent = dialog.find('.dialog-content');
var nfDialogData = {};
if (isDefinedAndNotNull(dialog.data('nf-dialog'))) {
nfDialogData = dialog.data('nf-dialog');
}
//initialize responsive properties
if (!isDefinedAndNotNull(nfDialogData.responsive)) {
nfDialogData.responsive = {};
if (!isDefinedAndNotNull(nfDialogData.responsive.x)) {
nfDialogData.responsive.x = true;
}
if (!isDefinedAndNotNull(nfDialogData.responsive.y)) {
nfDialogData.responsive.y = true;
}
} else {
if (!isDefinedAndNotNull(nfDialogData.responsive.x)) {
nfDialogData.responsive.x = true;
} else {
nfDialogData.responsive.x = (nfDialogData.responsive.x == "true" || nfDialogData.responsive.x == true) ? true : false;
}
if (!isDefinedAndNotNull(nfDialogData.responsive.y)) {
nfDialogData.responsive.y = true;
} else {
nfDialogData.responsive.y = (nfDialogData.responsive.y == "true" || nfDialogData.responsive.y == true) ? true : false;
}
}
if (nfDialogData.responsive.y || nfDialogData.responsive.x) {
var fullscreenHeight;
var fullscreenWidth;
if (isDefinedAndNotNull(nfDialogData.responsive['fullscreen-height'])) {
fullscreenHeight = parseInt(nfDialogData.responsive['fullscreen-height'], 10);
} else {
nfDialogData.responsive['fullscreen-height'] = dialog.height() + 'px';
fullscreenHeight = parseInt(nfDialogData.responsive['fullscreen-height'], 10);
}
if (isDefinedAndNotNull(nfDialogData.responsive['fullscreen-width'])) {
fullscreenWidth = parseInt(nfDialogData.responsive['fullscreen-width'], 10);
} else {
nfDialogData.responsive['fullscreen-width'] = dialog.width() + 'px';
fullscreenWidth = parseInt(nfDialogData.responsive['fullscreen-width'], 10);
}
if (!isDefinedAndNotNull(nfDialogData.width)) {
nfDialogData.width = dialog.css('width');
}
if (!isDefinedAndNotNull(nfDialogData['min-width'])) {
if (parseInt(dialog.css('min-width'), 10) > 0) {
nfDialogData['min-width'] = dialog.css('min-width');
} else {
nfDialogData['min-width'] = nfDialogData.width;
}
}
//min-width should always be set in terms of px
if (nfDialogData['min-width'].indexOf("%") > 0) {
nfDialogData['min-width'] = ($(window).width() * (parseInt(nfDialogData['min-width'], 10) / 100)) + 'px';
}
if (!isDefinedAndNotNull(nfDialogData.height)) {
nfDialogData.height = dialog.css('height');
}
if (!isDefinedAndNotNull(nfDialogData['min-height'])) {
if (parseInt(dialog.css('min-height'), 10) > 0) {
nfDialogData['min-height'] = dialog.css('min-height');
} else {
nfDialogData['min-height'] = nfDialogData.height;
}
}
//min-height should always be set in terms of px
if (nfDialogData['min-height'].indexOf("%") > 0) {
nfDialogData['min-height'] = ($(window).height() * (parseInt(nfDialogData['min-height'], 10) / 100)) + 'px';
}
//resize dialog
if ($(window).height() < fullscreenHeight) {
if (nfDialogData.responsive.y) {
dialog.css('height', '100%');
dialog.css('min-height', '100%');
}
} else {
//set the dialog min-height
dialog.css('min-height', nfDialogData['min-height']);
if (nfDialogData.responsive.y) {
//make sure nfDialogData.height is in terms of %
if (nfDialogData.height.indexOf("px") > 0) {
nfDialogData.height = (parseInt(nfDialogData.height, 10) / $(window).height() * 100) + '%';
}
dialog.css('height', nfDialogData.height);
}
}
if ($(window).width() < fullscreenWidth) {
if (nfDialogData.responsive.x) {
dialog.css('width', '100%');
dialog.css('min-width', '100%');
}
} else {
//set the dialog width
dialog.css('min-width', nfDialogData['min-width']);
if (nfDialogData.responsive.x) {
//make sure nfDialogData.width is in terms of %
if (nfDialogData.width.indexOf("px") > 0) {
nfDialogData.width = (parseInt(nfDialogData.width, 10) / $(window).width() * 100) + '%';
}
dialog.css('width', nfDialogData.width);
}
}
dialog.center();
//persist data attribute
dialog.data('nfDialog', nfDialogData);
}
//apply scrollable style if applicable
if (dialogContent[0].offsetHeight < dialogContent[0].scrollHeight) {
// your element has overflow
if (isDefinedAndNotNull(nfDialogData.scrollableContentStyle)) {
dialogContent.addClass(nfDialogData.scrollableContentStyle);
}
} else {
// your element doesn't have overflow
if (isDefinedAndNotNull(nfDialogData.scrollableContentStyle)) {
dialogContent.removeClass(nfDialogData.scrollableContentStyle);
}
}
if (isDefinedAndNotNull(nfDialogData.handler)) {
var handler = nfDialogData.handler.resize;
if (isDefinedAndNotNull(handler) && typeof handler === 'function') {
// invoke the handler
handler.call(dialog);
}
}
},
/**
* Shows the dialog.
*/
show: function () {
var dialog = $(this);
var zIndex = dialog.css('z-index');
if (zIndex === 'auto') {
if (isDefinedAndNotNull(dialog.data('nf-dialog'))) {
zIndex = (isDefinedAndNotNull(dialog.data('nf-dialog')['z-index'])) ?
dialog.data('nf-dialog')['z-index'] : 1301;
} else {
zIndex = 1301;
}
}
var openDialogs = $.makeArray($('.dialog:visible'));
if (openDialogs.length >= 1){
var zVals = openDialogs.map(function(openDialog){
var index;
return isNaN(index = parseInt($(openDialog).css("z-index"), 10)) ? 0 : index;
});
//Add 2 so that we have room for the glass pane overlay of the new dialog
zIndex = Math.max.apply(null, zVals) + 2;
}
dialog.css('z-index', zIndex);
var nfDialogData = {};
if (isDefinedAndNotNull(dialog.data('nf-dialog'))) {
nfDialogData = dialog.data('nf-dialog');
}
var glasspane;
if (isDefinedAndNotNull(nfDialogData.glasspane)) {
glasspane = nfDialogData.glasspane;
} else {
nfDialogData.glasspane = glasspane = dialog.find('.dialog-header').css('background-color'); //default to header color
}
if(top !== window || !isDefinedAndNotNull(nfDialogData.glasspane)) {
nfDialogData.glasspane = glasspane = 'transparent';
}
if (!$('body').find("[data-nf-dialog-parent='" + dialog.attr('id') + "']").is(':visible')) {
//create glass pane overlay
$('<div></div>').attr('data-nf-dialog-parent', dialog.attr('id')).addClass("modal-glass").css({
"background-color": glasspane,
"z-index": zIndex - 1
}).appendTo($('body'));
}
//persist data attribute
dialog.data('nfDialog', nfDialogData);
return this.each(function () {
// show the dialog
if (!dialog.is(':visible')) {
dialog.show();
dialog.modal('resize');
dialog.center();
if (isDefinedAndNotNull(nfDialogData.handler)) {
var handler = nfDialogData.handler.open;
if (isDefinedAndNotNull(handler) && typeof handler === 'function') {
// invoke the handler
handler.call(dialog);
}
}
}
});
},
/**
* Hides the dialog.
*/
hide: function () {
return this.each(function () {
var dialog = $(this);
var nfDialogData = {};
if (isDefinedAndNotNull(dialog.data('nf-dialog'))) {
nfDialogData = dialog.data('nf-dialog');
}
if (isDefinedAndNotNull(nfDialogData.handler)) {
var handler = nfDialogData.handler.close;
if (isDefinedAndNotNull(handler) && typeof handler === 'function') {
// invoke the handler
handler.call(dialog);
}
}
// remove the modal glass pane overlay
$('body').find("[data-nf-dialog-parent='" + dialog.attr('id') + "']").remove();
if (dialog.is(':visible')) {
// hide the dialog
dialog.hide();
}
});
}
};
$.fn.modal = function (method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else {
return methods.init.apply(this, arguments);
}
};
})(jQuery);