0d45dd2b97b5abb7de493076993ebd777df67ca6
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Resources / Public / JavaScript / Modal.js
1 /*
2 * This file is part of the TYPO3 CMS project.
3 *
4 * It is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License, either version 2
6 * of the License, or any later version.
7 *
8 * For the full copyright and license information, please read the
9 * LICENSE.txt file that was distributed with this source code.
10 *
11 * The TYPO3 project - inspiring people to share!
12 */
13
14 /**
15 * API for modal windows powered by Twitter Bootstrap.
16 * This module depends on TYPO3/CMS/Backend/Notification due to top.TYPO3.Severity.
17 */
18 define('TYPO3/CMS/Backend/Modal', ['jquery', 'TYPO3/CMS/Backend/Notification', 'bootstrap'], function($) {
19
20 /**
21 * The main object of the modal API
22 *
23 * @type {{instances: Array, currentModal: null, template: (*|jQuery|HTMLElement)}}
24 */
25 var Modal = {
26 instances: [],
27 currentModal: null,
28 template: $(
29 '<div class="t3-modal modal fade">' +
30 '<div class="modal-dialog">' +
31 '<div class="modal-content">' +
32 '<div class="modal-header">' +
33 '<button class="close">' +
34 '<span aria-hidden="true">&times;</span>' +
35 '<span class="sr-only"></span>' +
36 '</button>' +
37 '<h4 class="modal-title"></h4>' +
38 '</div>' +
39 '<div class="modal-body"></div>' +
40 '<div class="modal-footer"></div>' +
41 '</div>' +
42 '</div>' +
43 '</div>'
44 )
45 };
46
47 /**
48 * Get the correct css class for given severity
49 *
50 * @param {int} severity use constants from top.TYPO3.Severity.*
51 * @returns {string}
52 * @private
53 */
54 Modal.getSeverityClass = function(severity) {
55 var severityClass;
56 switch (severity) {
57 case top.TYPO3.Severity.notice:
58 severityClass = 'notice';
59 break;
60 case top.TYPO3.Severity.ok:
61 severityClass = 'success';
62 break;
63 case top.TYPO3.Severity.warning:
64 severityClass = 'warning';
65 break;
66 case top.TYPO3.Severity.error:
67 severityClass = 'danger';
68 break;
69 case top.TYPO3.Severity.info:
70 default:
71 severityClass = 'info';
72 break;
73 }
74 return severityClass;
75 };
76
77 /**
78 * Shows a confirmation dialog
79 * Events:
80 * - button.clicked
81 * - confirm.button.cancel
82 * - confirm.button.ok
83 *
84 * @param {string} title the title for the confirm modal
85 * @param {string} content the content for the conform modal, e.g. the main question
86 * @param {int} severity default top.TYPO3.Severity.warning
87 * @param {array} buttons an array with buttons, default no buttons
88 */
89 Modal.confirm = function(title, content, severity, buttons) {
90 severity = (typeof severity !== 'undefined' ? severity : top.TYPO3.Severity.warning);
91 buttons = buttons || [
92 {
93 text: $(this).data('button-close-text') || TYPO3.lang['button.cancel'] || 'Cancel',
94 active: true,
95 name: 'cancel'
96 },
97 {
98 text: $(this).data('button-ok-text') || TYPO3.lang['button.ok'] || 'OK',
99 btnClass: 'btn-' + Modal.getSeverityClass(severity),
100 name: 'ok'
101 }
102 ];
103 $modal = Modal.show(title, content, severity, buttons);
104 $modal.on('button.clicked', function(e) {
105 if (e.target.name === 'cancel') {
106 $(this).trigger('confirm.button.cancel');
107 } else if (e.target.name === 'ok') {
108 $(this).trigger('confirm.button.ok');
109 }
110 });
111 return $modal;
112 };
113
114 /**
115 * load URL with AJAX, append the content to the modal-body
116 * and trigger the callback
117 *
118 * @param {string} title
119 * @param {int} severity
120 * @param {array} buttons
121 * @param {string} url
122 * @param {string} target
123 * @param {function} callback
124 */
125 Modal.loadUrl = function(title, severity, buttons, url, callback, target) {
126 $.get(url, function(response) {
127 Modal.currentModal.find(target ? target : '.modal-body').empty().append(response);
128 if (callback) {
129 callback();
130 }
131 Modal.currentModal.trigger('modal-loaded');
132 }, 'html');
133 return Modal.show(title, '<p class="loadmessage"><i class="fa fa-spinner fa-spin fa-5x "></i></p>', severity, buttons);
134 };
135
136
137 /**
138 * Shows a dialog
139 * Events:
140 * - button.clicked
141 *
142 * @param {string} title the title for the confirm modal
143 * @param {string} content the content for the conform modal, e.g. the main question
144 * @param {int} severity default top.TYPO3.Severity.info
145 * @param {array} buttons an array with buttons, default no buttons
146 */
147 Modal.show = function(title, content, severity, buttons) {
148 severity = (typeof severity !== 'undefined' ? severity : top.TYPO3.Severity.info);
149 buttons = buttons || [];
150 Modal.currentModal = Modal.template.clone();
151 Modal.currentModal.attr('tabindex', '-1');
152 Modal.currentModal.find('.modal-title').text(title);
153 Modal.currentModal.find('.modal-header .close').on('click', function() {
154 Modal.currentModal.trigger('modal-dismiss');
155 });
156
157 if (typeof content === 'object') {
158 Modal.currentModal.find('.modal-body').append(content);
159 } else {
160 // we need html, check if we have to wrap content in <p>
161 if (!/^<[a-z][\s\S]*>/i.test(content)) {
162 content = $('<p />').text(content);
163 }
164
165 Modal.currentModal.find('.modal-body').html(content);
166 }
167
168 Modal.currentModal.addClass('t3-modal-' + Modal.getSeverityClass(severity));
169 if (buttons.length > 0) {
170 for (var i=0; i<buttons.length; i++) {
171 var button = buttons[i];
172 var $button = $('<button />', {class: 'btn'});
173 $button.html(button.text);
174 if (button.active) {
175 $button.addClass('t3js-active');
176 }
177 if (button.btnClass) {
178 $button.addClass(button.btnClass);
179 }
180 if (button.name) {
181 $button.attr('name', button.name);
182 }
183 if (button.trigger) {
184 $button.on('click', button.trigger);
185 }
186 Modal.currentModal.find('.modal-footer').append($button);
187 }
188 Modal.currentModal
189 .find('.modal-footer button')
190 .on('click', function() {
191 $(this).trigger('button.clicked');
192 });
193
194 } else {
195 Modal.currentModal.find('.modal-footer').remove();
196 }
197 Modal.currentModal.on('shown.bs.modal', function(e) {
198 // focus the button which was configured as active button
199 $(this).find('.modal-footer .t3js-active').first().focus();
200 });
201 Modal.currentModal.on('hidden.bs.modal', function(e) {
202 if (Modal.instances.length > 0) {
203 var lastIndex = Modal.instances.length-1;
204 Modal.instances.splice(lastIndex, 1);
205 Modal.currentModal = Modal.instances[lastIndex-1];
206 }
207 $(this).remove();
208 // Keep class modal-open on body tag as long as open modals exist
209 if (Modal.instances.length > 0) {
210 top.TYPO3.jQuery('body').addClass('modal-open');
211 }
212 });
213 Modal.currentModal.on('show.bs.modal', function(e) {
214 Modal.instances.push(Modal.currentModal);
215 Modal.center();
216 });
217 Modal.currentModal.on('modal-dismiss', Modal.dismiss);
218 top.TYPO3.jQuery('body').append(Modal.currentModal);
219 Modal.currentModal.modal();
220
221 return Modal.currentModal;
222 };
223
224 /**
225 * Close the current open modal
226 */
227 Modal.dismiss = function() {
228 if (Modal.currentModal) {
229 Modal.currentModal.modal('hide');
230 Modal.currentModal = null;
231 }
232 };
233
234 /**
235 * Center the modal windows
236 */
237 Modal.center = function() {
238 $(window).off('resize', Modal.center);
239 if(Modal.instances.length > 0){
240 $(window).on('resize', Modal.center);
241 $(Modal.instances).each(function() {
242 var $me = $(this),
243 $clone = $me.clone().css('display', 'block').appendTo('body'),
244 top = Math.max(0, Math.round(($clone.height() - $clone.find('.modal-content').height()) / 2));
245
246 $clone.remove();
247 $me.find('.modal-content').css('margin-top', top);
248 });
249 }
250 };
251
252 /**
253 * Initialize markup with data attributes
254 */
255 Modal.initializeMarkupTrigger = function() {
256 $(document).on('click', '.t3js-modal-trigger', function(evt) {
257 evt.preventDefault();
258 var $element = $(this);
259 var url = $element.data('url') || null;
260 var title = $element.data('title') || 'Alert';
261 var content = $element.data('content') || 'Are you sure?';
262 var severity = (typeof top.TYPO3.Severity[$element.data('severity')] !== 'undefined') ? top.TYPO3.Severity[$element.data('severity')] : top.TYPO3.Severity.info;
263 var buttons = [
264 {
265 text: $element.data('button-close-text') || 'Close',
266 active: true,
267 btnClass: 'btn-default',
268 trigger: function() {
269 if (typeof top.TYPO3 !== 'undefined' && typeof top.TYPO3.Modal !== 'undefined') {
270 top.TYPO3.Modal.currentModal.trigger('modal-dismiss');
271 } else {
272 Modal.trigger('modal-dismiss');
273 }
274 }
275 },
276 {
277 text: $element.data('button-ok-text') || 'OK',
278 btnClass: 'btn-' + Modal.getSeverityClass(severity),
279 trigger: function() {
280 if (typeof top.TYPO3 !== 'undefined' && typeof top.TYPO3.Modal !== 'undefined') {
281 top.TYPO3.Modal.currentModal.trigger('modal-dismiss');
282 } else {
283 Modal.trigger('modal-dismiss');
284 }
285 self.location.href = $element.data('href') || $element.attr('href');
286 }
287 }
288 ];
289 if (url !== null) {
290 var separator = (url.indexOf('?') > -1) ? '&' : '?';
291 var params = $.param({data: $element.data()});
292 if (typeof top.TYPO3 !== 'undefined' && typeof top.TYPO3.Modal !== 'undefined') {
293 top.TYPO3.Modal.loadUrl(title, severity, buttons, url + separator + params);
294 } else {
295 Modal.loadUrl(title, severity, buttons, url + separator + params);
296 }
297 } else {
298 if (typeof top.TYPO3 !== 'undefined' && typeof top.TYPO3.Modal !== 'undefined') {
299 top.TYPO3.Modal.show(title, content, severity, buttons);
300 } else {
301 Modal.show(title, content, severity, buttons);
302 }
303 }
304 });
305 };
306
307 /**
308 * Custom event, fired if modal gets closed
309 */
310 $(document).on('modal-dismiss', Modal.dismiss);
311
312 /**
313 * Return the Modal object
314 */
315 return function() {
316 Modal.initializeMarkupTrigger();
317 TYPO3.Modal = Modal;
318 return Modal;
319 }();
320 });