Initial Commit

This commit is contained in:
usmannasir
2017-10-24 19:16:36 +05:00
commit 11eae3f9fe
2124 changed files with 528735 additions and 0 deletions

View File

@@ -0,0 +1,163 @@
@-webkit-keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
};
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
};
}
@-webkit-keyframes fadeInDown {
0% {
opacity: 0;
-webkit-transform: translate3d(0, -100%, 0);
transform: translate3d(0, -100%, 0);
}
100% {
opacity: 1;
-webkit-transform: none;
transform: none;
};
}
@keyframes fadeInDown {
0% {
opacity: 0;
-webkit-transform: translate3d(0, -100%, 0);
transform: translate3d(0, -100%, 0);
}
100% {
opacity: 1;
-webkit-transform: none;
transform: none;
};
}
@keyframes rotate {
100% {
transform: rotate(360deg);
};
}
@-webkit-keyframes rotate {
100% {
-webkit-transform: rotate(360deg);
};
}
@keyframes colors {
0% {
stroke: #4285F4;
}
25% {
stroke: #DE3E35;
}
50% {
stroke: #F7C223;
}
75% {
stroke: #1B9A59;
}
100% {
stroke: #4285F4;
};
}
@keyframes dash {
0% {
stroke-dasharray: 1,150;
stroke-dashoffset: 0;
stroke: red;
}
50% {
stroke-dasharray: 90,150;
stroke-dashoffset: -35;
stroke: yellow;
}
100% {
stroke-dasharray: 90,150;
stroke-dashoffset: -124;
stroke: green;
};
}
@-webkit-keyframes dash {
0% {
stroke-dasharray: 1,150;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90,150;
stroke-dashoffset: -35;
}
100% {
stroke-dasharray: 90,150;
stroke-dashoffset: -124;
};
}
.animated {
-webkit-animation-duration: .7s;
animation-duration: .7s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.modal.animated,
.animated.fast {
-webkit-animation-duration: .2s;
animation-duration: .2s;
}
.animated.slow {
-webkit-animation-duration: 1.1s;
animation-duration: 1.1s;
}
.animated.fadeInDown {
-webkit-animation-name: fadeInDown;
animation-name: fadeInDown;
}
.animated.fadeIn {
-webkit-animation-name: fadeIn;
animation-name: fadeIn;
}
.spinner-container {
-webkit-animation: rotate 2s linear infinite;
animation: rotate 2s linear infinite;
z-index: 2;
width: 65px;
height: 65px;
}
.spinner-container .path {
stroke-dasharray: 1,150;
stroke-dashoffset: 0;
stroke: #2196F3;
stroke-linecap: round;
-webkit-animation: dash 1.5s ease-in-out infinite, colors 5.6s ease-in-out infinite;
animation: dash 1.5s ease-in-out infinite, colors 5.6s ease-in-out infinite;
}

View File

@@ -0,0 +1,96 @@
.modal {
word-wrap: break-word;
}
.modal .label.error-msg {
display: block;
font-size: 12px;
margin-top: 5px;
padding: 0;
padding: 5px;
margin-top: 10px;
text-align: left;
}
.modal .label.error-msg > span {
white-space: pre-wrap;
}
.modal .breadcrumb {
margin: 0;
background: #00bcd4;
font-size: 16px;
max-height: inherit;
padding: 0 10px;
margin-bottom: 5px;
}
.modal-fullscreen .modal-dialog,
.modal-fullscreen .modal-content {
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
}
.modal-fullscreen .modal-dialog {
margin: 0;
width: 100%;
}
.modal-fullscreen .modal-content {
border: none;
-moz-border-radius: 0;
border-radius: 0;
-webkit-box-shadow: inherit;
-moz-box-shadow: inherit;
-o-box-shadow: inherit;
box-shadow: inherit;
}
.modal-fullscreen textarea.code {
min-height: 450px;
}
.modal img.preview {
max-width: 100%;
max-height: 640px;
border-radius: 3px;
}
.modal img.preview.loading {
width: 100%;
height: 1px;
opacity: 0;
}
.modal .modal-content {
border-radius: 10px 10px 4px 4px;
}
.modal .modal-header {
border-radius: 4px 4px 0 0;
background: #2196F3;
padding: 1.3em;
}
.modal .modal-header .modal-title {
font-size: 20px;
line-height: 100%;
color: #D4E5F5;
margin: 0;
}
.modal .modal-header .close {
opacity: 1;
color: #D4E5F5;
}
.modal .modal-header .close.fullscreen {
font-size: 14px;
position: relative;
top: 4px;
margin-right: .8em;
}

View File

@@ -0,0 +1,446 @@
body {
font-size: 14px;
height: 100vh;
}
*,
*:focus {
outline: 0!important;
}
.navbar {
min-height: 32px;
margin-bottom: 0;
border: 0;
border-radius: 0;
color: #fff;
}
.navbar .navbar-collapse {
overflow: visible;
padding: 0;
}
.navbar .navbar-toggle {
padding: 5px 10px;
}
.navbar .navbar-brand {
font-size: inherit;
height: 55px;
line-height: 100%;
}
.btn.btn-default {
color: #444;
background-color: #FAFAFA;
}
.btn {
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .26);
font-weight: 500;
letter-spacing: .01em;
border: none;
}
textarea.code {
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
font-size: 13px;
min-height: 250px;
resize: vertical;
color: #000;
}
.sub-header {
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.sidebar {
display: none;
background: #fafafa;
margin-top: 2px;
padding: 0;
overflow-x: hidden;
overflow-y: auto;
border-right: 1px solid #eee;
}
.btn-go-back {
margin-top: -5px;
}
.nav-sidebar {
margin-right: -21px;
margin-bottom: 20px;
margin-left: -20px;
}
.nav-sidebar > li > a {
color: #7a7a7a;
padding: 7px 0 7px 16px;
}
.nav-sidebar> li > a:hover,
.nav-sidebar> li > a:focus {
background: none;
color: #1378b9;
}
.nav-sidebar > li.active > a {
color: #2196F3;
}
.main {
padding: 0;
}
.main .page-header {
margin-top: 0;
}
.file-tree ul.nav.nav-sidebar {
margin: 0;
padding: 0;
padding-left: 12px;
}
.file-tree ul.nav.nav-sidebar:first-child {
padding-left: 0;
}
.file-tree ul.nav.nav-sidebar.file-tree-root > li {
border-left: none;
padding-left: 0px;
}
.table td {
vertical-align: middle;
}
#context-menu {
position: absolute;
display: none;
z-index: 9999;
}
.iconset {
padding: 10px;
}
.col-120 {
width: 100px;
max-height: 100px;
float: left;
margin-bottom: 9px;
margin-right: 9px;
}
.col-120:last-child {
margin-right: 0;
}
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Chrome/Safari/Opera */
-khtml-user-select: none; /* Konqueror */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE/Edge */
user-select: none; /* non-prefixed version, currently */
}
.iconset .thumbnail {
border-radius: 0;
overflow: hidden;
margin: 0;
padding: 0;
padding: 10px 0;
border: none;
background: none;
}
.table-files .selected,
.iconset .thumbnail.selected {
background: #2196F3;
}
.iconset .thumbnail.selected,
.table-files .selected td,
.table-files .selected td a {
color:#fff;
}
.iconset .thumbnail .item-icon {
font-size: 32px;
}
.detail-sources {
text-overflow: ellipsis;
overflow: hidden;
word-wrap: break-word;
}
::-webkit-scrollbar {
width: 10px;
height: 10px;
background-color: #fff;
box-shadow: inset 1px 1px 0 rgba(0, 0, 0, .1), inset -1px -1px 0 rgba(0, 0, 0, .07);
}
::-webkit-scrollbar:hover {
background-color: #eee;
}
::-webkit-scrollbar-thumb {
min-height: 0.8em;
min-width: 0.8em;
background-color: rgba(0, 0, 0, .2);
box-shadow: inset 1px 1px 0 rgba(0, 0, 0, .1), inset -1px -1px 0 rgba(0, 0, 0, .07);
}
::-webkit-scrollbar-thumb:hover {
background-color: #bbb;
}
::-webkit-scrollbar-thumb:active {
background-color: #888;
}
.dropdown-menu {
font-size: 14px;
}
.dropdown-menu > li > a {
padding: 6px 20px;
}
.dropdown-menu > li > a > i {
margin-right: 4px;
}
.dropdown-menu.dropdown-right-click {
display: block;
position: static;
margin-bottom: 5px;
}
.dropdown-menu.dropdown-right-click .divider {
margin: 3px 0;
}
.upload-dragover .main {
opacity: .4;
}
.upload-dragover:before {
content: "\e198";
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 100;
color: #2196F3;
font-size: 8em;
font-family: 'Glyphicons Halflings';
}
.upload-list {
margin-top: 20px;
}
.spinner-wrapper {
margin: 0 auto;
text-align: center;
margin-top: 8%;
}
a:hover,
a:active,
a:focus,
table th > a:hover,
table th > a:active,
table th > a:focus {
text-decoration: none;
}
.sortorder:after {
color: #2196f3;
content: '\25bc';
}
.sortorder.reverse:after {
color: #2196f3;
content: '\25b2';
}
[ng\:cloak], [ng-cloak],
[data-ng-cloak], [x-ng-cloak],
.ng-cloak, .x-ng-cloak {
display: none !important;
}
.mr2 {
margin-right: 2px;
}
.mr5 {
margin-right: 5px;
}
.mt10 {
margin-top: 10px;
}
.mb0 {
margin-bottom: 0;
}
.pointer {
cursor: pointer;
}
.block {
display: block;
}
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.bold {
font-weight: bold;
}
.main {
overflow-y: auto;
}
@media (min-width: 768px) {
.main {
padding-right: 0;
padding-left: 0;
}
/* The view should fill all available vertical space */
angular-filemanager > div,.row,.main,.sidebar {
height: 100%;
}
.container-fluid {
height: -webkit-calc(100% - 58px);
height: -moz-calc(100% - 58px);
height: calc(100% - 58px);
}
.sidebar {
display: block;
}
}
.selected-file-details {
padding-left: 20px;
}
.item-extension::after {
font-family: "Roboto","Helvetica Neue",Helvetica,Arial,sans-serif;
content: attr(data-ext);
left: 4px;
position: absolute;
color: #fff;
font-size: 9px;
text-transform: uppercase;
top: 21px;
}
.selected .item-extension::after {
color: #2196F3;
}
.form-control.search-input {
max-width: 20em;
display: inline;
}
.like-code {
display: inline;
}
.point {
margin-right: 8px;
font-size: 10px;
}
.navbar .btn.btn-flat {
padding: 2px;
width: 32px;
height: 30px;
margin-left: 5px;
}
.navbar-inverse .navbar-toggle .icon-bar {
background: #fff;
}
.navbar-inverse .navbar-form input[type="text"] {
color: #7a7a7a;
box-shadow: none;
margin: 0 10px;
}
.navbar .navbar-form {
border-bottom: none;
border-top: none;
box-shadow: none;
padding: 0;
margin: 12px 0;
}
.breadcrumb {
background: none;
padding: 0;
font-size: 17px;
margin: 12px 0;
overflow: hidden;
max-height: 30px
}
.breadcrumb>.active,
.breadcrumb a {
color: #fff;
}
.breadcrumb>li+li:before {
font-family: 'Glyphicons Halflings';
content: "\e080";
font-size: 12px;
color: #fff;
}
.scrollable-menu {
height: auto;
max-height: 200px;
overflow-x: hidden;
}
.btn.btn-flat {
background: none;
color: #fff;
}
.btn-group.open > .btn-flat,
.btn.btn-flat,
.btn.btn-flat:active {
box-shadow: none;
}
.btn.btn-flat > i {
font-size: 18px;
width: 18px;
height: 18px;
line-height: 100%;
}

View File

@@ -0,0 +1,58 @@
(function(window, angular, $) {
'use strict';
angular.module('FileManagerApp', ['pascalprecht.translate', 'ngFileUpload']);
/**
* jQuery inits
*/
$(window.document).on('shown.bs.modal', '.modal', function() {
window.setTimeout(function() {
$('[autofocus]', this).focus();
}.bind(this), 100);
});
$(window.document).on('click', function() {
$('#context-menu').hide();
});
$(window.document).on('contextmenu', '.main-navigation .table-files tr.item-list:has("td"), .item-list', function(e) {
var menu = $('#context-menu');
if (e.pageX >= window.innerWidth - menu.width()) {
e.pageX -= menu.width();
}
if (e.pageY >= window.innerHeight - menu.height()) {
e.pageY -= menu.height();
}
menu.hide().css({
left: e.pageX,
top: e.pageY
}).appendTo('body').show();
e.preventDefault();
});
if (! Array.prototype.find) {
Array.prototype.find = function(predicate) {
if (this == null) {
throw new TypeError('Array.prototype.find called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return value;
}
}
return undefined;
};
}
})(window, angular, jQuery);

View File

@@ -0,0 +1,361 @@
(function(angular, $) {
'use strict';
angular.module('FileManagerApp').controller('FileManagerCtrl', [
'$scope', '$rootScope', '$window', '$translate', 'fileManagerConfig', 'item', 'fileNavigator', 'apiMiddleware',
function($scope, $rootScope, $window, $translate, 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 = [];
$scope.query = '';
$rootScope.selectedModalPath = $scope.fileNavigator.currentPath;
};
$scope.setTemplate = function(name) {
$storage.setItem('viewTemplate', name);
$scope.viewTemplate = name;
};
$scope.changeLanguage = function (locale) {
if (locale) {
$storage.setItem('language', locale);
return $translate.use(locale);
}
$translate.use($storage.getItem('language') || fileManagerConfig.defaultLang);
};
$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 = [];
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.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;
}
$scope.temps = [item];
};
$scope.singleSelection = function() {
return $scope.temps.length === 1 && $scope.temps[0];
};
$scope.totalSelecteds = function() {
return {
total: $scope.temps.length
};
};
$scope.selectionHas = function(type) {
return $scope.temps.find(function(item) {
return item && item.model.type === type;
});
};
$scope.prepareNewFolder = function() {
var item = new Item(null, $scope.fileNavigator.currentPath);
$scope.temps = [item];
return item;
};
$scope.smartClick = function(item) {
var pick = $scope.config.allowedActions.pickFiles;
if (item.isFolder()) {
return $scope.fileNavigator.folderClick(item);
}
if (typeof $scope.config.pickCallback === 'function' && pick) {
var callbackSuccess = $scope.config.pickCallback(item.model);
if (callbackSuccess === true) {
return;
}
}
if (item.isImage()) {
if ($scope.config.previewImagesInModal) {
return $scope.openImagePreview(item);
}
return $scope.apiMiddleware.download(item, true);
}
if (item.isEditable()) {
return $scope.openEditItem(item);
}
};
$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.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.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.modalWithPathSelector = function(id) {
$rootScope.selectedModalPath = $scope.fileNavigator.currentPath;
return $scope.modal(id);
};
$scope.isInThisPath = function(path) {
var currentPath = $scope.fileNavigator.currentPath.join('/') + '/';
return currentPath.indexOf(path + '/') !== -1;
};
$scope.edit = function() {
$scope.apiMiddleware.edit($scope.singleSelection()).then(function() {
$scope.modal('edit', true);
});
};
$scope.changePermissions = function() {
$scope.apiMiddleware.changePermissions($scope.temps, $scope.temp).then(function() {
$scope.fileNavigator.refresh();
$scope.modal('changepermissions', true);
});
};
$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();
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($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.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;
});
};
$scope.extract = function() {
var item = $scope.temp;
var name = $scope.temp.tempModel.name.trim();
var nameExists = $scope.fileNavigator.fileNameExists(name);
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.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;
});
};
$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.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.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.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;
});
};
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;
};
$scope.changeLanguage(getQueryParam('lang'));
$scope.isWindows = getQueryParam('server') === 'Windows';
$scope.fileNavigator.refresh();
}]);
})(angular, jQuery);

View File

@@ -0,0 +1,58 @@
(function(angular) {
'use strict';
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.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('/').filter(Boolean);
$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
}
*/
});
};
$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(/\/\//, '/');
};
}]);
})(angular);

View File

@@ -0,0 +1,40 @@
(function(angular) {
'use strict';
var app = angular.module('FileManagerApp');
app.directive('angularFilemanager', ['$parse', 'fileManagerConfig', function($parse, fileManagerConfig) {
return {
restrict: 'EA',
templateUrl: fileManagerConfig.tplPath + '/main.html'
};
}]);
app.directive('ngFile', ['$parse', function($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.ngFile);
var modelSetter = model.assign;
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);

View File

@@ -0,0 +1,109 @@
(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));
}
this.owner = codes.owner;
this.group = codes.group;
this.others = codes.others;
}
};
Chmod.prototype.toOctal = function(prepend, append) {
var result = [];
['owner', 'group', 'others'].forEach(function(key, i) {
result[i] = this[key].read && this.octalValues.read || 0;
result[i] += this[key].write && this.octalValues.write || 0;
result[i] += this[key].exec && this.octalValues.exec || 0;
}.bind(this));
return (prepend||'') + result.join('') + (append||'');
};
Chmod.prototype.toCode = function(prepend, append) {
var result = [];
['owner', 'group', 'others'].forEach(function(key, i) {
result[i] = this[key].read && this.codeValues.read || '-';
result[i] += this[key].write && this.codeValues.write || '-';
result[i] += this[key].exec && this.codeValues.exec || '-';
}.bind(this));
return (prepend||'') + result.join('') + (append||'');
};
Chmod.prototype.getRwxObj = function() {
return {
read: false,
write: false,
exec: false
};
};
Chmod.prototype.octalValues = {
read: 4, write: 2, exec: 1
};
Chmod.prototype.codeValues = {
read: 'r', write: 'w', exec: 'x'
};
Chmod.prototype.convertfromCode = function (str) {
str = ('' + str).replace(/\s/g, '');
str = str.length === 10 ? str.substr(1) : str;
if (! /^[-rwxts]{9}$/.test(str)) {
return;
}
var result = [], vals = str.match(/.{1,3}/g);
for (var i in vals) {
var rwxObj = this.getRwxObj();
rwxObj.read = /r/.test(vals[i]);
rwxObj.write = /w/.test(vals[i]);
rwxObj.exec = /x|t/.test(vals[i]);
result.push(rwxObj);
}
return {
owner : result[0],
group : result[1],
others: result[2]
};
};
Chmod.prototype.convertfromOctal = function (str) {
str = ('' + str).replace(/\s/g, '');
str = str.length === 4 ? str.substr(1) : str;
if (! /^[0-7]{3}$/.test(str)) {
return;
}
var result = [], vals = str.match(/.{1}/g);
for (var i in vals) {
var rwxObj = this.getRwxObj();
rwxObj.read = /[4567]/.test(vals[i]);
rwxObj.write = /[2367]/.test(vals[i]);
rwxObj.exec = /[1357]/.test(vals[i]);
result.push(rwxObj);
}
return {
owner : result[0],
group : result[1],
others: result[2]
};
};
return Chmod;
});
})(angular);

View File

@@ -0,0 +1,68 @@
(function(angular) {
'use strict';
angular.module('FileManagerApp').factory('item', ['fileManagerConfig', 'chmod', function(fileManagerConfig, Chmod) {
var Item = function(model, path) {
var rawModel = {
name: model && model.name || '',
path: path || [],
type: model && model.type || 'file',
size: model && parseInt(model.size || 0),
date: parseMySQLDate(model && model.date),
perms: new Chmod(model && model.rights),
content: model && model.content || '',
recursive: false,
fullPath: function() {
var path = this.path.filter(Boolean);
return ('/' + path.join('/') + '/' + this.name).replace(/\/\//, '/');
}
};
this.error = '';
this.processing = false;
this.model = angular.copy(rawModel);
this.tempModel = angular.copy(rawModel);
function parseMySQLDate(mysqlDate) {
var d = (mysqlDate || '').toString().split(/[- :]/);
return new Date(d[0], d[1] - 1, d[2], d[3], d[4], d[5]);
}
};
Item.prototype.update = function() {
angular.extend(this.model, angular.copy(this.tempModel));
};
Item.prototype.revert = function() {
angular.extend(this.tempModel, angular.copy(this.model));
this.error = '';
};
Item.prototype.isFolder = function() {
return this.model.type === 'dir';
};
Item.prototype.isEditable = function() {
return !this.isFolder() && fileManagerConfig.isEditableFilePattern.test(this.model.name);
};
Item.prototype.isImage = function() {
return fileManagerConfig.isImageFilePattern.test(this.model.name);
};
Item.prototype.isCompressible = function() {
return this.isFolder();
};
Item.prototype.isExtractable = function() {
return !this.isFolder() && fileManagerConfig.isExtractableFilePattern.test(this.model.name);
};
Item.prototype.isSelectable = function() {
return (this.isFolder() && fileManagerConfig.allowedActions.pickFolders) || (!this.isFolder() && fileManagerConfig.allowedActions.pickFiles);
};
return Item;
}]);
})(angular);

View File

@@ -0,0 +1,46 @@
(function(angular) {
'use strict';
var app = angular.module('FileManagerApp');
app.filter('strLimit', ['$filter', function($filter) {
return function(input, limit, more) {
if (input.length <= limit) {
return input;
}
return $filter('limitTo')(input, limit) + (more || '...');
};
}]);
app.filter('fileExtension', ['$filter', function($filter) {
return function(input) {
return /\./.test(input) && $filter('strLimit')(input.split('.').pop(), 3, '..') || '';
};
}]);
app.filter('formatDate', ['$filter', function() {
return function(input) {
return input instanceof Date ?
input.toISOString().substring(0, 19).replace('T', ' ') :
(input.toLocaleString || input.toString).apply(input);
};
}]);
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;
};
}]);
})(angular);

View File

@@ -0,0 +1,75 @@
(function(angular) {
'use strict';
angular.module('FileManagerApp').provider('fileManagerConfig', function() {
var values = {
appName: 'angular-filemanager v1.5',
defaultLang: 'en',
listUrl: 'bridges/php/handler.php',
uploadUrl: 'bridges/php/handler.php',
renameUrl: 'bridges/php/handler.php',
copyUrl: 'bridges/php/handler.php',
moveUrl: 'bridges/php/handler.php',
removeUrl: 'bridges/php/handler.php',
editUrl: 'bridges/php/handler.php',
getContentUrl: 'bridges/php/handler.php',
createFolderUrl: 'bridges/php/handler.php',
downloadFileUrl: 'bridges/php/handler.php',
downloadMultipleUrl: 'bridges/php/handler.php',
compressUrl: 'bridges/php/handler.php',
extractUrl: 'bridges/php/handler.php',
permissionsUrl: 'bridges/php/handler.php',
basePath: '/',
searchForm: true,
sidebar: true,
breadcrumb: true,
allowedActions: {
upload: true,
rename: true,
move: true,
copy: true,
edit: true,
changePermissions: true,
compress: true,
compressChooseName: true,
extract: true,
download: true,
downloadMultiple: true,
preview: true,
remove: true,
createFolder: true,
pickFiles: false,
pickFolders: false
},
multipleDownloadFileName: 'angular-filemanager.zip',
filterFileExtensions: [],
showExtensionIcons: true,
showSizeForDirectories: false,
useBinarySizePrefixes: false,
downloadFilesByAjax: true,
previewImagesInModal: true,
enablePermissionsRecursive: true,
compressAsync: false,
extractAsync: false,
pickCallback: null,
isEditableFilePattern: /\.(txt|diff?|patch|svg|asc|cnf|cfg|conf|html?|.html|cfm|cgi|aspx?|ini|pl|py|md|css|cs|js|jsp|log|htaccess|htpasswd|gitignore|gitattributes|env|json|atom|eml|rss|markdown|sql|xml|xslt?|sh|rb|as|bat|cmd|cob|for|ftn|frm|frx|inc|lisp|scm|coffee|php[3-6]?|java|c|cbl|go|h|scala|vb|tmpl|lock|go|yml|yaml|tsv|lst)$/i,
isImageFilePattern: /\.(jpe?g|gif|bmp|png|svg|tiff?)$/i,
isExtractableFilePattern: /\.(gz|tar|rar|g?zip)$/i,
tplPath: 'src/templates'
};
return {
$get: function() {
return values;
},
set: function (constants) {
angular.extend(values, constants);
}
};
});
})(angular);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,370 @@
(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, exts) {
var self = this;
var dfHandler = customDeferredHandler || self.deferredHandler;
var deferred = $q.defer();
var data = {
action: 'list',
path: path,
fileExtensions: exts && exts.length ? exts : undefined
};
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, items, permsOctal, permsCode, recursive) {
var self = this;
var deferred = $q.defer();
var data = {
action: 'changePermissions',
items: items,
perms: permsOctal,
permsCode: permsCode,
recursive: !!recursive
};
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;
};
return ApiHandler;
}]);
})(angular, jQuery);

View File

@@ -0,0 +1,135 @@
(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) {
return this.apiHandler.list(fileManagerConfig.listUrl, this.getPath(path), customDeferredHandler);
};
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) {
//TODO: add spinner to indicate file is downloading
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(files, dataItem) {
var items = this.getFileList(files);
var code = dataItem.tempModel.perms.toCode();
var octal = dataItem.tempModel.perms.toOctal();
var recursive = !!dataItem.tempModel.recursive;
return this.apiHandler.changePermissions(fileManagerConfig.permissionsUrl, items, code, octal, recursive);
};
ApiMiddleware.prototype.createFolder = function(item) {
var path = item.tempModel.fullPath();
return this.apiHandler.createFolder(fileManagerConfig.createFolderUrl, path);
};
return ApiMiddleware;
}]);
})(angular);

View File

@@ -0,0 +1,159 @@
(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.getBasePath();
this.history = [];
this.error = '';
this.onRefresh = function() {};
};
FileNavigator.prototype.getBasePath = function() {
var path = (fileManagerConfig.basePath || '').replace(/^\//, '');
return path.trim() ? path.split('/') : [];
};
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 (code == 200) {
this.error = null;
}
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() {
return this.apiMiddleware.list(this.currentPath, this.deferredHandler.bind(this));
};
FileNavigator.prototype.refresh = function() {
var self = this;
if (! self.currentPath.length) {
self.currentPath = this.getBasePath();
}
var path = self.currentPath.join('/');
self.requesting = true;
self.fileList = [];
return self.list().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 && 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: []});
!this.history.length && this.history.push({ name: this.getBasePath()[0] || '', 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 && item.model.name.trim() === fileName.trim();
});
};
FileNavigator.prototype.listHasFolders = function() {
return this.fileList.find(function(item) {
return item.model.type === 'dir';
});
};
FileNavigator.prototype.getCurrentFolderName = function() {
return this.currentPath.slice(-1)[0] || '/';
};
return FileNavigator;
}]);
})(angular);

View File

@@ -0,0 +1,15 @@
<ol class="breadcrumb">
<li>
<a href="" ng-click="fileNavigator.goTo(-1)">
{{"filemanager" | translate}}
</a>
</li>
<li ng-repeat="(key, dir) in fileNavigator.currentPath track by key" ng-class="{'active':$last}" class="animated fast fadeIn">
<a href="" ng-show="!$last" ng-click="fileNavigator.goTo(key)">
{{dir | strLimit : 8}}
</a>
<span ng-show="$last">
{{dir | strLimit : 12}}
</span>
</li>
</ol>

View File

@@ -0,0 +1,98 @@
<div id="context-menu" class="dropdown clearfix animated fast fadeIn">
<ul class="dropdown-menu dropdown-right-click" role="menu" aria-labelledby="dropdownMenu" ng-show="temps.length">
<li ng-show="singleSelection() && singleSelection().isFolder()">
<a href="" tabindex="-1" ng-click="smartClick(singleSelection())">
<i class="glyphicon glyphicon-folder-open"></i> {{'open' | translate}}
</a>
</li>
<li ng-show="config.pickCallback && singleSelection() && singleSelection().isSelectable()">
<a href="" tabindex="-1" ng-click="config.pickCallback(singleSelection().model)">
<i class="glyphicon glyphicon-hand-up"></i> {{'select_this' | translate}}
</a>
</li>
<li ng-show="config.allowedActions.download && !selectionHas('dir') && singleSelection()">
<a href="" tabindex="-1" ng-click="download()">
<i class="glyphicon glyphicon-cloud-download"></i> {{'download' | translate}}
</a>
</li>
<li ng-show="config.allowedActions.downloadMultiple && !selectionHas('dir') && !singleSelection()">
<a href="" tabindex="-1" ng-click="download()">
<i class="glyphicon glyphicon-cloud-download"></i> {{'download_as_zip' | translate}}
</a>
</li>
<li ng-show="config.allowedActions.preview && singleSelection().isImage() && singleSelection()">
<a href="" tabindex="-1" ng-click="openImagePreview()">
<i class="glyphicon glyphicon-picture"></i> {{'view_item' | translate}}
</a>
</li>
<li ng-show="config.allowedActions.rename && singleSelection()">
<a href="" tabindex="-1" ng-click="modal('rename')">
<i class="glyphicon glyphicon-edit"></i> {{'rename' | translate}}
</a>
</li>
<li ng-show="config.allowedActions.move">
<a href="" tabindex="-1" ng-click="modalWithPathSelector('move')">
<i class="glyphicon glyphicon-arrow-right"></i> {{'move' | translate}}
</a>
</li>
<li ng-show="config.allowedActions.copy && !selectionHas('dir')">
<a href="" tabindex="-1" ng-click="modalWithPathSelector('copy')">
<i class="glyphicon glyphicon-log-out"></i> {{'copy' | translate}}
</a>
</li>
<li ng-show="config.allowedActions.edit && singleSelection() && singleSelection().isEditable()">
<a href="" tabindex="-1" ng-click="openEditItem()">
<i class="glyphicon glyphicon-pencil"></i> {{'edit' | translate}}
</a>
</li>
<li ng-show="config.allowedActions.changePermissions">
<a href="" tabindex="-1" ng-click="modal('changepermissions')">
<i class="glyphicon glyphicon-lock"></i> {{'permissions' | translate}}
</a>
</li>
<li ng-show="config.allowedActions.compress && (!singleSelection() || selectionHas('dir'))">
<a href="" tabindex="-1" ng-click="modal('compress')">
<i class="glyphicon glyphicon-compressed"></i> {{'compress' | translate}}
</a>
</li>
<li ng-show="config.allowedActions.extract && singleSelection() && singleSelection().isExtractable()">
<a href="" tabindex="-1" ng-click="modal('extract')">
<i class="glyphicon glyphicon-export"></i> {{'extract' | translate}}
</a>
</li>
<li class="divider" ng-show="config.allowedActions.remove"></li>
<li ng-show="config.allowedActions.remove">
<a href="" tabindex="-1" ng-click="modal('remove')">
<i class="glyphicon glyphicon-trash"></i> {{'remove' | translate}}
</a>
</li>
</ul>
<ul class="dropdown-menu dropdown-right-click" role="menu" aria-labelledby="dropdownMenu" ng-show="!temps.length">
<li ng-show="config.allowedActions.createFolder">
<a href="" tabindex="-1" ng-click="modal('newfolder') && prepareNewFolder()">
<i class="glyphicon glyphicon-plus"></i> {{'new_folder' | translate}}
</a>
</li>
<li ng-show="config.allowedActions.upload">
<a href="" tabindex="-1" ng-click="modal('uploadfile')">
<i class="glyphicon glyphicon-cloud-upload"></i> {{'upload_files' | translate}}
</a>
</li>
</ul>
</div>

View File

@@ -0,0 +1,25 @@
<div class="iconset noselect">
<div class="item-list clearfix" ng-click="selectOrUnselect(null, $event)" ng-right-click="selectOrUnselect(null, $event)" prevent="true">
<div class="col-120" ng-repeat="item in $parent.fileList = (fileNavigator.fileList | filter: {model:{name: query}})" ng-show="!fileNavigator.requesting && !fileNavigator.error">
<a href="" class="thumbnail text-center" ng-click="selectOrUnselect(item, $event)" ng-dblclick="smartClick(item)" ng-right-click="selectOrUnselect(item, $event)" title="{{item.model.name}} ({{item.model.size | humanReadableFileSize}})" ng-class="{selected: isSelected(item)}">
<div class="item-icon">
<i class="glyphicon glyphicon-folder-open" ng-show="item.model.type === 'dir'"></i>
<i class="glyphicon glyphicon-file" data-ext="{{ item.model.name | fileExtension }}" ng-show="item.model.type === 'file'" ng-class="{'item-extension': config.showExtensionIcons}"></i>
</div>
{{item.model.name | strLimit : 11 }}
</a>
</div>
</div>
<div ng-show="fileNavigator.requesting">
<div ng-include="config.tplPath + '/spinner.html'"></div>
</div>
<div class="alert alert-warning" ng-show="!fileNavigator.requesting && fileNavigator.fileList.length < 1 && !fileNavigator.error">
{{"no_files_in_folder" | translate}}...
</div>
<div class="alert alert-danger" ng-show="!fileNavigator.requesting && fileNavigator.error">
{{ fileNavigator.error }}
</div>
</div>

View File

@@ -0,0 +1,46 @@
<table class="table table-condensed table-modal-condensed mb0">
<thead>
<tr>
<th>
<a href="" ng-click="order('model.name')">
{{"name" | translate}}
<span class="sortorder" ng-show="predicate[1] === 'model.name'" ng-class="{reverse:reverse}"></span>
</a>
</th>
<th class="text-right"></th>
</tr>
</thead>
<tbody class="file-item">
<tr ng-show="fileNavigator.requesting">
<td colspan="2">
<div ng-include="config.tplPath + '/spinner.html'"></div>
</td>
</tr>
<tr ng-show="!fileNavigator.requesting && !fileNavigator.listHasFolders() && !fileNavigator.error">
<td>
{{"no_folders_in_folder" | translate}}...
</td>
<td class="text-right">
<button class="btn btn-sm btn-default" ng-click="fileNavigator.upDir()">{{"go_back" | translate}}</button>
</td>
</tr>
<tr ng-show="!fileNavigator.requesting && fileNavigator.error">
<td colspan="2">
{{ fileNavigator.error }}
</td>
</tr>
<tr ng-repeat="item in fileNavigator.fileList | orderBy:predicate:reverse" ng-show="!fileNavigator.requesting && item.model.type === 'dir'" ng-if="!selectedFilesAreChildOfPath(item)">
<td>
<a href="" ng-click="fileNavigator.folderClick(item)" title="{{item.model.name}} ({{item.model.size | humanReadableFileSize}})">
<i class="glyphicon glyphicon-folder-close"></i>
{{item.model.name | strLimit : 32}}
</a>
</td>
<td class="text-right">
<button class="btn btn-sm btn-default" ng-click="select(item)">
<i class="glyphicon glyphicon-hand-up"></i> {{"select_this" | translate}}
</button>
</td>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,67 @@
<table class="table mb0 table-files noselect">
<thead>
<tr>
<th>
<a href="" ng-click="order('model.name')">
{{"name" | translate}}
<span class="sortorder" ng-show="predicate[1] === 'model.name'" ng-class="{reverse:reverse}"></span>
</a>
</th>
<th class="hidden-xs" ng-hide="config.hideSize">
<a href="" ng-click="order('model.size')">
{{"size" | translate}}
<span class="sortorder" ng-show="predicate[1] === 'model.size'" ng-class="{reverse:reverse}"></span>
</a>
</th>
<th class="hidden-sm hidden-xs" ng-hide="config.hideDate">
<a href="" ng-click="order('model.date')">
{{"date" | translate}}
<span class="sortorder" ng-show="predicate[1] === 'model.date'" ng-class="{reverse:reverse}"></span>
</a>
</th>
<th class="hidden-sm hidden-xs" ng-hide="config.hidePermissions">
<a href="" ng-click="order('model.permissions')">
{{"permissions" | translate}}
<span class="sortorder" ng-show="predicate[1] === 'model.permissions'" ng-class="{reverse:reverse}"></span>
</a>
</th>
</tr>
</thead>
<tbody class="file-item">
<tr ng-show="fileNavigator.requesting">
<td colspan="5">
<div ng-include="config.tplPath + '/spinner.html'"></div>
</td>
</tr>
<tr ng-show="!fileNavigator.requesting &amp;&amp; fileNavigator.fileList.length < 1 &amp;&amp; !fileNavigator.error">
<td colspan="5">
{{"no_files_in_folder" | translate}}...
</td>
</tr>
<tr ng-show="!fileNavigator.requesting &amp;&amp; fileNavigator.error">
<td colspan="5">
{{ fileNavigator.error }}
</td>
</tr>
<tr class="item-list" ng-repeat="item in $parent.fileList = (fileNavigator.fileList | filter: {model:{name: query}} | orderBy:predicate:reverse)" ng-show="!fileNavigator.requesting" ng-click="selectOrUnselect(item, $event)" ng-dblclick="smartClick(item)" ng-right-click="selectOrUnselect(item, $event)" ng-class="{selected: isSelected(item)}">
<td>
<a href="" title="{{item.model.name}} ({{item.model.size | humanReadableFileSize}})">
<i class="glyphicon glyphicon-folder-close" ng-show="item.model.type === 'dir'"></i>
<i class="glyphicon glyphicon-file" ng-show="item.model.type === 'file'"></i>
{{item.model.name | strLimit : 64}}
</a>
</td>
<td class="hidden-xs">
<span ng-show="item.model.type !== 'dir' || config.showSizeForDirectories">
{{item.model.size | humanReadableFileSize}}
</span>
</td>
<td class="hidden-sm hidden-xs" ng-hide="config.hideDate">
{{item.model.date | formatDate }}
</td>
<td class="hidden-sm hidden-xs" ng-hide="config.hidePermissions">
{{item.model.perms.toCode(item.model.type === 'dir'?'d':'-')}}
</td>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,18 @@
<div ng-controller="FileManagerCtrl" ngf-drop="addForUpload($files)" ngf-drag-over-class="'upload-dragover'" ngf-multiple="true">
<div ng-include="config.tplPath + '/navbar.html'"></div>
<div class="container-fluid">
<div class="row">
<div class="col-sm-4 col-md-3 sidebar file-tree animated slow fadeIn" ng-include="config.tplPath + '/sidebar.html'" ng-show="config.sidebar &amp;&amp; fileNavigator.history[0]">
</div>
<div class="main" ng-class="config.sidebar &amp;&amp; fileNavigator.history[0] &amp;&amp; 'col-sm-8 col-md-9'">
<div ng-include="config.tplPath + '/' + viewTemplate" class="main-navigation clearfix"></div>
</div>
</div>
</div>
<div ng-include="config.tplPath + '/modals.html'"></div>
<div ng-include="config.tplPath + '/item-context-menu.html'"></div>
</div>

View File

@@ -0,0 +1,445 @@
<div class="modal animated fadeIn" id="imagepreview">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">{{"close" | translate}}</span>
</button>
<h4 class="modal-title">{{"preview" | translate}}</h4>
</div>
<div class="modal-body">
<div class="text-center">
<img id="imagepreview-target" class="preview" alt="{{singleSelection().model.name}}" ng-class="{'loading': apiMiddleware.apiHandler.inprocess}">
<span class="label label-warning" ng-show="apiMiddleware.apiHandler.inprocess">{{'loading' | translate}} ...</span>
</div>
<div ng-include data-src="'error-bar'" class="clearfix"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="apiMiddleware.apiHandler.inprocess">{{"close" | translate}}</button>
</div>
</div>
</div>
</div>
<div class="modal animated fadeIn" id="remove">
<div class="modal-dialog">
<div class="modal-content">
<form ng-submit="remove()">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">{{"close" | translate}}</span>
</button>
<h4 class="modal-title">{{"confirm" | translate}}</h4>
</div>
<div class="modal-body">
{{'sure_to_delete' | translate}} <span ng-include data-src="'selected-files-msg'"></span>
<div ng-include data-src="'error-bar'" class="clearfix"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="apiMiddleware.apiHandler.inprocess">{{"cancel" | translate}}</button>
<button type="submit" class="btn btn-primary" ng-disabled="apiMiddleware.apiHandler.inprocess" autofocus="autofocus">{{"remove" | translate}}</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal animated fadeIn" id="move">
<div class="modal-dialog">
<div class="modal-content">
<form ng-submit="move()">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">{{"close" | translate}}</span>
</button>
<h4 class="modal-title">{{'move' | translate}}</h4>
</div>
<div class="modal-body">
<div ng-include data-src="'path-selector'" class="clearfix"></div>
<div ng-include data-src="'error-bar'" class="clearfix"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="apiMiddleware.apiHandler.inprocess">{{"cancel" | translate}}</button>
<button type="submit" class="btn btn-primary" ng-disabled="apiMiddleware.apiHandler.inprocess">{{'move' | translate}}</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal animated fadeIn" id="rename">
<div class="modal-dialog">
<div class="modal-content">
<form ng-submit="rename()">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">{{"close" | translate}}</span>
</button>
<h4 class="modal-title">{{'rename' | translate}}</h4>
</div>
<div class="modal-body">
<label class="radio">{{'enter_new_name_for' | translate}} <b>{{singleSelection() && singleSelection().model.name}}</b></label>
<input class="form-control" ng-model="singleSelection().tempModel.name" autofocus="autofocus">
<div ng-include data-src="'error-bar'" class="clearfix"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="apiMiddleware.apiHandler.inprocess">{{"cancel" | translate}}</button>
<button type="submit" class="btn btn-primary" ng-disabled="apiMiddleware.apiHandler.inprocess">{{'rename' | translate}}</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal animated fadeIn" id="copy">
<div class="modal-dialog">
<div class="modal-content">
<form ng-submit="copy()">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">{{"close" | translate}}</span>
</button>
<h4 class="modal-title">{{'copy_file' | translate}}</h4>
</div>
<div class="modal-body">
<div ng-show="singleSelection()">
<label class="radio">{{'enter_new_name_for' | translate}} <b>{{singleSelection().model.name}}</b></label>
<input class="form-control" ng-model="singleSelection().tempModel.name" autofocus="autofocus">
</div>
<div ng-include data-src="'path-selector'" class="clearfix"></div>
<div ng-include data-src="'error-bar'" class="clearfix"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="apiMiddleware.apiHandler.inprocess">{{"cancel" | translate}}</button>
<button type="submit" class="btn btn-primary" ng-disabled="apiMiddleware.apiHandler.inprocess">{{"copy" | translate}}</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal animated fadeIn" id="compress">
<div class="modal-dialog">
<div class="modal-content">
<form ng-submit="compress()">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">{{"close" | translate}}</span>
</button>
<h4 class="modal-title">{{'compress' | translate}}</h4>
</div>
<div class="modal-body">
<div ng-show="apiMiddleware.apiHandler.asyncSuccess">
<div class="label label-success error-msg">{{'compression_started' | translate}}</div>
</div>
<div ng-hide="apiMiddleware.apiHandler.asyncSuccess">
<div ng-hide="config.allowedActions.compressChooseName">
{{'sure_to_start_compression_with' | translate}} <b>{{singleSelection().model.name}}</b> ?
</div>
<div ng-show="config.allowedActions.compressChooseName">
<label class="radio">
{{'enter_file_name_for_compression' | translate}}
<span ng-include data-src="'selected-files-msg'"></span>
</label>
<input class="form-control" ng-model="temp.tempModel.name" autofocus="autofocus">
</div>
</div>
<div ng-include data-src="'error-bar'" class="clearfix"></div>
</div>
<div class="modal-footer">
<div ng-show="apiMiddleware.apiHandler.asyncSuccess">
<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="apiMiddleware.apiHandler.inprocess">{{"close" | translate}}</button>
</div>
<div ng-hide="apiMiddleware.apiHandler.asyncSuccess">
<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="apiMiddleware.apiHandler.inprocess">{{"cancel" | translate}}</button>
<button type="submit" class="btn btn-primary" ng-disabled="apiMiddleware.apiHandler.inprocess">{{'compress' | translate}}</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="modal animated fadeIn" id="extract" ng-init="singleSelection().emptyName()">
<div class="modal-dialog">
<div class="modal-content">
<form ng-submit="extract()">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">{{"close" | translate}}</span>
</button>
<h4 class="modal-title">{{'extract_item' | translate}}</h4>
</div>
<div class="modal-body">
<div ng-show="apiMiddleware.apiHandler.asyncSuccess">
<div class="label label-success error-msg">{{'extraction_started' | translate}}</div>
</div>
<div ng-hide="apiMiddleware.apiHandler.asyncSuccess">
<label class="radio">{{'enter_folder_name_for_extraction' | translate}} <b>{{singleSelection().model.name}}</b></label>
<input class="form-control" ng-model="singleSelection().tempModel.name" autofocus="autofocus">
</div>
<div ng-include data-src="'error-bar'" class="clearfix"></div>
</div>
<div class="modal-footer">
<div ng-show="apiMiddleware.apiHandler.asyncSuccess">
<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="apiMiddleware.apiHandler.inprocess">{{"close" | translate}}</button>
</div>
<div ng-hide="apiMiddleware.apiHandler.asyncSuccess">
<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="apiMiddleware.apiHandler.inprocess">{{"cancel" | translate}}</button>
<button type="submit" class="btn btn-primary" ng-disabled="apiMiddleware.apiHandler.inprocess">{{'extract' | translate}}</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="modal animated fadeIn" id="edit" ng-class="{'modal-fullscreen': fullscreen}">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<form ng-submit="edit()">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">{{"close" | translate}}</span>
</button>
<button type="button" class="close fullscreen" ng-click="fullscreen=!fullscreen">
<i class="glyphicon glyphicon-fullscreen"></i>
<span class="sr-only">{{'toggle_fullscreen' | translate}}</span>
</button>
<h4 class="modal-title">{{'edit_file' | translate}}</h4>
</div>
<div class="modal-body">
<label class="radio bold">{{ singleSelection().model.fullPath() }}</label>
<span class="label label-warning" ng-show="apiMiddleware.apiHandler.inprocess">{{'loading' | translate}} ...</span>
<textarea class="form-control code" ng-model="singleSelection().tempModel.content" ng-show="!apiMiddleware.apiHandler.inprocess" autofocus="autofocus"></textarea>
<div ng-include data-src="'error-bar'" class="clearfix"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="apiMiddleware.apiHandler.inprocess">{{'close' | translate}}</button>
<button type="submit" class="btn btn-primary" ng-show="config.allowedActions.edit" ng-disabled="apiMiddleware.apiHandler.inprocess">{{'edit' | translate}}</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal animated fadeIn" id="newfolder">
<div class="modal-dialog">
<div class="modal-content">
<form ng-submit="createFolder()">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">{{"close" | translate}}</span>
</button>
<h4 class="modal-title">{{'new_folder' | translate}}</h4>
</div>
<div class="modal-body">
<label class="radio">{{'folder_name' | translate}}</label>
<input class="form-control" ng-model="singleSelection().tempModel.name" autofocus="autofocus">
<div ng-include data-src="'error-bar'" class="clearfix"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="apiMiddleware.apiHandler.inprocess">{{"cancel" | translate}}</button>
<button type="submit" class="btn btn-primary" ng-disabled="apiMiddleware.apiHandler.inprocess">{{'create' | translate}}</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal animated fadeIn" id="uploadfile">
<div class="modal-dialog">
<div class="modal-content">
<form>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">{{"close" | translate}}</span>
</button>
<h4 class="modal-title">{{"upload_files" | translate}}</h4>
</div>
<div class="modal-body">
<label class="radio">
{{"files_will_uploaded_to" | translate}}
<b>/{{fileNavigator.currentPath.join('/')}}</b>
</label>
<button class="btn btn-default btn-block" ngf-select="$parent.addForUpload($files)" ngf-multiple="true">
{{"select_files" | translate}}
</button>
<div class="upload-list">
<ul class="list-group">
<li class="list-group-item" ng-repeat="(index, uploadFile) in $parent.uploadFileList">
<button class="btn btn-sm btn-danger pull-right" ng-click="$parent.removeFromUpload(index)">
&times;
</button>
<h5 class="list-group-item-heading">{{uploadFile.name}}</h5>
<p class="list-group-item-text">{{uploadFile.size | humanReadableFileSize}}</p>
</li>
</ul>
<div ng-show="apiMiddleware.apiHandler.inprocess">
<em>{{"uploading" | translate}}... {{apiMiddleware.apiHandler.progress}}%</em>
<div class="progress mb0">
<div class="progress-bar active" role="progressbar" aria-valuenow="{{apiMiddleware.apiHandler.progress}}" aria-valuemin="0" aria-valuemax="100" style="width: {{apiMiddleware.apiHandler.progress}}%"></div>
</div>
</div>
</div>
<div ng-include data-src="'error-bar'" class="clearfix"></div>
</div>
<div class="modal-footer">
<div>
<button type="button" class="btn btn-default" data-dismiss="modal">{{"cancel" | translate}}</button>
<button type="submit" class="btn btn-primary" ng-disabled="!$parent.uploadFileList.length || apiMiddleware.apiHandler.inprocess" ng-click="uploadFiles()">{{'upload' | translate}}</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="modal animated fadeIn" id="changepermissions">
<div class="modal-dialog">
<div class="modal-content">
<form ng-submit="changePermissions()">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">{{"close" | translate}}</span>
</button>
<h4 class="modal-title">{{'change_permissions' | translate}}</h4>
</div>
<div class="modal-body">
<table class="table mb0">
<thead>
<tr>
<th>{{'permissions' | translate}}</th>
<th class="col-xs-1 text-center">{{'read' | translate}}</th>
<th class="col-xs-1 text-center">{{'write' | translate}}</th>
<th class="col-xs-1 text-center">{{'exec' | translate}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="(permTypeKey, permTypeValue) in temp.tempModel.perms">
<td>{{permTypeKey | translate}}</td>
<td ng-repeat="(permKey, permValue) in permTypeValue" class="col-xs-1 text-center" ng-click="main()">
<label class="col-xs-12">
<input type="checkbox" ng-model="temp.tempModel.perms[permTypeKey][permKey]">
</label>
</td>
</tr>
</tbody>
</table>
<div class="checkbox" ng-show="config.enablePermissionsRecursive && selectionHas('dir')">
<label>
<input type="checkbox" ng-model="temp.tempModel.recursive"> {{'recursive' | translate}}
</label>
</div>
<div class="clearfix mt10">
<span class="label label-primary pull-left" ng-hide="temp.multiple">
{{'original' | translate}}:
{{temp.model.perms.toCode(selectionHas('dir') ? 'd':'-')}}
({{temp.model.perms.toOctal()}})
</span>
<span class="label label-primary pull-right">
{{'changes' | translate}}:
{{temp.tempModel.perms.toCode(selectionHas('dir') ? 'd':'-')}}
({{temp.tempModel.perms.toOctal()}})
</span>
</div>
<div ng-include data-src="'error-bar'" class="clearfix"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{"cancel" | translate}}</button>
<button type="submit" class="btn btn-primary" ng-disabled="">{{'change' | translate}}</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal animated fadeIn" id="selector" ng-controller="ModalFileManagerCtrl">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">{{"close" | translate}}</span>
</button>
<h4 class="modal-title">{{"select_destination_folder" | translate}}</h4>
</div>
<div class="modal-body">
<div>
<div ng-include="config.tplPath + '/current-folder-breadcrumb.html'"></div>
<div ng-include="config.tplPath + '/main-table-modal.html'"></div>
<hr />
<button class="btn btn-sm btn-default" ng-click="selectCurrent()">
<i class="glyphicon"></i> {{"select_this" | translate}}
</button>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="apiMiddleware.apiHandler.inprocess">{{"close" | translate}}</button>
</div>
</div>
</div>
</div>
<script type="text/ng-template" id="path-selector">
<div class="panel panel-primary mt10 mb0">
<div class="panel-body">
<div class="detail-sources">
<div class="like-code mr5"><b>{{"selection" | translate}}:</b>
<span ng-include="'selected-files-msg'"></span>
</div>
</div>
<div class="detail-sources">
<div class="like-code mr5">
<b>{{"destination" | translate}}:</b> {{ getSelectedPath() }}
</div>
<a href="" class="label label-primary" ng-click="openNavigator(fileNavigator.currentPath)">
{{'change' | translate}}
</a>
</div>
</div>
</div>
</script>
<script type="text/ng-template" id="error-bar">
<div class="label label-danger error-msg pull-left animated fadeIn" ng-show="apiMiddleware.apiHandler.error">
<i class="glyphicon glyphicon-remove-circle"></i>
<span>{{apiMiddleware.apiHandler.error}}</span>
</div>
</script>
<script type="text/ng-template" id="selected-files-msg">
<span ng-show="temps.length == 1">
{{singleSelection().model.name}}
</span>
<span ng-show="temps.length > 1">
{{'these_elements' | translate:totalSelecteds()}}
<a href="" class="label label-primary" ng-click="showDetails = !showDetails">
{{showDetails ? '-' : '+'}} {{'details' | translate}}
</a>
</span>
<div ng-show="temps.length > 1 &amp;&amp; showDetails">
<ul class="selected-file-details">
<li ng-repeat="tempItem in temps">
<b>{{tempItem.model.name}}</b>
</li>
</ul>
</div>
</script>

View File

@@ -0,0 +1,84 @@
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<div class="row">
<div class="col-sm-9 col-md-10 hidden-xs">
<div ng-show="!config.breadcrumb">
<a class="navbar-brand hidden-xs ng-binding" href="">angular-{{"filemanager" | translate}}</a>
</div>
<div ng-include="config.tplPath + '/current-folder-breadcrumb.html'" ng-show="config.breadcrumb">
</div>
</div>
<div class="col-sm-3 col-md-2">
<div class="navbar-collapse">
<div class="navbar-form navbar-right text-right">
<div class="pull-left visible-xs" ng-if="fileNavigator.currentPath.length">
<button class="btn btn-primary btn-flat" ng-click="fileNavigator.upDir()">
<i class="glyphicon glyphicon-chevron-left"></i>
</button>
{{fileNavigator.getCurrentFolderName() | strLimit : 12}}
</div>
<div class="btn-group">
<button class="btn btn-flat btn-sm dropdown-toggle" type="button" id="dropDownMenuSearch" data-toggle="dropdown" aria-expanded="true">
<i class="glyphicon glyphicon-search mr2"></i>
</button>
<div class="dropdown-menu animated fast fadeIn pull-right" role="menu" aria-labelledby="dropDownMenuLang">
<input type="text" class="form-control" ng-show="config.searchForm" placeholder="{{'search' | translate}}..." ng-model="$parent.query">
</div>
</div>
<button class="btn btn-flat btn-sm" ng-click="$parent.setTemplate('main-icons.html')" ng-show="$parent.viewTemplate !=='main-icons.html'" title="{{'icons' | translate}}">
<i class="glyphicon glyphicon-th-large"></i>
</button>
<button class="btn btn-flat btn-sm" ng-click="$parent.setTemplate('main-table.html')" ng-show="$parent.viewTemplate !=='main-table.html'" title="{{'list' | translate}}">
<i class="glyphicon glyphicon-th-list"></i>
</button>
<div class="btn-group">
<button class="btn btn-flat btn-sm dropdown-toggle" type="button" id="dropDownMenuLang" data-toggle="dropdown" aria-expanded="true">
<i class="glyphicon glyphicon-globe mr2"></i>
</button>
<ul class="dropdown-menu scrollable-menu animated fast fadeIn pull-right" role="menu" aria-labelledby="dropDownMenuLang">
<li role="presentation"><a role="menuitem" tabindex="-1" href="" ng-click="changeLanguage('en')">English</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="" ng-click="changeLanguage('zh_tw')">正體中文</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="" ng-click="changeLanguage('zh_cn')">简体中文</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="" ng-click="changeLanguage('es')">Español</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="" ng-click="changeLanguage('pt')">Portugues</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="" ng-click="changeLanguage('fr')">Français</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="" ng-click="changeLanguage('de')">Deutsch</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="" ng-click="changeLanguage('he')">עברי</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="" ng-click="changeLanguage('it')">italiano</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="" ng-click="changeLanguage('sk')">Slovenčina</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="" ng-click="changeLanguage('ru')">русский</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="" ng-click="changeLanguage('ua')">український</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="" ng-click="changeLanguage('tr')">Türkçe</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="" ng-click="changeLanguage('fa')">فارسی</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="" ng-click="changeLanguage('pl')">Polski</a></li>
</ul>
</div>
<div class="btn-group">
<button class="btn btn-flat btn-sm dropdown-toggle" type="button" id="more" data-toggle="dropdown" aria-expanded="true">
<i class="glyphicon glyphicon-option-vertical"></i>
</button>
<ul class="dropdown-menu scrollable-menu animated fast fadeIn pull-right" role="menu" aria-labelledby="more">
<li role="presentation" ng-show="config.allowedActions.createFolder" ng-click="modal('newfolder') && prepareNewFolder()">
<a href="" role="menuitem" tabindex="-1">
<i class="glyphicon glyphicon-plus"></i> {{"new_folder" | translate}}
</a>
</li>
<li role="presentation" ng-show="config.allowedActions.upload" ng-click="modal('uploadfile')">
<a href="" role="menuitem" tabindex="-1">
<i class="glyphicon glyphicon-cloud-upload"></i> {{"upload_files" | translate}}
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</nav>

View File

@@ -0,0 +1,20 @@
<ul class="nav nav-sidebar file-tree-root">
<li ng-repeat="item in fileNavigator.history" ng-include="'folder-branch-item'" ng-class="{'active': item.name == fileNavigator.currentPath.join('/')}"></li>
</ul>
<script type="text/ng-template" id="folder-branch-item">
<a href="" ng-click="fileNavigator.folderClick(item.item)" class="animated fast fadeInDown">
<span class="point">
<i class="glyphicon glyphicon-chevron-down" ng-show="isInThisPath(item.name)"></i>
<i class="glyphicon glyphicon-chevron-right" ng-show="!isInThisPath(item.name)"></i>
</span>
<i class="glyphicon glyphicon-folder-open mr2" ng-show="isInThisPath(item.name)"></i>
<i class="glyphicon glyphicon-folder-close mr2" ng-show="!isInThisPath(item.name)"></i>
{{ (item.name.split('/').pop() || fileNavigator.getBasePath().join('/') || '/') | strLimit : 30 }}
</a>
<ul class="nav nav-sidebar">
<li ng-repeat="item in item.nodes" ng-include="'folder-branch-item'" ng-class="{'active': item.name == fileNavigator.currentPath.join('/')}"></li>
</ul>
</script>

View File

@@ -0,0 +1,5 @@
<div class="spinner-wrapper col-xs-12">
<svg class="spinner-container" style="width:65px;height:65px" viewBox="0 0 44 44">
<circle class="path" cx="22" cy="22" r="20" fill="none" stroke-width="4"></circle>
</svg>
</div>