Commit 05fd2e34 authored by Benjamin Fischer's avatar Benjamin Fischer
Browse files

Merge

parents 1e44561c 1c13e818
......@@ -32,7 +32,7 @@ class BusController(AbstractController):
@cherrypy.expose
@cherrypy.tools.ajax()
def poll(self, timeout=10):
def poll(self, timeoutms=10000):
sid = cherrypy.session.id
uid = cherrypy.request.user.id
publisher = self.get_polling_publisher(sid, uid)
......@@ -40,7 +40,7 @@ class BusController(AbstractController):
return []
POLLING_TIMESTAMPS[sid] = int(time())
self.release()
return publisher.fetch(int(timeout))
return publisher.fetch(int(timeoutms)*0.001)
@cherrypy.expose
def send(self, *args, **kwargs):
......
......@@ -25,7 +25,8 @@ class FSController(AbstractController):
download = True
wid = cherrypy.request.private_params.get('_workspaceId', None)
data, contenttype, _ = self.handleDownload(path, wid)
cherrypy.response.headers['Content-Type'] = contenttype
if contenttype != None:
cherrypy.response.headers['Content-Type'] = contenttype
if download: # or not isbrowserfile:
disposition = 'attachment; filename=%s' % path.split('/')[-1]
cherrypy.response.headers['Content-Disposition'] = disposition
......@@ -36,11 +37,11 @@ class FSController(AbstractController):
# get the content type depending on the file extension
ext = path.split('.')[-1]
mimetype = fs.get_mime_type(path)
if mimetype is None:
raise Exception('The file extension \'%s\ is not supported by this server' % ext)
# if mimetype is None:
# raise Exception('The file extension \'%s\ is not supported by this server' % ext)
if not fs.exists(path, 'f'):
raise Exception('The file \'%s\' does not exist' % path)
raise MessageException('The file \'%s\' does not exist' % path)
# Set the Last-Modified response header, so that
# modified-since validation code can work.
......@@ -83,6 +84,19 @@ class FSAjaxController(AbstractController):
else:
return "Failed"
@cherrypy.expose
@cherrypy.tools.ajax()
def filecount(self, path):
self.release_session()
fs = self.get('fs')
self.release_database()
count = fs.get_file_count(path)
if count < 0:
raise MessageException("%s does not exist" % path)
else:
return {"count": count}
@cherrypy.expose
@cherrypy.tools.ajax(encoded=True)
def filelist(self, path, deep=False, filefilter=None, reverse=False):
......
......@@ -22,6 +22,6 @@ class CodeEditorExtension(AbstractExtension):
self.add_js("js/preview.js")
self.add_js("js/prefs.js")
self.add_css("css/styles.less")
self.add_css("css/styles.css")
self.add_workspace_directoy()
.codeeditor {
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
margin: 0px;
padding: 0px;
}
.codeeditor #execute {
min-width: 100px;
}
.codeeditor #abort {
margin-right: -1px;
min-width: 100px;
}
.codeeditor #command-line {
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
}
.codeeditor-left {
position: absolute;
top: 0px;
right: none;
bottom: 0px;
left: 0px;
width: 100%;
overflow: auto;
border: 1px solid #b8b8b8;
border-width: 0px 1px 0px 0px;
}
.codeeditor-right {
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: none;
width: 0%;
overflow: auto;
padding-left: 1px;
}
.codeeditor-right-top {
position: absolute;
top: 0px;
right: 0px;
bottom: none;
left: 0px;
height: 100%;
overflow: hidden;
box-shadow: 0px 1px 0px #b8b8b8;
}
.codeeditor-right-bottom {
position: absolute;
top: none;
right: 0px;
bottom: 0px;
left: 0px;
height: 0%;
overflow: auto;
padding: 11px 10px 10px 10px;
}
.codeeditor-commandline > * {
border-radius: 0px;
}
.codeeditor-commandline > .input-group-addon {
border-left: 0px;
}
.codeeditor-commandline button {
border-radius: 0px;
}
.codeeditor-ace {
position: absolute !important;
top: 0px !important;
right: 0px !important;
bottom: 0px !important;
left: 0px !important;
}
.codeeditor-output {
position: absolute !important;
top: 35px !important;
right: 0px !important;
bottom: 0px !important;
left: 0px !important;
border-radius: 0px;
margin: 0px 0px 0px 1px;
border: 0px;
}
.codeeditor-output-buttons {
position: absolute;
top: 45px;
right: 10px;
}
.codeeditor-right-top pre {
overflow: auto;
}
.codeeditor-right-bottom .well {
position: absolute;
top: 55px;
right: 10px;
bottom: 10px;
left: 10px;
padding: 10px;
margin: 0px;
white-space: nowrap;
overflow-x: auto;
overflow-y: auto;
}
.codeeditor-right-bottom .codeeditor-preview {
position: relative;
display: inline-block;
margin: 0px 10px 10px 0px;
padding: 6px;
border-radius: 3px;
border: 1px solid #b8b8b8;
background-color: white;
float: left;
overflow: hidden;
}
.codeeditor-right-bottom .codeeditor-preview:hover {
border-color: #428bca;
}
.codeeditor-right-bottom .codeeditor-preview-label {
position: absolute;
top: 0px;
right: 0px;
left: 0px;
height: 24px;
text-align: center;
}
.codeeditor-right-bottom .codeeditor-preview-img {
position: absolute;
top: 24px;
right: 0px;
bottom: 0px;
left: 0px;
text-align: center;
}
.codeeditor-right-bottom .codeeditor-preview img {
padding-top: 24px;
height: 100px;
width: auto;
}
.codeeditor-ace div.ace_scrollbar {
overflow-x: auto !important;
overflow-y: auto !important;
}
......@@ -31,7 +31,6 @@ class DemoExtension(AbstractExtension):
# the paths are relative to vispa/extensions/<name>/static
self.add_js("js/extension.js")
self.add_css("css/style.css")
self.add_css("css/style.less")
# tell vispa to transfer files located in the workspace folder
# (vispa/extensions/<name>/workspace) to selected workspaces
......
/*
* This is a basic css file. In order to avoid naming collisions, you should
* use prefixes in your class names.
* This is a less-css file. It allows class mixinx, variables, anc basic
* computation tasks (addition, multiplication, etc). You can find more
* information on less-css at http://lesscss.org/. In order to avoid naming
* collisions, you should use prefixes in your class names.
*/
.demo-content {
background-color: rgba(61, 129, 187, 1.0);
background-color: #3d81bb;
}
.demo-content-text {
color: #ffffff;
padding: 5px 10px;
}
......@@ -8,6 +8,10 @@
// define a variable for the text color
@content-color: #ffffff;
.demo-content {
background-color: rgba(61, 129, 187, 1.0);
}
// define a mixin class with default values
.demo-text-padding (@vertical: 5px, @horizontal: 10px) {
padding: @vertical @horizontal;
......
......@@ -44,6 +44,7 @@ class FileBrowserExtension(AbstractExtension):
self.add_js('js/selector/selector.js')
self.add_js('js/selector/actions.js')
self.add_js('js/selector/items.js')
self.add_js('js/selector/selections.js')
self.add_js('js/selector/selectortableview.js')
......
......@@ -71,7 +71,12 @@ var FileBaseActions = Class.extend({
function(result) {
if (result === true) {
self._openWithCodeEditor(path);
}
} else self.FileBase.instance.confirm("Do you want to download \"" + name + " (" +
size + ")\"?", function(res2) {
if(res2 === true) {
self.download();
}
});
});
}
// })
......
......@@ -49,34 +49,47 @@ var FileBase = Class.extend({
updateView: function() {
var self = this;
this.instance.setLoading(true);
this.selections.unselectAll();
// remove the links needed for the preview lightbox
$("a[data-lightbox=" + this.view.previewBoxID + "]").remove();
if (self.workflow.lastRefresh === null || $.now() - self.workflow.lastRefresh > 1000) {
// Check if last request is less than one second away
var promise = this.instance.GET("/ajax/fs/filelist", {
"path": this.workflow.path,
});
self.workflow.lastRefresh = $.now();
promise.done(function(content) {
if (self.workflow.lastRefresh === null || $.now() - self.workflow.lastRefresh > 500) {
this.instance.GET("/ajax/fs/filecount", {
path: this.workflow.path
}).done(function(res) {
self.instance.setLoading(true, res.count > 50 ? 0 : null);
// Check if last request is less than one second away
var promise = self.instance.GET("/ajax/fs/filelist", {
"path": self.workflow.path
});
self.workflow.lastRefresh = $.now();
if (Object.keys(content.filelist).length == 1 && content.filelist[0].warning !==
undefined) {
self.instance.alert(content.filelist[0].warning);
vispa.messenger.error(content.filelist[0].warning);
content.filelist = [];
}
self.refresh(content);
if (self.view.fileContentContainer !== null) {
self.selections.unselectAll()
self.view.fileContentContainer.empty();
};
promise.done(function(content) {
self.workflow.lastRefresh = $.now();
if (Object.keys(content.filelist).length == 1 && content.filelist[0].warning !==
undefined) {
self.instance.alert(content.filelist[0].warning);
vispa.messenger.error(content.filelist[0].warning);
content.filelist = [];
}
self.refresh(content);
}).fail(function(err) {
self.instance.setLoading(false);
});
});
} else {
this.instance.setLoading(false);
}
},
refresh: function(content, sort, reverse, filter) {
var self = this;
if (!content) {
return this;
return;
}
sort = sort === undefined ? this.workflow.sort : sort;
reverse = reverse === undefined ? this.workflow.reverse : reverse;
......@@ -100,8 +113,8 @@ var FileBase = Class.extend({
}
}
this.helper.sortItems(content, sort, reverse);
this.menuitems.hideMenu();
this.workflow.currentView.setContent(content);
this.menuitems.hideMenu();
this.instance.setLabel(this.workflow.path, true);
this.instance.setLoading(false);
}
......
......@@ -43,18 +43,18 @@ var FileBaseView = Class.extend({
},
// convert timestamp to dd.mm.jjjj hh:mm:ss
"mtime": {
"text": function(nodeObj) {
"text": function() {
return self.FileBase.helper.convertTimestamp(this.mtime);
}
},
// convert size from bytes to kB, Mb, etc
"size": {
"text": function(nodeObj) {
"text": function() {
return self.FileBase.helper.convertSize(this.size).size;
}
},
"sizeSuffix": {
"text": function(nodeObj) {
"text": function() {
return self.FileBase.helper.convertSize(this.size).sizeSuffix;
}
}
......
......@@ -26,7 +26,6 @@ var Symbolview = Class.extend({
this.data = data;
// Get the container, empty it and fill the template
this.fileContentContainer.empty();
this.fileContentContainer.html(this.template);
if (this.FileBase.instance.getPreference("Open") == "single" || vispa.device.hasTouch) {
......
......@@ -27,7 +27,6 @@ var Tableview = Class.extend({
this.data = data;
// Get the container, empty it and fill the template
this.fileContentContainer.empty();
this.fileContentContainer.html(this.template);
// Set the right path
......
......@@ -25,6 +25,7 @@ function getFileMenuEntries() {
iconClass: "glyphicon glyphicon-arrow-up",
buttonClass: "btn-default",
callback: function() {
this.fb.workflow.lastRefresh = null;
this.fb.workflow.path = this.fb.workflow.parentpath;
this.fb.updateView();
}
......
var FileSelectorItems = FileBaseMenuItems.extend({
init: function(FileBase) {
this._super(FileBase);
this.FileBase = FileBase;
},
getContextMenuPostion: function(event, contextMenu) {
var menuPostion = {};
var menuDimension = {};
// store the fileContentContainer fCC
var fCC = this.FileBase.view.fileContentContainer;
var xOffsetModal = +fCC.parent().parent().parent().position().left + fCC.parent().position()
.left;
var offset = fCC.offset().top;
menuDimension.x = contextMenu.outerWidth();
menuDimension.y = contextMenu.outerHeight();
menuPostion.x = 2 * event.clientX - event.pageX;
menuPostion.y = event.clientY - offset;
menuPostion.x -= xOffsetModal;
if (vispa.device.hasTouch) {
menuPostion.x += 5;
menuPostion.y += 5;
}
// Check if the side borders are reached
if (menuPostion.x + menuDimension.x > fCC.width()) {
menuPostion.x = fCC.width() - menuDimension.x;
}
// Check if the lower border is reached
if (menuPostion.y + menuDimension.y > fCC.height()) {
menuPostion.y = fCC.height() - menuDimension.y;
}
return menuPostion;
}
});
\ No newline at end of file
......@@ -68,6 +68,7 @@ var FileSelector = FileBase.extend({
// this.view = new SelectorView(this);
this.actions = new FileSelectorActions(this);
this.selections = new FileSelectorSelections(this);
this.menuitems = new FileSelectorItems(this);
this.workflow.sort = args.sort !== undefined ? args.sort : this.workflow.sort;
this.workflow.reverse = args.reverse !== undefined ? args.reverse : this.workflow.reverse;
......
......@@ -4,63 +4,72 @@ function getShortcuts() {
singleSelect: {
description: "Key for selection of single files or folders",
value: "down:"+ctrlMeta,
callback: function() {
callback: function(event) {
event.preventDefault();
this.fb.workflow.selectmode = true;
}
},
abortSingleSelect: {
description: "Key for selection of single files or folders",
value: "up:"+ctrlMeta,
callback: function() {
callback: function(event) {
event.preventDefault();
this.fb.workflow.selectmode = false;
}
},
copy: {
description: "Copy current selection",
value: ctrlMeta+"+c",
callback: function() {
callback: function(event) {
event.preventDefault();
this.fb.actions.copy();
}
},
paste: {
description: "Paste elements",
value: ctrlMeta+"+v",
callback: function() {
callback: function(event) {
event.preventDefault();
this.fb.actions.paste();
}
},
cut: {
description: "Cut elements",
value: ctrlMeta+"+x",
callback: function() {
callback: function(event) {
event.preventDefault();
this.fb.actions.cut();
}
},
"delete": {
description: "Delete elements",
value: "del",
callback: function() {
callback: function(event) {
event.preventDefault();
this.fb.actions.remove();
}
},
rename: {
description: "Rename element",
value: ctrlMeta+"+t",
callback: function() {
callback: function(event) {
event.preventDefault();
this.fb.actions.rename();
}
},
arrowLeft: {
description: "Naviagtion in Filebrowser",
value: "left",
callback: function() {
callback: function(event) {
event.preventDefault();
this.fb.selections.selectPrevNode();
}
},
arrowRight: {
description: "Naviagtion in Filebrowser",
value: "right",
callback: function() {
callback: function(event) {
event.preventDefault();
this.fb.selections.selectNextNode();
}
......@@ -68,7 +77,8 @@ function getShortcuts() {
arrowDown: {
description: "Naviagtion in Filebrowser",
value: "down",
callback: function() {
callback: function(event) {
event.preventDefault();
if (this.fb.workflow.currentView.type == "Table") {
this.fb.selections.selectNextNode();
} else {
......@@ -84,7 +94,8 @@ function getShortcuts() {
arrowUp: {
description: "Naviagtion in Filebrowser",
value: "up",
callback: function() {
callback: function(event) {
event.preventDefault();
if (this.fb.workflow.currentView.type == "Table") {
this.fb.selections.selectPrevNode();
} else {
......@@ -100,7 +111,8 @@ function getShortcuts() {
open: {
description: "Open in Filebrowser",
value: "return",
callback: function() {
callback: function(event) {
event.preventDefault();
var selectedEntries = this.fb.selections.entries;
var nEntries = Object.keys(selectedEntries).length;
if (nEntries === 0 || nEntries > 1) {
......@@ -119,7 +131,9 @@ function getShortcuts() {
up: {
description: "Up (parentpath) in Filebrowser",
value: "backspace",
callback: function() {
callback: function(event) {
event.preventDefault();
this.fb.workflow.lastRefresh = null;
this.fb.workflow.path = this.fb.workflow.parentpath;
this.fb.updateView();
}
......@@ -127,14 +141,16 @@ function getShortcuts() {
refresh: {
description: "Reload the content of the current folder",
value: "F5",
callback: function() {
callback: function(event) {
event.preventDefault();
this.fb.updateView();
}
},