Plato on Github
Report Home
src/JSEngine/libs/modal.js
Maintainability
67.57
Lines of code
563
Difficulty
40.50
Estimated Errors
3.48
Function weight
By Complexity
By SLOC
/* -------------------------------------------------------------- modal.js 2016-02-23 Gambio GmbH http://www.gambio.de Copyright (c) 2016 Gambio GmbH Released under the GNU General Public License (Version 2) [http://www.gnu.org/licenses/gpl-2.0.html] -------------------------------------------------------------- */ /* globals Mustache */ jse.libs.modal = jse.libs.modal || {}; /** * ## Modal Dialogs Library * * Library that handles modal dialogs within the app. This function depends on jQuery & jQuery UI * dialog widget. You are able to select the HTML template to be displayed as a modal and set other * parameters to readjust the modal behavior. If no template option is provided the library will search * for a default "#modal_alert" element. Place the markup from the following example into your page * to quickly display messages to the user. * * ### Quick Usage Example (No Configuration) * * ```javascript * Modal.message({ * title: 'My Title', // Required * content: 'My Content' // Required * buttons: { ... } // Optional * // Other jQueryUI Dialog Widget Options * }); * ``` * * ### Example With Default HTML * * Insert the following HTML in your page: * * ```html * <div id="modal_alert"> * <div> * {{#content}} * <div class="icon"> </div> * <p>{{{.}}}</p> * {{/content}} * </div> * </div> *``` * * If you don't specify a template the library will use the "#modal_alert" element. * * ```javascript * Modal.alert({ * title: 'My Modal Title', * content: 'My modal content.', * position: { my: 'center', at: 'center', of: $('#parent-element-id') } * }); * ``` * * @todo Refactor the file and remove the methods that are not needed (like alert). * * @module JSE/Libs/modal * @exports jse.libs.modal */ (function (exports) { 'use strict'; var /** * Body Element Selector * * @type {object} */ $body = $('body'), /** * Contains Default Modal Buttons * * @type {object} */ buttons = { 'yes': { 'name': jse.core.lang.translate('yes', 'buttons'), 'type': 'success' }, 'no': { 'name': jse.core.lang.translate('no', 'buttons'), 'type': 'fail' }, 'abort': { 'name': jse.core.lang.translate('abort', 'buttons'), 'type': 'fail' }, 'ok': { 'name': jse.core.lang.translate('ok', 'buttons'), 'type': 'success' }, 'close': { 'name': jse.core.lang.translate('close', 'buttons'), 'type': 'fail' } }; /** * Get Form Data * * Returns all form data, which is stored inside the layer. * * @param {object} $self jQuery selector of the layer. * @param {bool} validateForm Flag that determines whether the form must be validated * before we get the data. * * @return {json} Returns a JSON with all form data. * * @private */ var _getFormData = function ($self, validateForm) { var $forms = $self .filter('form') .add($self.find('form')), formData = {}, promises = []; if ($forms.length) { $forms.each(function () { var $form = $(this); if (validateForm) { var localDeferred = $.Deferred(); promises.push(localDeferred); $form.trigger('validator.validate', { 'deferred': localDeferred }); } var key = $form.attr('name') || $form.attr('id') || ('form_' + new Date().getTime() * Math.random()); formData[key] = window.jse.lib.form.getData($form); }); } return $.when .apply(undefined, promises) .then(function () { return formData; }, function () { return formData; }) .promise(); }; /** * Reject Handler * * @param {object} $element Selector element. * @param {object} deferred Deferred object. * * @private */ var _rejectHandler = function ($element, deferred) { _getFormData($element).always(function (result) { deferred.reject(result); $element .dialog('close') .remove(); }); }; /** * Resolve Handler * * @param {object} $element Selector element. * @param {object} deferred Deferred object. * * @private */ var _resolveHandler = function ($element, deferred) { _getFormData($element, true).done(function (result) { deferred.resolve(result); $element .dialog('close') .remove(); }); }; /** * Generate Buttons * * Transforms the custom buttons object (which is incompatible with jQuery UI) * to a jQuery UI compatible format and returns it. * * @param {object} dataset Custom buttons object for the dialog. * @param {object} deferred Deferred-object to resolve/reject on close. * * @return {array} Returns a jQuery UI dialog compatible buttons array. * * @private */ var _generateButtons = function (dataset, deferred) { var newButtons = [], tmpButton = null; // Check if buttons are available. if (dataset) { $.each(dataset, function (k, v) { // Setup a new button. tmpButton = {}; tmpButton.text = v.name || 'BUTTON'; // Setup click handler. tmpButton.click = function () { var $self = $(this); // If a callback is given, execute it with the current scope. if (typeof v.callback === 'function') { v.callback.apply($self, []); } // Add the default behaviour for the close functionality. On fail, // reject the deferred object, else resolve it. switch (v.type) { case 'fail': _rejectHandler($self, deferred); break; case 'success': _resolveHandler($self, deferred); break; default: break; } }; // Add to the new buttons array. newButtons.push(tmpButton); }); } return newButtons; }; /** * Get Template * * This method will return a promise object that can be used to execute code, * once the template HTML of the modal is found. * * @param {object} options Options to be applied to the template. * * @return {object} Returns a deferred object. * * @private */ var _getTemplate = function (options) { var $selection = [], deferred = $.Deferred(); try { $selection = $(options.template); } catch (exception) { jse.core.debug(jse.core.lang.templateNotFound(options.template)); } if ($selection.length) { deferred.resolve($selection.html()); } else { window.jse.lib.ajax({ 'url': options.template, 'dataType': 'html' }).done(function (result) { if (options.storeTemplate) { var $append = $('<div />') .attr('id', options.template) .html(result); $body.append($append); } deferred.resolve(result); }).fail(function () { deferred.reject(); }); } return deferred; }; /** * Create Modal Layer * * @param {object} options Extra modal options to be applied to the * @param {string} title Modal title * @param {string} className Class name to be added to the modal element. * @param {object} defaultButtons Modal buttons for the layer. * @param {string} template Template name to be used for the modal. * * @return {object} Returns a modal promise object. * * @private */ var _createLayer = function (options, title, className, defaultButtons, template) { // Setup defaults & deferred objects. var deferred = $.Deferred(), promise = deferred.promise(), $template = '', defaults = { 'title': title || '', 'dialogClass': className || '', 'modal': true, 'resizable': false, 'buttons': defaultButtons || [buttons.close], 'draggable': false, 'closeOnEscape': false, 'autoOpen': false, 'template': template || '#modal_alert', 'storeTemplate': false, 'closeX': true, 'modalClose': false }, instance = null, $forms = null; // Merge custom settings with default settings options = options || {}; options = $.extend({}, defaults, options); options.buttons = _generateButtons(options.buttons, deferred); _getTemplate(options).done(function (html) { // Generate template $template = $(Mustache.render(html, options)); if (options.validator) { $template .find('form') .attr('data-gx-widget', 'validator') .find('input') .attr({ 'data-validator-validate': options.validator.validate, 'data-validator-regex': options.validator.regex || '' }) .addClass('validate'); } // Setup dialog $template.dialog(options); try { instance = $template.dialog('instance'); } catch (exception) { instance = $template.data('ui-dialog'); } // Add bootstrap button classes to buttonSet. instance .uiButtonSet .children() .addClass('btn btn-default'); // If the closeX-option is set to false, remove the button from the layout // else bind an event listener to reject the deferred object. if (options.closeX === false) { instance .uiDialogTitlebarClose .remove(); } else { instance .uiDialogTitlebarClose .html('×') .one('click', function () { _rejectHandler(instance.element, deferred); }); } // Add an event listener to the modal overlay if the option is set. if (options.modalClose) { $('body') .find('.ui-widget-overlay') .last() .one('click', function () { _rejectHandler(instance.element, deferred); }); } // Prevent submit on enter in inner forms $forms = instance.element.find('form'); if ($forms.length) { $forms.on('submit', function (event) { event.preventDefault(); }); } if (options.executeCode && typeof options.executeCode === 'function') { options.executeCode.call($(instance.element)); } // Add a close layer method to the promise. // @todo Test that ... promise.close = function (fail) { if (fail) { _rejectHandler(instance.element, deferred); } else { _resolveHandler(instance.element, deferred); } }; $template.dialog('open'); if (window.gx && window.jse.widgets && window.jse.widgets.init) { window.jse.widgets.init($template); window.jse.controllers.init($template); window.jse.extensions.init($template); } }).fail(function () { deferred.reject({ 'error': 'Template not found' }); }); return promise; }; /** * Generates the default alert layer. * * @param {object} options Mix of jQuery UI dialog options and custom options * @param {string} title Default title for the type of alert layer * @param {string} className Default class for the type of alert layer * @param {array} defbuttons Array wih the default buttons for the array type * @param {string} template Selector for the jQuery-object used as template * * @return {object} Returns a promise object. */ var _alert = function (options) { var data = $.extend({}, { 'draggable': true }, options); return _createLayer(data, jse.core.lang.translate('hint', 'labels'), '', [buttons.ok]); }; /** * Returns a confirm layer. * * @param {object} options Mix of jQuery UI dialog options and custom options. * * @return {promise} Returns a promise */ var _confirm = function (options) { var data = $.extend({}, { 'draggable': true }, options); return _createLayer(data, jse.core.lang.translate('confirm', 'labels'), 'confirm_dialog', [buttons.no, buttons.yes]); }; /** * Returns a prompt layer. * * @param {object} options Mix of jQuery UI dialog options and custom options. * * @return {promise} Returns a promise object. */ var _prompt = function (options) { var data = $.extend({}, { 'draggable': true }, options); return _createLayer(data, jse.core.lang.translate('prompt', 'labels'), 'prompt_dialog', [buttons.abort, buttons.ok], '#modal_prompt'); }; /** * Returns a success layer. * * @param {object} options Mix of jQuery UI dialog options and custom options. * * @return {object} Returns a promise object. */ var _success = function (options) { var data = $.extend({}, { 'draggable': true }, options); return _createLayer(data, jse.core.lang.translate('success', 'labels'), 'success_dialog'); }; /** * Returns an error layer. * * @param {object} options Mix of jQuery UI dialog options and custom options. * * @return {object} Returns a promise object. */ var _error = function (options) { var data = $.extend({}, { 'draggable': true }, options); return _createLayer(data, jse.core.lang.translate('error', 'labels'), 'error_dialog'); }; /** * Returns a warning layer. * * @name Core/Modal.warn * @public * @method * * @param {object} options Mix of jQuery UI dialog options and custom options. * * @return {object} Returns a promise object. */ var _warn = function (options) { var data = $.extend({}, { 'draggable': true }, options); return _createLayer(data, jse.core.lang.translate('warning', 'labels'), 'warn_dialog'); }; /** * Returns an info layer. * * @param {object} options Mix of jQuery UI dialog options and custom options. * * @return {promise} Returns a promise object. */ var _info = function (options) { var data = $.extend({}, { 'draggable': true }, options); return _createLayer(data, jse.core.lang.translate('info', 'labels'), 'info_dialog'); }; /** * ## Quickly display a message without setting up view files or JavaScript code. * * This method provides an easy way to display a message to the user without * having to worry about templates and other dependencies of the library. * * @param {object} options Modal options are the same as the jQuery dialog widget. */ var _message = function (options) { // Create div element for modal dialog. $('body').append('<div class="modal-layer">' + options.content + '</div>'); // Append options object with extra dialog options. options.modal = true; options.dialogClass = 'gx-container'; // Set default buttons, if option wasn't provided. if (options.buttons === undefined) { options.buttons = [ { text: buttons.close.name, click: function () { $(this).dialog('close'); $(this).remove(); } } ]; } // Display message to the user. $('.modal-layer:last').dialog(options); }; // ------------------------------------------------------------------------ // VARIABLE EXPORT // ------------------------------------------------------------------------ exports.error = _error; exports.warn = _warn; exports.info = _info; exports.success = _success; exports.alert = _alert; exports.prompt = _prompt; exports.confirm = _confirm; exports.custom = _createLayer; exports.message = _message; }(jse.libs.modal));