/*
* Copyright (c) 2014
*
* This file is licensed under the Affero General Public License version 3
* or later.
*
* See the COPYING-README file.
*
*/
/* global OC, t, n, FileList, FileActions, Files, FileSummary, BreadCrumb */
/* global dragOptions, folderDropOptions */
window.FileList = {
SORT_INDICATOR_ASC_CLASS: 'icon-triangle-s',
SORT_INDICATOR_DESC_CLASS: 'icon-triangle-n',
appName: t('files', 'Files'),
isEmpty: true,
useUndo:true,
$el: $('#filestable'),
$fileList: $('#fileList'),
breadcrumb: null,
/**
* Instance of FileSummary
*/
fileSummary: null,
initialized: false,
// number of files per page
pageSize: 20,
/**
* Array of files in the current folder.
* The entries are of file data.
*/
files: [],
/**
* Map of file id to file data
*/
_selectedFiles: {},
/**
* Summary of selected files.
* Instance of FileSummary.
*/
_selectionSummary: null,
/**
* Sort attribute
*/
_sort: 'name',
/**
* Sort direction: 'asc' or 'desc'
*/
_sortDirection: 'asc',
/**
* Sort comparator function for the current sort
*/
_sortComparator: null,
/**
* Initialize the file list and its components
*/
initialize: function() {
var self = this;
if (this.initialized) {
return;
}
// TODO: FileList should not know about global elements
this.$el = $('#filestable');
this.$fileList = $('#fileList');
this.files = [];
this._selectedFiles = {};
this._selectionSummary = new FileSummary();
this.fileSummary = this._createSummary();
this.setSort('name', 'asc');
this.breadcrumb = new BreadCrumb({
onClick: this._onClickBreadCrumb,
onDrop: _.bind(this._onDropOnBreadCrumb, this),
getCrumbUrl: function(part, index) {
return self.linkTo(part.dir);
}
});
$('#controls').prepend(this.breadcrumb.$el);
this.$el.find('thead th .columntitle').click(_.bind(this._onClickHeader, this));
$(window).resize(function() {
// TODO: debounce this ?
var width = $(this).width();
FileList.breadcrumb.resize(width, false);
});
this.$fileList.on('click','td.filename a', _.bind(this._onClickFile, this));
this.$fileList.on('change', 'td.filename input:checkbox', _.bind(this._onClickFileCheckbox, this));
this.$el.find('#select_all').click(_.bind(this._onClickSelectAll, this));
this.$el.find('.download').click(_.bind(this._onClickDownloadSelected, this));
this.$el.find('.delete-selected').click(_.bind(this._onClickDeleteSelected, this));
},
/**
* Selected/deselects the given file element and updated
* the internal selection cache.
*
* @param $tr single file row element
* @param state true to select, false to deselect
*/
_selectFileEl: function($tr, state) {
var $checkbox = $tr.find('input:checkbox');
var oldData = !!this._selectedFiles[$tr.data('id')];
var data;
$checkbox.prop('checked', state);
$tr.toggleClass('selected', state);
// already selected ?
if (state === oldData) {
return;
}
data = this.elementToFile($tr);
if (state) {
this._selectedFiles[$tr.data('id')] = data;
this._selectionSummary.add(data);
}
else {
delete this._selectedFiles[$tr.data('id')];
this._selectionSummary.remove(data);
}
this.$el.find('#select_all').prop('checked', this._selectionSummary.getTotal() === this.files.length);
},
/**
* Event handler for when clicking on files to select them
*/
_onClickFile: function(event) {
var $tr = $(event.target).closest('tr');
if (event.ctrlKey || event.shiftKey) {
event.preventDefault();
if (event.shiftKey) {
var $lastTr = $(this._lastChecked);
var lastIndex = $lastTr.index();
var currentIndex = $tr.index();
var $rows = this.$fileList.children('tr');
// last clicked checkbox below current one ?
if (lastIndex > currentIndex) {
var aux = lastIndex;
lastIndex = currentIndex;
currentIndex = aux;
}
// auto-select everything in-between
for (var i = lastIndex + 1; i < currentIndex; i++) {
this._selectFileEl($rows.eq(i), true);
}
}
else {
this._lastChecked = $tr;
}
var $checkbox = $tr.find('input:checkbox');
this._selectFileEl($tr, !$checkbox.prop('checked'));
this.updateSelectionSummary();
} else {
var filename = $tr.attr('data-file');
var renaming = $tr.data('renaming');
if (!renaming) {
FileActions.currentFile = $tr.find('td');
var mime=FileActions.getCurrentMimeType();
var type=FileActions.getCurrentType();
var permissions = FileActions.getCurrentPermissions();
var action=FileActions.getDefault(mime,type, permissions);
if (action) {
event.preventDefault();
action(filename);
}
}
}
},
/**
* Event handler for when clicking on a file's checkbox
*/
_onClickFileCheckbox: function(e) {
var $tr = $(e.target).closest('tr');
this._selectFileEl($tr, !$tr.hasClass('selected'));
this._lastChecked = $tr;
this.updateSelectionSummary();
},
/**
* Event handler for when selecting/deselecting all files
*/
_onClickSelectAll: function(e) {
var checked = $(e.target).prop('checked');
this.$fileList.find('td.filename input:checkbox').prop('checked', checked)
.closest('tr').toggleClass('selected', checked);
this._selectedFiles = {};
this._selectionSummary.clear();
if (checked) {
for (var i = 0; i < this.files.length; i++) {
var fileData = this.files[i];
this._selectedFiles[fileData.id] = fileData;
this._selectionSummary.add(fileData);
}
}
this.updateSelectionSummary();
},
/**
* Event handler for when clicking on "Download" for the selected files
*/
_onClickDownloadSelected: function(event) {
var files;
var dir = this.getCurrentDirectory();
if (this.isAllSelected()) {
files = OC.basename(dir);
dir = OC.dirname(dir) || '/';
}
else {
files = _.pluck(this.getSelectedFiles(), 'name');
}
OC.Notification.show(t('files','Your download is being prepared. This might take some time if the files are big.'));
OC.redirect(Files.getDownloadUrl(files, dir));
return false;
},
/**
* Event handler for when clicking on "Delete" for the selected files
*/
_onClickDeleteSelected: function(event) {
var files = null;
if (!FileList.isAllSelected()) {
files = _.pluck(this.getSelectedFiles(), 'name');
}
this.do_delete(files);
event.preventDefault();
return false;
},
/**
* Event handler when clicking on a table header
*/
_onClickHeader: function(e) {
var $target = $(e.target);
var sort;
if (!$target.is('a')) {
$target = $target.closest('a');
}
sort = $target.attr('data-sort');
if (sort) {
if (this._sort === sort) {
this.setSort(sort, (this._sortDirection === 'desc')?'asc':'desc');
}
else {
this.setSort(sort, 'asc');
}
this.reload();
}
},
/**
* Event handler when clicking on a bread crumb
*/
_onClickBreadCrumb: function(e) {
var $el = $(e.target).closest('.crumb'),
$targetDir = $el.data('dir');
if ($targetDir !== undefined) {
e.preventDefault();
FileList.changeDirectory($targetDir);
}
},
_onScroll: function(e) {
if ($(window).scrollTop() + $(window).height() > $(document).height() - 500) {
this._nextPage(true);
}
},
/**
* Event handler when dropping on a breadcrumb
*/
_onDropOnBreadCrumb: function( event, ui ) {
var $target = $(event.target);
if (!$target.is('.crumb')) {
$target = $target.closest('.crumb');
}
var targetPath = $(event.target).data('dir');
var dir = this.getCurrentDirectory();
while (dir.substr(0,1) === '/') {//remove extra leading /'s
dir = dir.substr(1);
}
dir = '/' + dir;
if (dir.substr(-1,1) !== '/') {
dir = dir + '/';
}
// do nothing if dragged on current dir
if (targetPath === dir || targetPath + '/' === dir) {
return;
}
var files = this.getSelectedFiles();
if (files.length === 0) {
// single one selected without checkbox?
files = _.map(ui.helper.find('tr'), FileList.elementToFile);
}
FileList.move(_.pluck(files, 'name'), targetPath);
},
/**
* Sets a new page title
*/
setPageTitle: function(title){
if (title) {
title += ' - ';
} else {
title = '';
}
title += FileList.appName;
// Sets the page title with the " - ownCloud" suffix as in templates
window.document.title = title + ' - ' + oc_defaults.title;
return true;
},
/**
* Returns the tr element for a given file name
* @param fileName file name
*/
findFileEl: function(fileName){
// use filterAttr to avoid escaping issues
return this.$fileList.find('tr').filterAttr('data-file', fileName);
},
/**
* Returns the file data from a given file element.
* @param $el file tr element
* @return file data
*/
elementToFile: function($el){
$el = $($el);
return {
id: parseInt($el.attr('data-id'), 10),
name: $el.attr('data-file'),
mimetype: $el.attr('data-mime'),
type: $el.attr('data-type'),
size: parseInt($el.attr('data-size'), 10),
etag: $el.attr('data-etag')
};
},
/**
* Appends the next page of files into the table
* @param animate true to animate the new elements
*/
_nextPage: function(animate) {
var index = this.$fileList.children().length,
count = this.pageSize,
tr,
fileData,
newTrs = [],
isAllSelected = this.isAllSelected();
if (index >= this.files.length) {
return;
}
while (count > 0 && index < this.files.length) {
fileData = this.files[index];
tr = this._renderRow(fileData, {updateSummary: false});
this.$fileList.append(tr);
if (isAllSelected || this._selectedFiles[fileData.id]) {
tr.addClass('selected');
tr.find('input:checkbox').prop('checked', true);
}
if (animate) {
tr.addClass('appear transparent');
newTrs.push(tr);
}
index++;
count--;
}
if (animate) {
// defer, for animation
window.setTimeout(function() {
for (var i = 0; i < newTrs.length; i++ ) {
newTrs[i].removeClass('transparent');
}
}, 0);
}
},
/**
* Sets the files to be displayed in the list.
* This operation will re-render the list and update the summary.
* @param filesArray array of file data (map)
*/
setFiles: function(filesArray) {
// detach to make adding multiple rows faster
this.files = filesArray;
this.$fileList.detach();
this.$fileList.empty();
// clear "Select all" checkbox
this.$el.find('#select_all').prop('checked', false);
this.isEmpty = this.files.length === 0;
this._nextPage();
this.$el.find('thead').after(this.$fileList);
this.updateEmptyContent();
this.$fileList.trigger(jQuery.Event("fileActionsReady"));
// "Files" might not be loaded in extending apps
if (window.Files) {
Files.setupDragAndDrop();
}
this.fileSummary.calculate(filesArray);
FileList.updateSelectionSummary();
$(window).scrollTop(0);
this.$fileList.trigger(jQuery.Event("updated"));
},
/**
* Creates a new table row element using the given file data.
* @param fileData map of file attributes
* @param options map of attribute "loading" whether the entry is currently loading
* @return new tr element (not appended to the table)
*/
_createRow: function(fileData, options) {
var td, simpleSize, basename, extension, sizeColor,
icon = OC.Util.replaceSVGIcon(fileData.icon),
name = fileData.name,
type = fileData.type || 'file',
mtime = parseInt(fileData.mtime, 10) || new Date().getTime(),
mime = fileData.mimetype,
linkUrl;
options = options || {};
if (type === 'dir') {
mime = mime || 'httpd/unix-directory';
}
// user should always be able to rename a share mount point
var allowRename = 0;
if (fileData.isShareMountPoint) {
allowRename = OC.PERMISSION_UPDATE;
}
//containing tr
var tr = $('