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