text: /*
   * This file is part of the TYPO3 CMS project.
   *
   * It is free software; you can redistribute it and/or modify it under
   * the terms of the GNU General Public License, either version 2
   * of the License, or any later version.
   *
   * For the full copyright and license information, please read the
   * LICENSE.txt file that was distributed with this source code.
   *
   * The TYPO3 project - inspiring people to share!
   */
  
  /**
   * Module: TYPO3/CMS/Recycler/Recycler
   * RequireJS module for Recycler
   */
  define(['jquery',
  		'nprogress',
  		'TYPO3/CMS/Backend/Modal',
  		'TYPO3/CMS/Backend/Notification',
  		'TYPO3/CMS/Backend/Severity',
  		'TYPO3/CMS/Backend/jquery.clearable'
  	   ], function($, NProgress, Modal, Notification, Severity) {
  	'use strict';
  
  	/**
  	 *
  	 * @type {{identifiers: {searchForm: string, searchText: string, searchSubmitBtn: string, depthSelector: string, tableSelector: string, recyclerTable: string, paginator: string, reloadAction: string, massUndo: string, massDelete: string, toggleAll: string}, elements: {}, paging: {currentPage: number, totalPages: number, totalItems: number, itemsPerPage: number}, markedRecordsForMassAction: Array, allToggled: boolean}}
  	 * @exports TYPO3/CMS/Recycler/Recycler
  	 */
  	var Recycler = {
  		identifiers: {
  			searchForm: '#recycler-form',
  			searchText: '#recycler-form [name=search-text]',
  			searchSubmitBtn: '#recycler-form button[type=submit]',
  			depthSelector: '#recycler-form [name=depth]',
  			tableSelector: '#recycler-form [name=pages]',
  			recyclerTable: '#itemsInRecycler',
  			paginator: '#recycler-index nav',
  			reloadAction: 'a[data-action=reload]',
  			massUndo: 'button[data-action=massundo]',
  			massDelete: 'button[data-action=massdelete]',
  			toggleAll: '.t3js-toggle-all'
  		},
  		elements: {}, // filled in getElements()
  		paging: {
  			currentPage: 1,
  			totalPages: 1,
  			totalItems: 0,
  			itemsPerPage: TYPO3.settings.Recycler.pagingSize
  		},
  		markedRecordsForMassAction: [],
  		allToggled: false
  	};
  
  	/**
  	 * Gets required elements
  	 */
  	Recycler.getElements = function() {
  		Recycler.elements = {
  			$searchForm: $(Recycler.identifiers.searchForm),
  			$searchTextField: $(Recycler.identifiers.searchText),
  			$searchSubmitBtn: $(Recycler.identifiers.searchSubmitBtn),
  			$depthSelector: $(Recycler.identifiers.depthSelector),
  			$tableSelector: $(Recycler.identifiers.tableSelector),
  			$recyclerTable: $(Recycler.identifiers.recyclerTable),
  			$tableBody: $(Recycler.identifiers.recyclerTable).find('tbody'),
  			$paginator: $(Recycler.identifiers.paginator),
  			$reloadAction: $(Recycler.identifiers.reloadAction),
  			$massUndo: $(Recycler.identifiers.massUndo),
  			$massDelete: $(Recycler.identifiers.massDelete),
  			$toggleAll: $(Recycler.identifiers.toggleAll)
  		};
  	};
  
  	/**
  	 * Register events
  	 */
  	Recycler.registerEvents = function() {
  		// submitting the form
  		Recycler.elements.$searchForm.on('submit', function(e) {
  			e.preventDefault();
  			if (Recycler.elements.$searchTextField.val() !== '') {
  				Recycler.loadDeletedElements();
  			}
  		});
  
  		// changing the search field
  		Recycler.elements.$searchTextField.on('keyup', function() {
  			var $me = $(this);
  
  			if ($me.val() !== '') {
  				Recycler.elements.$searchSubmitBtn.removeClass('disabled');
  			} else {
  				Recycler.elements.$searchSubmitBtn.addClass('disabled');
  				Recycler.loadDeletedElements();
  			}
  		}).clearable(
  			{
  				onClear: function() {
  					Recycler.elements.$searchSubmitBtn.addClass('disabled');
  					Recycler.loadDeletedElements();
  				}
  			}
  		);
  
  		// changing "depth"
  		Recycler.elements.$depthSelector.on('change', function() {
  			$.when(Recycler.loadAvailableTables()).done(function() {
  				Recycler.loadDeletedElements();
  			});
  		});
  
  		// changing "table"
  		Recycler.elements.$tableSelector.on('change', function() {
  			Recycler.paging.currentPage = 1;
  			Recycler.loadDeletedElements();
  		});
  
  		// clicking "recover" in single row
  		Recycler.elements.$recyclerTable.on('click', '[data-action=undo]', Recycler.undoRecord);
  
  		// clicking "delete" in single row
  		Recycler.elements.$recyclerTable.on('click', '[data-action=delete]', Recycler.deleteRecord);
  
  		Recycler.elements.$reloadAction.on('click', function(e) {
  			e.preventDefault();
  			$.when(Recycler.loadAvailableTables()).done(function() {
  				Recycler.loadDeletedElements();
  			});
  		});
  
  		// clicking an action in the paginator
  		Recycler.elements.$paginator.on('click', 'a[data-action]', function(e) {
  			e.preventDefault();
  
  			var $el = $(this),
  				reload = false;
  
  			switch ($el.data('action')) {
  				case 'previous':
  					if (Recycler.paging.currentPage > 1) {
  						Recycler.paging.currentPage--;
  						reload = true;
  					}
  					break;
  				case 'next':
  					if (Recycler.paging.currentPage < Recycler.paging.totalPages) {
  						Recycler.paging.currentPage++;
  						reload = true;
  					}
  					break;
  				case 'page':
  					Recycler.paging.currentPage = parseInt($el.find('span').text());
  					reload = true;
  					break;
  			}
  
  			if (reload) {
  				Recycler.loadDeletedElements();
  			}
  		});
  
  		if (!TYPO3.settings.Recycler.deleteDisable) {
  			Recycler.elements.$massDelete.show();
  		} else {
  			Recycler.elements.$massDelete.remove();
  		}
  
  		Recycler.elements.$recyclerTable.on('show.bs.collapse hide.bs.collapse', 'tr.collapse', function(e) {
  			var $trigger = $(e.currentTarget).prev('tr').find('[data-action=expand]'),
  				$iconEl = $trigger.find('.t3-icon'),
  				removeClass,
  				addClass;
  
  			switch (e.type) {
  				case 'show':
  					removeClass = 't3-icon-pagetree-collapse';
  					addClass = 't3-icon-pagetree-expand';
  					break;
  				case 'hide':
  					removeClass = 't3-icon-pagetree-expand';
  					addClass = 't3-icon-pagetree-collapse';
  					break;
  			}
  
  			$iconEl.removeClass(removeClass).addClass(addClass);
  		});
  
  		// checkboxes in the table
  		Recycler.elements.$toggleAll.on('click', function() {
  			Recycler.allToggled = !Recycler.allToggled;
  			$('input[type="checkbox"]').prop('checked', Recycler.allToggled).trigger('change');
  		});
  		Recycler.elements.$recyclerTable.on('change', 'tr input[type=checkbox]', Recycler.handleCheckboxSelects);
  
  		Recycler.elements.$massUndo.on('click', Recycler.undoRecord);
  		Recycler.elements.$massDelete.on('click', Recycler.deleteRecord);
  	};
  
  	/**
  	 * Initialize the recycler module
  	 */
  	Recycler.initialize = function() {
  		NProgress.configure({parent: '.module-loading-indicator', showSpinner: false});
  
  		Recycler.getElements();
  		Recycler.registerEvents();
  
  		if (TYPO3.settings.Recycler.depthSelection > 0) {
  			Recycler.elements.$depthSelector.val(TYPO3.settings.Recycler.depthSelection).trigger('change');
  		} else {
  			$.when(Recycler.loadAvailableTables()).done(function() {
  				Recycler.loadDeletedElements();
  			});
  		}
  	};
  
  	/**
  	 * Handles the clicks on checkboxes in the records table
  	 */
  	Recycler.handleCheckboxSelects = function() {
  		var $checkbox = $(this),
  			$tr = $checkbox.parents('tr'),
  			table = $tr.data('table'),
  			uid = $tr.data('uid'),
  			record = table + ':' + uid;
  
  		if ($checkbox.prop('checked')) {
  			Recycler.markedRecordsForMassAction.push(record);
  			$tr.addClass('warning');
  		} else {
  			var index = Recycler.markedRecordsForMassAction.indexOf(record);
  			if (index > -1) {
  				Recycler.markedRecordsForMassAction.splice(index, 1);
  			}
  			$tr.removeClass('warning');
  		}
  
  		if (Recycler.markedRecordsForMassAction.length > 0) {
  			if (Recycler.elements.$massUndo.hasClass('disabled')) {
  				Recycler.elements.$massUndo.removeClass('disabled');
  			}
  			if (Recycler.elements.$massDelete.hasClass('disabled')) {
  				Recycler.elements.$massDelete.removeClass('disabled');
  			}
  
  			var btnTextUndo = Recycler.createMessage(TYPO3.lang['button.undoselected'], [Recycler.markedRecordsForMassAction.length]),
  				btnTextDelete = Recycler.createMessage(TYPO3.lang['button.deleteselected'], [Recycler.markedRecordsForMassAction.length]);
  
  			Recycler.elements.$massUndo.find('span.text').text(btnTextUndo);
  			Recycler.elements.$massDelete.find('span.text').text(btnTextDelete);
  
  		} else {
  			Recycler.resetMassActionButtons();
  		}
  	};
  
  	/**
  	 * Resets the mass action state
  	 */
  	Recycler.resetMassActionButtons = function() {
  		Recycler.markedRecordsForMassAction = [];
  		Recycler.elements.$massUndo.addClass('disabled');
  		Recycler.elements.$massUndo.find('span.text').text(TYPO3.lang['button.undo']);
  		Recycler.elements.$massDelete.addClass('disabled');
  		Recycler.elements.$massDelete.find('span.text').text(TYPO3.lang['button.delete']);
  	};
  
  	/**
  	 * Loads all tables which contain deleted records.
  	 *
  	 * @returns {Promise}
  	 */
  	Recycler.loadAvailableTables = function() {
  		return $.ajax({
  			url: TYPO3.settings.ajaxUrls['recycler'],
  			dataType: 'json',
  			data: {
  				action: 'getTables',
  				startUid: TYPO3.settings.Recycler.startUid,
  				depth: Recycler.elements.$depthSelector.find('option:selected').val()
  			},
  			beforeSend: function() {
  				NProgress.start();
  				Recycler.elements.$tableSelector.val('');
  				Recycler.paging.currentPage = 1;
  			},
  			success: function(data) {
  				var tables = [];
  				Recycler.elements.$tableSelector.children().remove();
  				$.each(data, function(_, value) {
  					var tableName = value[0],
  						deletedRecords = value[1],
  						tableDescription = value[2];
  
  					if (tableDescription === '') {
  						tableDescription = TYPO3.lang['label_allrecordtypes'];
  					}
  					var optionText = tableDescription + ' (' + deletedRecords + ')';
  					tables.push($('<option />').val(tableName).text(optionText))
  				});
  
  				if (tables.length > 0) {
  					Recycler.elements.$tableSelector.append(tables);
  					if (TYPO3.settings.Recycler.tableSelection !== '') {
  						Recycler.elements.$tableSelector.val(TYPO3.settings.Recycler.tableSelection);
  					}
  				}
  			},
  			complete: function() {
  				NProgress.done();
  			}
  		});
  	};
  
  	/**
  	 * Loads the deleted elements, based on the filters
  	 *
  	 * @returns {Promise}
  	 */
  	Recycler.loadDeletedElements = function() {
  		return $.ajax({
  			url: TYPO3.settings.ajaxUrls['recycler'],
  			dataType: 'json',
  			data: {
  				action: 'getDeletedRecords',
  				depth: Recycler.elements.$depthSelector.find('option:selected').val(),
  				startUid: TYPO3.settings.Recycler.startUid,
  				table: Recycler.elements.$tableSelector.find('option:selected').val(),
  				filterTxt: Recycler.elements.$searchTextField.val(),
  				start: (Recycler.paging.currentPage -1) * Recycler.paging.itemsPerPage,
  				limit: Recycler.paging.itemsPerPage
  			},
  			beforeSend: function() {
  				NProgress.start();
  				Recycler.resetMassActionButtons();
  			},
  			success: function(data) {
  				Recycler.elements.$tableBody.html(data.rows);
  				Recycler.buildPaginator(data.totalItems);
  			},
  			complete: function() {
  				NProgress.done();
  			}
  		});
  	};
  
  	/**
  	 *
  	 */
  	Recycler.deleteRecord = function() {
  		if (TYPO3.settings.Recycler.deleteDisable) {
  			return;
  		}
  
  		var $tr = $(this).parents('tr'),
  			isMassDelete = $tr.parent().prop('tagName') !== 'TBODY'; // deleteRecord() was invoked by the mass delete button
  
  		var records, message;
  		if (isMassDelete) {
  			records = Recycler.markedRecordsForMassAction;
  			message = TYPO3.lang['modal.massdelete.text'];
  		} else {
  			var uid = $tr.data('uid'),
  				table = $tr.data('table'),
  				recordTitle = $tr.data('recordtitle');
  			records = table + ':' + uid;
  			message = table === 'pages' ? TYPO3.lang['modal.deletepage.text'] : TYPO3.lang['modal.deletecontent.text'];
  			message = Recycler.createMessage(message, [recordTitle, '[' + records + ']']);
  		}
  
  		Modal.confirm(TYPO3.lang['modal.delete.header'], message, Severity.error, [
  			{
  				text: TYPO3.lang['button.cancel'],
  				btnClass: 'btn-default',
  				trigger: function() {
  					Modal.dismiss();
  				}
  			}, {
  				text: TYPO3.lang['button.delete'],
  				btnClass: 'btn-danger',
  				trigger: function() {
  					Recycler.callAjaxAction('delete', typeof records === 'object' ? records : [records], isMassDelete);
  				}
  			}
  		]);
  	};
  
  	/**
  	 *
  	 */
  	Recycler.undoRecord = function() {
  		var $tr = $(this).parents('tr'),
  			isMassUndo = $tr.parent().prop('tagName') !== 'TBODY'; // undoRecord() was invoked by the mass delete button
  
  		var records, messageText, recoverPages;
  		if (isMassUndo) {
  			records = Recycler.markedRecordsForMassAction;
  			messageText = TYPO3.lang['modal.massundo.text'];
  			recoverPages = true;
  		} else {
  			var uid = $tr.data('uid'),
  				table = $tr.data('table'),
  				recordTitle = $tr.data('recordtitle');
  
  			records = table + ':' + uid;
  			recoverPages = table === 'pages';
  			messageText = recoverPages ? TYPO3.lang['modal.undopage.text'] : TYPO3.lang['modal.undocontent.text'];
  			messageText = Recycler.createMessage(messageText, [recordTitle, '[' + records + ']']);
  
  			if (recoverPages && $tr.data('parentDeleted')) {
  				messageText += TYPO3.lang['modal.undo.parentpages'];
  			}
  		}
  
  		var $message = null;
  		if (recoverPages) {
  			$message = $('<div />').append(
  				$('<p />').text(messageText),
  				$('<div />', {class: 'checkbox'}).append(
  					$('<label />').append(TYPO3.lang['modal.undo.recursive']).prepend($('<input />', {id: 'undo-recursive', type: 'checkbox'}))
  				)
  			);
  		} else {
  			$message = messageText;
  		}
  
  		Modal.confirm(TYPO3.lang['modal.undo.header'], $message, Severity.ok, [
  			{
  				text: TYPO3.lang['button.cancel'],
  				btnClass: 'btn-default',
  				trigger: function() {
  					Modal.dismiss();
  				}
  			}, {
  				text: TYPO3.lang['button.undo'],
  				btnClass: 'btn-success',
  				trigger: function() {
  					Recycler.callAjaxAction('undo', typeof records === 'object' ? records : [records], isMassUndo);
  				}
  			}
  		]);
  	};
  
  	/**
  	 *
  	 * @param {String} action
  	 * @param {Object} records
  	 * @param {Boolean} isMassAction
  	 */
  	Recycler.callAjaxAction = function(action, records, isMassAction) {
  		var data = {
  				records: records,
  				action: ''
  			},
  			reloadPageTree = false;
  		if (action === 'undo') {
  			data.action = 'undoRecords';
  			data.recursive = $('#undo-recursive').prop('checked')  ? 1 : 0;
  			reloadPageTree = true;
  		} else if (action === 'delete') {
  			data.action = 'deleteRecords';
  		} else {
  			return;
  		}
  
  		$.ajax({
  			url: TYPO3.settings.ajaxUrls['recycler'],
  			dataType: 'json',
  			data: data,
  			beforeSend: function() {
  				NProgress.start();
  			},
  			success: function(data) {
  				if (data.success) {
  					Notification.success('', data.message);
  				} else {
  					Notification.error('', data.message);
  				}
  
  				// reload recycler data
  				Recycler.paging.currentPage = 1;
  
  				$.when(Recycler.loadAvailableTables()).done(function() {
  					Recycler.loadDeletedElements();
  					if (isMassAction) {
  						Recycler.resetMassActionButtons();
  					}
  
  					if (reloadPageTree) {
  						Recycler.refreshPageTree();
  					}
  
  					// Reset toggle state
  					Recycler.allToggled = false;
  				});
  			},
  			complete: function() {
  				Modal.dismiss();
  				NProgress.done();
  			}
  		});
  	};
  
  	/**
  	 * Replaces the placeholders with actual values
  	 *
  	 * @param {String} message
  	 * @param {Array} placeholders
  	 * @returns {*}
  	 */
  	Recycler.createMessage = function(message, placeholders) {
  		if (typeof message === 'undefined') {
  			return '';
  		}
  
  		return message.replace(
  			/\{([0-9]+)\}/g,
  			function(_, index) {
  				return placeholders[index];
  			}
  		);
  	};
  
  	/**
  	 * Reloads the page tree
  	 */
  	Recycler.refreshPageTree = function() {
  		if (top.TYPO3 && top.TYPO3.Backend && top.TYPO3.Backend.NavigationContainer && top.TYPO3.Backend.NavigationContainer.PageTree) {
  			top.TYPO3.Backend.NavigationContainer.PageTree.refreshTree();
  		}
  	};
  
  	/**
  	 * Build the paginator
  	 *
  	 * @param {Number} totalItems
  	 */
  	Recycler.buildPaginator = function(totalItems) {
  		if (totalItems === 0) {
  			Recycler.elements.$paginator.contents().remove();
  			return;
  		}
  
  		Recycler.paging.totalItems = totalItems;
  		Recycler.paging.totalPages = Math.ceil(totalItems / Recycler.paging.itemsPerPage);
  
  		if (Recycler.paging.totalPages === 1) {
  			// early abort if only one page is available
  			Recycler.elements.$paginator.contents().remove();
  			return;
  		}
  
  		var $ul = $('<ul />', {class: 'pagination pagination-block'}),
  			liElements = [],
  			$controlFirstPage = $('<li />').append(
  				$('<a />', {'data-action': 'previous'}).append(
  					$('<span />', {class: 't3-icon fa fa-arrow-left'})
  				)
  			),
  			$controlLastPage = $('<li />').append(
  				$('<a />', {'data-action': 'next'}).append(
  					$('<span />', {class: 't3-icon fa fa-arrow-right'})
  				)
  			);
  
  		if (Recycler.paging.currentPage === 1) {
  			$controlFirstPage.disablePagingAction();
  		}
  
  		if (Recycler.paging.currentPage === Recycler.paging.totalPages) {
  			$controlLastPage.disablePagingAction();
  		}
  
  		for (var i = 1; i <= Recycler.paging.totalPages; i++) {
  			var $li = $('<li />', {class: Recycler.paging.currentPage === i ? 'active' : ''});
  			$li.append(
  				$('<a />', {'data-action': 'page'}).append(
  					$('<span />').text(i)
  				)
  			);
  			liElements.push($li);
  		}
  
  		$ul.append($controlFirstPage, liElements, $controlLastPage);
  		Recycler.elements.$paginator.html($ul);
  	};
  
  	/**
  	 * Changes the markup of a pagination action being disabled
  	 */
  	$.fn.disablePagingAction = function() {
  		$(this).addClass('disabled').find('.t3-icon').unwrap().wrap($('<span />'));
  	};
  
  	$(Recycler.initialize);
  
  	return Recycler;
  });
  