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 - '
'+ - '
'+ - '
'+ - ''+ - '
'+ - // 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 + '
'+ + '
'+ + '
'+ + ''+ + '
'+ + // 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
  1. \n \n {{ config.appName }}\n \n
  2. \n
  3. \n \n {{dir | strLimit : 8}}\n \n \n {{dir | strLimit : 12}}\n \n
  4. \n
"); -$templateCache.put("src/templates/item-context-menu.html",""); -$templateCache.put("src/templates/main-icons-preview.html","
\n \n\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\n
\n {{\"no_files_in_folder\" | translate}}...\n
\n \n
\n {{ fileNavigator.error }}\n
\n
"); -$templateCache.put("src/templates/main-table-modal.html","\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 {{\"name\" | translate}}\n \n \n
\n
\n
\n {{\"no_folders_in_folder\" | translate}}...\n \n \n
\n {{ fileNavigator.error }}\n
\n \n \n {{item.model.name | strLimit : 32}}\n \n \n \n
"); -$templateCache.put("src/templates/main-table.html","\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 {{\"name\" | translate}}\n \n \n \n \n {{\"size\" | translate}}\n \n \n \n \n {{\"date\" | translate}}\n \n \n \n \n {{\"permissions\" | translate}}\n \n \n
\n
\n
\n {{\"no_files_in_folder\" | translate}}...\n
\n {{ fileNavigator.error }}\n
\n \n \n \n {{item.model.name | strLimit : 64}}\n \n \n \n {{item.model.size | humanReadableFileSize}}\n \n \n {{item.model.date | formatDate }}\n \n {{item.model.perms.toCode(item.model.type === \'dir\'?\'d\':\'-\')}}\n
\n"); -$templateCache.put("src/templates/main.html","
\n
\n\n
\n
\n\n
\n
\n\n
\n
\n
\n
\n
\n\n
\n
\n
\n"); -$templateCache.put("src/templates/modals.html","
\n
\n
\n
\n \n

{{\"preview\" | translate}}

\n
\n
\n
\n\n \"{{singleSelection().model.name}}\"\n {{\'loading\' | translate}} ...\n
\n
\n
\n
\n \n
\n
\n
\n
\n\n
\n
\n
\n
\n
\n \n

{{\"confirm\" | translate}}

\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

{{\'move\' | translate}}

\n
\n
\n
\n
\n
\n
\n \n \n
\n
\n
\n
\n
\n\n\n
\n
\n
\n
\n
\n \n

{{\'rename\' | translate}}

\n
\n
\n \n \n\n
\n
\n
\n \n \n
\n
\n
\n
\n
\n\n
\n
\n
\n
\n
\n \n

{{\'copy_file\' | 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

{{\'compress\' | translate}}

\n
\n
\n
\n
{{\'compression_started\' | translate}}
\n
\n
\n
\n {{\'sure_to_start_compression_with\' | translate}} {{singleSelection().model.name}} ?\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

{{\'extract_item\' | translate}}

\n
\n
\n
\n
{{\'extraction_started\' | 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

{{\'edit_file\' | translate}}

\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

{{\'new_folder\' | translate}}

\n
\n
\n \n \n
\n
\n
\n \n \n
\n
\n
\n
\n
\n\n
\n
\n
\n
\n
\n \n

{{\"upload_files\" | translate}}

\n
\n
\n \n \n\n
\n
    \n
  • \n \n
    {{uploadFile.name}}
    \n

    {{uploadFile.size | humanReadableFileSize}}

    \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
\n
\n \n
\n \n

{{\'change_permissions\' | translate}}

\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
\n \n

{{\'copy_links\' | 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\n
\n
\n \n
\n
\n
\n
\n
\n\n
\n
\n
\n
\n \n

{{\"select_destination_folder\" | translate}}

\n
\n
\n
\n
\n
\n
\n \n
\n
\n
\n \n
\n
\n
\n
\n\n\n\n\n\n\n"); -$templateCache.put("src/templates/navbar.html",""); -$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","
\n \n\n \n
"); + $templateCache.put("src/templates/main-icons-preview.html","
\n
\n \n
\n\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
\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","\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 {{\"name\" | translate}}\n \n \n
\n
\n
\n {{\"no_folders_in_folder\" | translate}}...\n \n \n
\n {{ fileNavigator.error }}\n
\n \n \n {{item.model.name | strLimit : 32}}\n \n \n \n
"); + $templateCache.put("src/templates/main-table.html","\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 {{\"name\" | translate}}\n \n \n \n \n {{\"size\" | translate}}\n \n \n \n \n {{\"date\" | translate}}\n \n \n \n \n {{\"permissions\" | translate}}\n \n \n
\n
\n
\n {{\"no_files_in_folder\" | translate}}...\n
\n {{ fileNavigator.error }}\n
\n \n \n \n {{item.model.name | strLimit : 64}}\n \n \n \n {{item.model.size | humanReadableFileSize}}\n \n \n {{item.model.date | formatDate }}\n \n {{item.model.perms.toCode(item.model.type === \'dir\'?\'d\':\'-\')}}\n
\n"); + $templateCache.put("src/templates/main.html","
\n
\n\n
\n
\n\n
\n
\n\n
\n
\n
\n
\n
\n\n
\n
\n
\n"); + $templateCache.put("src/templates/modals.html","
\n
\n
\n
\n \n

{{\"preview\" | translate}}

\n
\n
\n
\n\n \"{{singleSelection().model.name}}\"\n {{\'loading\' | translate}} ...\n
\n
\n
\n
\n \n
\n
\n
\n
\n\n
\n
\n
\n
\n
\n \n

{{\"confirm\" | translate}}

\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

{{\'move\' | translate}}

\n
\n
\n
\n
\n
\n
\n \n \n
\n
\n
\n
\n
\n\n\n
\n
\n
\n
\n
\n \n

{{\'rename\' | translate}}

\n
\n
\n \n \n\n
\n
\n
\n \n \n
\n
\n
\n
\n
\n\n
\n
\n
\n
\n
\n \n

{{\'copy_file\' | 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

{{\'compress\' | translate}}

\n
\n
\n
\n
{{\'compression_started\' | translate}}
\n
\n
\n
\n {{\'sure_to_start_compression_with\' | translate}} {{singleSelection().model.name}} ?\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

{{\'extract_item\' | translate}}

\n
\n
\n
\n
{{\'extraction_started\' | 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

{{\'edit_file\' | translate}}

\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

{{\'new_folder\' | translate}}

\n
\n
\n \n \n
\n
\n
\n \n \n
\n
\n
\n
\n
\n\n
\n
\n
\n
\n
\n \n

{{\"upload_files\" | translate}}

\n
\n
\n \n \n\n
\n
    \n
  • \n \n
    {{uploadFile.name}}
    \n

    {{uploadFile.size | humanReadableFileSize}}

    \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
\n
\n \n
\n \n

{{\'change_permissions\' | translate}}

\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
\n \n

{{\'copy_links\' | 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\n
\n
\n \n
\n
\n
\n
\n
\n\n
\n
\n
\n
\n \n

{{\"select_destination_folder\" | translate}}

\n
\n
\n
\n
\n
\n
\n \n
\n
\n
\n \n
\n
\n
\n
\n\n\n\n\n\n\n"); + $templateCache.put("src/templates/navbar.html",""); + $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,