pix-angular-filebrowser
Version:
File browser for Web2
371 lines (327 loc) • 12.5 kB
JavaScript
var _ = require("lodash");
function FileBrowserController(
$scope, $translate, $q, FileBrowserService, ItemsCollectionModel, NotificationsService, uiGridConstants, PIXFolder, $templateCache, hotkeys) {
// our grid api
var gridApi;
var self = this;
// register our templates
$templateCache.put('pix.filebrowser.empty.html', require('../template/pix.filebrowser.empty.html'));
$templateCache.put('pix.filebrowser.loading.html', require('../template/pix.filebrowser.loading.html'));
$templateCache.put('pix.filebrowser.toolbar.html', require('../template/pix.filebrowser.toolbar.html'));
// and array to hold the currently selected items
self.selection = [];
// a way to keep track of our selection direction for keyboard hotkeys
var lastSelectionDirection = 0;
// set the loading state to true
self.isLoading = true;
// set up our grid options
self.gridOptions = {
// sorting options
enableSorting: true,
useExternalSorting: true,
// selection options
enableRowSelection: true,
// enableSelectAll: true,
enableSelectionBatchEvent: true,
enableRowHeaderSelection: false,
multiSelect: true,
modifierKeysToMultiSelect: true,
// core setup
columnDefs: [],
rowTemplate: require('../template/pix.filebrowser.row.html'),
data: FileBrowserService.items,
excessRows: 50,
onRegisterApi: onRegisterApi,
enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER
};
// setup our columns, first translate the header strings
$q.all({
name: $translate('FileBrowser.Column.Name'),
description: $translate('FileBrowser.Column.Description'),
createdOn: $translate('FileBrowser.Column.CreatedOn'),
createdBy: $translate('FileBrowser.Column.CreatedBy'),
modifiedOn: $translate('FileBrowser.Column.ModifiedOn'),
modifiedBy: $translate('FileBrowser.Column.ModifiedBy')
})
.then(function(names) {
self.gridOptions.columnDefs = [
{
name: names.name,
field: 'viewData.fields.name',
enableColumnMenu: false,
cellTemplate: require('../template/pix.filebrowser.name.html')
},
{
name: names.description,
field: 'viewData.fields.description',
enableColumnMenu: false,
cellClass: 'text-muted'
},
{
name: names.createdOn,
field: 'viewData.fields.created_on',
enableColumnMenu: false,
cellTemplate: require('../template/pix.filebrowser.date.html'),
cellClass: 'text-muted'
},
{
name: names.createdBy,
field: 'viewData.createdBy.viewData.label',
enableColumnMenu: false,
cellClass: 'text-muted'
},
{
displayName: names.modifiedOn,
field: 'viewData.fields.modified_on',
enableColumnMenu: false,
cellTemplate: require('../template/pix.filebrowser.date.html'),
cellClass: 'text-muted'
},
{
displayName: names.modifiedBy,
field: 'viewData.modifiedBy.viewData.label',
enableColumnMenu: false,
cellClass: 'text-muted'
}
]});
/**
* A callback which is called when the grid API is ready
*
* @param gridApi {object} an instance of the ui-grid api
*/
function onRegisterApi(api) {
// stash the grid API for later
gridApi = api;
// register our selection and sort changed handler
gridApi.selection.on.rowSelectionChanged($scope, onSelectionChanged);
gridApi.selection.on.rowSelectionChangedBatch($scope, onSelectionChanged);
gridApi.core.on.sortChanged($scope, onSortChanged);
}
/**
* The selection changed handler
*/
function onSelectionChanged() {
// update our selection array when the selection has changed
self.selection = gridApi.selection.getSelectedRows();
}
/**
* The sort changed handler
*
* @param grid {object} an instance of the grid
* @param sortColumns {array} an array of columns to sort by
*/
function onSortChanged(grid, sortColumns) {
// build out our accessors array (an array of functions that return the cell value for a given column/row
var accessors = [];
var directions = [];
_.each(sortColumns, function(col) {
accessors.push(function(item) {
return _.result(item, col.field);
})
directions.push(col.sort.direction === 'asc');
});
FileBrowserService.sort(accessors, directions);
}
/**
* Moves the current selection or appends to it (this is used for keyboard shortcuts)
*
* @param direction 1 means forward, -1 means backwards
* @param append if true selected items will be appended otherwise they will be replaced
*/
function moveSelection(direction, append) {
if (self.selection.length === 0) {
gridApi.selection.selectRowByVisibleIndex(0);
}
else {
// if we are in append mode and we just changed direction, we should unset what we just set
if (append && self.selection.length > 1 && lastSelectionDirection !== 0 && lastSelectionDirection != direction) {
// grab the end item and unselect it
var end = self.selection[direction > 0 ? 0 : self.selection.length -1];
gridApi.selection.unSelectRow(end);
return;
}
else {
// otherwise just keep track of our direction
lastSelectionDirection = direction;
}
// grab the first or last item depending on the direction we are moving
var item = self.selection[direction < 0 ? 0 : self.selection.length -1];
// if we are not appending unselect everything
if (!append) {
_.each(self.selection, gridApi.selection.unSelectRow);
}
// get the next or prev item index
var index = _.findIndex(FileBrowserService.items, function(i) {
return i.id === item.id;
});
index += direction;
// keep index in range
index = Math.min(Math.max(index, 0), FileBrowserService.items.length-1);
var nextItem = FileBrowserService.items[index];
// select the row
gridApi.selection.selectRow(nextItem);
}
}
// set up our hotkeys
hotkeys.bindTo($scope)
.add({
combo: 'up',
description: 'Move up',
callback: function() {
moveSelection(-1, false);
}
})
.add({
combo: 'shift+up',
description: "Move down",
callback: function() {
moveSelection(-1, true);
}
})
.add({
combo: 'down',
description: 'Move down',
callback: function() {
moveSelection(1, false);
}
})
.add({
combo: 'shift+down',
description: 'Move down',
callback: function() {
moveSelection(1, true);
}
})
.add({
combo: 'enter',
description: "Open in viewer",
callback: function() {
openInViewer();
}
})
.add({
combo: 'left',
description: 'collapse',
callback: function() {
_.each(self.selection, function(item) {
if (item.isContainer) {
self.collapse(item);
}
})
}
})
.add({
combo: 'right',
description: 'collapse',
callback: function() {
_.each(self.selection, function(item) {
if (item.isContainer) {
self.expand(item);
}
})
}
});
// add our expand function to the scope
self.expand = function(item, $event) {
// stop the mouse event (an expand doesn't mean we are selecting that item)
if ($event) {
$event.stopImmediatePropagation();
}
// set the loading state
item.$$loading = true;
// call expand on our service
FileBrowserService.expand(item)
.then(function() {
// on success set expanded to true
item.$$expanded = true;
}, function() {
// on failure flash an error
NotificationsService.flashWarnings($translate('FileBrowser.Error.Expand', item));
item.$$expanded = false;
})
.finally(function() {
// either way set our loading state to false
item.$$loading = false;
})
};
// add our collapse function to the scope
self.collapse = function(item, $event) {
// stop the event propagation (collapse does not mean selection)
if ($event) {
$event.stopImmediatePropagation();
}
FileBrowserService.collapse(item);
item.$$expanded = false;
};
/**
* Open an item (or the current selection in the viewer)
*
* @param item {PIXItem} the item open. If no item is passed, we'll use the current selection
*/
function openInViewer(item) {
// if we have an item wrap it in an array, otherwise just grab the selection
var collection = item ? [item] : self.selection;
// create a new ItemsCollectionModel using our selected items
var collectionModel = new ItemsCollectionModel();
collectionModel.initializeFromExistingData(collection);
// dispatch an event with our collection to open the viewer
$scope.emitBusEvent('PIX.VIEWER.OPEN', {
items: collectionModel,
current: collectionModel.collection[0],
skipUnpackCurrent: true
});
}
// add open in viewer to the scope
self.openInViewer = openInViewer;
/**
* Shares the currently selected item(s)
*/
function share() {
// create a collection from our selection
var collectionModel = new ItemsCollectionModel();
collectionModel.initializeFromExistingData(self.selection);
// set the attachments to our collection and show the message dialog
self.sendAttachments = collectionModel;
self.sendVisible = true;
}
self.share = share;
// register a listener for the share close event and upon i hide the message-send directive
$scope.onBusEvent('PIX.SHARE.CLOSE', function() {
self.sendVisible = false;
});
self.isLoading = true;
FileBrowserService.init().then(function() {
/**
* Check if the item is a container and already expanded. If it is, update the UI.
* @param item
*/
function checkDeepExpansion(item) {
if (item.contentsCollectionModel && item.$$expanded) {
FileBrowserService.expand(item);
item.contentsCollectionModel.collection.forEach(function(innerItem) {
checkDeepExpansion(innerItem);
});
}
}
self.isLoading = false;
FileBrowserService.items.forEach(function(item) {
checkDeepExpansion(item);
});
});
Object.defineProperty(self, 'canShare', {
get: function () {
if (!self.selection || self.selection.length === 0) {
return false;
}
return _.every(self.selection, function (item) {
return item.viewData.permissions.canSend && !(item instanceof PIXFolder);
});
}
});
}
module.exports = angular
.module('PIX.FileBrowser.Controller', [
'angularMoment',
'cfp.hotkeys'
])
.controller('FileBrowserController', FileBrowserController);