mirror of
https://github.com/Febbweiss/filebrowser-durandal-widget.git
synced 2026-03-05 06:35:49 +00:00
init
This commit is contained in:
43
app/main.js
Normal file
43
app/main.js
Normal file
@@ -0,0 +1,43 @@
|
||||
requirejs.config({
|
||||
paths: {
|
||||
'text' : '../lib/requirejs-text/text',
|
||||
'durandal' : '../lib/durandal/js',
|
||||
'plugins' : '../lib/durandal/js/plugins',
|
||||
'transitions' : '../lib/durandal/js/transitions',
|
||||
'knockout' : '../lib/knockout.js/knockout',
|
||||
'knockout.mapping' : '../lib/bower-knockout-mapping/dist/knockout.mapping.min',
|
||||
'knockout.validation': '../lib/knockout-validation/dist/knockout.validation.min',
|
||||
'jquery' : '../lib/jquery/jquery.min',
|
||||
'perfect.scrollbar' : '../lib/perfect-scrollbar/js/perfect-scrollbar.jquery'
|
||||
},
|
||||
shim: {
|
||||
'knockout.mapping': {
|
||||
deps: ['knockout'],
|
||||
exports: 'knockout.mapping'
|
||||
},
|
||||
'knockout.validation': {
|
||||
deps: ['knockout'],
|
||||
exports: 'knockout.validation'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
define(['durandal/system', 'durandal/app'], function (system, app) {
|
||||
system.debug(true);
|
||||
|
||||
app.title = 'File browser Durandal Widget';
|
||||
|
||||
app.configurePlugins({
|
||||
router : true,
|
||||
dialog : true,
|
||||
widget : {
|
||||
kinds: [
|
||||
'filebrowser'
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
app.start().then(function() {
|
||||
app.setRoot('shell');
|
||||
});
|
||||
});
|
||||
13
app/shell.html
Normal file
13
app/shell.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<div class="row-fluid">
|
||||
<aside class="col-md-3" id="filebrowser">
|
||||
<div data-bind="widget: {kind:'filebrowser'}"></div>
|
||||
</aside>
|
||||
<main class="col-md-6" id="main">
|
||||
<div class="scrollable" id="mainOutput">
|
||||
|
||||
</div>
|
||||
</main>
|
||||
<aside class="col-md-3" id="helpPane">
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
7
app/shell.js
Normal file
7
app/shell.js
Normal file
@@ -0,0 +1,7 @@
|
||||
define(function (require) {
|
||||
var app = require('durandal/app'),
|
||||
ko = require('knockout');
|
||||
|
||||
return {
|
||||
};
|
||||
});
|
||||
31
app/widgets/filebrowser/newItemModal.html
Normal file
31
app/widgets/filebrowser/newItemModal.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" data-bind="click: close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">New item</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form data-bind="submit: ok" class="form-horizontal">
|
||||
<div class="row form-group">
|
||||
<label class="col-sm-2">Type</label>
|
||||
<div class="col-sm-10">
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="typeItem" id="typeItemFile" value="file" data-bind="checked: typeItem"> File
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="typeItem" id="typeItemFolder" value="folder" data-bind="checked: typeItem"> Folder
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row form-group">
|
||||
<label for="nameInput" class="col-sm-2 control-label">Name</label>
|
||||
<div class="col-sm-10">
|
||||
<input data-bind="value: input, valueUpdate: 'afterkeydown'" name="nameInput" class="form-control autofocus"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-bind="click: ok, visible: isValid">Create</button>
|
||||
<button class="btn btn-default" data-bind="click: close">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
34
app/widgets/filebrowser/newItemModal.js
Normal file
34
app/widgets/filebrowser/newItemModal.js
Normal file
@@ -0,0 +1,34 @@
|
||||
define(['plugins/dialog', 'knockout', 'knockout.validation'], function (dialog, ko, ko_validation) {
|
||||
|
||||
ko.validation = ko_validation;
|
||||
|
||||
var NewItemModal = function() {
|
||||
var self = this;
|
||||
self.input = ko.observable('').extend({
|
||||
required: true,
|
||||
pattern: {
|
||||
message : 'The name must not contain a \'/\'',
|
||||
params : '^[^/]+$'
|
||||
}
|
||||
});
|
||||
self.typeItem = ko.observable('file');
|
||||
self.form = ko.validatedObservable( {input: self.input} );
|
||||
self.isValid = ko.computed(function() {
|
||||
return self.form.isValid();
|
||||
});
|
||||
};
|
||||
|
||||
NewItemModal.prototype.ok = function() {
|
||||
dialog.close(this, { name: this.input(), type: this.typeItem()});
|
||||
};
|
||||
|
||||
NewItemModal.prototype.close = function() {
|
||||
dialog.close(this);
|
||||
};
|
||||
|
||||
NewItemModal.show = function(defaultValue){
|
||||
return dialog.show(new NewItemModal(defaultValue));
|
||||
};
|
||||
|
||||
return NewItemModal;
|
||||
});
|
||||
18
app/widgets/filebrowser/renameModal.html
Normal file
18
app/widgets/filebrowser/renameModal.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" data-bind="click: close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">Rename</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form data-bind="submit: ok" class="form-inline">
|
||||
<div class="form-group">
|
||||
<label for="renameInput">New name</label>
|
||||
<input data-bind="value: input, valueUpdate: 'afterkeydown'" name="renameInput" class="form-control autofocus"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-bind="click: ok, visible: isValid">Ok</button>
|
||||
<button class="btn btn-default" data-bind="click: close">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
34
app/widgets/filebrowser/renameModal.js
Normal file
34
app/widgets/filebrowser/renameModal.js
Normal file
@@ -0,0 +1,34 @@
|
||||
define(['plugins/dialog', 'knockout', , 'knockout.validation'], function (dialog, ko, ko_validation) {
|
||||
|
||||
ko.validation = ko_validation;
|
||||
|
||||
var RenameModal = function(defaultValue) {
|
||||
var self = this;
|
||||
self.previousName = defaultValue;
|
||||
self.input = ko.observable(defaultValue).extend({
|
||||
required: true,
|
||||
pattern: {
|
||||
message : 'The name must not contain a \'/\'',
|
||||
params : '^[^/]+$'
|
||||
}
|
||||
});
|
||||
self.form = ko.validatedObservable( {input: self.input} );
|
||||
self.isValid = ko.computed(function() {
|
||||
return self.form.isValid() && self.input() != self.previousName;
|
||||
});
|
||||
};
|
||||
|
||||
RenameModal.prototype.ok = function() {
|
||||
dialog.close(this, this.input());
|
||||
};
|
||||
|
||||
RenameModal.prototype.close = function() {
|
||||
dialog.close(this);
|
||||
};
|
||||
|
||||
RenameModal.show = function(defaultValue){
|
||||
return dialog.show(new RenameModal(defaultValue));
|
||||
};
|
||||
|
||||
return RenameModal;
|
||||
});
|
||||
45
app/widgets/filebrowser/view.html
Normal file
45
app/widgets/filebrowser/view.html
Normal file
@@ -0,0 +1,45 @@
|
||||
<div id="filebrowser" class="filebrowser">
|
||||
<!-- ko if: folder() -->
|
||||
<!-- ko let: { loopRoot: $data } -->
|
||||
<ul data-bind="template: { name: 'tree-template', foreach: folder().children() }" class="tree-file"></ul>
|
||||
<!-- /ko -->
|
||||
<!-- /ko -->
|
||||
|
||||
<script id="tree-template" type="text/html">
|
||||
<!-- ko if: $data.type() === "folder" -->
|
||||
<li class="folder">
|
||||
<i class="fa fa-folder-o" data-bind="attr: {id: 'icon_folder_' + $data.uuid()}"></i>
|
||||
<span data-bind="text: $data.name, event: { dblclick: loopRoot.openFolder, contextmenu: loopRoot.openContextMenu, click: loopRoot.select }"/>
|
||||
<input type="checkbox" data-bind="attr: {id: $data.uuid}" />
|
||||
<ul data-bind="template: { name: 'tree-template', foreach: $data.children }"></ul>
|
||||
</li>
|
||||
<!-- /ko -->
|
||||
<!--ko if: $data.type() !== "folder"-->
|
||||
<li data-bind="attr: {'data-id': $data.uuid, 'data-filetype': $data.type()}" class="file">
|
||||
<!--ko ifnot: $data.type -->
|
||||
<i data-bind="attr: {class: 'fa fa-file-o'}"></i>
|
||||
<!-- /ko -->
|
||||
<!--ko if: $data.type -->
|
||||
<i data-bind="attr: {class: 'fa fa-file-' + $data.type() + '-o', title: $data.type()}"></i>
|
||||
<!-- /ko -->
|
||||
<span data-bind="text: $data.name, event: { dblclick: loopRoot.openFile, contextmenu: loopRoot.openContextMenu, click: loopRoot.select }, attr: {'data-extra': $data.extra ? $data.extra : ''}"></span>
|
||||
</li>
|
||||
<!-- /ko -->
|
||||
</script>
|
||||
|
||||
<!-- Context menu -->
|
||||
<div id="fileBrowserContextMenu" class="dropdown open" data-bind="visible: showContextMenu" tabindex="1">
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="contextMenu">
|
||||
<!-- ko if: selected() != undefined && !selected().extra -->
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1" href="#" data-bind="click: newItem">New ...</a></li>
|
||||
<!-- /ko -->
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1" href="#" data-bind="click: openRenamePopup">Rename</a></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1" href="#" data-bind="click: copy">Copy</a></li>
|
||||
<!-- ko if: selected() != undefined && !selected().extra -->
|
||||
<li role="presentation" data-bind="css: { 'disabled': !hasCopied()}"><a role="menuitem" tabindex="-1" href="#" data-bind="click: paste">Paste</a></li>
|
||||
<!-- /ko -->
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1" href="#" data-bind="click: openDeletePopup">Delete</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- End of Context menu -->
|
||||
</div>
|
||||
135
app/widgets/filebrowser/viewmodel.js
Normal file
135
app/widgets/filebrowser/viewmodel.js
Normal file
@@ -0,0 +1,135 @@
|
||||
define(['durandal/app', 'durandal/composition', 'plugins/http',
|
||||
'jquery', 'knockout', 'knockout.mapping',
|
||||
'perfect.scrollbar',
|
||||
'./renameModal', './newItemModal'],
|
||||
function(app, composition,http, $, ko, ko_mapping, perfectScrollbar, RenameModal, NewItemModal) {
|
||||
|
||||
ko.mapping = ko_mapping;
|
||||
|
||||
ko.bindingHandlers['let'] = {
|
||||
'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
|
||||
// Make a modified binding context, with extra properties, and apply it to descendant elements
|
||||
var innerContext = bindingContext.extend(valueAccessor());
|
||||
ko.applyBindingsToDescendants(innerContext, element);
|
||||
|
||||
return { controlsDescendantBindings: true };
|
||||
}
|
||||
};
|
||||
ko.virtualElements.allowedBindings['let'] = true;
|
||||
|
||||
var ctor = function() { },
|
||||
selected = ko.observableArray(),
|
||||
showContextMenu = ko.observable(false),
|
||||
copied = ko.observable(undefined),
|
||||
folder = ko.observable(ko.mapping.fromJS({children: []})),
|
||||
scrollable = $('#filebrowser'),
|
||||
cachedData;
|
||||
|
||||
ctor.prototype.activate = function(settings) {
|
||||
this.settings = settings;
|
||||
this.selected = selected;
|
||||
this.hasCopied = ko.computed( function() {
|
||||
return copied() !== undefined;
|
||||
});
|
||||
this.showContextMenu = showContextMenu;
|
||||
this.folder = folder;
|
||||
scrollable.perfectScrollbar();
|
||||
};
|
||||
|
||||
ctor.prototype.openFile = function(object, event) {
|
||||
console.log('File dblclick ', arguments);
|
||||
};
|
||||
ctor.prototype.select = function(object, event) {
|
||||
if( !event.ctrlKey ) {
|
||||
$('li > span.select').removeClass('select');
|
||||
selected.removeAll();
|
||||
}
|
||||
$(event.target).toggleClass('select');
|
||||
selected.push( ko.mapping.fromJS(object) );
|
||||
};
|
||||
ctor.prototype.openFolder = function(event) {
|
||||
var id = arguments[0].uuid();
|
||||
console.log('openFolder', id);
|
||||
$('input[type=checkbox][id=' + id + ']').click();
|
||||
$('#icon_folder_' + id).toggleClass('fa-folder-o').toggleClass('fa-folder-open-o');
|
||||
$('#filebrowser').perfectScrollbar('update');
|
||||
};
|
||||
|
||||
/** Context Menu **/
|
||||
|
||||
ctor.prototype.openContextMenu = function(object, event) {
|
||||
console.log('openContextMenu');
|
||||
selected( ko.mapping.fromJS(object) );
|
||||
// Position du menu, calculer la pos pour eviter sortie du viewport
|
||||
var posX = event.pageX,
|
||||
posY = event.pageY,
|
||||
windowWidth = $(window).width(),
|
||||
windowHeight = $(window).height(),
|
||||
contextMenu = $('#fileBrowserContextMenu'),
|
||||
menuWidth = contextMenu.width(),
|
||||
menuHeight = contextMenu.height();
|
||||
|
||||
posX = Math.min(posX - 45, windowWidth - menuWidth - 15);
|
||||
posY = Math.min(posY - 80, windowHeight - menuHeight - 10);
|
||||
|
||||
// affichage
|
||||
contextMenu.css({
|
||||
left : posX + 'px',
|
||||
top : posY + 'px'
|
||||
});
|
||||
|
||||
showContextMenu(true);
|
||||
};
|
||||
|
||||
ctor.prototype.openRenamePopup = function(ctor, event) {
|
||||
RenameModal.show(ctor.selected().name()).then(function(response) {
|
||||
if( response !== undefined ) {
|
||||
ctor.selected().name(response);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ctor.prototype.openDeletePopup = function(ctor, event) {
|
||||
app.showMessage(
|
||||
'Are you sure you want to delete this ' +
|
||||
(ctor.selected().is_folder ? ' folder and all its content' : 'file') + '?',
|
||||
'Delete ' + ctor.selected().name(), [ { text: "Yes", value: "yes" }, { text: "No", value: "no" }]).then( function( dialogResult ) {
|
||||
if( dialogResult === 'yes' ) {
|
||||
console.log('Deleting', ctor.selected().name());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ctor.prototype.copy = function(ctor, event) {
|
||||
console.log('Copied', ctor.selected().name());
|
||||
copied( ctor.selected() );
|
||||
};
|
||||
|
||||
ctor.prototype.paste = function(ctor, event) {
|
||||
if( copied() !== undefined ) {
|
||||
console.log('Paste', copied().name(), 'into', ctor.selected().name());
|
||||
copied( undefined );
|
||||
}
|
||||
};
|
||||
|
||||
ctor.prototype.newItem = function(ctor, event) {
|
||||
NewItemModal.show().then(function(response) {
|
||||
if( response !== undefined ) {
|
||||
console.log('New item : ' + response.type + ' - ' + response.name );
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(document).on('click', function() {
|
||||
showContextMenu(false);
|
||||
});
|
||||
/** End of Context Menu */
|
||||
|
||||
http.get('/data/filesystem.json').then(function(response) {
|
||||
console.log(response);
|
||||
folder(ko.mapping.fromJS(response));
|
||||
$('#filebrowser').perfectScrollbar();
|
||||
});
|
||||
|
||||
return ctor;
|
||||
});
|
||||
Reference in New Issue
Block a user