diff --git a/composer.json b/composer.json
index c3ff60a..c2563b8 100644
--- a/composer.json
+++ b/composer.json
@@ -32,6 +32,9 @@
"bower-asset-library": "vendor/bower"
}
},
+ "suggest": {
+ "pheme/yii2-settings": "Allows more advanced settings fpr e.g. FileManagerWidget"
+ },
"autoload": {
"psr-4": {
"hrzg\\filemanager\\": "src/"
diff --git a/src/assets/dist/angular-filemanager.min.js b/src/assets/dist/angular-filemanager.min.js
index 6b631b9..3f55af6 100755
--- a/src/assets/dist/angular-filemanager.min.js
+++ b/src/assets/dist/angular-filemanager.min.js
@@ -24,13 +24,13 @@
if(leftBorder>=window.innerWidth-width){
leftBorder-=width;
}
-
+
menu.hide().css( {
- position: 'fixed',
+ position: 'fixed',
left: leftBorder,
top: e.pageY-offset.top
}).show();
-
+
e.preventDefault();
});
@@ -56,7 +56,7 @@
return undefined;
};
}
-
+
})(window, angular, jQuery);
(function(angular, $) {
@@ -65,283 +65,302 @@
'$scope', '$rootScope', '$window', '$translate', '$http', 'fileManagerConfig', 'item', 'fileNavigator', 'apiMiddleware',
function($scope, $rootScope, $window, $translate, $http, fileManagerConfig, Item, FileNavigator, ApiMiddleware) {
- var $storage = $window.localStorage;
- $scope.config = fileManagerConfig;
- $scope.reverse = false;
- $scope.predicate = ['model.type', 'model.name'];
- $scope.order = function(predicate) {
- $scope.reverse = ($scope.predicate[1] === predicate) ? !$scope.reverse : false;
- $scope.predicate[1] = predicate;
- };
- $scope.query = '';
- $scope.fileNavigator = new FileNavigator();
- $scope.apiMiddleware = new ApiMiddleware();
- $scope.uploadFileList = [];
- $scope.viewTemplate = $storage.getItem('viewTemplate') || 'main-icons.html';
- $scope.fileList = [];
- $scope.temps = [];
-
- $scope.$watch('temps', function() {
- if ($scope.singleSelection()) {
- $scope.temp = $scope.singleSelection();
- } else {
- $scope.temp = new Item({rights: 644});
- $scope.temp.multiple = true;
- }
- $scope.temp.revert();
- });
-
- $scope.fileNavigator.onRefresh = function() {
- $scope.temps = [];
+ var $storage = $window.localStorage;
+ $scope.config = fileManagerConfig;
+ $scope.reverse = false;
+ $scope.predicate = ['model.type', 'model.name'];
+ $scope.order = function(predicate) {
+ $scope.reverse = ($scope.predicate[1] === predicate) ? !$scope.reverse : false;
+ $scope.predicate[1] = predicate;
+ };
$scope.query = '';
- $rootScope.selectedModalPath = $scope.fileNavigator.currentPath;
- };
+ $scope.fileNavigator = new FileNavigator();
+ $scope.apiMiddleware = new ApiMiddleware();
+ $scope.uploadFileList = [];
+ $scope.viewTemplate = $storage.getItem('viewTemplate') || 'main-icons.html';
+ $scope.fileList = [];
+ $scope.temps = [];
- $scope.setTemplate = function(name) {
- $storage.setItem('viewTemplate', name);
- $scope.viewTemplate = name;
- };
+ $scope.$watch('temps', function() {
+ if ($scope.singleSelection()) {
+ $scope.temp = $scope.singleSelection();
+ } else {
+ $scope.temp = new Item({rights: 644});
+ $scope.temp.multiple = true;
+ }
+ $scope.temp.revert();
+ });
- $scope.changeLanguage = function (locale) {
- if (locale) {
- $storage.setItem('language', locale);
- return $translate.use(locale);
- }
- $translate.use($storage.getItem('language') || fileManagerConfig.defaultLang);
- };
+ $scope.fileNavigator.onRefresh = function() {
+ $scope.temps = [];
+ $scope.query = '';
+ $rootScope.selectedModalPath = $scope.fileNavigator.currentPath;
+ };
- $scope.isSelected = function(item) {
- return $scope.temps.indexOf(item) !== -1;
- };
+ $scope.setTemplate = function(name) {
+ $storage.setItem('viewTemplate', name);
+ $scope.viewTemplate = name;
+ };
- $scope.selectOrUnselect = function(item, $event) {
- var indexInTemp = $scope.temps.indexOf(item);
- var isRightClick = $event && $event.which == 3;
+ $scope.changeLanguage = function (locale) {
+ if (locale) {
+ $storage.setItem('language', locale);
+ return $translate.use(locale);
+ }
+ $translate.use($storage.getItem('language') || fileManagerConfig.defaultLang);
+ };
- if ($event && $event.target.hasAttribute('prevent')) {
- $scope.temps = [];
- return;
- }
- if (! item || (isRightClick && $scope.isSelected(item))) {
- return;
- }
- if ($event && $event.shiftKey && !isRightClick) {
- var list = $scope.fileList;
- var indexInList = list.indexOf(item);
- var lastSelected = $scope.temps[0];
- var i = list.indexOf(lastSelected);
- var current = undefined;
- if (lastSelected && list.indexOf(lastSelected) < indexInList) {
+ $scope.isSelected = function(item) {
+ return $scope.temps.indexOf(item) !== -1;
+ };
+
+ $scope.selectOrUnselect = function(item, $event) {
+ var indexInTemp = $scope.temps.indexOf(item);
+ var isRightClick = $event && $event.which == 3;
+
+ if ($event && $event.target.hasAttribute('prevent')) {
$scope.temps = [];
- while (i <= indexInList) {
- current = list[i];
- !$scope.isSelected(current) && $scope.temps.push(current);
- i++;
- }
return;
}
- if (lastSelected && list.indexOf(lastSelected) > indexInList) {
- $scope.temps = [];
- while (i >= indexInList) {
- current = list[i];
- !$scope.isSelected(current) && $scope.temps.push(current);
- i--;
+ if (! item || (isRightClick && $scope.isSelected(item))) {
+ return;
+ }
+ if ($event && $event.shiftKey && !isRightClick) {
+ var list = $scope.fileList;
+ var indexInList = list.indexOf(item);
+ var lastSelected = $scope.temps[0];
+ var i = list.indexOf(lastSelected);
+ var current = undefined;
+ if (lastSelected && list.indexOf(lastSelected) < indexInList) {
+ $scope.temps = [];
+ while (i <= indexInList) {
+ current = list[i];
+ !$scope.isSelected(current) && $scope.temps.push(current);
+ i++;
+ }
+ return;
}
+ if (lastSelected && list.indexOf(lastSelected) > indexInList) {
+ $scope.temps = [];
+ while (i >= indexInList) {
+ current = list[i];
+ !$scope.isSelected(current) && $scope.temps.push(current);
+ i--;
+ }
+ return;
+ }
+ }
+ if ($event && !isRightClick && ($event.ctrlKey || $event.metaKey)) {
+ $scope.isSelected(item) ? $scope.temps.splice(indexInTemp, 1) : $scope.temps.push(item);
return;
}
- }
- if ($event && !isRightClick && ($event.ctrlKey || $event.metaKey)) {
- $scope.isSelected(item) ? $scope.temps.splice(indexInTemp, 1) : $scope.temps.push(item);
- return;
- }
- $scope.temps = [item];
- };
-
- $scope.singleSelection = function() {
- return $scope.temps.length === 1 && $scope.temps[0];
- };
+ $scope.temps = [item];
+ };
- $scope.totalSelecteds = function() {
- return {
- total: $scope.temps.length
+ $scope.singleSelection = function() {
+ return $scope.temps.length === 1 && $scope.temps[0];
};
- };
- $scope.selectionHas = function(type) {
- return $scope.temps.find(function(item) {
- return item && item.model.type === type;
- });
- };
+ $scope.totalSelecteds = function() {
+ return {
+ total: $scope.temps.length
+ };
+ };
- $scope.prepareNewFolder = function() {
- var item = new Item(null, $scope.fileNavigator.currentPath);
- $scope.temps = [item];
- return item;
- };
+ $scope.selectionHas = function(type) {
+ return $scope.temps.find(function(item) {
+ return item && item.model.type === type;
+ });
+ };
- $scope.smartClick = function(item) {
- var pick = $scope.config.allowedActions.pickFiles;
- if (item.isFolder()) {
- return $scope.fileNavigator.folderClick(item);
- }
+ $scope.prepareNewFolder = function() {
+ var item = new Item(null, $scope.fileNavigator.currentPath);
+ $scope.temps = [item];
+ return item;
+ };
- if (typeof $scope.config.pickCallback === 'function' && pick) {
- var callbackSuccess = $scope.config.pickCallback(item.model);
- if (callbackSuccess === true) {
- return;
+ $scope.smartClick = function(item) {
+ var pick = $scope.config.allowedActions.pickFiles;
+ if (item.isFolder()) {
+ return $scope.fileNavigator.folderClick(item);
}
- }
- if (item.isImage()) {
- if ($scope.config.previewImagesInModal) {
- return $scope.openImagePreview(item);
+ if (typeof $scope.config.pickCallback === 'function' && pick) {
+ var callbackSuccess = $scope.config.pickCallback(item.model);
+ if (callbackSuccess === true) {
+ return;
+ }
}
- return $scope.apiMiddleware.download(item, true);
- }
- if (item.isEditable()) {
- return $scope.openEditItem(item);
- }
- };
+ if (item.isImage()) {
+ if ($scope.config.previewImagesInModal) {
+ return $scope.openImagePreview(item);
+ }
+ return $scope.apiMiddleware.download(item, true);
+ }
- $scope.openImagePreview = function() {
- var item = $scope.singleSelection();
- $scope.apiMiddleware.apiHandler.inprocess = true;
- $scope.modal('imagepreview', null, true)
- .find('#imagepreview-target')
- .attr('src', $scope.apiMiddleware.getUrl(item))
- .unbind('load error')
- .on('load error', function() {
- $scope.apiMiddleware.apiHandler.inprocess = false;
- $scope.$apply();
- });
- };
+ if (item.isEditable()) {
+ return $scope.openEditItem(item);
+ }
+ };
- $scope.openEditItem = function() {
- var item = $scope.singleSelection();
- $scope.apiMiddleware.getContent(item).then(function(data) {
- item.tempModel.content = item.model.content = data.result;
- });
- $scope.modal('edit');
- };
+ $scope.openImagePreview = function() {
+ var item = $scope.singleSelection();
+ $scope.apiMiddleware.apiHandler.inprocess = true;
+ $scope.modal('imagepreview', null, true)
+ .find('#imagepreview-target')
+ .attr('src', $scope.apiMiddleware.getUrl(item))
+ .unbind('load error')
+ .on('load error', function() {
+ $scope.apiMiddleware.apiHandler.inprocess = false;
+ $scope.$apply();
+ });
+ };
- $scope.modal = function(id, hide, returnElement) {
- var element = $('#' + id);
- element.modal(hide ? 'hide' : 'show');
- $scope.apiMiddleware.apiHandler.error = '';
- $scope.apiMiddleware.apiHandler.asyncSuccess = false;
- return returnElement ? element : true;
- };
+ $scope.openEditItem = function() {
+ var item = $scope.singleSelection();
+ $scope.apiMiddleware.getContent(item).then(function(data) {
+ item.tempModel.content = item.model.content = data.result;
+ });
+ $scope.modal('edit');
+ };
- $scope.modalWithPathSelector = function(id) {
- $rootScope.selectedModalPath = $scope.fileNavigator.currentPath;
- return $scope.modal(id);
- };
+ $scope.modal = function(id, hide, returnElement) {
+ var element = $('#' + id);
+ element.modal(hide ? 'hide' : 'show');
+ $scope.apiMiddleware.apiHandler.error = '';
+ $scope.apiMiddleware.apiHandler.asyncSuccess = false;
+ return returnElement ? element : true;
+ };
- $scope.isInThisPath = function(path) {
- var currentPath = $scope.fileNavigator.currentPath.join('/');
- return currentPath.indexOf(path) !== -1;
- };
+ $scope.modalWithPathSelector = function(id) {
+ $rootScope.selectedModalPath = $scope.fileNavigator.currentPath;
+ return $scope.modal(id);
+ };
- $scope.edit = function() {
- $scope.apiMiddleware.edit($scope.singleSelection()).then(function() {
- $scope.modal('edit', true);
- });
- };
+ $scope.isInThisPath = function(path) {
+ var currentPath = $scope.fileNavigator.currentPath.join('/');
+ return currentPath.indexOf(path) !== -1;
+ };
- $scope.updatePermissions = function () {
- var item = $scope.singleSelection();
- var apiUrl = fileManagerConfig.permissionsUrl;
- var data = {
- action: 'resolvePermissions',
- path: item.model.fullPath()
+ $scope.edit = function() {
+ $scope.apiMiddleware.edit($scope.singleSelection()).then(function() {
+ $scope.modal('edit', true);
+ });
};
- $http.post(apiUrl, data)
- .success(function (data) {
- item.model.authRead = data.auth.read;
- item.model.authUpdate = data.auth.update;
- item.model.authDelete = data.auth.delete;
- }).error(function () {
+ $scope.updatePermissions = function () {
+ var item = $scope.singleSelection();
+ var apiUrl = fileManagerConfig.permissionsUrl;
+ var data = {
+ action: 'resolvePermissions',
+ path: item.model.fullPath()
+ };
+
+ $http.post(apiUrl, data)
+ .success(function (data) {
+ item.model.authRead = data.auth.read;
+ item.model.authUpdate = data.auth.update;
+ item.model.authDelete = data.auth.delete;
+ }).error(function () {
$scope.apiMiddleware.apiHandler.error = $translate.instant('error_get_permissions');
})['finally'](function () {
- $scope.modal('changepermissions', false);
- });
- };
- $scope.changePermissions = function() {
- var item = $scope.singleSelection();
- $scope.apiMiddleware.changePermissions(item.tempModel).then(function() {
- $scope.modal('changepermissions', true);
- });
- };
+ $scope.modal('changepermissions', false);
+ });
+ };
+ $scope.changePermissions = function() {
+ var item = $scope.singleSelection();
+ $scope.apiMiddleware.changePermissions(item.tempModel).then(function() {
+ $scope.modal('changepermissions', true);
+ });
+ };
- $scope.rename = function() {
- var item = $scope.singleSelection();
- var name = item.tempModel.name;
- var samePath = item.tempModel.path.join('') === item.model.path.join('');
- if (!name || (samePath && $scope.fileNavigator.fileNameExists(name))) {
- $scope.apiMiddleware.apiHandler.error = $translate.instant('error_invalid_filename');
- return false;
- }
- $scope.apiMiddleware.rename(item).then(function() {
- $scope.fileNavigator.refresh();
- $scope.modal('rename', true);
- });
- };
+ $scope.rename = function() {
+ var item = $scope.singleSelection();
+ var name = item.tempModel.name;
+ var samePath = item.tempModel.path.join('') === item.model.path.join('');
+ if (!name || (samePath && $scope.fileNavigator.fileNameExists(name))) {
+ $scope.apiMiddleware.apiHandler.error = $translate.instant('error_invalid_filename');
+ return false;
+ }
+ $scope.apiMiddleware.rename(item).then(function() {
+ $scope.fileNavigator.refresh();
+ $scope.modal('rename', true);
+ });
+ };
- $scope.refreshLocation = function(recycle) {
- $scope.fileNavigator.refresh(recycle);
- };
+ $scope.refreshLocation = function(recycle) {
+ $scope.fileNavigator.refresh(recycle);
+ };
- $scope.downloadLink = function() {
- var item = $scope.singleSelection();
- if ($scope.selectionHas('dir')) {
- return;
- }
+ $scope.downloadLink = function() {
+ var item = $scope.singleSelection();
+ if ($scope.selectionHas('dir')) {
+ return;
+ }
- var itemFullPath = item.model.fullPath();
+ var itemFullPath = item.model.fullPath();
- // generate download url
- var downloadData = {
- action: 'download',
- path: itemFullPath
- };
+ // generate download url
+ var downloadData = {
+ action: 'download',
+ path: itemFullPath
+ };
- // generate stream url
- var streamData = {
- action: 'stream',
- path: itemFullPath
- };
+ // generate stream url
+ var streamData = {
+ action: 'stream',
+ path: itemFullPath
+ };
- var domain = window.location.protocol + '//' + window.location.host;
- var downloadPath = [fileManagerConfig.downloadFileUrl, $.param(downloadData)].join('?');
- var streamPath = [fileManagerConfig.downloadFileUrl, $.param(streamData)].join('?');
+ var domain = window.location.protocol + '//' + window.location.host;
+ var downloadPath = [fileManagerConfig.downloadFileUrl, $.param(downloadData)].join('?');
+ var streamPath = [fileManagerConfig.downloadFileUrl, $.param(streamData)].join('?');
- $scope.dlLink = domain + downloadPath;
- $scope.streamLink = streamPath;
- $scope.path = itemFullPath;
+ $scope.dlLink = domain + downloadPath;
+ $scope.streamLink = streamPath;
+ $scope.path = itemFullPath;
- //open modal
- $scope.modal('downloadlink', false);
- };
+ //open modal
+ $scope.modal('downloadlink', false);
+ };
- $scope.download = function() {
- var item = $scope.singleSelection();
- if ($scope.selectionHas('dir')) {
- return;
- }
- if (item) {
- return $scope.apiMiddleware.download(item);
- }
- return $scope.apiMiddleware.downloadMultiple($scope.temps);
- };
+ $scope.download = function() {
+ var item = $scope.singleSelection();
+ if ($scope.selectionHas('dir')) {
+ return;
+ }
+ if (item) {
+ return $scope.apiMiddleware.download(item);
+ }
+ return $scope.apiMiddleware.downloadMultiple($scope.temps);
+ };
- $scope.copy = function() {
- var item = $scope.singleSelection();
- if (item) {
- var name = item.tempModel.name.trim();
+ $scope.copy = function() {
+ var item = $scope.singleSelection();
+ if (item) {
+ var name = item.tempModel.name.trim();
+ var nameExists = $scope.fileNavigator.fileNameExists(name);
+ if (nameExists && validateSamePath(item)) {
+ $scope.apiMiddleware.apiHandler.error = $translate.instant('error_invalid_filename');
+ return false;
+ }
+ if (!name) {
+ $scope.apiMiddleware.apiHandler.error = $translate.instant('error_invalid_filename');
+ return false;
+ }
+ }
+ $scope.apiMiddleware.copy($scope.temps, $rootScope.selectedModalPath).then(function() {
+ $scope.fileNavigator.refresh();
+ $scope.modal('copy', true);
+ });
+ };
+
+ $scope.compress = function() {
+ var name = $scope.temp.tempModel.name.trim();
var nameExists = $scope.fileNavigator.fileNameExists(name);
- if (nameExists && validateSamePath(item)) {
+
+ if (nameExists && validateSamePath($scope.temp)) {
$scope.apiMiddleware.apiHandler.error = $translate.instant('error_invalid_filename');
return false;
}
@@ -349,249 +368,189 @@
$scope.apiMiddleware.apiHandler.error = $translate.instant('error_invalid_filename');
return false;
}
- }
- $scope.apiMiddleware.copy($scope.temps, $rootScope.selectedModalPath).then(function() {
- $scope.fileNavigator.refresh();
- $scope.modal('copy', true);
- });
- };
- $scope.compress = function() {
- var name = $scope.temp.tempModel.name.trim();
- var nameExists = $scope.fileNavigator.fileNameExists(name);
+ $scope.apiMiddleware.compress($scope.temps, name, $rootScope.selectedModalPath).then(function() {
+ $scope.fileNavigator.refresh();
+ if (! $scope.config.compressAsync) {
+ return $scope.modal('compress', true);
+ }
+ $scope.apiMiddleware.apiHandler.asyncSuccess = true;
+ }, function() {
+ $scope.apiMiddleware.apiHandler.asyncSuccess = false;
+ });
+ };
- if (nameExists && validateSamePath($scope.temp)) {
- $scope.apiMiddleware.apiHandler.error = $translate.instant('error_invalid_filename');
- return false;
- }
- if (!name) {
- $scope.apiMiddleware.apiHandler.error = $translate.instant('error_invalid_filename');
- return false;
- }
+ $scope.extract = function() {
+ var item = $scope.temp;
+ var name = $scope.temp.tempModel.name.trim();
+ var nameExists = $scope.fileNavigator.fileNameExists(name);
- $scope.apiMiddleware.compress($scope.temps, name, $rootScope.selectedModalPath).then(function() {
- $scope.fileNavigator.refresh();
- if (! $scope.config.compressAsync) {
- return $scope.modal('compress', true);
+ if (nameExists && validateSamePath($scope.temp)) {
+ $scope.apiMiddleware.apiHandler.error = $translate.instant('error_invalid_filename');
+ return false;
+ }
+ if (!name) {
+ $scope.apiMiddleware.apiHandler.error = $translate.instant('error_invalid_filename');
+ return false;
}
- $scope.apiMiddleware.apiHandler.asyncSuccess = true;
- }, function() {
- $scope.apiMiddleware.apiHandler.asyncSuccess = false;
- });
- };
- $scope.extract = function() {
- var item = $scope.temp;
- var name = $scope.temp.tempModel.name.trim();
- var nameExists = $scope.fileNavigator.fileNameExists(name);
+ $scope.apiMiddleware.extract(item, name, $rootScope.selectedModalPath).then(function() {
+ $scope.fileNavigator.refresh();
+ if (! $scope.config.extractAsync) {
+ return $scope.modal('extract', true);
+ }
+ $scope.apiMiddleware.apiHandler.asyncSuccess = true;
+ }, function() {
+ $scope.apiMiddleware.apiHandler.asyncSuccess = false;
+ });
+ };
- if (nameExists && validateSamePath($scope.temp)) {
- $scope.apiMiddleware.apiHandler.error = $translate.instant('error_invalid_filename');
- return false;
- }
- if (!name) {
- $scope.apiMiddleware.apiHandler.error = $translate.instant('error_invalid_filename');
- return false;
- }
+ $scope.remove = function() {
+ $scope.apiMiddleware.remove($scope.temps).then(function() {
+ $scope.fileNavigator.refresh();
+ $scope.modal('remove', true);
+ });
+ };
- $scope.apiMiddleware.extract(item, name, $rootScope.selectedModalPath).then(function() {
- $scope.fileNavigator.refresh();
- if (! $scope.config.extractAsync) {
- return $scope.modal('extract', true);
+ $scope.move = function() {
+ var anyItem = $scope.singleSelection() || $scope.temps[0];
+ if (anyItem && validateSamePath(anyItem)) {
+ $scope.apiMiddleware.apiHandler.error = $translate.instant('error_cannot_move_same_path');
+ return false;
}
- $scope.apiMiddleware.apiHandler.asyncSuccess = true;
- }, function() {
- $scope.apiMiddleware.apiHandler.asyncSuccess = false;
- });
- };
-
- $scope.remove = function() {
- $scope.apiMiddleware.remove($scope.temps).then(function() {
- $scope.fileNavigator.refresh();
- $scope.modal('remove', true);
- });
- };
-
- $scope.move = function() {
- var anyItem = $scope.singleSelection() || $scope.temps[0];
- if (anyItem && validateSamePath(anyItem)) {
- $scope.apiMiddleware.apiHandler.error = $translate.instant('error_cannot_move_same_path');
- return false;
- }
- $scope.apiMiddleware.move($scope.temps, $rootScope.selectedModalPath).then(function() {
- $scope.fileNavigator.refresh();
- $scope.modal('move', true);
- });
- };
+ $scope.apiMiddleware.move($scope.temps, $rootScope.selectedModalPath).then(function() {
+ $scope.fileNavigator.refresh();
+ $scope.modal('move', true);
+ });
+ };
- $scope.createFolder = function() {
- var item = $scope.singleSelection();
- var name = item.tempModel.name;
- if (!name || $scope.fileNavigator.fileNameExists(name)) {
- return $scope.apiMiddleware.apiHandler.error = $translate.instant('error_invalid_filename');
- }
- $scope.apiMiddleware.createFolder(item).then(function() {
- $scope.fileNavigator.refresh();
- $scope.modal('newfolder', true);
- });
- };
+ $scope.createFolder = function() {
+ var item = $scope.singleSelection();
+ var name = item.tempModel.name;
+ if (!name || $scope.fileNavigator.fileNameExists(name)) {
+ return $scope.apiMiddleware.apiHandler.error = $translate.instant('error_invalid_filename');
+ }
+ $scope.apiMiddleware.createFolder(item).then(function() {
+ $scope.fileNavigator.refresh();
+ $scope.modal('newfolder', true);
+ });
+ };
- $scope.addForUpload = function($files) {
- $scope.uploadFileList = $scope.uploadFileList.concat($files);
- $scope.modal('uploadfile');
- };
+ $scope.addForUpload = function($files) {
+ $scope.uploadFileList = $scope.uploadFileList.concat($files);
+ $scope.modal('uploadfile');
+ };
- $scope.removeFromUpload = function(index) {
- $scope.uploadFileList.splice(index, 1);
- };
+ $scope.removeFromUpload = function(index) {
+ $scope.uploadFileList.splice(index, 1);
+ };
- $scope.uploadFiles = function() {
- $scope.apiMiddleware.upload($scope.uploadFileList, $scope.fileNavigator.currentPath).then(function() {
- $scope.fileNavigator.refresh();
- $scope.uploadFileList = [];
- $scope.modal('uploadfile', true);
- }, function(data) {
- var errorMsg = data.result && data.result.error || $translate.instant('error_uploading_files');
- $scope.apiMiddleware.apiHandler.error = errorMsg;
- });
- };
+ $scope.uploadFiles = function() {
+ $scope.apiMiddleware.upload($scope.uploadFileList, $scope.fileNavigator.currentPath).then(function() {
+ $scope.fileNavigator.refresh();
+ $scope.uploadFileList = [];
+ $scope.modal('uploadfile', true);
+ }, function(data) {
+ var errorMsg = data.result && data.result.error || $translate.instant('error_uploading_files');
+ $scope.apiMiddleware.apiHandler.error = errorMsg;
+ });
+ };
- var validateSamePath = function(item) {
- var selectedPath = $rootScope.selectedModalPath.join('');
- var selectedItemsPath = item && item.model.path.join('');
- return selectedItemsPath === selectedPath;
- };
+ var validateSamePath = function(item) {
+ var selectedPath = $rootScope.selectedModalPath.join('');
+ var selectedItemsPath = item && item.model.path.join('');
+ return selectedItemsPath === selectedPath;
+ };
- var getQueryParam = function(param) {
- var found = $window.location.search.substr(1).split('&').filter(function(item) {
- return param === item.split('=')[0];
- });
- return found[0] && found[0].split('=')[1] || undefined;
- };
+ var getQueryParam = function(param) {
+ var found = $window.location.search.substr(1).split('&').filter(function(item) {
+ return param === item.split('=')[0];
+ });
+ return found[0] && found[0].split('=')[1] || undefined;
+ };
- $scope.changeLanguage(getQueryParam('lang'));
- $scope.isWindows = getQueryParam('server') === 'Windows';
- $scope.fileNavigator.refresh();
+ $scope.changeLanguage(getQueryParam('lang'));
+ $scope.isWindows = getQueryParam('server') === 'Windows';
+ $scope.fileNavigator.refresh();
- }]);
+ }]);
})(angular, jQuery);
(function(angular) {
'use strict';
- angular.module('FileManagerApp').controller('ModalFileManagerCtrl',
+ angular.module('FileManagerApp').controller('ModalFileManagerCtrl',
['$scope', '$rootScope', 'fileNavigator', function($scope, $rootScope, FileNavigator) {
- $scope.reverse = false;
- $scope.predicate = ['model.type', 'model.name'];
- $scope.fileNavigator = new FileNavigator();
- $rootScope.selectedModalPath = [];
+ $scope.reverse = false;
+ $scope.predicate = ['model.type', 'model.name'];
+ $scope.fileNavigator = new FileNavigator();
+ $rootScope.selectedModalPath = [];
- $scope.order = function(predicate) {
- $scope.reverse = ($scope.predicate[1] === predicate) ? !$scope.reverse : false;
- $scope.predicate[1] = predicate;
- };
+ $scope.order = function(predicate) {
+ $scope.reverse = ($scope.predicate[1] === predicate) ? !$scope.reverse : false;
+ $scope.predicate[1] = predicate;
+ };
- $scope.select = function(item) {
- $rootScope.selectedModalPath = item.model.fullPath().split('/');
- $scope.modal('selector', true);
- };
+ $scope.select = function(item) {
+ $rootScope.selectedModalPath = item.model.fullPath().split('/');
+ $scope.modal('selector', true);
+ };
- $scope.selectCurrent = function() {
- $rootScope.selectedModalPath = $scope.fileNavigator.currentPath;
- $scope.modal('selector', true);
- };
+ $scope.selectCurrent = function() {
+ $rootScope.selectedModalPath = $scope.fileNavigator.currentPath;
+ $scope.modal('selector', true);
+ };
- $scope.selectedFilesAreChildOfPath = function(item) {
- var path = item.model.fullPath();
- return $scope.temps.find(function(item) {
- var itemPath = item.model.fullPath();
- if (path == itemPath) {
- return true;
- }
- /*
- if (path.startsWith(itemPath)) {
- fixme names in same folder like folder-one and folder-one-two
- at the moment fixed hidding affected folders
- }
- */
- });
- };
+ $scope.selectedFilesAreChildOfPath = function(item) {
+ var path = item.model.fullPath();
+ return $scope.temps.find(function(item) {
+ var itemPath = item.model.fullPath();
+ if (path == itemPath) {
+ return true;
+ }
+ /*
+ if (path.startsWith(itemPath)) {
+ fixme names in same folder like folder-one and folder-one-two
+ at the moment fixed hidding affected folders
+ }
+ */
+ });
+ };
- $rootScope.openNavigator = function(path) {
- $scope.fileNavigator.currentPath = path;
- $scope.fileNavigator.refresh();
- $scope.modal('selector');
- };
+ $rootScope.openNavigator = function(path) {
+ $scope.fileNavigator.currentPath = path;
+ $scope.fileNavigator.refresh();
+ $scope.modal('selector');
+ };
- $rootScope.getSelectedPath = function() {
- var path = $rootScope.selectedModalPath.filter(Boolean);
- var result = '/' + path.join('/');
- if ($scope.singleSelection() && !$scope.singleSelection().isFolder()) {
- result += '/' + $scope.singleSelection().tempModel.name;
- }
- return result.replace(/\/\//, '/');
- };
+ $rootScope.getSelectedPath = function() {
+ var path = $rootScope.selectedModalPath.filter(Boolean);
+ var result = '/' + path.join('/');
+ if ($scope.singleSelection() && !$scope.singleSelection().isFolder()) {
+ result += '/' + $scope.singleSelection().tempModel.name;
+ }
+ return result.replace(/\/\//, '/');
+ };
- }]);
+ }]);
})(angular);
(function(angular) {
'use strict';
- var app = angular.module('FileManagerApp');
+ angular.module('FileManagerApp').service('chmod', function () {
- app.directive('angularFilemanager', ['$parse', 'fileManagerConfig', function($parse, fileManagerConfig) {
- return {
- restrict: 'EA',
- templateUrl: fileManagerConfig.tplPath + '/main.html'
- };
- }]);
+ var Chmod = function(initValue) {
+ this.owner = this.getRwxObj();
+ this.group = this.getRwxObj();
+ this.others = this.getRwxObj();
- app.directive('ngFile', ['$parse', function($parse) {
- return {
- restrict: 'A',
- link: function(scope, element, attrs) {
- var model = $parse(attrs.ngFile);
- var modelSetter = model.assign;
+ if (initValue) {
+ var codes = isNaN(initValue) ?
+ this.convertfromCode(initValue):
+ this.convertfromOctal(initValue);
- element.bind('change', function() {
- scope.$apply(function() {
- modelSetter(scope, element[0].files);
- });
- });
- }
- };
- }]);
-
- app.directive('ngRightClick', ['$parse', function($parse) {
- return function(scope, element, attrs) {
- var fn = $parse(attrs.ngRightClick);
- element.bind('contextmenu', function(event) {
- scope.$apply(function() {
- event.preventDefault();
- fn(scope, {$event: event});
- });
- });
- };
- }]);
-
-})(angular);
-
-(function(angular) {
- 'use strict';
- angular.module('FileManagerApp').service('chmod', function () {
-
- var Chmod = function(initValue) {
- this.owner = this.getRwxObj();
- this.group = this.getRwxObj();
- this.others = this.getRwxObj();
-
- if (initValue) {
- var codes = isNaN(initValue) ?
- this.convertfromCode(initValue):
- this.convertfromOctal(initValue);
-
- if (! codes) {
- throw new Error('Invalid chmod input data (%s)'.replace('%s', initValue));
- }
+ if (! codes) {
+ throw new Error('Invalid chmod input data (%s)'.replace('%s', initValue));
+ }
this.owner = codes.owner;
this.group = codes.group;
@@ -752,654 +711,47 @@
return Item;
}]);
})(angular);
-(function(angular, $) {
- 'use strict';
- angular.module('FileManagerApp').service('apiHandler', ['$http', '$q', '$window', '$translate', 'Upload',
- function ($http, $q, $window, $translate, Upload) {
-
- $http.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
-
- var ApiHandler = function() {
- this.inprocess = false;
- this.asyncSuccess = false;
- this.error = '';
- };
-
- ApiHandler.prototype.deferredHandler = function(data, deferred, code, defaultMsg) {
- if (!data || typeof data !== 'object') {
- this.error = 'Error %s - Bridge response error, please check the API docs or this ajax response.'.replace('%s', code);
- }
- if (code == 404) {
- this.error = 'Error 404 - Backend bridge is not working, please check the ajax response.';
- }
- if (data.result && data.result.error) {
- this.error = data.result.error;
- }
- if (!this.error && data.error) {
- this.error = data.error.message;
- }
- if (!this.error && defaultMsg) {
- this.error = defaultMsg;
- }
- if (this.error) {
- return deferred.reject(data);
- }
- return deferred.resolve(data);
- };
-
- ApiHandler.prototype.list = function(apiUrl, path, customDeferredHandler, recycle) {
- var self = this;
- var dfHandler = customDeferredHandler || self.deferredHandler;
- var deferred = $q.defer();
- var data = {
- action: 'list',
- path: path,
- recycle: recycle
- };
-
- self.inprocess = true;
- self.error = '';
-
- $http.post(apiUrl, data).success(function(data, code) {
- dfHandler(data, deferred, code);
- }).error(function(data, code) {
- dfHandler(data, deferred, code, 'Unknown error listing, check the response');
- })['finally'](function() {
- self.inprocess = false;
- });
- return deferred.promise;
- };
-
- ApiHandler.prototype.copy = function(apiUrl, items, path, singleFilename) {
- var self = this;
- var deferred = $q.defer();
- var data = {
- action: 'copy',
- items: items,
- newPath: path
- };
-
- if (singleFilename && items.length === 1) {
- data.singleFilename = singleFilename;
- }
-
- self.inprocess = true;
- self.error = '';
- $http.post(apiUrl, data).success(function(data, code) {
- self.deferredHandler(data, deferred, code);
- }).error(function(data, code) {
- self.deferredHandler(data, deferred, code, $translate.instant('error_copying'));
- })['finally'](function() {
- self.inprocess = false;
- });
- return deferred.promise;
- };
-
- ApiHandler.prototype.move = function(apiUrl, items, path) {
- var self = this;
- var deferred = $q.defer();
- var data = {
- action: 'move',
- items: items,
- newPath: path
- };
- self.inprocess = true;
- self.error = '';
- $http.post(apiUrl, data).success(function(data, code) {
- self.deferredHandler(data, deferred, code);
- }).error(function(data, code) {
- self.deferredHandler(data, deferred, code, $translate.instant('error_moving'));
- })['finally'](function() {
- self.inprocess = false;
- });
- return deferred.promise;
- };
-
- ApiHandler.prototype.remove = function(apiUrl, items) {
- var self = this;
- var deferred = $q.defer();
- var data = {
- action: 'remove',
- items: items
- };
-
- self.inprocess = true;
- self.error = '';
- $http.post(apiUrl, data).success(function(data, code) {
- self.deferredHandler(data, deferred, code);
- }).error(function(data, code) {
- self.deferredHandler(data, deferred, code, $translate.instant('error_deleting'));
- })['finally'](function() {
- self.inprocess = false;
- });
- return deferred.promise;
- };
-
- ApiHandler.prototype.upload = function(apiUrl, destination, files) {
- var self = this;
- var deferred = $q.defer();
- self.inprocess = true;
- self.progress = 0;
- self.error = '';
-
- var data = {
- destination: destination
- };
-
- for (var i = 0; i < files.length; i++) {
- data['file-' + i] = files[i];
- }
-
- if (files && files.length) {
- Upload.upload({
- url: apiUrl,
- data: data
- }).then(function (data) {
- self.deferredHandler(data.data, deferred, data.status);
- }, function (data) {
- self.deferredHandler(data.data, deferred, data.status, 'Unknown error uploading files');
- }, function (evt) {
- self.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total)) - 1;
- })['finally'](function() {
- self.inprocess = false;
- self.progress = 0;
- });
- }
-
- return deferred.promise;
- };
-
- ApiHandler.prototype.getContent = function(apiUrl, itemPath) {
- var self = this;
- var deferred = $q.defer();
- var data = {
- action: 'getContent',
- item: itemPath
- };
-
- self.inprocess = true;
- self.error = '';
- $http.post(apiUrl, data).success(function(data, code) {
- self.deferredHandler(data, deferred, code);
- }).error(function(data, code) {
- self.deferredHandler(data, deferred, code, $translate.instant('error_getting_content'));
- })['finally'](function() {
- self.inprocess = false;
- });
- return deferred.promise;
- };
-
- ApiHandler.prototype.edit = function(apiUrl, itemPath, content) {
- var self = this;
- var deferred = $q.defer();
- var data = {
- action: 'edit',
- item: itemPath,
- content: content
- };
-
- self.inprocess = true;
- self.error = '';
-
- $http.post(apiUrl, data).success(function(data, code) {
- self.deferredHandler(data, deferred, code);
- }).error(function(data, code) {
- self.deferredHandler(data, deferred, code, $translate.instant('error_modifying'));
- })['finally'](function() {
- self.inprocess = false;
- });
- return deferred.promise;
- };
-
- ApiHandler.prototype.rename = function(apiUrl, itemPath, newPath) {
- var self = this;
- var deferred = $q.defer();
- var data = {
- action: 'rename',
- item: itemPath,
- newItemPath: newPath
- };
- self.inprocess = true;
- self.error = '';
- $http.post(apiUrl, data).success(function(data, code) {
- self.deferredHandler(data, deferred, code);
- }).error(function(data, code) {
- self.deferredHandler(data, deferred, code, $translate.instant('error_renaming'));
- })['finally'](function() {
- self.inprocess = false;
- });
- return deferred.promise;
- };
-
- ApiHandler.prototype.getUrl = function(apiUrl, path) {
- var data = {
- action: 'download',
- path: path
- };
- return path && [apiUrl, $.param(data)].join('?');
- };
-
- ApiHandler.prototype.download = function(apiUrl, itemPath, toFilename, downloadByAjax, forceNewWindow) {
- var self = this;
- var url = this.getUrl(apiUrl, itemPath);
-
- if (!downloadByAjax || forceNewWindow || !$window.saveAs) {
- !$window.saveAs && $window.console.log('Your browser dont support ajax download, downloading by default');
- return !!$window.open(url, '_blank', '');
- }
-
- var deferred = $q.defer();
- self.inprocess = true;
- $http.get(url).success(function(data) {
- var bin = new $window.Blob([data]);
- deferred.resolve(data);
- $window.saveAs(bin, toFilename);
- }).error(function(data, code) {
- self.deferredHandler(data, deferred, code, $translate.instant('error_downloading'));
- })['finally'](function() {
- self.inprocess = false;
- });
- return deferred.promise;
- };
-
- ApiHandler.prototype.downloadMultiple = function(apiUrl, items, toFilename, downloadByAjax, forceNewWindow) {
- var self = this;
- var deferred = $q.defer();
- var data = {
- action: 'downloadMultiple',
- items: items,
- toFilename: toFilename
- };
- var url = [apiUrl, $.param(data)].join('?');
-
- if (!downloadByAjax || forceNewWindow || !$window.saveAs) {
- !$window.saveAs && $window.console.log('Your browser dont support ajax download, downloading by default');
- return !!$window.open(url, '_blank', '');
- }
-
- self.inprocess = true;
- $http.get(apiUrl).success(function(data) {
- var bin = new $window.Blob([data]);
- deferred.resolve(data);
- $window.saveAs(bin, toFilename);
- }).error(function(data, code) {
- self.deferredHandler(data, deferred, code, $translate.instant('error_downloading'));
- })['finally'](function() {
- self.inprocess = false;
- });
- return deferred.promise;
- };
-
- ApiHandler.prototype.compress = function(apiUrl, items, compressedFilename, path) {
- var self = this;
- var deferred = $q.defer();
- var data = {
- action: 'compress',
- items: items,
- destination: path,
- compressedFilename: compressedFilename
- };
-
- self.inprocess = true;
- self.error = '';
- $http.post(apiUrl, data).success(function(data, code) {
- self.deferredHandler(data, deferred, code);
- }).error(function(data, code) {
- self.deferredHandler(data, deferred, code, $translate.instant('error_compressing'));
- })['finally'](function() {
- self.inprocess = false;
- });
- return deferred.promise;
- };
-
- ApiHandler.prototype.extract = function(apiUrl, item, folderName, path) {
- var self = this;
- var deferred = $q.defer();
- var data = {
- action: 'extract',
- item: item,
- destination: path,
- folderName: folderName
- };
-
- self.inprocess = true;
- self.error = '';
- $http.post(apiUrl, data).success(function(data, code) {
- self.deferredHandler(data, deferred, code);
- }).error(function(data, code) {
- self.deferredHandler(data, deferred, code, $translate.instant('error_extracting'));
- })['finally'](function() {
- self.inprocess = false;
- });
- return deferred.promise;
- };
-
- ApiHandler.prototype.changePermissions = function(apiUrl, item) {
- var self = this;
- var deferred = $q.defer();
- var data = {
- action: 'changePermissions',
- path: item.fullPath(),
- item: item
- };
-
- self.inprocess = true;
- self.error = '';
- $http.post(apiUrl, data).success(function(data, code) {
- self.deferredHandler(data, deferred, code);
- }).error(function(data, code) {
- self.deferredHandler(data, deferred, code, $translate.instant('error_changing_perms'));
- })['finally'](function() {
- self.inprocess = false;
- });
- return deferred.promise;
- };
-
- ApiHandler.prototype.createFolder = function(apiUrl, path) {
- var self = this;
- var deferred = $q.defer();
- var data = {
- action: 'createFolder',
- newPath: path
- };
-
- self.inprocess = true;
- self.error = '';
- $http.post(apiUrl, data).success(function(data, code) {
- self.deferredHandler(data, deferred, code);
- }).error(function(data, code) {
- self.deferredHandler(data, deferred, code, $translate.instant('error_creating_folder'));
- })['finally'](function() {
- self.inprocess = false;
- });
-
- return deferred.promise;
- };
-
- ApiHandler.prototype.slug = function(str) {
- var trimmed = $.trim(str);
- var $slug = trimmed.replace(/[^a-z0-9-]/gi, '-').
- replace(/-+/g, '-').
- replace(/^-|-$/g, '');
- return $slug.toLowerCase();
- };
-
- return ApiHandler;
-
- }]);
-})(angular, jQuery);
(function(angular) {
'use strict';
- angular.module('FileManagerApp').service('apiMiddleware', ['$window', 'fileManagerConfig', 'apiHandler',
- function ($window, fileManagerConfig, ApiHandler) {
-
- var ApiMiddleware = function() {
- this.apiHandler = new ApiHandler();
- };
-
- ApiMiddleware.prototype.getPath = function(arrayPath) {
- return '/' + arrayPath.join('/');
- };
-
- ApiMiddleware.prototype.getFileList = function(files) {
- return (files || []).map(function(file) {
- return file && file.model.fullPath();
- });
- };
-
- ApiMiddleware.prototype.getFilePath = function(item) {
- return item && item.model.fullPath();
- };
-
- ApiMiddleware.prototype.list = function(path, customDeferredHandler, recycle) {
- return this.apiHandler.list(fileManagerConfig.listUrl, this.getPath(path), customDeferredHandler, recycle);
- };
-
- ApiMiddleware.prototype.copy = function(files, path) {
- var items = this.getFileList(files);
- var singleFilename = items.length === 1 ? files[0].tempModel.name : undefined;
- return this.apiHandler.copy(fileManagerConfig.copyUrl, items, this.getPath(path), singleFilename);
- };
-
- ApiMiddleware.prototype.move = function(files, path) {
- var items = this.getFileList(files);
- return this.apiHandler.move(fileManagerConfig.moveUrl, items, this.getPath(path));
- };
-
- ApiMiddleware.prototype.remove = function(files) {
- var items = this.getFileList(files);
- return this.apiHandler.remove(fileManagerConfig.removeUrl, items);
- };
-
- ApiMiddleware.prototype.upload = function(files, path) {
- if (! $window.FormData) {
- throw new Error('Unsupported browser version');
- }
-
- var destination = this.getPath(path);
-
- return this.apiHandler.upload(fileManagerConfig.uploadUrl, destination, files);
- };
-
- ApiMiddleware.prototype.getContent = function(item) {
- var itemPath = this.getFilePath(item);
- return this.apiHandler.getContent(fileManagerConfig.getContentUrl, itemPath);
- };
-
- ApiMiddleware.prototype.edit = function(item) {
- var itemPath = this.getFilePath(item);
- return this.apiHandler.edit(fileManagerConfig.editUrl, itemPath, item.tempModel.content);
- };
-
- ApiMiddleware.prototype.rename = function(item) {
- var itemPath = this.getFilePath(item);
- var newPath = item.tempModel.fullPath();
- return this.apiHandler.rename(fileManagerConfig.renameUrl, itemPath, newPath);
- };
-
- ApiMiddleware.prototype.getUrl = function(item) {
- var itemPath = this.getFilePath(item);
- return this.apiHandler.getUrl(fileManagerConfig.downloadFileUrl, itemPath);
- };
-
- ApiMiddleware.prototype.download = function(item, forceNewWindow) {
- var itemPath = this.getFilePath(item);
- var toFilename = item.model.name;
-
- if (item.isFolder()) {
- return;
- }
-
- return this.apiHandler.download(
- fileManagerConfig.downloadFileUrl,
- itemPath,
- toFilename,
- fileManagerConfig.downloadFilesByAjax,
- forceNewWindow
- );
- };
-
- ApiMiddleware.prototype.downloadMultiple = function(files, forceNewWindow) {
- var items = this.getFileList(files);
- var timestamp = new Date().getTime().toString().substr(8, 13);
- var toFilename = timestamp + '-' + fileManagerConfig.multipleDownloadFileName;
-
- return this.apiHandler.downloadMultiple(
- fileManagerConfig.downloadMultipleUrl,
- items,
- toFilename,
- fileManagerConfig.downloadFilesByAjax,
- forceNewWindow
- );
- };
-
- ApiMiddleware.prototype.compress = function(files, compressedFilename, path) {
- var items = this.getFileList(files);
- return this.apiHandler.compress(fileManagerConfig.compressUrl, items, compressedFilename, this.getPath(path));
- };
-
- ApiMiddleware.prototype.extract = function(item, folderName, path) {
- var itemPath = this.getFilePath(item);
- return this.apiHandler.extract(fileManagerConfig.extractUrl, itemPath, folderName, this.getPath(path));
- };
-
- ApiMiddleware.prototype.changePermissions = function(item) {
- return this.apiHandler.changePermissions(fileManagerConfig.permissionsUrl, item);
- };
+ var app = angular.module('FileManagerApp');
- ApiMiddleware.prototype.createFolder = function(item) {
- item.tempModel.name = this.apiHandler.slug(item.tempModel.name);
- var path = item.tempModel.fullPath();
- return this.apiHandler.createFolder(fileManagerConfig.createFolderUrl, path);
+ app.directive('angularFilemanager', ['$parse', 'fileManagerConfig', function($parse, fileManagerConfig) {
+ return {
+ restrict: 'EA',
+ templateUrl: fileManagerConfig.tplPath + '/main.html'
};
-
- return ApiMiddleware;
-
}]);
-})(angular);
-(function(angular) {
- 'use strict';
- angular.module('FileManagerApp').service('fileNavigator', [
- 'apiMiddleware', 'fileManagerConfig', 'item', function (ApiMiddleware, fileManagerConfig, Item) {
- var FileNavigator = function() {
- this.apiMiddleware = new ApiMiddleware();
- this.requesting = false;
- this.fileList = [];
- this.currentPath = [];
- this.history = [];
- this.error = '';
-
- this.onRefresh = function() {};
- };
-
- FileNavigator.prototype.deferredHandler = function(data, deferred, code, defaultMsg) {
- if (!data || typeof data !== 'object') {
- this.error = 'Error %s - Bridge response error, please check the API docs or this ajax response.'.replace('%s', code);
- }
- if (code == 404) {
- this.error = 'Error 404 - Backend bridge is not working, please check the ajax response.';
- }
- if (!this.error && data.result && data.result.error) {
- this.error = data.result.error;
- }
- if (!this.error && data.error) {
- this.error = data.error.message;
- }
- if (!this.error && defaultMsg) {
- this.error = defaultMsg;
- }
- if (this.error) {
- return deferred.reject(data);
- }
- return deferred.resolve(data);
- };
-
- FileNavigator.prototype.list = function(recycle) {
- return this.apiMiddleware.list(this.currentPath, this.deferredHandler.bind(this), recycle);
- };
+ app.directive('ngFile', ['$parse', function($parse) {
+ return {
+ restrict: 'A',
+ link: function(scope, element, attrs) {
+ var model = $parse(attrs.ngFile);
+ var modelSetter = model.assign;
- FileNavigator.prototype.refresh = function(recycle) {
- var self = this;
- var path = self.currentPath.join('/');
- self.requesting = true;
- self.fileList = [];
- return self.list(recycle).then(function(data) {
- self.fileList = (data.result || []).map(function(file) {
- return new Item(file, self.currentPath);
- });
- self.buildTree(path);
- self.onRefresh();
- }).finally(function() {
- self.requesting = false;
- });
- };
-
- FileNavigator.prototype.buildTree = function(path) {
- var flatNodes = [], selectedNode = {};
-
- function recursive(parent, item, path) {
- var absName = path ? (path + '/' + item.model.name) : item.model.name;
- if (parent.name.trim() && path.trim().indexOf(parent.name) !== 0) {
- parent.nodes = [];
- }
- if (parent.name !== path) {
- parent.nodes.forEach(function(nd) {
- recursive(nd, item, path);
+ element.bind('change', function() {
+ scope.$apply(function() {
+ modelSetter(scope, element[0].files);
});
- } else {
- for (var e in parent.nodes) {
- if (parent.nodes[e].name === absName) {
- return;
- }
- }
- parent.nodes.push({item: item, name: absName, nodes: []});
- }
-
- parent.nodes = parent.nodes.sort(function(a, b) {
- return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : a.name.toLowerCase() === b.name.toLowerCase() ? 0 : 1;
});
}
-
- function flatten(node, array) {
- array.push(node);
- for (var n in node.nodes) {
- flatten(node.nodes[n], array);
- }
- }
-
- function findNode(data, path) {
- return data.filter(function (n) {
- return n.name === path;
- })[0];
- }
-
- !this.history.length && this.history.push({name: '', nodes: []});
- flatten(this.history[0], flatNodes);
- selectedNode = findNode(flatNodes, path);
- selectedNode && (selectedNode.nodes = []);
-
- for (var o in this.fileList) {
- var item = this.fileList[o];
- item instanceof Item && item.isFolder() && recursive(this.history[0], item, path);
- }
- };
-
- FileNavigator.prototype.folderClick = function(item) {
- this.currentPath = [];
- if (item && item.isFolder()) {
- this.currentPath = item.model.fullPath().split('/').splice(1);
- }
- this.refresh();
- };
-
- FileNavigator.prototype.upDir = function() {
- if (this.currentPath[0]) {
- this.currentPath = this.currentPath.slice(0, -1);
- this.refresh();
- }
- };
-
- FileNavigator.prototype.goTo = function(index) {
- this.currentPath = this.currentPath.slice(0, index + 1);
- this.refresh();
- };
-
- FileNavigator.prototype.fileNameExists = function(fileName) {
- return this.fileList.find(function(item) {
- return fileName.trim && item.model.name.trim() === fileName.trim();
- });
};
+ }]);
- FileNavigator.prototype.listHasFolders = function() {
- return this.fileList.find(function(item) {
- return item.model.type === 'dir';
+ app.directive('ngRightClick', ['$parse', function($parse) {
+ return function(scope, element, attrs) {
+ var fn = $parse(attrs.ngRightClick);
+ element.bind('contextmenu', function(event) {
+ scope.$apply(function() {
+ event.preventDefault();
+ fn(scope, {$event: event});
+ });
});
};
-
- return FileNavigator;
}]);
+
})(angular);
+
(function(angular) {
'use strict';
var app = angular.module('FileManagerApp');
@@ -1428,22 +780,22 @@
}]);
app.filter('humanReadableFileSize', ['$filter', 'fileManagerConfig', function($filter, fileManagerConfig) {
- // See https://en.wikipedia.org/wiki/Binary_prefix
- var decimalByteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB'];
- var binaryByteUnits = ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
-
- return function(input) {
- var i = -1;
- var fileSizeInBytes = input;
-
- do {
- fileSizeInBytes = fileSizeInBytes / 1024;
- i++;
- } while (fileSizeInBytes > 1024);
-
- var result = fileManagerConfig.useBinarySizePrefixes ? binaryByteUnits[i] : decimalByteUnits[i];
- return Math.max(fileSizeInBytes, 0.1).toFixed(1) + ' ' + result;
- };
+ // See https://en.wikipedia.org/wiki/Binary_prefix
+ var decimalByteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB'];
+ var binaryByteUnits = ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
+
+ return function(input) {
+ var i = -1;
+ var fileSizeInBytes = input;
+
+ do {
+ fileSizeInBytes = fileSizeInBytes / 1024;
+ i++;
+ } while (fileSizeInBytes > 1024);
+
+ var result = fileManagerConfig.useBinarySizePrefixes ? binaryByteUnits[i] : decimalByteUnits[i];
+ return Math.max(fileSizeInBytes, 0.1).toFixed(1) + ' ' + result;
+ };
}]);
})(angular);
@@ -2683,1126 +2035,1779 @@
}]);
})(angular);
-/*
- * Angular JS Multi Select
- * Creates a dropdown-like button with checkboxes.
- *
- * Project started on: Tue, 14 Jan 2014 - 5:18:02 PM
- * Current version: 4.0.0
- *
- * Released under the MIT License
- * --------------------------------------------------------------------------------
- * The MIT License (MIT)
- *
- * Copyright (c) 2014 Ignatius Steven (https://github.com/isteven)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- * --------------------------------------------------------------------------------
- */
-
-'use strict'
-
-angular.module( 'isteven-multi-select', ['ng'] ).directive( 'istevenMultiSelect' , [ '$sce', '$timeout', '$templateCache', function ( $sce, $timeout, $templateCache ) {
- return {
- restrict:
- 'AE',
-
- scope:
- {
- // models
- inputModel : '=',
- outputModel : '=',
-
- // settings based on attribute
- isDisabled : '=',
-
- // callbacks
- onClear : '&',
- onClose : '&',
- onSearchChange : '&',
- onItemClick : '&',
- onOpen : '&',
- onReset : '&',
- onSelectAll : '&',
- onSelectNone : '&',
-
- // i18n
- translation : '='
- },
-
- /*
- * The rest are attributes. They don't need to be parsed / binded, so we can safely access them by value.
- * - buttonLabel, directiveId, helperElements, itemLabel, maxLabels, orientation, selectionMode, minSearchLength,
- * tickProperty, disableProperty, groupProperty, searchProperty, maxHeight, outputProperties
- */
-
- templateUrl:
- 'isteven-multi-select.htm',
-
- link: function ( $scope, element, attrs ) {
-
- $scope.backUp = [];
- $scope.varButtonLabel = '';
- $scope.spacingProperty = '';
- $scope.indexProperty = '';
- $scope.orientationH = false;
- $scope.orientationV = true;
- $scope.filteredModel = [];
- $scope.inputLabel = { labelFilter: '' };
- $scope.tabIndex = 0;
- $scope.lang = {};
- $scope.helperStatus = {
- all : true,
- none : true,
- reset : true,
- filter : true
- };
-
- var
- prevTabIndex = 0,
- helperItems = [],
- helperItemsLength = 0,
- checkBoxLayer = '',
- scrolled = false,
- selectedItems = [],
- formElements = [],
- vMinSearchLength = 0,
- clickedItem = null
-
- // v3.0.0
- // clear button clicked
- $scope.clearClicked = function( e ) {
- $scope.inputLabel.labelFilter = '';
- $scope.updateFilter();
- $scope.select( 'clear', e );
- }
-
- // A little hack so that AngularJS ng-repeat can loop using start and end index like a normal loop
- // http://stackoverflow.com/questions/16824853/way-to-ng-repeat-defined-number-of-times-instead-of-repeating-over-array
- $scope.numberToArray = function( num ) {
- return new Array( num );
- }
-
- // Call this function when user type on the filter field
- $scope.searchChanged = function() {
- if ( $scope.inputLabel.labelFilter.length < vMinSearchLength && $scope.inputLabel.labelFilter.length > 0 ) {
- return false;
- }
- $scope.updateFilter();
- }
-
- $scope.updateFilter = function()
- {
- // we check by looping from end of input-model
- $scope.filteredModel = [];
- var i = 0;
-
- if ( typeof $scope.inputModel === 'undefined' ) {
- return false;
- }
-
- for( i = $scope.inputModel.length - 1; i >= 0; i-- ) {
-
- // if it's group end, we push it to filteredModel[];
- if ( typeof $scope.inputModel[ i ][ attrs.groupProperty ] !== 'undefined' && $scope.inputModel[ i ][ attrs.groupProperty ] === false ) {
- $scope.filteredModel.push( $scope.inputModel[ i ] );
- }
-
- // if it's data
- var gotData = false;
- if ( typeof $scope.inputModel[ i ][ attrs.groupProperty ] === 'undefined' ) {
-
- // If we set the search-key attribute, we use this loop.
- if ( typeof attrs.searchProperty !== 'undefined' && attrs.searchProperty !== '' ) {
-
- for (var key in $scope.inputModel[ i ] ) {
- if (
- typeof $scope.inputModel[ i ][ key ] !== 'boolean'
- && String( $scope.inputModel[ i ][ key ] ).toUpperCase().indexOf( $scope.inputLabel.labelFilter.toUpperCase() ) >= 0
- && attrs.searchProperty.indexOf( key ) > -1
- ) {
- gotData = true;
- break;
- }
- }
- }
- // if there's no search-key attribute, we use this one. Much better on performance.
- else {
- for ( var key in $scope.inputModel[ i ] ) {
- if (
- typeof $scope.inputModel[ i ][ key ] !== 'boolean'
- && String( $scope.inputModel[ i ][ key ] ).toUpperCase().indexOf( $scope.inputLabel.labelFilter.toUpperCase() ) >= 0
- ) {
- gotData = true;
- break;
- }
- }
- }
-
- if ( gotData === true ) {
- // push
- $scope.filteredModel.push( $scope.inputModel[ i ] );
- }
- }
-
- // if it's group start
- if ( typeof $scope.inputModel[ i ][ attrs.groupProperty ] !== 'undefined' && $scope.inputModel[ i ][ attrs.groupProperty ] === true ) {
-
- if ( typeof $scope.filteredModel[ $scope.filteredModel.length - 1 ][ attrs.groupProperty ] !== 'undefined'
- && $scope.filteredModel[ $scope.filteredModel.length - 1 ][ attrs.groupProperty ] === false ) {
- $scope.filteredModel.pop();
- }
- else {
- $scope.filteredModel.push( $scope.inputModel[ i ] );
- }
- }
- }
-
- $scope.filteredModel.reverse();
-
- $timeout( function() {
-
- $scope.getFormElements();
-
- // Callback: on filter change
- if ( $scope.inputLabel.labelFilter.length > vMinSearchLength ) {
-
- var filterObj = [];
-
- angular.forEach( $scope.filteredModel, function( value, key ) {
- if ( typeof value !== 'undefined' ) {
- if ( typeof value[ attrs.groupProperty ] === 'undefined' ) {
- var tempObj = angular.copy( value );
- var index = filterObj.push( tempObj );
- delete filterObj[ index - 1 ][ $scope.indexProperty ];
- delete filterObj[ index - 1 ][ $scope.spacingProperty ];
- }
- }
- });
-
- $scope.onSearchChange({
- data:
- {
- keyword: $scope.inputLabel.labelFilter,
- result: filterObj
- }
- });
- }
- },0);
- };
-
- // List all the input elements. We need this for our keyboard navigation.
- // This function will be called everytime the filter is updated.
- // Depending on the size of filtered mode, might not good for performance, but oh well..
- $scope.getFormElements = function() {
- formElements = [];
-
- var
- selectButtons = [],
- inputField = [],
- checkboxes = [],
- clearButton = [];
-
- // If available, then get select all, select none, and reset buttons
- if ( $scope.helperStatus.all || $scope.helperStatus.none || $scope.helperStatus.reset ) {
- selectButtons = element.children().children().next().children().children()[ 0 ].getElementsByTagName( 'button' );
- // If available, then get the search box and the clear button
- if ( $scope.helperStatus.filter ) {
- // Get helper - search and clear button.
- inputField = element.children().children().next().children().children().next()[ 0 ].getElementsByTagName( 'input' );
- clearButton = element.children().children().next().children().children().next()[ 0 ].getElementsByTagName( 'button' );
- }
- }
- else {
- if ( $scope.helperStatus.filter ) {
- // Get helper - search and clear button.
- inputField = element.children().children().next().children().children()[ 0 ].getElementsByTagName( 'input' );
- clearButton = element.children().children().next().children().children()[ 0 ].getElementsByTagName( 'button' );
- }
- }
-
- // Get checkboxes
- if ( !$scope.helperStatus.all && !$scope.helperStatus.none && !$scope.helperStatus.reset && !$scope.helperStatus.filter ) {
- checkboxes = element.children().children().next()[ 0 ].getElementsByTagName( 'input' );
- }
- else {
- checkboxes = element.children().children().next().children().next()[ 0 ].getElementsByTagName( 'input' );
- }
-
- // Push them into global array formElements[]
- for ( var i = 0; i < selectButtons.length ; i++ ) { formElements.push( selectButtons[ i ] ); }
- for ( var i = 0; i < inputField.length ; i++ ) { formElements.push( inputField[ i ] ); }
- for ( var i = 0; i < clearButton.length ; i++ ) { formElements.push( clearButton[ i ] ); }
- for ( var i = 0; i < checkboxes.length ; i++ ) { formElements.push( checkboxes[ i ] ); }
- }
-
- // check if an item has attrs.groupProperty (be it true or false)
- $scope.isGroupMarker = function( item , type ) {
- if ( typeof item[ attrs.groupProperty ] !== 'undefined' && item[ attrs.groupProperty ] === type ) return true;
- return false;
- }
-
- $scope.removeGroupEndMarker = function( item ) {
- if ( typeof item[ attrs.groupProperty ] !== 'undefined' && item[ attrs.groupProperty ] === false ) return false;
- return true;
- }
-
- // call this function when an item is clicked
- $scope.syncItems = function( item, e, ng_repeat_index ) {
-
- e.preventDefault();
- e.stopPropagation();
-
- // if the directive is globaly disabled, do nothing
- if ( typeof attrs.disableProperty !== 'undefined' && item[ attrs.disableProperty ] === true ) {
- return false;
- }
-
- // if item is disabled, do nothing
- if ( typeof attrs.isDisabled !== 'undefined' && $scope.isDisabled === true ) {
- return false;
- }
-
- // if end group marker is clicked, do nothing
- if ( typeof item[ attrs.groupProperty ] !== 'undefined' && item[ attrs.groupProperty ] === false ) {
- return false;
- }
-
- var index = $scope.filteredModel.indexOf( item );
-
- // if the start of group marker is clicked ( only for multiple selection! )
- // how it works:
- // - if, in a group, there are items which are not selected, then they all will be selected
- // - if, in a group, all items are selected, then they all will be de-selected
- if ( typeof item[ attrs.groupProperty ] !== 'undefined' && item[ attrs.groupProperty ] === true ) {
-
- // this is only for multiple selection, so if selection mode is single, do nothing
- if ( typeof attrs.selectionMode !== 'undefined' && attrs.selectionMode.toUpperCase() === 'SINGLE' ) {
- return false;
- }
-
- var i,j,k;
- var startIndex = 0;
- var endIndex = $scope.filteredModel.length - 1;
- var tempArr = [];
-
- // nest level is to mark the depth of the group.
- // when you get into a group (start group marker), nestLevel++
- // when you exit a group (end group marker), nextLevel--
- var nestLevel = 0;
-
- // we loop throughout the filtered model (not whole model)
- for( i = index ; i < $scope.filteredModel.length ; i++) {
-
- // this break will be executed when we're done processing each group
- if ( nestLevel === 0 && i > index )
- {
- break;
- }
-
- if ( typeof $scope.filteredModel[ i ][ attrs.groupProperty ] !== 'undefined' && $scope.filteredModel[ i ][ attrs.groupProperty ] === true ) {
-
- // To cater multi level grouping
- if ( tempArr.length === 0 ) {
- startIndex = i + 1;
- }
- nestLevel = nestLevel + 1;
- }
-
- // if group end
- else if ( typeof $scope.filteredModel[ i ][ attrs.groupProperty ] !== 'undefined' && $scope.filteredModel[ i ][ attrs.groupProperty ] === false ) {
-
- nestLevel = nestLevel - 1;
-
- // cek if all are ticked or not
- if ( tempArr.length > 0 && nestLevel === 0 ) {
-
- var allTicked = true;
-
- endIndex = i;
-
- for ( j = 0; j < tempArr.length ; j++ ) {
- if ( typeof tempArr[ j ][ $scope.tickProperty ] !== 'undefined' && tempArr[ j ][ $scope.tickProperty ] === false ) {
- allTicked = false;
- break;
- }
- }
-
- if ( allTicked === true ) {
- for ( j = startIndex; j <= endIndex ; j++ ) {
- if ( typeof $scope.filteredModel[ j ][ attrs.groupProperty ] === 'undefined' ) {
- if ( typeof attrs.disableProperty === 'undefined' ) {
- $scope.filteredModel[ j ][ $scope.tickProperty ] = false;
- // we refresh input model as well
- inputModelIndex = $scope.filteredModel[ j ][ $scope.indexProperty ];
- $scope.inputModel[ inputModelIndex ][ $scope.tickProperty ] = false;
- }
- else if ( $scope.filteredModel[ j ][ attrs.disableProperty ] !== true ) {
- $scope.filteredModel[ j ][ $scope.tickProperty ] = false;
- // we refresh input model as well
- inputModelIndex = $scope.filteredModel[ j ][ $scope.indexProperty ];
- $scope.inputModel[ inputModelIndex ][ $scope.tickProperty ] = false;
- }
- }
- }
- }
-
- else {
- for ( j = startIndex; j <= endIndex ; j++ ) {
- if ( typeof $scope.filteredModel[ j ][ attrs.groupProperty ] === 'undefined' ) {
- if ( typeof attrs.disableProperty === 'undefined' ) {
- $scope.filteredModel[ j ][ $scope.tickProperty ] = true;
- // we refresh input model as well
- inputModelIndex = $scope.filteredModel[ j ][ $scope.indexProperty ];
- $scope.inputModel[ inputModelIndex ][ $scope.tickProperty ] = true;
-
- }
- else if ( $scope.filteredModel[ j ][ attrs.disableProperty ] !== true ) {
- $scope.filteredModel[ j ][ $scope.tickProperty ] = true;
- // we refresh input model as well
- inputModelIndex = $scope.filteredModel[ j ][ $scope.indexProperty ];
- $scope.inputModel[ inputModelIndex ][ $scope.tickProperty ] = true;
- }
- }
- }
- }
- }
- }
-
- // if data
- else {
- tempArr.push( $scope.filteredModel[ i ] );
- }
- }
- }
-
- // if an item (not group marker) is clicked
- else {
-
- // If it's single selection mode
- if ( typeof attrs.selectionMode !== 'undefined' && attrs.selectionMode.toUpperCase() === 'SINGLE' ) {
-
- // first, set everything to false
- for( i=0 ; i < $scope.filteredModel.length ; i++) {
- $scope.filteredModel[ i ][ $scope.tickProperty ] = false;
- }
- for( i=0 ; i < $scope.inputModel.length ; i++) {
- $scope.inputModel[ i ][ $scope.tickProperty ] = false;
- }
-
- // then set the clicked item to true
- $scope.filteredModel[ index ][ $scope.tickProperty ] = true;
- }
-
- // Multiple
- else {
- $scope.filteredModel[ index ][ $scope.tickProperty ] = !$scope.filteredModel[ index ][ $scope.tickProperty ];
- }
-
- // we refresh input model as well
- var inputModelIndex = $scope.filteredModel[ index ][ $scope.indexProperty ];
- $scope.inputModel[ inputModelIndex ][ $scope.tickProperty ] = $scope.filteredModel[ index ][ $scope.tickProperty ];
- }
-
- // we execute the callback function here
- clickedItem = angular.copy( item );
- if ( clickedItem !== null ) {
- $timeout( function() {
- delete clickedItem[ $scope.indexProperty ];
- delete clickedItem[ $scope.spacingProperty ];
- $scope.onItemClick( { data: clickedItem } );
- clickedItem = null;
- }, 0 );
- }
-
- $scope.refreshOutputModel();
- $scope.refreshButton();
-
- // We update the index here
- prevTabIndex = $scope.tabIndex;
- $scope.tabIndex = ng_repeat_index + helperItemsLength;
-
- // Set focus on the hidden checkbox
- e.target.focus();
-
- // set & remove CSS style
- $scope.removeFocusStyle( prevTabIndex );
- $scope.setFocusStyle( $scope.tabIndex );
-
- if ( typeof attrs.selectionMode !== 'undefined' && attrs.selectionMode.toUpperCase() === 'SINGLE' ) {
- // on single selection mode, we then hide the checkbox layer
- $scope.toggleCheckboxes( e );
- }
- }
-
- // update $scope.outputModel
- $scope.refreshOutputModel = function() {
-
- $scope.outputModel = [];
- var
- outputProps = [],
- tempObj = {};
-
- // v4.0.0
- if ( typeof attrs.outputProperties !== 'undefined' ) {
- outputProps = attrs.outputProperties.split(' ');
- angular.forEach( $scope.inputModel, function( value, key ) {
- if (
- typeof value !== 'undefined'
- && typeof value[ attrs.groupProperty ] === 'undefined'
- && value[ $scope.tickProperty ] === true
- ) {
- tempObj = {};
- angular.forEach( value, function( value1, key1 ) {
- if ( outputProps.indexOf( key1 ) > -1 ) {
- tempObj[ key1 ] = value1;
- }
- });
- var index = $scope.outputModel.push( tempObj );
- delete $scope.outputModel[ index - 1 ][ $scope.indexProperty ];
- delete $scope.outputModel[ index - 1 ][ $scope.spacingProperty ];
- }
- });
- }
- else {
- angular.forEach( $scope.inputModel, function( value, key ) {
- if (
- typeof value !== 'undefined'
- && typeof value[ attrs.groupProperty ] === 'undefined'
- && value[ $scope.tickProperty ] === true
- ) {
- var temp = angular.copy( value );
- var index = $scope.outputModel.push( temp );
- delete $scope.outputModel[ index - 1 ][ $scope.indexProperty ];
- delete $scope.outputModel[ index - 1 ][ $scope.spacingProperty ];
- }
- });
- }
- }
-
- // refresh button label
- $scope.refreshButton = function() {
-
- $scope.varButtonLabel = '';
- var ctr = 0;
-
- // refresh button label...
- if ( $scope.outputModel.length === 0 ) {
- // https://github.com/isteven/angular-multi-select/pull/19
- $scope.varButtonLabel = $scope.lang.nothingSelected;
- }
- else {
- var tempMaxLabels = $scope.outputModel.length;
- if ( typeof attrs.maxLabels !== 'undefined' && attrs.maxLabels !== '' ) {
- tempMaxLabels = attrs.maxLabels;
- }
-
- // if max amount of labels displayed..
- if ( $scope.outputModel.length > tempMaxLabels ) {
- $scope.more = true;
- }
- else {
- $scope.more = false;
- }
-
- angular.forEach( $scope.inputModel, function( value, key ) {
- if ( typeof value !== 'undefined' && value[ attrs.tickProperty ] === true ) {
- if ( ctr < tempMaxLabels ) {
- $scope.varButtonLabel += ( $scope.varButtonLabel.length > 0 ? ',
' : '
') + $scope.writeLabel( value, 'buttonLabel' );
- }
- ctr++;
- }
- });
-
- if ( $scope.more === true ) {
- // https://github.com/isteven/angular-multi-select/pull/16
- if (tempMaxLabels > 0) {
- $scope.varButtonLabel += ', ... ';
- }
- $scope.varButtonLabel += '(' + $scope.outputModel.length + ')';
- }
- }
- $scope.varButtonLabel = $sce.trustAsHtml( $scope.varButtonLabel + '
' );
- }
-
- // Check if a checkbox is disabled or enabled. It will check the granular control (disableProperty) and global control (isDisabled)
- // Take note that the granular control has higher priority.
- $scope.itemIsDisabled = function( item ) {
-
- if ( typeof attrs.disableProperty !== 'undefined' && item[ attrs.disableProperty ] === true ) {
- return true;
- }
- else {
- if ( $scope.isDisabled === true ) {
- return true;
- }
- else {
- return false;
- }
- }
-
- }
-
- // A simple function to parse the item label settings. Used on the buttons and checkbox labels.
- $scope.writeLabel = function( item, type ) {
-
- // type is either 'itemLabel' or 'buttonLabel'
- var temp = attrs[ type ].split( ' ' );
- var label = '';
-
- angular.forEach( temp, function( value, key ) {
- item[ value ] && ( label += ' ' + value.split( '.' ).reduce( function( prev, current ) {
- return prev[ current ];
- }, item ));
- });
-
- if ( type.toUpperCase() === 'BUTTONLABEL' ) {
- return label;
- }
- return $sce.trustAsHtml( label );
- }
-
- // UI operations to show/hide checkboxes based on click event..
- $scope.toggleCheckboxes = function( e ) {
-
- // We grab the button
- var clickedEl = element.children()[0];
-
- // Just to make sure.. had a bug where key events were recorded twice
- angular.element( document ).off( 'click', $scope.externalClickListener );
- angular.element( document ).off( 'keydown', $scope.keyboardListener );
-
- // The idea below was taken from another multi-select directive - https://github.com/amitava82/angular-multiselect
- // His version is awesome if you need a more simple multi-select approach.
-
- // close
- if ( angular.element( checkBoxLayer ).hasClass( 'show' )) {
-
- angular.element( checkBoxLayer ).removeClass( 'show' );
- angular.element( clickedEl ).removeClass( 'buttonClicked' );
- angular.element( document ).off( 'click', $scope.externalClickListener );
- angular.element( document ).off( 'keydown', $scope.keyboardListener );
-
- // clear the focused element;
- $scope.removeFocusStyle( $scope.tabIndex );
- if ( typeof formElements[ $scope.tabIndex ] !== 'undefined' ) {
- formElements[ $scope.tabIndex ].blur();
- }
-
- // close callback
- $timeout( function() {
- $scope.onClose();
- }, 0 );
-
- // set focus on button again
- element.children().children()[ 0 ].focus();
- }
- // open
- else
- {
- // clear filter
- $scope.inputLabel.labelFilter = '';
- $scope.updateFilter();
-
- helperItems = [];
- helperItemsLength = 0;
-
- angular.element( checkBoxLayer ).addClass( 'show' );
- angular.element( clickedEl ).addClass( 'buttonClicked' );
-
- // Attach change event listener on the input filter.
- // We need this because ng-change is apparently not an event listener.
- angular.element( document ).on( 'click', $scope.externalClickListener );
- angular.element( document ).on( 'keydown', $scope.keyboardListener );
-
- // to get the initial tab index, depending on how many helper elements we have.
- // priority is to always focus it on the input filter
- $scope.getFormElements();
- $scope.tabIndex = 0;
-
- var helperContainer = angular.element( element[ 0 ].querySelector( '.helperContainer' ) )[0];
-
- if ( typeof helperContainer !== 'undefined' ) {
- for ( var i = 0; i < helperContainer.getElementsByTagName( 'BUTTON' ).length ; i++ ) {
- helperItems[ i ] = helperContainer.getElementsByTagName( 'BUTTON' )[ i ];
- }
- helperItemsLength = helperItems.length + helperContainer.getElementsByTagName( 'INPUT' ).length;
- }
-
- // focus on the filter element on open.
- if ( element[ 0 ].querySelector( '.inputFilter' ) ) {
- element[ 0 ].querySelector( '.inputFilter' ).focus();
- $scope.tabIndex = $scope.tabIndex + helperItemsLength - 2;
- // blur button in vain
- angular.element( element ).children()[ 0 ].blur();
- }
- // if there's no filter then just focus on the first checkbox item
- else {
- if ( !$scope.isDisabled ) {
- $scope.tabIndex = $scope.tabIndex + helperItemsLength;
- if ( $scope.inputModel.length > 0 ) {
- formElements[ $scope.tabIndex ].focus();
- $scope.setFocusStyle( $scope.tabIndex );
- // blur button in vain
- angular.element( element ).children()[ 0 ].blur();
- }
- }
- }
-
- // open callback
- $scope.onOpen();
- }
- }
-
- // handle clicks outside the button / multi select layer
- $scope.externalClickListener = function( e ) {
-
- var targetsArr = element.find( e.target.tagName );
- for (var i = 0; i < targetsArr.length; i++) {
- if ( e.target == targetsArr[i] ) {
- return;
- }
- }
-
- angular.element( checkBoxLayer.previousSibling ).removeClass( 'buttonClicked' );
- angular.element( checkBoxLayer ).removeClass( 'show' );
- angular.element( document ).off( 'click', $scope.externalClickListener );
- angular.element( document ).off( 'keydown', $scope.keyboardListener );
-
- // close callback
- $timeout( function() {
- $scope.onClose();
- }, 0 );
-
- // set focus on button again
- element.children().children()[ 0 ].focus();
- }
-
- // select All / select None / reset buttons
- $scope.select = function( type, e ) {
-
- var helperIndex = helperItems.indexOf( e.target );
- $scope.tabIndex = helperIndex;
-
- switch( type.toUpperCase() ) {
- case 'ALL':
- angular.forEach( $scope.filteredModel, function( value, key ) {
- if ( typeof value !== 'undefined' && value[ attrs.disableProperty ] !== true ) {
- if ( typeof value[ attrs.groupProperty ] === 'undefined' ) {
- value[ $scope.tickProperty ] = true;
- }
- }
- });
- $scope.refreshOutputModel();
- $scope.refreshButton();
- $scope.onSelectAll();
- break;
- case 'NONE':
- angular.forEach( $scope.filteredModel, function( value, key ) {
- if ( typeof value !== 'undefined' && value[ attrs.disableProperty ] !== true ) {
- if ( typeof value[ attrs.groupProperty ] === 'undefined' ) {
- value[ $scope.tickProperty ] = false;
- }
- }
- });
- $scope.refreshOutputModel();
- $scope.refreshButton();
- $scope.onSelectNone();
- break;
- case 'RESET':
- angular.forEach( $scope.filteredModel, function( value, key ) {
- if ( typeof value[ attrs.groupProperty ] === 'undefined' && typeof value !== 'undefined' && value[ attrs.disableProperty ] !== true ) {
- var temp = value[ $scope.indexProperty ];
- value[ $scope.tickProperty ] = $scope.backUp[ temp ][ $scope.tickProperty ];
- }
- });
- $scope.refreshOutputModel();
- $scope.refreshButton();
- $scope.onReset();
- break;
- case 'CLEAR':
- $scope.tabIndex = $scope.tabIndex + 1;
- $scope.onClear();
- break;
- case 'FILTER':
- $scope.tabIndex = helperItems.length - 1;
- break;
- default:
- }
- }
-
- // just to create a random variable name
- function genRandomString( length ) {
- var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
- var temp = '';
- for( var i=0; i < length; i++ ) {
- temp += possible.charAt( Math.floor( Math.random() * possible.length ));
- }
- return temp;
- }
-
- // count leading spaces
- $scope.prepareGrouping = function() {
- var spacing = 0;
- angular.forEach( $scope.filteredModel, function( value, key ) {
- value[ $scope.spacingProperty ] = spacing;
- if ( value[ attrs.groupProperty ] === true ) {
- spacing+=2;
- }
- else if ( value[ attrs.groupProperty ] === false ) {
- spacing-=2;
- }
- });
- }
-
- // prepare original index
- $scope.prepareIndex = function() {
- var ctr = 0;
- angular.forEach( $scope.filteredModel, function( value, key ) {
- value[ $scope.indexProperty ] = ctr;
- ctr++;
- });
- }
-
- // navigate using up and down arrow
- $scope.keyboardListener = function( e ) {
-
- var key = e.keyCode ? e.keyCode : e.which;
- var isNavigationKey = false;
-
- // ESC key (close)
- if ( key === 27 ) {
- e.preventDefault();
- e.stopPropagation();
- $scope.toggleCheckboxes( e );
- }
-
-
- // next element ( tab, down & right key )
- else if ( key === 40 || key === 39 || ( !e.shiftKey && key == 9 ) ) {
-
- isNavigationKey = true;
- prevTabIndex = $scope.tabIndex;
- $scope.tabIndex++;
- if ( $scope.tabIndex > formElements.length - 1 ) {
- $scope.tabIndex = 0;
- prevTabIndex = formElements.length - 1;
- }
- while ( formElements[ $scope.tabIndex ].disabled === true ) {
- $scope.tabIndex++;
- if ( $scope.tabIndex > formElements.length - 1 ) {
- $scope.tabIndex = 0;
- }
- if ( $scope.tabIndex === prevTabIndex ) {
- break;
- }
- }
- }
-
- // prev element ( shift+tab, up & left key )
- else if ( key === 38 || key === 37 || ( e.shiftKey && key == 9 ) ) {
- isNavigationKey = true;
- prevTabIndex = $scope.tabIndex;
- $scope.tabIndex--;
- if ( $scope.tabIndex < 0 ) {
- $scope.tabIndex = formElements.length - 1;
- prevTabIndex = 0;
- }
- while ( formElements[ $scope.tabIndex ].disabled === true ) {
- $scope.tabIndex--;
- if ( $scope.tabIndex === prevTabIndex ) {
- break;
- }
- if ( $scope.tabIndex < 0 ) {
- $scope.tabIndex = formElements.length - 1;
- }
- }
- }
-
- if ( isNavigationKey === true ) {
-
- e.preventDefault();
-
- // set focus on the checkbox
- formElements[ $scope.tabIndex ].focus();
- var actEl = document.activeElement;
-
- if ( actEl.type.toUpperCase() === 'CHECKBOX' ) {
- $scope.setFocusStyle( $scope.tabIndex );
- $scope.removeFocusStyle( prevTabIndex );
- }
- else {
- $scope.removeFocusStyle( prevTabIndex );
- $scope.removeFocusStyle( helperItemsLength );
- $scope.removeFocusStyle( formElements.length - 1 );
- }
- }
-
- isNavigationKey = false;
- }
-
- // set (add) CSS style on selected row
- $scope.setFocusStyle = function( tabIndex ) {
- angular.element( formElements[ tabIndex ] ).parent().parent().parent().addClass( 'multiSelectFocus' );
- }
-
- // remove CSS style on selected row
- $scope.removeFocusStyle = function( tabIndex ) {
- angular.element( formElements[ tabIndex ] ).parent().parent().parent().removeClass( 'multiSelectFocus' );
- }
-
- /*********************
- *********************
- *
- * 1) Initializations
- *
- *********************
- *********************/
-
- // attrs to $scope - attrs-$scope - attrs - $scope
- // Copy some properties that will be used on the template. They need to be in the $scope.
- $scope.groupProperty = attrs.groupProperty;
- $scope.tickProperty = attrs.tickProperty;
- $scope.directiveId = attrs.directiveId;
-
- // Unfortunately I need to add these grouping properties into the input model
- var tempStr = genRandomString( 5 );
- $scope.indexProperty = 'idx_' + tempStr;
- $scope.spacingProperty = 'spc_' + tempStr;
-
- // set orientation css
- if ( typeof attrs.orientation !== 'undefined' ) {
-
- if ( attrs.orientation.toUpperCase() === 'HORIZONTAL' ) {
- $scope.orientationH = true;
- $scope.orientationV = false;
- }
- else
- {
- $scope.orientationH = false;
- $scope.orientationV = true;
- }
- }
-
- // get elements required for DOM operation
- checkBoxLayer = element.children().children().next()[0];
-
- // set max-height property if provided
- if ( typeof attrs.maxHeight !== 'undefined' ) {
- var layer = element.children().children().children()[0];
- angular.element( layer ).attr( "style", "height:" + attrs.maxHeight + "; overflow-y:scroll;" );
- }
-
- // some flags for easier checking
- for ( var property in $scope.helperStatus ) {
- if ( $scope.helperStatus.hasOwnProperty( property )) {
- if (
- typeof attrs.helperElements !== 'undefined'
- && attrs.helperElements.toUpperCase().indexOf( property.toUpperCase() ) === -1
- ) {
- $scope.helperStatus[ property ] = false;
- }
- }
- }
- if ( typeof attrs.selectionMode !== 'undefined' && attrs.selectionMode.toUpperCase() === 'SINGLE' ) {
- $scope.helperStatus[ 'all' ] = false;
- $scope.helperStatus[ 'none' ] = false;
- }
-
- // helper button icons.. I guess you can use html tag here if you want to.
- $scope.icon = {};
- $scope.icon.selectAll = '✓'; // a tick icon
- $scope.icon.selectNone = '×'; // x icon
- $scope.icon.reset = '↶'; // undo icon
- // this one is for the selected items
- $scope.icon.tickMark = '✓'; // a tick icon
-
- // configurable button labels
- if ( typeof attrs.translation !== 'undefined' ) {
- $scope.lang.selectAll = $sce.trustAsHtml( $scope.icon.selectAll + ' ' + $scope.translation.selectAll );
- $scope.lang.selectNone = $sce.trustAsHtml( $scope.icon.selectNone + ' ' + $scope.translation.selectNone );
- $scope.lang.reset = $sce.trustAsHtml( $scope.icon.reset + ' ' + $scope.translation.reset );
- $scope.lang.search = $scope.translation.search;
- $scope.lang.nothingSelected = $sce.trustAsHtml( $scope.translation.nothingSelected );
- }
- else {
- $scope.lang.selectAll = $sce.trustAsHtml( $scope.icon.selectAll + ' Select All' );
- $scope.lang.selectNone = $sce.trustAsHtml( $scope.icon.selectNone + ' Select None' );
- $scope.lang.reset = $sce.trustAsHtml( $scope.icon.reset + ' Reset' );
- $scope.lang.search = 'Search...';
- $scope.lang.nothingSelected = 'None Selected';
- }
- $scope.icon.tickMark = $sce.trustAsHtml( $scope.icon.tickMark );
-
- // min length of keyword to trigger the filter function
- if ( typeof attrs.MinSearchLength !== 'undefined' && parseInt( attrs.MinSearchLength ) > 0 ) {
- vMinSearchLength = Math.floor( parseInt( attrs.MinSearchLength ) );
- }
-
- /*******************************************************
- *******************************************************
- *
- * 2) Logic starts here, initiated by watch 1 & watch 2
- *
- *******************************************************
- *******************************************************/
-
- // watch1, for changes in input model property
- // updates multi-select when user select/deselect a single checkbox programatically
- // https://github.com/isteven/angular-multi-select/issues/8
- $scope.$watch( 'inputModel' , function( newVal ) {
- if ( newVal ) {
- $scope.refreshOutputModel();
- $scope.refreshButton();
- }
- }, true );
-
- // watch2 for changes in input model as a whole
- // this on updates the multi-select when a user load a whole new input-model. We also update the $scope.backUp variable
- $scope.$watch( 'inputModel' , function( newVal ) {
- if ( newVal ) {
- $scope.backUp = angular.copy( $scope.inputModel );
- $scope.updateFilter();
- $scope.prepareGrouping();
- $scope.prepareIndex();
- $scope.refreshOutputModel();
- $scope.refreshButton();
- }
- });
-
- // watch for changes in directive state (disabled or enabled)
- $scope.$watch( 'isDisabled' , function( newVal ) {
- $scope.isDisabled = newVal;
- });
-
- // this is for touch enabled devices. We don't want to hide checkboxes on scroll.
- var onTouchStart = function( e ) {
- $scope.$apply( function() {
- $scope.scrolled = false;
- });
- };
- angular.element( document ).bind( 'touchstart', onTouchStart);
- var onTouchMove = function( e ) {
- $scope.$apply( function() {
- $scope.scrolled = true;
- });
- };
- angular.element( document ).bind( 'touchmove', onTouchMove);
-
- // unbind document events to prevent memory leaks
- $scope.$on( '$destroy', function () {
- angular.element( document ).unbind( 'touchstart', onTouchStart);
- angular.element( document ).unbind( 'touchmove', onTouchMove);
- });
- }
- }
-}]).run( [ '$templateCache' , function( $templateCache ) {
- var template =
- '
' +
- // main button
- '' +
- ' ' +
- // overlay layer
- '' +
- // container of the helper elements
- '
' +
- // container of the first 3 buttons, select all, none and reset
- '
' +
- // select all
- '' +
- ' '+
- // select none
- '' +
- ' '+
- // reset
- ''+
- ' ' +
- '
' +
- // the search box
- '
'+
- // textfield
- ' '+
- // clear button
- '× '+
- '
'+
- '
'+
- // selection items
- '
'+
- '
'+
- // this is the spacing for grouped items
- '
'+
- '
'+
- '
'+
- ''+
- // input, so that it can accept focus on keyboard click
- ' '+
- // item label using ng-bind-hteml
- ''+
- ' '+
- ' '+
- '
'+
- // the tick/check mark
- '
'+
- '
'+
- '
'+
- '
'+
- ' ';
- $templateCache.put( 'isteven-multi-select.htm' , template );
-}]);
+(function(angular, $) {
+ 'use strict';
+ angular.module('FileManagerApp').service('apiHandler', ['$http', '$q', '$window', '$translate', 'Upload',
+ function ($http, $q, $window, $translate, Upload) {
+
+ $http.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
+
+ var ApiHandler = function() {
+ this.inprocess = false;
+ this.asyncSuccess = false;
+ this.error = '';
+ };
+
+ ApiHandler.prototype.deferredHandler = function(data, deferred, code, defaultMsg) {
+ if (!data || typeof data !== 'object') {
+ this.error = 'Error %s - Bridge response error, please check the API docs or this ajax response.'.replace('%s', code);
+ }
+ if (code == 404) {
+ this.error = 'Error 404 - Backend bridge is not working, please check the ajax response.';
+ }
+ if (data.result && data.result.error) {
+ this.error = data.result.error;
+ }
+ if (!this.error && data.error) {
+ this.error = data.error.message;
+ }
+ if (!this.error && defaultMsg) {
+ this.error = defaultMsg;
+ }
+ if (this.error) {
+ return deferred.reject(data);
+ }
+ return deferred.resolve(data);
+ };
+
+ ApiHandler.prototype.list = function(apiUrl, path, customDeferredHandler, recycle) {
+ var self = this;
+ var dfHandler = customDeferredHandler || self.deferredHandler;
+ var deferred = $q.defer();
+ var data = {
+ action: 'list',
+ path: path,
+ recycle: recycle
+ };
+
+ self.inprocess = true;
+ self.error = '';
+
+ $http.post(apiUrl, data).success(function(data, code) {
+ dfHandler(data, deferred, code);
+ }).error(function(data, code) {
+ dfHandler(data, deferred, code, 'Unknown error listing, check the response');
+ })['finally'](function() {
+ self.inprocess = false;
+ });
+ return deferred.promise;
+ };
+
+ ApiHandler.prototype.copy = function(apiUrl, items, path, singleFilename) {
+ var self = this;
+ var deferred = $q.defer();
+ var data = {
+ action: 'copy',
+ items: items,
+ newPath: path
+ };
+
+ if (singleFilename && items.length === 1) {
+ data.singleFilename = singleFilename;
+ }
+
+ self.inprocess = true;
+ self.error = '';
+ $http.post(apiUrl, data).success(function(data, code) {
+ self.deferredHandler(data, deferred, code);
+ }).error(function(data, code) {
+ self.deferredHandler(data, deferred, code, $translate.instant('error_copying'));
+ })['finally'](function() {
+ self.inprocess = false;
+ });
+ return deferred.promise;
+ };
+
+ ApiHandler.prototype.move = function(apiUrl, items, path) {
+ var self = this;
+ var deferred = $q.defer();
+ var data = {
+ action: 'move',
+ items: items,
+ newPath: path
+ };
+ self.inprocess = true;
+ self.error = '';
+ $http.post(apiUrl, data).success(function(data, code) {
+ self.deferredHandler(data, deferred, code);
+ }).error(function(data, code) {
+ self.deferredHandler(data, deferred, code, $translate.instant('error_moving'));
+ })['finally'](function() {
+ self.inprocess = false;
+ });
+ return deferred.promise;
+ };
+
+ ApiHandler.prototype.remove = function(apiUrl, items) {
+ var self = this;
+ var deferred = $q.defer();
+ var data = {
+ action: 'remove',
+ items: items
+ };
+
+ self.inprocess = true;
+ self.error = '';
+ $http.post(apiUrl, data).success(function(data, code) {
+ self.deferredHandler(data, deferred, code);
+ }).error(function(data, code) {
+ self.deferredHandler(data, deferred, code, $translate.instant('error_deleting'));
+ })['finally'](function() {
+ self.inprocess = false;
+ });
+ return deferred.promise;
+ };
+
+ ApiHandler.prototype.upload = function(apiUrl, destination, files) {
+ var self = this;
+ var deferred = $q.defer();
+ self.inprocess = true;
+ self.progress = 0;
+ self.error = '';
+
+ var data = {
+ destination: destination
+ };
+
+ for (var i = 0; i < files.length; i++) {
+ data['file-' + i] = files[i];
+ }
+
+ if (files && files.length) {
+ Upload.upload({
+ url: apiUrl,
+ data: data
+ }).then(function (data) {
+ self.deferredHandler(data.data, deferred, data.status);
+ }, function (data) {
+ self.deferredHandler(data.data, deferred, data.status, 'Unknown error uploading files');
+ }, function (evt) {
+ self.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total)) - 1;
+ })['finally'](function() {
+ self.inprocess = false;
+ self.progress = 0;
+ });
+ }
+
+ return deferred.promise;
+ };
+
+ ApiHandler.prototype.getContent = function(apiUrl, itemPath) {
+ var self = this;
+ var deferred = $q.defer();
+ var data = {
+ action: 'getContent',
+ item: itemPath
+ };
+
+ self.inprocess = true;
+ self.error = '';
+ $http.post(apiUrl, data).success(function(data, code) {
+ self.deferredHandler(data, deferred, code);
+ }).error(function(data, code) {
+ self.deferredHandler(data, deferred, code, $translate.instant('error_getting_content'));
+ })['finally'](function() {
+ self.inprocess = false;
+ });
+ return deferred.promise;
+ };
+
+ ApiHandler.prototype.edit = function(apiUrl, itemPath, content) {
+ var self = this;
+ var deferred = $q.defer();
+ var data = {
+ action: 'edit',
+ item: itemPath,
+ content: content
+ };
+
+ self.inprocess = true;
+ self.error = '';
+
+ $http.post(apiUrl, data).success(function(data, code) {
+ self.deferredHandler(data, deferred, code);
+ }).error(function(data, code) {
+ self.deferredHandler(data, deferred, code, $translate.instant('error_modifying'));
+ })['finally'](function() {
+ self.inprocess = false;
+ });
+ return deferred.promise;
+ };
+
+ ApiHandler.prototype.rename = function(apiUrl, itemPath, newPath) {
+ var self = this;
+ var deferred = $q.defer();
+ var data = {
+ action: 'rename',
+ item: itemPath,
+ newItemPath: newPath
+ };
+ self.inprocess = true;
+ self.error = '';
+ $http.post(apiUrl, data).success(function(data, code) {
+ self.deferredHandler(data, deferred, code);
+ }).error(function(data, code) {
+ self.deferredHandler(data, deferred, code, $translate.instant('error_renaming'));
+ })['finally'](function() {
+ self.inprocess = false;
+ });
+ return deferred.promise;
+ };
+
+ ApiHandler.prototype.getUrl = function(apiUrl, path) {
+ var data = {
+ action: 'download',
+ path: path
+ };
+ return path && [apiUrl, $.param(data)].join('?');
+ };
+
+ ApiHandler.prototype.download = function(apiUrl, itemPath, toFilename, downloadByAjax, forceNewWindow) {
+ var self = this;
+ var url = this.getUrl(apiUrl, itemPath);
+
+ if (!downloadByAjax || forceNewWindow || !$window.saveAs) {
+ !$window.saveAs && $window.console.log('Your browser dont support ajax download, downloading by default');
+ return !!$window.open(url, '_blank', '');
+ }
+
+ var deferred = $q.defer();
+ self.inprocess = true;
+ $http.get(url).success(function(data) {
+ var bin = new $window.Blob([data]);
+ deferred.resolve(data);
+ $window.saveAs(bin, toFilename);
+ }).error(function(data, code) {
+ self.deferredHandler(data, deferred, code, $translate.instant('error_downloading'));
+ })['finally'](function() {
+ self.inprocess = false;
+ });
+ return deferred.promise;
+ };
+
+ ApiHandler.prototype.downloadMultiple = function(apiUrl, items, toFilename, downloadByAjax, forceNewWindow) {
+ var self = this;
+ var deferred = $q.defer();
+ var data = {
+ action: 'downloadMultiple',
+ items: items,
+ toFilename: toFilename
+ };
+ var url = [apiUrl, $.param(data)].join('?');
+
+ if (!downloadByAjax || forceNewWindow || !$window.saveAs) {
+ !$window.saveAs && $window.console.log('Your browser dont support ajax download, downloading by default');
+ return !!$window.open(url, '_blank', '');
+ }
+
+ self.inprocess = true;
+ $http.get(apiUrl).success(function(data) {
+ var bin = new $window.Blob([data]);
+ deferred.resolve(data);
+ $window.saveAs(bin, toFilename);
+ }).error(function(data, code) {
+ self.deferredHandler(data, deferred, code, $translate.instant('error_downloading'));
+ })['finally'](function() {
+ self.inprocess = false;
+ });
+ return deferred.promise;
+ };
+
+ ApiHandler.prototype.compress = function(apiUrl, items, compressedFilename, path) {
+ var self = this;
+ var deferred = $q.defer();
+ var data = {
+ action: 'compress',
+ items: items,
+ destination: path,
+ compressedFilename: compressedFilename
+ };
+
+ self.inprocess = true;
+ self.error = '';
+ $http.post(apiUrl, data).success(function(data, code) {
+ self.deferredHandler(data, deferred, code);
+ }).error(function(data, code) {
+ self.deferredHandler(data, deferred, code, $translate.instant('error_compressing'));
+ })['finally'](function() {
+ self.inprocess = false;
+ });
+ return deferred.promise;
+ };
+
+ ApiHandler.prototype.extract = function(apiUrl, item, folderName, path) {
+ var self = this;
+ var deferred = $q.defer();
+ var data = {
+ action: 'extract',
+ item: item,
+ destination: path,
+ folderName: folderName
+ };
+
+ self.inprocess = true;
+ self.error = '';
+ $http.post(apiUrl, data).success(function(data, code) {
+ self.deferredHandler(data, deferred, code);
+ }).error(function(data, code) {
+ self.deferredHandler(data, deferred, code, $translate.instant('error_extracting'));
+ })['finally'](function() {
+ self.inprocess = false;
+ });
+ return deferred.promise;
+ };
+
+ ApiHandler.prototype.changePermissions = function(apiUrl, item) {
+ var self = this;
+ var deferred = $q.defer();
+ var data = {
+ action: 'changePermissions',
+ path: item.fullPath(),
+ item: item
+ };
+
+ self.inprocess = true;
+ self.error = '';
+ $http.post(apiUrl, data).success(function(data, code) {
+ self.deferredHandler(data, deferred, code);
+ }).error(function(data, code) {
+ self.deferredHandler(data, deferred, code, $translate.instant('error_changing_perms'));
+ })['finally'](function() {
+ self.inprocess = false;
+ });
+ return deferred.promise;
+ };
+
+ ApiHandler.prototype.createFolder = function(apiUrl, path) {
+ var self = this;
+ var deferred = $q.defer();
+ var data = {
+ action: 'createFolder',
+ newPath: path
+ };
+
+ self.inprocess = true;
+ self.error = '';
+ $http.post(apiUrl, data).success(function(data, code) {
+ self.deferredHandler(data, deferred, code);
+ }).error(function(data, code) {
+ self.deferredHandler(data, deferred, code, $translate.instant('error_creating_folder'));
+ })['finally'](function() {
+ self.inprocess = false;
+ });
+
+ return deferred.promise;
+ };
+
+ ApiHandler.prototype.slug = function(str) {
+ var trimmed = $.trim(str);
+ var $slug = trimmed.replace(/[^a-z0-9-]/gi, '-').
+ replace(/-+/g, '-').
+ replace(/^-|-$/g, '');
+ return $slug.toLowerCase();
+ };
+
+ return ApiHandler;
+
+ }]);
+})(angular, jQuery);
+(function(angular) {
+ 'use strict';
+ angular.module('FileManagerApp').service('apiMiddleware', ['$window', 'fileManagerConfig', 'apiHandler',
+ function ($window, fileManagerConfig, ApiHandler) {
+
+ var ApiMiddleware = function() {
+ this.apiHandler = new ApiHandler();
+ };
+
+ ApiMiddleware.prototype.getPath = function(arrayPath) {
+ return '/' + arrayPath.join('/');
+ };
+
+ ApiMiddleware.prototype.getFileList = function(files) {
+ return (files || []).map(function(file) {
+ return file && file.model.fullPath();
+ });
+ };
+
+ ApiMiddleware.prototype.getFilePath = function(item) {
+ return item && item.model.fullPath();
+ };
+
+ ApiMiddleware.prototype.list = function(path, customDeferredHandler, recycle) {
+ return this.apiHandler.list(fileManagerConfig.listUrl, this.getPath(path), customDeferredHandler, recycle);
+ };
+
+ ApiMiddleware.prototype.copy = function(files, path) {
+ var items = this.getFileList(files);
+ var singleFilename = items.length === 1 ? files[0].tempModel.name : undefined;
+ return this.apiHandler.copy(fileManagerConfig.copyUrl, items, this.getPath(path), singleFilename);
+ };
+
+ ApiMiddleware.prototype.move = function(files, path) {
+ var items = this.getFileList(files);
+ return this.apiHandler.move(fileManagerConfig.moveUrl, items, this.getPath(path));
+ };
+
+ ApiMiddleware.prototype.remove = function(files) {
+ var items = this.getFileList(files);
+ return this.apiHandler.remove(fileManagerConfig.removeUrl, items);
+ };
+
+ ApiMiddleware.prototype.upload = function(files, path) {
+ if (! $window.FormData) {
+ throw new Error('Unsupported browser version');
+ }
+
+ var destination = this.getPath(path);
+
+ return this.apiHandler.upload(fileManagerConfig.uploadUrl, destination, files);
+ };
+
+ ApiMiddleware.prototype.getContent = function(item) {
+ var itemPath = this.getFilePath(item);
+ return this.apiHandler.getContent(fileManagerConfig.getContentUrl, itemPath);
+ };
+
+ ApiMiddleware.prototype.edit = function(item) {
+ var itemPath = this.getFilePath(item);
+ return this.apiHandler.edit(fileManagerConfig.editUrl, itemPath, item.tempModel.content);
+ };
+
+ ApiMiddleware.prototype.rename = function(item) {
+ var itemPath = this.getFilePath(item);
+ var newPath = item.tempModel.fullPath();
+ return this.apiHandler.rename(fileManagerConfig.renameUrl, itemPath, newPath);
+ };
+
+ ApiMiddleware.prototype.getUrl = function(item) {
+ var itemPath = this.getFilePath(item);
+ return this.apiHandler.getUrl(fileManagerConfig.downloadFileUrl, itemPath);
+ };
+
+ ApiMiddleware.prototype.getThumbnailUrl = function(item) {
+ var itemPath = this.getFilePath(item);
+ return fileManagerConfig.thumbnailUrlPrefix + this.apiHandler.getUrl(fileManagerConfig.downloadFileUrl, itemPath) + fileManagerConfig.thumbnailUrlSuffix;
+ };
+
+ ApiMiddleware.prototype.download = function(item, forceNewWindow) {
+ var itemPath = this.getFilePath(item);
+ var toFilename = item.model.name;
+
+ if (item.isFolder()) {
+ return;
+ }
+
+ return this.apiHandler.download(
+ fileManagerConfig.downloadFileUrl,
+ itemPath,
+ toFilename,
+ fileManagerConfig.downloadFilesByAjax,
+ forceNewWindow
+ );
+ };
+
+ ApiMiddleware.prototype.downloadMultiple = function(files, forceNewWindow) {
+ var items = this.getFileList(files);
+ var timestamp = new Date().getTime().toString().substr(8, 13);
+ var toFilename = timestamp + '-' + fileManagerConfig.multipleDownloadFileName;
+
+ return this.apiHandler.downloadMultiple(
+ fileManagerConfig.downloadMultipleUrl,
+ items,
+ toFilename,
+ fileManagerConfig.downloadFilesByAjax,
+ forceNewWindow
+ );
+ };
+
+ ApiMiddleware.prototype.compress = function(files, compressedFilename, path) {
+ var items = this.getFileList(files);
+ return this.apiHandler.compress(fileManagerConfig.compressUrl, items, compressedFilename, this.getPath(path));
+ };
+
+ ApiMiddleware.prototype.extract = function(item, folderName, path) {
+ var itemPath = this.getFilePath(item);
+ return this.apiHandler.extract(fileManagerConfig.extractUrl, itemPath, folderName, this.getPath(path));
+ };
+
+ ApiMiddleware.prototype.changePermissions = function(item) {
+ return this.apiHandler.changePermissions(fileManagerConfig.permissionsUrl, item);
+ };
+
+ ApiMiddleware.prototype.createFolder = function(item) {
+ item.tempModel.name = this.apiHandler.slug(item.tempModel.name);
+ var path = item.tempModel.fullPath();
+ return this.apiHandler.createFolder(fileManagerConfig.createFolderUrl, path);
+ };
+
+ return ApiMiddleware;
+
+ }]);
+})(angular);
+(function(angular) {
+ 'use strict';
+ angular.module('FileManagerApp').service('fileNavigator', [
+ 'apiMiddleware', 'fileManagerConfig', 'item', function (ApiMiddleware, fileManagerConfig, Item) {
+
+ var FileNavigator = function() {
+ this.apiMiddleware = new ApiMiddleware();
+ this.requesting = false;
+ this.fileList = [];
+ this.currentPath = [];
+ this.history = [];
+ this.error = '';
+
+ this.onRefresh = function() {};
+ };
+
+ FileNavigator.prototype.deferredHandler = function(data, deferred, code, defaultMsg) {
+ if (!data || typeof data !== 'object') {
+ this.error = 'Error %s - Bridge response error, please check the API docs or this ajax response.'.replace('%s', code);
+ }
+ if (code == 404) {
+ this.error = 'Error 404 - Backend bridge is not working, please check the ajax response.';
+ }
+ if (!this.error && data.result && data.result.error) {
+ this.error = data.result.error;
+ }
+ if (!this.error && data.error) {
+ this.error = data.error.message;
+ }
+ if (!this.error && defaultMsg) {
+ this.error = defaultMsg;
+ }
+ if (this.error) {
+ return deferred.reject(data);
+ }
+ return deferred.resolve(data);
+ };
+
+ FileNavigator.prototype.list = function(recycle) {
+ return this.apiMiddleware.list(this.currentPath, this.deferredHandler.bind(this), recycle);
+ };
+
+ FileNavigator.prototype.refresh = function(recycle) {
+ var self = this;
+ var path = self.currentPath.join('/');
+ self.requesting = true;
+ self.fileList = [];
+ return self.list(recycle).then(function(data) {
+ self.fileList = (data.result || []).map(function(file) {
+ return new Item(file, self.currentPath);
+ });
+ self.buildTree(path);
+ self.onRefresh();
+ }).finally(function() {
+ self.requesting = false;
+ });
+ };
+
+ FileNavigator.prototype.buildTree = function(path) {
+ var flatNodes = [], selectedNode = {};
+
+ function recursive(parent, item, path) {
+ var absName = path ? (path + '/' + item.model.name) : item.model.name;
+ if (parent.name.trim() && path.trim().indexOf(parent.name) !== 0) {
+ parent.nodes = [];
+ }
+ if (parent.name !== path) {
+ parent.nodes.forEach(function(nd) {
+ recursive(nd, item, path);
+ });
+ } else {
+ for (var e in parent.nodes) {
+ if (parent.nodes[e].name === absName) {
+ return;
+ }
+ }
+ parent.nodes.push({item: item, name: absName, nodes: []});
+ }
+
+ parent.nodes = parent.nodes.sort(function(a, b) {
+ return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : a.name.toLowerCase() === b.name.toLowerCase() ? 0 : 1;
+ });
+ }
+
+ function flatten(node, array) {
+ array.push(node);
+ for (var n in node.nodes) {
+ flatten(node.nodes[n], array);
+ }
+ }
+
+ function findNode(data, path) {
+ return data.filter(function (n) {
+ return n.name === path;
+ })[0];
+ }
+
+ !this.history.length && this.history.push({name: '', nodes: []});
+ flatten(this.history[0], flatNodes);
+ selectedNode = findNode(flatNodes, path);
+ selectedNode && (selectedNode.nodes = []);
+
+ for (var o in this.fileList) {
+ var item = this.fileList[o];
+ item instanceof Item && item.isFolder() && recursive(this.history[0], item, path);
+ }
+ };
+
+ FileNavigator.prototype.folderClick = function(item) {
+ this.currentPath = [];
+ if (item && item.isFolder()) {
+ this.currentPath = item.model.fullPath().split('/').splice(1);
+ }
+ this.refresh();
+ };
+
+ FileNavigator.prototype.upDir = function() {
+ if (this.currentPath[0]) {
+ this.currentPath = this.currentPath.slice(0, -1);
+ this.refresh();
+ }
+ };
+
+ FileNavigator.prototype.goTo = function(index) {
+ this.currentPath = this.currentPath.slice(0, index + 1);
+ this.refresh();
+ };
+
+ FileNavigator.prototype.fileNameExists = function(fileName) {
+ return this.fileList.find(function(item) {
+ return fileName.trim && item.model.name.trim() === fileName.trim();
+ });
+ };
+
+ FileNavigator.prototype.listHasFolders = function() {
+ return this.fileList.find(function(item) {
+ return item.model.type === 'dir';
+ });
+ };
+
+ return FileNavigator;
+ }]);
+})(angular);
+/*
+ * Angular JS Multi Select
+ * Creates a dropdown-like button with checkboxes.
+ *
+ * Project started on: Tue, 14 Jan 2014 - 5:18:02 PM
+ * Current version: 4.0.0
+ *
+ * Released under the MIT License
+ * --------------------------------------------------------------------------------
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Ignatius Steven (https://github.com/isteven)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * --------------------------------------------------------------------------------
+ */
+
+'use strict'
+
+angular.module( 'isteven-multi-select', ['ng'] ).directive( 'istevenMultiSelect' , [ '$sce', '$timeout', '$templateCache', function ( $sce, $timeout, $templateCache ) {
+ return {
+ restrict:
+ 'AE',
+
+ scope:
+ {
+ // models
+ inputModel : '=',
+ outputModel : '=',
+
+ // settings based on attribute
+ isDisabled : '=',
+
+ // callbacks
+ onClear : '&',
+ onClose : '&',
+ onSearchChange : '&',
+ onItemClick : '&',
+ onOpen : '&',
+ onReset : '&',
+ onSelectAll : '&',
+ onSelectNone : '&',
+
+ // i18n
+ translation : '='
+ },
+
+ /*
+ * The rest are attributes. They don't need to be parsed / binded, so we can safely access them by value.
+ * - buttonLabel, directiveId, helperElements, itemLabel, maxLabels, orientation, selectionMode, minSearchLength,
+ * tickProperty, disableProperty, groupProperty, searchProperty, maxHeight, outputProperties
+ */
+
+ templateUrl:
+ 'isteven-multi-select.htm',
+
+ link: function ( $scope, element, attrs ) {
+
+ $scope.backUp = [];
+ $scope.varButtonLabel = '';
+ $scope.spacingProperty = '';
+ $scope.indexProperty = '';
+ $scope.orientationH = false;
+ $scope.orientationV = true;
+ $scope.filteredModel = [];
+ $scope.inputLabel = { labelFilter: '' };
+ $scope.tabIndex = 0;
+ $scope.lang = {};
+ $scope.helperStatus = {
+ all : true,
+ none : true,
+ reset : true,
+ filter : true
+ };
+
+ var
+ prevTabIndex = 0,
+ helperItems = [],
+ helperItemsLength = 0,
+ checkBoxLayer = '',
+ scrolled = false,
+ selectedItems = [],
+ formElements = [],
+ vMinSearchLength = 0,
+ clickedItem = null
+
+ // v3.0.0
+ // clear button clicked
+ $scope.clearClicked = function( e ) {
+ $scope.inputLabel.labelFilter = '';
+ $scope.updateFilter();
+ $scope.select( 'clear', e );
+ }
+
+ // A little hack so that AngularJS ng-repeat can loop using start and end index like a normal loop
+ // http://stackoverflow.com/questions/16824853/way-to-ng-repeat-defined-number-of-times-instead-of-repeating-over-array
+ $scope.numberToArray = function( num ) {
+ return new Array( num );
+ }
+
+ // Call this function when user type on the filter field
+ $scope.searchChanged = function() {
+ if ( $scope.inputLabel.labelFilter.length < vMinSearchLength && $scope.inputLabel.labelFilter.length > 0 ) {
+ return false;
+ }
+ $scope.updateFilter();
+ }
+
+ $scope.updateFilter = function()
+ {
+ // we check by looping from end of input-model
+ $scope.filteredModel = [];
+ var i = 0;
+
+ if ( typeof $scope.inputModel === 'undefined' ) {
+ return false;
+ }
+
+ for( i = $scope.inputModel.length - 1; i >= 0; i-- ) {
+
+ // if it's group end, we push it to filteredModel[];
+ if ( typeof $scope.inputModel[ i ][ attrs.groupProperty ] !== 'undefined' && $scope.inputModel[ i ][ attrs.groupProperty ] === false ) {
+ $scope.filteredModel.push( $scope.inputModel[ i ] );
+ }
+
+ // if it's data
+ var gotData = false;
+ if ( typeof $scope.inputModel[ i ][ attrs.groupProperty ] === 'undefined' ) {
+
+ // If we set the search-key attribute, we use this loop.
+ if ( typeof attrs.searchProperty !== 'undefined' && attrs.searchProperty !== '' ) {
+
+ for (var key in $scope.inputModel[ i ] ) {
+ if (
+ typeof $scope.inputModel[ i ][ key ] !== 'boolean'
+ && String( $scope.inputModel[ i ][ key ] ).toUpperCase().indexOf( $scope.inputLabel.labelFilter.toUpperCase() ) >= 0
+ && attrs.searchProperty.indexOf( key ) > -1
+ ) {
+ gotData = true;
+ break;
+ }
+ }
+ }
+ // if there's no search-key attribute, we use this one. Much better on performance.
+ else {
+ for ( var key in $scope.inputModel[ i ] ) {
+ if (
+ typeof $scope.inputModel[ i ][ key ] !== 'boolean'
+ && String( $scope.inputModel[ i ][ key ] ).toUpperCase().indexOf( $scope.inputLabel.labelFilter.toUpperCase() ) >= 0
+ ) {
+ gotData = true;
+ break;
+ }
+ }
+ }
+
+ if ( gotData === true ) {
+ // push
+ $scope.filteredModel.push( $scope.inputModel[ i ] );
+ }
+ }
+
+ // if it's group start
+ if ( typeof $scope.inputModel[ i ][ attrs.groupProperty ] !== 'undefined' && $scope.inputModel[ i ][ attrs.groupProperty ] === true ) {
+
+ if ( typeof $scope.filteredModel[ $scope.filteredModel.length - 1 ][ attrs.groupProperty ] !== 'undefined'
+ && $scope.filteredModel[ $scope.filteredModel.length - 1 ][ attrs.groupProperty ] === false ) {
+ $scope.filteredModel.pop();
+ }
+ else {
+ $scope.filteredModel.push( $scope.inputModel[ i ] );
+ }
+ }
+ }
+
+ $scope.filteredModel.reverse();
+
+ $timeout( function() {
+
+ $scope.getFormElements();
+
+ // Callback: on filter change
+ if ( $scope.inputLabel.labelFilter.length > vMinSearchLength ) {
+
+ var filterObj = [];
+
+ angular.forEach( $scope.filteredModel, function( value, key ) {
+ if ( typeof value !== 'undefined' ) {
+ if ( typeof value[ attrs.groupProperty ] === 'undefined' ) {
+ var tempObj = angular.copy( value );
+ var index = filterObj.push( tempObj );
+ delete filterObj[ index - 1 ][ $scope.indexProperty ];
+ delete filterObj[ index - 1 ][ $scope.spacingProperty ];
+ }
+ }
+ });
+
+ $scope.onSearchChange({
+ data:
+ {
+ keyword: $scope.inputLabel.labelFilter,
+ result: filterObj
+ }
+ });
+ }
+ },0);
+ };
+
+ // List all the input elements. We need this for our keyboard navigation.
+ // This function will be called everytime the filter is updated.
+ // Depending on the size of filtered mode, might not good for performance, but oh well..
+ $scope.getFormElements = function() {
+ formElements = [];
+
+ var
+ selectButtons = [],
+ inputField = [],
+ checkboxes = [],
+ clearButton = [];
+
+ // If available, then get select all, select none, and reset buttons
+ if ( $scope.helperStatus.all || $scope.helperStatus.none || $scope.helperStatus.reset ) {
+ selectButtons = element.children().children().next().children().children()[ 0 ].getElementsByTagName( 'button' );
+ // If available, then get the search box and the clear button
+ if ( $scope.helperStatus.filter ) {
+ // Get helper - search and clear button.
+ inputField = element.children().children().next().children().children().next()[ 0 ].getElementsByTagName( 'input' );
+ clearButton = element.children().children().next().children().children().next()[ 0 ].getElementsByTagName( 'button' );
+ }
+ }
+ else {
+ if ( $scope.helperStatus.filter ) {
+ // Get helper - search and clear button.
+ inputField = element.children().children().next().children().children()[ 0 ].getElementsByTagName( 'input' );
+ clearButton = element.children().children().next().children().children()[ 0 ].getElementsByTagName( 'button' );
+ }
+ }
+
+ // Get checkboxes
+ if ( !$scope.helperStatus.all && !$scope.helperStatus.none && !$scope.helperStatus.reset && !$scope.helperStatus.filter ) {
+ checkboxes = element.children().children().next()[ 0 ].getElementsByTagName( 'input' );
+ }
+ else {
+ checkboxes = element.children().children().next().children().next()[ 0 ].getElementsByTagName( 'input' );
+ }
+
+ // Push them into global array formElements[]
+ for ( var i = 0; i < selectButtons.length ; i++ ) { formElements.push( selectButtons[ i ] ); }
+ for ( var i = 0; i < inputField.length ; i++ ) { formElements.push( inputField[ i ] ); }
+ for ( var i = 0; i < clearButton.length ; i++ ) { formElements.push( clearButton[ i ] ); }
+ for ( var i = 0; i < checkboxes.length ; i++ ) { formElements.push( checkboxes[ i ] ); }
+ }
+
+ // check if an item has attrs.groupProperty (be it true or false)
+ $scope.isGroupMarker = function( item , type ) {
+ if ( typeof item[ attrs.groupProperty ] !== 'undefined' && item[ attrs.groupProperty ] === type ) return true;
+ return false;
+ }
+
+ $scope.removeGroupEndMarker = function( item ) {
+ if ( typeof item[ attrs.groupProperty ] !== 'undefined' && item[ attrs.groupProperty ] === false ) return false;
+ return true;
+ }
+
+ // call this function when an item is clicked
+ $scope.syncItems = function( item, e, ng_repeat_index ) {
+
+ e.preventDefault();
+ e.stopPropagation();
+
+ // if the directive is globaly disabled, do nothing
+ if ( typeof attrs.disableProperty !== 'undefined' && item[ attrs.disableProperty ] === true ) {
+ return false;
+ }
+
+ // if item is disabled, do nothing
+ if ( typeof attrs.isDisabled !== 'undefined' && $scope.isDisabled === true ) {
+ return false;
+ }
+
+ // if end group marker is clicked, do nothing
+ if ( typeof item[ attrs.groupProperty ] !== 'undefined' && item[ attrs.groupProperty ] === false ) {
+ return false;
+ }
+
+ var index = $scope.filteredModel.indexOf( item );
+
+ // if the start of group marker is clicked ( only for multiple selection! )
+ // how it works:
+ // - if, in a group, there are items which are not selected, then they all will be selected
+ // - if, in a group, all items are selected, then they all will be de-selected
+ if ( typeof item[ attrs.groupProperty ] !== 'undefined' && item[ attrs.groupProperty ] === true ) {
+
+ // this is only for multiple selection, so if selection mode is single, do nothing
+ if ( typeof attrs.selectionMode !== 'undefined' && attrs.selectionMode.toUpperCase() === 'SINGLE' ) {
+ return false;
+ }
+
+ var i,j,k;
+ var startIndex = 0;
+ var endIndex = $scope.filteredModel.length - 1;
+ var tempArr = [];
+
+ // nest level is to mark the depth of the group.
+ // when you get into a group (start group marker), nestLevel++
+ // when you exit a group (end group marker), nextLevel--
+ var nestLevel = 0;
+
+ // we loop throughout the filtered model (not whole model)
+ for( i = index ; i < $scope.filteredModel.length ; i++) {
+
+ // this break will be executed when we're done processing each group
+ if ( nestLevel === 0 && i > index )
+ {
+ break;
+ }
+
+ if ( typeof $scope.filteredModel[ i ][ attrs.groupProperty ] !== 'undefined' && $scope.filteredModel[ i ][ attrs.groupProperty ] === true ) {
+
+ // To cater multi level grouping
+ if ( tempArr.length === 0 ) {
+ startIndex = i + 1;
+ }
+ nestLevel = nestLevel + 1;
+ }
+
+ // if group end
+ else if ( typeof $scope.filteredModel[ i ][ attrs.groupProperty ] !== 'undefined' && $scope.filteredModel[ i ][ attrs.groupProperty ] === false ) {
+
+ nestLevel = nestLevel - 1;
+
+ // cek if all are ticked or not
+ if ( tempArr.length > 0 && nestLevel === 0 ) {
+
+ var allTicked = true;
+
+ endIndex = i;
+
+ for ( j = 0; j < tempArr.length ; j++ ) {
+ if ( typeof tempArr[ j ][ $scope.tickProperty ] !== 'undefined' && tempArr[ j ][ $scope.tickProperty ] === false ) {
+ allTicked = false;
+ break;
+ }
+ }
+
+ if ( allTicked === true ) {
+ for ( j = startIndex; j <= endIndex ; j++ ) {
+ if ( typeof $scope.filteredModel[ j ][ attrs.groupProperty ] === 'undefined' ) {
+ if ( typeof attrs.disableProperty === 'undefined' ) {
+ $scope.filteredModel[ j ][ $scope.tickProperty ] = false;
+ // we refresh input model as well
+ inputModelIndex = $scope.filteredModel[ j ][ $scope.indexProperty ];
+ $scope.inputModel[ inputModelIndex ][ $scope.tickProperty ] = false;
+ }
+ else if ( $scope.filteredModel[ j ][ attrs.disableProperty ] !== true ) {
+ $scope.filteredModel[ j ][ $scope.tickProperty ] = false;
+ // we refresh input model as well
+ inputModelIndex = $scope.filteredModel[ j ][ $scope.indexProperty ];
+ $scope.inputModel[ inputModelIndex ][ $scope.tickProperty ] = false;
+ }
+ }
+ }
+ }
+
+ else {
+ for ( j = startIndex; j <= endIndex ; j++ ) {
+ if ( typeof $scope.filteredModel[ j ][ attrs.groupProperty ] === 'undefined' ) {
+ if ( typeof attrs.disableProperty === 'undefined' ) {
+ $scope.filteredModel[ j ][ $scope.tickProperty ] = true;
+ // we refresh input model as well
+ inputModelIndex = $scope.filteredModel[ j ][ $scope.indexProperty ];
+ $scope.inputModel[ inputModelIndex ][ $scope.tickProperty ] = true;
+
+ }
+ else if ( $scope.filteredModel[ j ][ attrs.disableProperty ] !== true ) {
+ $scope.filteredModel[ j ][ $scope.tickProperty ] = true;
+ // we refresh input model as well
+ inputModelIndex = $scope.filteredModel[ j ][ $scope.indexProperty ];
+ $scope.inputModel[ inputModelIndex ][ $scope.tickProperty ] = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // if data
+ else {
+ tempArr.push( $scope.filteredModel[ i ] );
+ }
+ }
+ }
+
+ // if an item (not group marker) is clicked
+ else {
+
+ // If it's single selection mode
+ if ( typeof attrs.selectionMode !== 'undefined' && attrs.selectionMode.toUpperCase() === 'SINGLE' ) {
+
+ // first, set everything to false
+ for( i=0 ; i < $scope.filteredModel.length ; i++) {
+ $scope.filteredModel[ i ][ $scope.tickProperty ] = false;
+ }
+ for( i=0 ; i < $scope.inputModel.length ; i++) {
+ $scope.inputModel[ i ][ $scope.tickProperty ] = false;
+ }
+
+ // then set the clicked item to true
+ $scope.filteredModel[ index ][ $scope.tickProperty ] = true;
+ }
+
+ // Multiple
+ else {
+ $scope.filteredModel[ index ][ $scope.tickProperty ] = !$scope.filteredModel[ index ][ $scope.tickProperty ];
+ }
+
+ // we refresh input model as well
+ var inputModelIndex = $scope.filteredModel[ index ][ $scope.indexProperty ];
+ $scope.inputModel[ inputModelIndex ][ $scope.tickProperty ] = $scope.filteredModel[ index ][ $scope.tickProperty ];
+ }
+
+ // we execute the callback function here
+ clickedItem = angular.copy( item );
+ if ( clickedItem !== null ) {
+ $timeout( function() {
+ delete clickedItem[ $scope.indexProperty ];
+ delete clickedItem[ $scope.spacingProperty ];
+ $scope.onItemClick( { data: clickedItem } );
+ clickedItem = null;
+ }, 0 );
+ }
+
+ $scope.refreshOutputModel();
+ $scope.refreshButton();
+
+ // We update the index here
+ prevTabIndex = $scope.tabIndex;
+ $scope.tabIndex = ng_repeat_index + helperItemsLength;
+
+ // Set focus on the hidden checkbox
+ e.target.focus();
+
+ // set & remove CSS style
+ $scope.removeFocusStyle( prevTabIndex );
+ $scope.setFocusStyle( $scope.tabIndex );
+
+ if ( typeof attrs.selectionMode !== 'undefined' && attrs.selectionMode.toUpperCase() === 'SINGLE' ) {
+ // on single selection mode, we then hide the checkbox layer
+ $scope.toggleCheckboxes( e );
+ }
+ }
+
+ // update $scope.outputModel
+ $scope.refreshOutputModel = function() {
+
+ $scope.outputModel = [];
+ var
+ outputProps = [],
+ tempObj = {};
+
+ // v4.0.0
+ if ( typeof attrs.outputProperties !== 'undefined' ) {
+ outputProps = attrs.outputProperties.split(' ');
+ angular.forEach( $scope.inputModel, function( value, key ) {
+ if (
+ typeof value !== 'undefined'
+ && typeof value[ attrs.groupProperty ] === 'undefined'
+ && value[ $scope.tickProperty ] === true
+ ) {
+ tempObj = {};
+ angular.forEach( value, function( value1, key1 ) {
+ if ( outputProps.indexOf( key1 ) > -1 ) {
+ tempObj[ key1 ] = value1;
+ }
+ });
+ var index = $scope.outputModel.push( tempObj );
+ delete $scope.outputModel[ index - 1 ][ $scope.indexProperty ];
+ delete $scope.outputModel[ index - 1 ][ $scope.spacingProperty ];
+ }
+ });
+ }
+ else {
+ angular.forEach( $scope.inputModel, function( value, key ) {
+ if (
+ typeof value !== 'undefined'
+ && typeof value[ attrs.groupProperty ] === 'undefined'
+ && value[ $scope.tickProperty ] === true
+ ) {
+ var temp = angular.copy( value );
+ var index = $scope.outputModel.push( temp );
+ delete $scope.outputModel[ index - 1 ][ $scope.indexProperty ];
+ delete $scope.outputModel[ index - 1 ][ $scope.spacingProperty ];
+ }
+ });
+ }
+ }
+
+ // refresh button label
+ $scope.refreshButton = function() {
+
+ $scope.varButtonLabel = '';
+ var ctr = 0;
+
+ // refresh button label...
+ if ( $scope.outputModel.length === 0 ) {
+ // https://github.com/isteven/angular-multi-select/pull/19
+ $scope.varButtonLabel = $scope.lang.nothingSelected;
+ }
+ else {
+ var tempMaxLabels = $scope.outputModel.length;
+ if ( typeof attrs.maxLabels !== 'undefined' && attrs.maxLabels !== '' ) {
+ tempMaxLabels = attrs.maxLabels;
+ }
+
+ // if max amount of labels displayed..
+ if ( $scope.outputModel.length > tempMaxLabels ) {
+ $scope.more = true;
+ }
+ else {
+ $scope.more = false;
+ }
+
+ angular.forEach( $scope.inputModel, function( value, key ) {
+ if ( typeof value !== 'undefined' && value[ attrs.tickProperty ] === true ) {
+ if ( ctr < tempMaxLabels ) {
+ $scope.varButtonLabel += ( $scope.varButtonLabel.length > 0 ? '
,
' : '
') + $scope.writeLabel( value, 'buttonLabel' );
+ }
+ ctr++;
+ }
+ });
+
+ if ( $scope.more === true ) {
+ // https://github.com/isteven/angular-multi-select/pull/16
+ if (tempMaxLabels > 0) {
+ $scope.varButtonLabel += ', ... ';
+ }
+ $scope.varButtonLabel += '(' + $scope.outputModel.length + ')';
+ }
+ }
+ $scope.varButtonLabel = $sce.trustAsHtml( $scope.varButtonLabel + '
' );
+ }
+
+ // Check if a checkbox is disabled or enabled. It will check the granular control (disableProperty) and global control (isDisabled)
+ // Take note that the granular control has higher priority.
+ $scope.itemIsDisabled = function( item ) {
+
+ if ( typeof attrs.disableProperty !== 'undefined' && item[ attrs.disableProperty ] === true ) {
+ return true;
+ }
+ else {
+ if ( $scope.isDisabled === true ) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ }
+
+ // A simple function to parse the item label settings. Used on the buttons and checkbox labels.
+ $scope.writeLabel = function( item, type ) {
+
+ // type is either 'itemLabel' or 'buttonLabel'
+ var temp = attrs[ type ].split( ' ' );
+ var label = '';
+
+ angular.forEach( temp, function( value, key ) {
+ item[ value ] && ( label += ' ' + value.split( '.' ).reduce( function( prev, current ) {
+ return prev[ current ];
+ }, item ));
+ });
+
+ if ( type.toUpperCase() === 'BUTTONLABEL' ) {
+ return label;
+ }
+ return $sce.trustAsHtml( label );
+ }
+
+ // UI operations to show/hide checkboxes based on click event..
+ $scope.toggleCheckboxes = function( e ) {
+
+ // We grab the button
+ var clickedEl = element.children()[0];
+
+ // Just to make sure.. had a bug where key events were recorded twice
+ angular.element( document ).off( 'click', $scope.externalClickListener );
+ angular.element( document ).off( 'keydown', $scope.keyboardListener );
+
+ // The idea below was taken from another multi-select directive - https://github.com/amitava82/angular-multiselect
+ // His version is awesome if you need a more simple multi-select approach.
+
+ // close
+ if ( angular.element( checkBoxLayer ).hasClass( 'show' )) {
+
+ angular.element( checkBoxLayer ).removeClass( 'show' );
+ angular.element( clickedEl ).removeClass( 'buttonClicked' );
+ angular.element( document ).off( 'click', $scope.externalClickListener );
+ angular.element( document ).off( 'keydown', $scope.keyboardListener );
+
+ // clear the focused element;
+ $scope.removeFocusStyle( $scope.tabIndex );
+ if ( typeof formElements[ $scope.tabIndex ] !== 'undefined' ) {
+ formElements[ $scope.tabIndex ].blur();
+ }
+
+ // close callback
+ $timeout( function() {
+ $scope.onClose();
+ }, 0 );
+
+ // set focus on button again
+ element.children().children()[ 0 ].focus();
+ }
+ // open
+ else
+ {
+ // clear filter
+ $scope.inputLabel.labelFilter = '';
+ $scope.updateFilter();
+
+ helperItems = [];
+ helperItemsLength = 0;
+
+ angular.element( checkBoxLayer ).addClass( 'show' );
+ angular.element( clickedEl ).addClass( 'buttonClicked' );
+
+ // Attach change event listener on the input filter.
+ // We need this because ng-change is apparently not an event listener.
+ angular.element( document ).on( 'click', $scope.externalClickListener );
+ angular.element( document ).on( 'keydown', $scope.keyboardListener );
+
+ // to get the initial tab index, depending on how many helper elements we have.
+ // priority is to always focus it on the input filter
+ $scope.getFormElements();
+ $scope.tabIndex = 0;
+
+ var helperContainer = angular.element( element[ 0 ].querySelector( '.helperContainer' ) )[0];
+
+ if ( typeof helperContainer !== 'undefined' ) {
+ for ( var i = 0; i < helperContainer.getElementsByTagName( 'BUTTON' ).length ; i++ ) {
+ helperItems[ i ] = helperContainer.getElementsByTagName( 'BUTTON' )[ i ];
+ }
+ helperItemsLength = helperItems.length + helperContainer.getElementsByTagName( 'INPUT' ).length;
+ }
+
+ // focus on the filter element on open.
+ if ( element[ 0 ].querySelector( '.inputFilter' ) ) {
+ element[ 0 ].querySelector( '.inputFilter' ).focus();
+ $scope.tabIndex = $scope.tabIndex + helperItemsLength - 2;
+ // blur button in vain
+ angular.element( element ).children()[ 0 ].blur();
+ }
+ // if there's no filter then just focus on the first checkbox item
+ else {
+ if ( !$scope.isDisabled ) {
+ $scope.tabIndex = $scope.tabIndex + helperItemsLength;
+ if ( $scope.inputModel.length > 0 ) {
+ formElements[ $scope.tabIndex ].focus();
+ $scope.setFocusStyle( $scope.tabIndex );
+ // blur button in vain
+ angular.element( element ).children()[ 0 ].blur();
+ }
+ }
+ }
+
+ // open callback
+ $scope.onOpen();
+ }
+ }
+
+ // handle clicks outside the button / multi select layer
+ $scope.externalClickListener = function( e ) {
+
+ var targetsArr = element.find( e.target.tagName );
+ for (var i = 0; i < targetsArr.length; i++) {
+ if ( e.target == targetsArr[i] ) {
+ return;
+ }
+ }
+
+ angular.element( checkBoxLayer.previousSibling ).removeClass( 'buttonClicked' );
+ angular.element( checkBoxLayer ).removeClass( 'show' );
+ angular.element( document ).off( 'click', $scope.externalClickListener );
+ angular.element( document ).off( 'keydown', $scope.keyboardListener );
+
+ // close callback
+ $timeout( function() {
+ $scope.onClose();
+ }, 0 );
+
+ // set focus on button again
+ element.children().children()[ 0 ].focus();
+ }
+
+ // select All / select None / reset buttons
+ $scope.select = function( type, e ) {
+
+ var helperIndex = helperItems.indexOf( e.target );
+ $scope.tabIndex = helperIndex;
+
+ switch( type.toUpperCase() ) {
+ case 'ALL':
+ angular.forEach( $scope.filteredModel, function( value, key ) {
+ if ( typeof value !== 'undefined' && value[ attrs.disableProperty ] !== true ) {
+ if ( typeof value[ attrs.groupProperty ] === 'undefined' ) {
+ value[ $scope.tickProperty ] = true;
+ }
+ }
+ });
+ $scope.refreshOutputModel();
+ $scope.refreshButton();
+ $scope.onSelectAll();
+ break;
+ case 'NONE':
+ angular.forEach( $scope.filteredModel, function( value, key ) {
+ if ( typeof value !== 'undefined' && value[ attrs.disableProperty ] !== true ) {
+ if ( typeof value[ attrs.groupProperty ] === 'undefined' ) {
+ value[ $scope.tickProperty ] = false;
+ }
+ }
+ });
+ $scope.refreshOutputModel();
+ $scope.refreshButton();
+ $scope.onSelectNone();
+ break;
+ case 'RESET':
+ angular.forEach( $scope.filteredModel, function( value, key ) {
+ if ( typeof value[ attrs.groupProperty ] === 'undefined' && typeof value !== 'undefined' && value[ attrs.disableProperty ] !== true ) {
+ var temp = value[ $scope.indexProperty ];
+ value[ $scope.tickProperty ] = $scope.backUp[ temp ][ $scope.tickProperty ];
+ }
+ });
+ $scope.refreshOutputModel();
+ $scope.refreshButton();
+ $scope.onReset();
+ break;
+ case 'CLEAR':
+ $scope.tabIndex = $scope.tabIndex + 1;
+ $scope.onClear();
+ break;
+ case 'FILTER':
+ $scope.tabIndex = helperItems.length - 1;
+ break;
+ default:
+ }
+ }
+
+ // just to create a random variable name
+ function genRandomString( length ) {
+ var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
+ var temp = '';
+ for( var i=0; i < length; i++ ) {
+ temp += possible.charAt( Math.floor( Math.random() * possible.length ));
+ }
+ return temp;
+ }
+
+ // count leading spaces
+ $scope.prepareGrouping = function() {
+ var spacing = 0;
+ angular.forEach( $scope.filteredModel, function( value, key ) {
+ value[ $scope.spacingProperty ] = spacing;
+ if ( value[ attrs.groupProperty ] === true ) {
+ spacing+=2;
+ }
+ else if ( value[ attrs.groupProperty ] === false ) {
+ spacing-=2;
+ }
+ });
+ }
+
+ // prepare original index
+ $scope.prepareIndex = function() {
+ var ctr = 0;
+ angular.forEach( $scope.filteredModel, function( value, key ) {
+ value[ $scope.indexProperty ] = ctr;
+ ctr++;
+ });
+ }
+
+ // navigate using up and down arrow
+ $scope.keyboardListener = function( e ) {
+
+ var key = e.keyCode ? e.keyCode : e.which;
+ var isNavigationKey = false;
+
+ // ESC key (close)
+ if ( key === 27 ) {
+ e.preventDefault();
+ e.stopPropagation();
+ $scope.toggleCheckboxes( e );
+ }
+
+
+ // next element ( tab, down & right key )
+ else if ( key === 40 || key === 39 || ( !e.shiftKey && key == 9 ) ) {
+
+ isNavigationKey = true;
+ prevTabIndex = $scope.tabIndex;
+ $scope.tabIndex++;
+ if ( $scope.tabIndex > formElements.length - 1 ) {
+ $scope.tabIndex = 0;
+ prevTabIndex = formElements.length - 1;
+ }
+ while ( formElements[ $scope.tabIndex ].disabled === true ) {
+ $scope.tabIndex++;
+ if ( $scope.tabIndex > formElements.length - 1 ) {
+ $scope.tabIndex = 0;
+ }
+ if ( $scope.tabIndex === prevTabIndex ) {
+ break;
+ }
+ }
+ }
+
+ // prev element ( shift+tab, up & left key )
+ else if ( key === 38 || key === 37 || ( e.shiftKey && key == 9 ) ) {
+ isNavigationKey = true;
+ prevTabIndex = $scope.tabIndex;
+ $scope.tabIndex--;
+ if ( $scope.tabIndex < 0 ) {
+ $scope.tabIndex = formElements.length - 1;
+ prevTabIndex = 0;
+ }
+ while ( formElements[ $scope.tabIndex ].disabled === true ) {
+ $scope.tabIndex--;
+ if ( $scope.tabIndex === prevTabIndex ) {
+ break;
+ }
+ if ( $scope.tabIndex < 0 ) {
+ $scope.tabIndex = formElements.length - 1;
+ }
+ }
+ }
+
+ if ( isNavigationKey === true ) {
+
+ e.preventDefault();
+
+ // set focus on the checkbox
+ formElements[ $scope.tabIndex ].focus();
+ var actEl = document.activeElement;
+
+ if ( actEl.type.toUpperCase() === 'CHECKBOX' ) {
+ $scope.setFocusStyle( $scope.tabIndex );
+ $scope.removeFocusStyle( prevTabIndex );
+ }
+ else {
+ $scope.removeFocusStyle( prevTabIndex );
+ $scope.removeFocusStyle( helperItemsLength );
+ $scope.removeFocusStyle( formElements.length - 1 );
+ }
+ }
+
+ isNavigationKey = false;
+ }
+
+ // set (add) CSS style on selected row
+ $scope.setFocusStyle = function( tabIndex ) {
+ angular.element( formElements[ tabIndex ] ).parent().parent().parent().addClass( 'multiSelectFocus' );
+ }
+
+ // remove CSS style on selected row
+ $scope.removeFocusStyle = function( tabIndex ) {
+ angular.element( formElements[ tabIndex ] ).parent().parent().parent().removeClass( 'multiSelectFocus' );
+ }
+
+ /*********************
+ *********************
+ *
+ * 1) Initializations
+ *
+ *********************
+ *********************/
+
+ // attrs to $scope - attrs-$scope - attrs - $scope
+ // Copy some properties that will be used on the template. They need to be in the $scope.
+ $scope.groupProperty = attrs.groupProperty;
+ $scope.tickProperty = attrs.tickProperty;
+ $scope.directiveId = attrs.directiveId;
+
+ // Unfortunately I need to add these grouping properties into the input model
+ var tempStr = genRandomString( 5 );
+ $scope.indexProperty = 'idx_' + tempStr;
+ $scope.spacingProperty = 'spc_' + tempStr;
+
+ // set orientation css
+ if ( typeof attrs.orientation !== 'undefined' ) {
+
+ if ( attrs.orientation.toUpperCase() === 'HORIZONTAL' ) {
+ $scope.orientationH = true;
+ $scope.orientationV = false;
+ }
+ else
+ {
+ $scope.orientationH = false;
+ $scope.orientationV = true;
+ }
+ }
+
+ // get elements required for DOM operation
+ checkBoxLayer = element.children().children().next()[0];
+
+ // set max-height property if provided
+ if ( typeof attrs.maxHeight !== 'undefined' ) {
+ var layer = element.children().children().children()[0];
+ angular.element( layer ).attr( "style", "height:" + attrs.maxHeight + "; overflow-y:scroll;" );
+ }
+
+ // some flags for easier checking
+ for ( var property in $scope.helperStatus ) {
+ if ( $scope.helperStatus.hasOwnProperty( property )) {
+ if (
+ typeof attrs.helperElements !== 'undefined'
+ && attrs.helperElements.toUpperCase().indexOf( property.toUpperCase() ) === -1
+ ) {
+ $scope.helperStatus[ property ] = false;
+ }
+ }
+ }
+ if ( typeof attrs.selectionMode !== 'undefined' && attrs.selectionMode.toUpperCase() === 'SINGLE' ) {
+ $scope.helperStatus[ 'all' ] = false;
+ $scope.helperStatus[ 'none' ] = false;
+ }
+
+ // helper button icons.. I guess you can use html tag here if you want to.
+ $scope.icon = {};
+ $scope.icon.selectAll = '✓'; // a tick icon
+ $scope.icon.selectNone = '×'; // x icon
+ $scope.icon.reset = '↶'; // undo icon
+ // this one is for the selected items
+ $scope.icon.tickMark = '✓'; // a tick icon
+
+ // configurable button labels
+ if ( typeof attrs.translation !== 'undefined' ) {
+ $scope.lang.selectAll = $sce.trustAsHtml( $scope.icon.selectAll + ' ' + $scope.translation.selectAll );
+ $scope.lang.selectNone = $sce.trustAsHtml( $scope.icon.selectNone + ' ' + $scope.translation.selectNone );
+ $scope.lang.reset = $sce.trustAsHtml( $scope.icon.reset + ' ' + $scope.translation.reset );
+ $scope.lang.search = $scope.translation.search;
+ $scope.lang.nothingSelected = $sce.trustAsHtml( $scope.translation.nothingSelected );
+ }
+ else {
+ $scope.lang.selectAll = $sce.trustAsHtml( $scope.icon.selectAll + ' Select All' );
+ $scope.lang.selectNone = $sce.trustAsHtml( $scope.icon.selectNone + ' Select None' );
+ $scope.lang.reset = $sce.trustAsHtml( $scope.icon.reset + ' Reset' );
+ $scope.lang.search = 'Search...';
+ $scope.lang.nothingSelected = 'None Selected';
+ }
+ $scope.icon.tickMark = $sce.trustAsHtml( $scope.icon.tickMark );
+
+ // min length of keyword to trigger the filter function
+ if ( typeof attrs.MinSearchLength !== 'undefined' && parseInt( attrs.MinSearchLength ) > 0 ) {
+ vMinSearchLength = Math.floor( parseInt( attrs.MinSearchLength ) );
+ }
+
+ /*******************************************************
+ *******************************************************
+ *
+ * 2) Logic starts here, initiated by watch 1 & watch 2
+ *
+ *******************************************************
+ *******************************************************/
+
+ // watch1, for changes in input model property
+ // updates multi-select when user select/deselect a single checkbox programatically
+ // https://github.com/isteven/angular-multi-select/issues/8
+ $scope.$watch( 'inputModel' , function( newVal ) {
+ if ( newVal ) {
+ $scope.refreshOutputModel();
+ $scope.refreshButton();
+ }
+ }, true );
+
+ // watch2 for changes in input model as a whole
+ // this on updates the multi-select when a user load a whole new input-model. We also update the $scope.backUp variable
+ $scope.$watch( 'inputModel' , function( newVal ) {
+ if ( newVal ) {
+ $scope.backUp = angular.copy( $scope.inputModel );
+ $scope.updateFilter();
+ $scope.prepareGrouping();
+ $scope.prepareIndex();
+ $scope.refreshOutputModel();
+ $scope.refreshButton();
+ }
+ });
+
+ // watch for changes in directive state (disabled or enabled)
+ $scope.$watch( 'isDisabled' , function( newVal ) {
+ $scope.isDisabled = newVal;
+ });
+
+ // this is for touch enabled devices. We don't want to hide checkboxes on scroll.
+ var onTouchStart = function( e ) {
+ $scope.$apply( function() {
+ $scope.scrolled = false;
+ });
+ };
+ angular.element( document ).bind( 'touchstart', onTouchStart);
+ var onTouchMove = function( e ) {
+ $scope.$apply( function() {
+ $scope.scrolled = true;
+ });
+ };
+ angular.element( document ).bind( 'touchmove', onTouchMove);
+
+ // unbind document events to prevent memory leaks
+ $scope.$on( '$destroy', function () {
+ angular.element( document ).unbind( 'touchstart', onTouchStart);
+ angular.element( document ).unbind( 'touchmove', onTouchMove);
+ });
+ }
+ }
+}]).run( [ '$templateCache' , function( $templateCache ) {
+ var template =
+ '
' +
+ // main button
+ '' +
+ ' ' +
+ // overlay layer
+ '' +
+ // container of the helper elements
+ '
' +
+ // container of the first 3 buttons, select all, none and reset
+ '
' +
+ // select all
+ '' +
+ ' '+
+ // select none
+ '' +
+ ' '+
+ // reset
+ ''+
+ ' ' +
+ '
' +
+ // the search box
+ '
'+
+ // textfield
+ ' '+
+ // clear button
+ '× '+
+ '
'+
+ '
'+
+ // selection items
+ '
'+
+ '
'+
+ // this is the spacing for grouped items
+ '
'+
+ '
'+
+ '
'+
+ ''+
+ // input, so that it can accept focus on keyboard click
+ ' '+
+ // item label using ng-bind-hteml
+ ''+
+ ' '+
+ ' '+
+ '
'+
+ // the tick/check mark
+ '
'+
+ '
'+
+ '
'+
+ '
'+
+ ' ';
+ $templateCache.put( 'isteven-multi-select.htm' , template );
+}]);
angular.module("FileManagerApp").run(["$templateCache", function($templateCache) {$templateCache.put("src/templates/current-folder-breadcrumb.html","
\n \n \n {{ config.appName }}\n \n \n \n \n {{dir | strLimit : 8}}\n \n \n {{dir | strLimit : 12}}\n \n \n ");
-$templateCache.put("src/templates/item-context-menu.html","");
-$templateCache.put("src/templates/main-icons-preview.html","
\n
\n\n
\n\n
\n {{\"no_files_in_folder\" | translate}}...\n
\n \n
\n {{ fileNavigator.error }}\n
\n
");
-$templateCache.put("src/templates/main-icons.html","
\n
\n\n
\n\n
\n {{\"no_files_in_folder\" | translate}}...\n
\n \n
\n {{ fileNavigator.error }}\n
\n
");
-$templateCache.put("src/templates/main-table-modal.html","
");
-$templateCache.put("src/templates/main-table.html","
\n");
-$templateCache.put("src/templates/main.html","
\n");
-$templateCache.put("src/templates/modals.html","
\n
\n
\n \n
\n
\n\n
\n
{{\'loading\' | translate}} ... \n
\n
\n
\n \n
\n
\n
\n\n
\n\n
\n\n\n
\n\n
\n\n
\n\n\n\n
\n\n
\n
\n
\n
\n \n \n
{{\'folder_name\' | translate}} \n
\n
\n
\n \n \n
\n
\n
\n\n
\n
\n
\n
\n \n \n
\n {{\"files_will_uploaded_to\" | translate}}\n /{{fileNavigator.currentPath.join(\'/\')}} \n \n
\n {{\"select_files\" | translate}}\n \n\n
\n
\n
\n
{{\"uploading\" | translate}}... {{apiMiddleware.apiHandler.progress}}% \n
\n
\n
\n
\n
\n \n \n
\n
\n
\n\n
\n
\n
\n
\n \n \n \n
\n
\n
{{\'read\' | translate}} \n
\n
\n
\n
\n
\n
\n
\n
{{\'write\' | translate}} \n\n
\n
\n
\n
\n
\n
\n
\n
{{\'delete\' | translate}} \n
\n
\n
\n
\n
\n
\n
\n \n \n
\n
\n
\n\n
\n
\n
\n
\n \n \n\n
{{\'file_path\' | translate}} \n\n
\n \n \n \n \n \n \n
\n\n
{{\'stream_link\' | translate}} \n\n
\n \n \n \n \n \n \n
\n\n
{{\'download_link\' | translate}} \n\n
\n \n \n \n \n \n \n
\n\n
\n \n \n
\n
\n
\n\n
\n
\n
\n \n
\n
\n
\n
\n
\n
\n {{\"select_this\" | translate}}\n \n
\n
\n \n
\n
\n
\n\n\n\n\n\n\n");
-$templateCache.put("src/templates/navbar.html","
\n \n ");
-$templateCache.put("src/templates/sidebar.html","
\n\n");
-$templateCache.put("src/templates/spinner.html","
\n \n \n \n
");}]);
\ No newline at end of file
+ $templateCache.put("src/templates/item-context-menu.html","");
+ $templateCache.put("src/templates/main-icons-preview.html","
\n
\n\n
\n\n
\n {{\"no_files_in_folder\" | translate}}...\n
\n \n
\n {{ fileNavigator.error }}\n
\n
");
+ $templateCache.put("src/templates/main-icons.html","
\n
\n\n
\n\n
\n {{\"no_files_in_folder\" | translate}}...\n
\n \n
\n {{ fileNavigator.error }}\n
\n
");
+ $templateCache.put("src/templates/main-table-modal.html","
");
+ $templateCache.put("src/templates/main-table.html","
\n");
+ $templateCache.put("src/templates/main.html","
\n");
+ $templateCache.put("src/templates/modals.html","
\n
\n
\n \n
\n
\n\n
\n
{{\'loading\' | translate}} ... \n
\n
\n
\n \n
\n
\n
\n\n
\n
\n
\n
\n \n \n {{\'sure_to_delete\' | translate}}
\n\n
\n
\n \n \n
\n
\n
\n\n
\n\n\n
\n
\n
\n
\n \n \n
{{\'enter_new_name_for\' | translate}} {{singleSelection() && singleSelection().model.name}} \n
\n\n
\n
\n \n \n
\n
\n
\n\n
\n
\n
\n
\n \n \n
\n {{\'enter_new_name_for\' | translate}} {{singleSelection().model.name}} \n \n
\n\n
\n
\n
\n \n \n
\n
\n
\n\n
\n
\n
\n
\n \n \n
\n
{{\'compression_started\' | translate}}
\n
\n
\n
\n {{\'sure_to_start_compression_with\' | translate}} {{singleSelection().model.name}} ?\n
\n
\n \n {{\'enter_file_name_for_compression\' | translate}}\n \n \n \n
\n
\n\n
\n
\n \n \n
\n
\n
\n\n\n\n
\n
\n
\n
\n \n \n
{{ singleSelection().model.fullPath() }} \n
{{\'loading\' | translate}} ... \n
\n
\n
\n \n \n
\n
\n
\n\n
\n
\n
\n
\n \n \n
{{\'folder_name\' | translate}} \n
\n
\n
\n \n \n
\n
\n
\n\n
\n
\n
\n
\n \n \n
\n {{\"files_will_uploaded_to\" | translate}}\n /{{fileNavigator.currentPath.join(\'/\')}} \n \n
\n {{\"select_files\" | translate}}\n \n\n
\n
\n
\n
{{\"uploading\" | translate}}... {{apiMiddleware.apiHandler.progress}}% \n
\n
\n
\n
\n
\n \n \n
\n
\n
\n\n
\n
\n
\n
\n \n \n \n
\n
\n
{{\'read\' | translate}} \n
\n
\n
\n
\n
\n
\n
\n
{{\'write\' | translate}} \n\n
\n
\n
\n
\n
\n
\n
\n
{{\'delete\' | translate}} \n
\n
\n
\n
\n
\n
\n
\n \n \n
\n
\n
\n\n
\n
\n
\n
\n \n \n\n
{{\'file_path\' | translate}} \n\n
\n \n \n \n \n \n \n
\n\n
{{\'stream_link\' | translate}} \n\n
\n \n \n \n \n \n \n
\n\n
{{\'download_link\' | translate}} \n\n
\n \n \n \n \n \n \n
\n\n
\n \n \n
\n
\n
\n\n
\n
\n
\n \n
\n
\n
\n
\n
\n
\n {{\"select_this\" | translate}}\n \n
\n
\n \n
\n
\n
\n\n\n\n\n\n\n");
+ $templateCache.put("src/templates/navbar.html","
\n \n ");
+ $templateCache.put("src/templates/sidebar.html","
\n\n");
+ $templateCache.put("src/templates/spinner.html","
\n \n \n \n
");}]);
\ No newline at end of file
diff --git a/src/widgets/FileManagerWidget.php b/src/widgets/FileManagerWidget.php
index e0bc4c8..f223e4e 100644
--- a/src/widgets/FileManagerWidget.php
+++ b/src/widgets/FileManagerWidget.php
@@ -39,6 +39,16 @@ class FileManagerWidget extends Widget
*/
public $template = '
';
+ /**
+ * @var string
+ */
+ public $thumbnailUrlPrefix;
+
+ /**
+ * @var string
+ */
+ public $thumbnailUrlSuffix;
+
/**
* @inheritdoc
*/
@@ -130,6 +140,8 @@ protected function setFileManagerConfig()
downloadFilesByAjax: true,
previewImagesInModal: true,
enablePermissionsRecursive: false,
+ thumbnailUrlPrefix: '{$this->thumbnailUrlPrefix}',
+ thumbnailUrlSuffix: '{$this->thumbnailUrlSuffix}',
// File patterns
isEditableFilePattern: /\.(!)/i,