cf99838682312229757b22704e52b83f066ca541
[Packages/TYPO3.CMS.git] / typo3 / sysext / lang / Resources / Public / JavaScript / LanguageModule.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 * Language module class
16 *
17 * @author Kai Vogel <k.vogel@reply.de>
18 */
19 define('TYPO3/CMS/Lang/LanguageModule', ['jquery', 'moment', 'datatables', 'TYPO3/CMS/Backend/jquery.clearable'], function($, moment) {
20 var LanguageModule = {
21 me: this,
22 context: null,
23 table: null,
24 topMenu: null,
25 currentRequest: null,
26 settings: {},
27 icons: {},
28 labels: {},
29 identifiers: {
30 searchField: 'div.dataTables_filter input',
31 topMenu: 'div.menuItems',
32 activateIcon: 'span.activateIcon',
33 deactivateIcon: 'span.deactivateIcon',
34 downloadIcon: 'span.downloadIcon',
35 loadingIcon: 'span.loadingIcon',
36 completeIcon: 'span.completeIcon',
37 progressBar: 'div.progressBar',
38 progressBarText: 'div.progress-text',
39 progressBarInner: 'div.progress-bar',
40 lastUpdate: 'td.lastUpdate',
41 languagePrefix: 'language-',
42 extensionPrefix: 'extension-'
43 },
44 classes: {
45 enabled: 'enabled',
46 disabled: 'disabled',
47 processing: 'processing',
48 complete: 'complete',
49 extension: 'extensionName',
50 actions: 'actions',
51 progressBar: 'progressBar',
52 loading: 'loading',
53 lastUpdate: 'lastUpdate'
54 }
55 };
56
57 /**
58 * Initialize language table
59 */
60 LanguageModule.initializeLanguageTable = function(contextElement, tableElement) {
61 LanguageModule.context = $(contextElement);
62 LanguageModule.topMenu = $(LanguageModule.identifiers.topMenu);
63 LanguageModule.settings = LanguageModule.context.data();
64 LanguageModule.icons = LanguageModule.buildIcons();
65 LanguageModule.labels = LanguageModule.buildLabels();
66 LanguageModule.table = LanguageModule.buildLanguageTable(tableElement);
67 LanguageModule.initializeSearchField();
68 LanguageModule.initializeEventHandler();
69 }
70
71 /**
72 * Initialize translation table
73 */
74 LanguageModule.initializeTranslationTable = function(contextElement, tableElement) {
75 LanguageModule.context = $(contextElement);
76 LanguageModule.topMenu = $(LanguageModule.identifiers.topMenu);
77 LanguageModule.settings = LanguageModule.context.data();
78 LanguageModule.icons = LanguageModule.buildIcons();
79 LanguageModule.labels = LanguageModule.buildLabels();
80 LanguageModule.table = LanguageModule.buildTranslationTable(tableElement);
81 LanguageModule.initializeSearchField();
82 LanguageModule.initializeEventHandler();
83 };
84
85 /**
86 * Activate a language
87 */
88 LanguageModule.activateLanguageAction = function(triggerElement, parameters) {
89 var $row = $(triggerElement).closest('tr'),
90 locale = $row.data('locale');
91
92 if ($row.hasClass(LanguageModule.classes.processing)) {
93 LanguageModule.abortAjaxRequest();
94 }
95 LanguageModule.executeAjaxRequest(LanguageModule.settings.activateLanguageUri, {locale: locale}, function(response, status) {
96 if (status === 'success' && response.success) {
97 $row.removeClass(LanguageModule.classes.disabled).addClass(LanguageModule.classes.enabled);
98 LanguageModule.displaySuccess(LanguageModule.labels.languageActivated);
99 } else {
100 LanguageModule.displayError(LanguageModule.labels.errorOccurred);
101 }
102 });
103 };
104
105 /**
106 * Deactivate a language
107 */
108 LanguageModule.deactivateLanguageAction = function(triggerElement, parameters) {
109 var $row = $(triggerElement).closest('tr'),
110 locale = $row.data('locale');
111
112 if ($row.hasClass(LanguageModule.classes.processing)) {
113 LanguageModule.abortAjaxRequest();
114 }
115 LanguageModule.executeAjaxRequest(LanguageModule.settings.deactivateLanguageUri, {locale: locale}, function(response, status) {
116 if (status === 'success' && response.success) {
117 $row.removeClass(LanguageModule.classes.enabled).removeClass(LanguageModule.classes.complete).addClass(LanguageModule.classes.disabled);
118 LanguageModule.displaySuccess(LanguageModule.labels.languageDeactivated);
119 } else {
120 LanguageModule.displayError(LanguageModule.labels.errorOccurred);
121 }
122 });
123 };
124
125 /**
126 * Update a language
127 */
128 LanguageModule.updateLanguageAction = function(triggerElement, parameters) {
129 var $row = $(triggerElement).closest('tr'),
130 locale = $row.data('locale'),
131 $progressBar = $(LanguageModule.identifiers.progressBar, $row),
132 $lastUpdate = $(LanguageModule.identifiers.lastUpdate, $row);
133
134 $row.addClass(LanguageModule.classes.processing);
135 LanguageModule.loadTranslationsByLocale(locale, function(status, data, response) {
136 if (status === 'success') {
137 LanguageModule.setProgress($progressBar, 100);
138 LanguageModule.displaySuccess(LanguageModule.labels.updateComplete);
139 $row.removeClass(LanguageModule.classes.processing).addClass(LanguageModule.classes.complete);
140 $lastUpdate.html(LanguageModule.formatDate(response.timestamp));
141 } else if (status === 'progress') {
142 LanguageModule.setProgress($progressBar, parseFloat(response.progress));
143 } else if (status === 'error') {
144 LanguageModule.displayError(LanguageModule.labels.errorOccurred);
145 }
146 });
147 };
148
149 /**
150 * Update all active languages
151 */
152 LanguageModule.updateActiveLanguagesAction = function(triggerElement, parameters) {
153 var $activeRows = $('tr.' + LanguageModule.classes.enabled, LanguageModule.table.table().container());
154
155 LanguageModule.topMenu.addClass(LanguageModule.classes.processing);
156 $activeRows.addClass(LanguageModule.classes.processing);
157 LanguageModule.loadTranslationsByRows($activeRows, function(row, status, data, response) {
158 var $progressBar = $(LanguageModule.identifiers.progressBar, row),
159 $lastUpdate = $(LanguageModule.identifiers.lastUpdate, row);
160
161 if (status === 'success') {
162 LanguageModule.setProgress($progressBar, 100);
163 row.removeClass(LanguageModule.classes.processing).addClass(LanguageModule.classes.complete);
164 $lastUpdate.html(LanguageModule.formatDate(response.timestamp));
165 } else if (status === 'progress') {
166 LanguageModule.setProgress($progressBar, parseFloat(response.progress));
167 } else if (status === 'error') {
168 LanguageModule.displayError(LanguageModule.labels.errorOccurred);
169 } else if (status === 'finished') {
170 LanguageModule.displaySuccess(LanguageModule.labels.updateComplete);
171 LanguageModule.topMenu.removeClass(LanguageModule.classes.processing);
172 }
173 });
174 };
175
176 /**
177 * Cancel language update
178 */
179 LanguageModule.cancelLanguageUpdateAction = function(triggerElement, parameters) {
180 var $activeRows = $('tr.' + LanguageModule.classes.enabled, LanguageModule.table.table().container());
181
182 LanguageModule.topMenu.removeClass(LanguageModule.classes.processing);
183 $activeRows.removeClass(LanguageModule.classes.processing);
184 LanguageModule.abortAjaxRequest();
185 };
186
187 /**
188 * Update an extension translation
189 */
190 LanguageModule.updateTranslationAction = function(triggerElement, parameters) {
191 var $row = $(triggerElement).closest('tr'),
192 $cell = $(triggerElement).closest('td'),
193 extension = $row.data('extension'),
194 locale = LanguageModule.table.cell($cell).data().locale;
195
196 $cell.addClass(LanguageModule.classes.processing);
197 LanguageModule.loadTranslationByExtensionAndLocale(extension, locale, function(status, data, response) {
198 if (status === 'success') {
199 LanguageModule.displaySuccess(LanguageModule.labels.updateComplete);
200 $cell.removeClass(LanguageModule.classes.processing).addClass(LanguageModule.classes.complete);
201 } else if (status === 'error') {
202 LanguageModule.displayError(LanguageModule.labels.errorOccurred);
203 }
204 });
205 };
206
207 /**
208 * Build icons
209 */
210 LanguageModule.buildIcons = function() {
211 return {
212 activate: $(LanguageModule.identifiers.activateIcon, LanguageModule.context).html(),
213 deactivate: $(LanguageModule.identifiers.deactivateIcon, LanguageModule.context).html(),
214 download: $(LanguageModule.identifiers.downloadIcon, LanguageModule.context).html(),
215 loading: $(LanguageModule.identifiers.loadingIcon, LanguageModule.context).html(),
216 complete: $(LanguageModule.identifiers.completeIcon, LanguageModule.context).html(),
217 progressBar: $(LanguageModule.identifiers.progressBar, LanguageModule.context).html()
218 }
219 };
220
221 /**
222 * Build labels
223 */
224 LanguageModule.buildLabels = function() {
225 return {
226 processing: TYPO3.lang['table.processing'],
227 search: TYPO3.lang['table.search'],
228 loadingRecords: TYPO3.lang['table.loadingRecords'],
229 zeroRecords: TYPO3.lang['table.zeroRecords'],
230 emptyTable: TYPO3.lang['table.emptyTable'],
231 dateFormat: TYPO3.lang['table.dateFormat'],
232 errorHeader: TYPO3.lang['flashmessage.error'],
233 infoHeader: TYPO3.lang['flashmessage.information'],
234 successHeader: TYPO3.lang['flashmessage.success'],
235 languageActivated: TYPO3.lang['flashmessage.languageActivated'],
236 errorOccurred: TYPO3.lang['flashmessage.errorOccurred'],
237 languageDeactivated: TYPO3.lang['flashmessage.languageDeactivated'],
238 updateComplete: TYPO3.lang['flashmessage.updateComplete']
239 }
240 };
241
242 /**
243 * Build language table
244 */
245 LanguageModule.buildLanguageTable = function(tableElement) {
246 return $(tableElement).DataTable({
247 serverSide: false,
248 stateSave: true,
249 paging: false,
250 info: false,
251 ordering: true,
252 language: LanguageModule.labels,
253 order: [[1, 'asc']]
254 });
255 };
256
257 /**
258 * Initialize translation table
259 */
260 LanguageModule.buildTranslationTable = function(tableElement) {
261 var languageCount = $(tableElement).data('languageCount'),
262 columns = [
263 {
264 render: function(data, type, row) {
265 return LanguageModule.buildImage(data.icon, data.title, data.title, data.width, data.height);
266 },
267 width: '20px',
268 orderable: false,
269 targets: 0
270 }, {
271 render: function(data, type, row) {
272 return data.title;
273 },
274 className: LanguageModule.classes.extension,
275 targets: 1
276 }
277 ];
278
279 for (var i = 0; i < languageCount; i++) {
280 columns.push({
281 render: function(data, type, row) {
282 var links = [
283 LanguageModule.buildActionLink('updateTranslation', data, LanguageModule.icons.download),
284 LanguageModule.buildLoadingIndicator(),
285 LanguageModule.buildCompleteIndicator()
286 ];
287 return links.join('');
288 },
289 className: 'dt-center',
290 targets: (i + 2)
291 });
292 }
293
294 return $(tableElement).DataTable({
295 serverSide: false,
296 stateSave: true,
297 paging: false,
298 info: false,
299 ordering: true,
300 language: LanguageModule.labels,
301 ajax: LanguageModule.settings.listTranslationsUri,
302 order: [[1, 'asc']],
303 columnDefs: columns,
304 createdRow: function (row, data, index) {
305 var $row = $(row);
306 $row.attr('id', LanguageModule.identifiers.extensionPrefix + data[1].key);
307 $row.attr('data-extension', data[1].key);
308 }
309 });
310 };
311
312 /**
313 * Initialize search field
314 */
315 LanguageModule.initializeSearchField = function() {
316 $(LanguageModule.identifiers.searchField, LanguageModule.context).clearable({
317 onClear: function() {
318 if (LanguageModule.table !== null) {
319 LanguageModule.table.search('').draw();
320 }
321 }
322 });
323 };
324
325 /**
326 * Initialize event handler, redirect clicks to controller actions
327 */
328 LanguageModule.initializeEventHandler = function() {
329 $(document).on('click', function(event) {
330 var $element = $(event.target);
331
332 if ($element.data('action') !== undefined) {
333 LanguageModule.handleActionEvent($element, event);
334 } else if ($element.parent().data('action') !== undefined) {
335 LanguageModule.handleActionEvent($element.parent(), event);
336 } else if ($element.parent().parent().data('action') !== undefined) {
337 LanguageModule.handleActionEvent($element.parent().parent(), event);
338 }
339 });
340 };
341
342 /**
343 * Handler for "action" events
344 */
345 LanguageModule.handleActionEvent = function(element, event) {
346 event.preventDefault();
347 var data = element.data();
348 var actionName = data.action + 'Action';
349 if (actionName in LanguageModule) {
350 LanguageModule[actionName](element, data);
351 }
352 };
353
354 /**
355 * Load translations for all extensions by given locale
356 */
357 LanguageModule.loadTranslationsByLocale = function(locale, callback, counter) {
358 counter = counter || 0;
359 var data = {locale: locale, count: counter};
360 LanguageModule.executeAjaxRequest(LanguageModule.settings.updateLanguageUri, data, function(response, status) {
361 if (status === 'success' && response.success) {
362 if (parseFloat(response.progress) < 100) {
363 callback('progress', data, response);
364 counter++;
365 LanguageModule.loadTranslationsByLocale(locale, callback, counter);
366 } else {
367 callback('success', data, response);
368 }
369 } else {
370 callback('error', data, response);
371 }
372 });
373 };
374
375 /**
376 * Load translations for all extensions by given rows
377 */
378 LanguageModule.loadTranslationsByRows = function(rows, callback) {
379 if (rows) {
380 rows = $(rows).toArray();
381 var $row = $(rows.shift()),
382 locale = $row.data('locale');
383
384 LanguageModule.loadTranslationsByLocale(locale, function(status, data, response) {
385 callback($row, status, data, response);
386 if (status === 'success') {
387 if (rows.length) {
388 LanguageModule.loadTranslationsByRows(rows, callback);
389 } else {
390 callback($row, 'finished', data, response);
391 }
392 }
393 });
394 }
395 };
396
397 /**
398 * Load translation for one extension by given locale
399 */
400 LanguageModule.loadTranslationByExtensionAndLocale = function(extension, locale, callback) {
401 var data = {extension: extension, locale: locale};
402 LanguageModule.executeAjaxRequest(LanguageModule.settings.updateTranslationUri, data, function(response, status) {
403 if (status === 'success' && response.success) {
404 callback('success', data, response);
405 } else {
406 callback('error', data, response);
407 }
408 });
409 };
410
411 /**
412 * Execute AJAX request
413 */
414 LanguageModule.executeAjaxRequest = function(uri, data, callback) {
415 var newData = {};
416 newData[LanguageModule.settings.prefix] = {
417 data: data
418 };
419 LanguageModule.currentRequest = $.ajax({
420 type: 'POST',
421 cache: false,
422 url: uri,
423 data: newData,
424 dataType: 'json',
425 success: function(response, status) {
426 if (typeof callback === 'function') {
427 callback(response, status, '');
428 }
429 },
430 error: function(response, status, error) {
431 if (typeof callback === 'function') {
432 callback(response, status, error);
433 }
434 }
435 });
436 };
437
438 /**
439 * Abort current AJAX request
440 */
441 LanguageModule.abortAjaxRequest = function() {
442 if (LanguageModule.currentRequest) {
443 LanguageModule.currentRequest.abort();
444 }
445 };
446
447 /**
448 * Display error flash message
449 */
450 LanguageModule.displayError = function(label) {
451 if (typeof label === 'string' && label !== '') {
452 top.TYPO3.Notification.error(LanguageModule.labels.errorHeader, label);
453 }
454 };
455
456 /**
457 * Display information flash message
458 */
459 LanguageModule.displayInformation = function(label) {
460 if (typeof label === 'string' && label !== '') {
461 top.TYPO3.Notification.info(LanguageModule.labels.infoHeader, label);
462 }
463 };
464
465 /**
466 * Display success flash message
467 */
468 LanguageModule.displaySuccess = function(label) {
469 if (typeof label === 'string' && label !== '') {
470 top.TYPO3.Notification.success(LanguageModule.labels.successHeader, label);
471 }
472 };
473
474 /**
475 * Build action link
476 */
477 LanguageModule.buildActionLink = function(action, parameters, content) {
478 var $link = $('<a>');
479
480 $link.addClass(action + 'Link');
481 $link.attr('data-action', action);
482 for (var name in parameters) {
483 $link.attr('data-' + name, parameters[name]);
484 }
485 $link.html(content);
486 return $link.wrap('<span>').parent().html();
487 };
488
489 /**
490 * Build progress bar
491 */
492 LanguageModule.buildProgressBar = function() {
493 var $span = $('<span>');
494 $span.addClass(LanguageModule.classes.progressBar);
495 $span.html(LanguageModule.icons.progressBar);
496 return $span.wrap('<span>').parent().html();
497 };
498
499 /**
500 * Build loading indicator
501 */
502 LanguageModule.buildLoadingIndicator = function() {
503 var $span = $('<span>');
504 $span.addClass(LanguageModule.classes.loading);
505 $span.html(LanguageModule.icons.loading);
506 return $span.wrap('<span>').parent().html();
507 };
508
509 /**
510 * Build complete state indicator
511 */
512 LanguageModule.buildCompleteIndicator = function() {
513 var $span = $('<span>');
514 $span.addClass(LanguageModule.classes.complete);
515 $span.html(LanguageModule.icons.complete);
516 return $span.wrap('<span>').parent().html();
517 };
518
519 /**
520 * Build image
521 */
522 LanguageModule.buildImage = function(uri, alt, title, width, heigth) {
523 var $image = $('<img>');
524 $image.attr('src', uri);
525 $image.attr('alt', alt ? alt : '');
526 $image.attr('title', title ? title : '');
527 $image.attr('style', 'width: ' + width + 'px; height: ' + heigth + 'px;');
528 var $span = $('<span>');
529 $span.addClass('typo3-app-icon');
530 $span.attr('style', 'background: none; text-align: center;');
531 $span.html($image);
532 return $span.wrap('<span>').parent().html();
533 };
534
535 /**
536 * Format date
537 */
538 LanguageModule.formatDate = function(timestamp) {
539 return moment.unix(timestamp).format(LanguageModule.labels.dateFormat);
540 };
541
542 /**
543 * Set progress bar progress
544 */
545 LanguageModule.setProgress = function(progressBar, progress) {
546 var $inner = $(LanguageModule.identifiers.progressBarInner, progressBar),
547 $text = $(LanguageModule.identifiers.progressBarText, progressBar);
548 $inner.css({width: progress + '%'});
549 $inner.attr('aria-valuenow', progress);
550 $text.text(Math.round(progress) + '%');
551 };
552
553 return function() {
554 $(document).ready(function() {
555 if ($('div.typo3-module-lang #typo3-language-list').length) {
556 LanguageModule.initializeLanguageTable('div.typo3-module-lang', '#typo3-language-list');
557 } else if ($('div.typo3-module-lang #typo3-translation-list').length) {
558 LanguageModule.initializeTranslationTable('div.typo3-module-lang', '#typo3-translation-list');
559 }
560 });
561
562 TYPO3.LanguageModule = LanguageModule;
563 return LanguageModule;
564 }();
565 });