Added sorting and filtering options for gcode files.

Closes #21
master
Gina Häußge 2013-02-10 21:48:24 +01:00
parent 4035ebcec5
commit bd68e0ae54
5 changed files with 133 additions and 30 deletions

View File

@ -216,6 +216,7 @@ It also uses the following libraries and frameworks for backend and frontend:
* jQuery: http://jquery.com/
* Bootstrap: http://twitter.github.com/bootstrap/
* Knockout.js: http://knockoutjs.com/
* Underscore.js: http://underscorejs.org/
* Flot: http://www.flotcharts.org/
* jQuery File Upload: http://blueimp.github.com/jQuery-File-Upload/
* gCodeVisualizer: https://github.com/hudbrog/gCodeViewer

View File

@ -16,6 +16,8 @@ body {
border-bottom-left-radius: 4px;
}
.accordion-heading a.accordion-toggle { display: inline-block; }
.nav {
margin-bottom: 0px;
}
@ -190,6 +192,11 @@ table th.timelapse_files_action, table td.timelapse_files_action {
min-width: 50px;
}
.accordion-heading .settings-trigger {
float: right;
padding: 0px 15px;
}
.text-right {
text-align: right;
}

View File

@ -615,9 +615,13 @@ function TerminalViewModel() {
function GcodeFilesViewModel() {
var self = this;
self.allFiles = [];
self.files = ko.observableArray([]);
self.pageSize = ko.observable(CONFIG_FILESPERPAGE);
self.currentPage = ko.observable(0);
self.currentSorting = ko.observable("name");
self.currentFilters = ko.observableArray([]);
self.paginatedFiles = ko.dependentObservable(function() {
if (self.files() == undefined) {
@ -673,14 +677,8 @@ function GcodeFilesViewModel() {
}
self.fromResponse = function(response) {
var sortedFiles = response.files;
sortedFiles.sort(function(a, b) {
if (a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) return -1;
if (a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase()) return 1;
return 0;
});
self.files(sortedFiles);
self.allFiles = response.files;
self._updateFiles();
if (response.filename) {
// got a file to scroll to
@ -739,6 +737,88 @@ function GcodeFilesViewModel() {
}
}
self.changeSorting = function(sorting) {
if (sorting != "name" && sorting != "upload")
return;
self.currentSorting(sorting);
self._updateFiles();
}
self.toggleFilter = function(filter) {
if (_.contains(self.currentFilters(), filter)) {
self.removeFilter(filter);
} else {
self.addFilter(filter);
}
}
self.addFilter = function(filter) {
if (filter != "printed")
return;
var filters = self.currentFilters();
filters.push(filter);
self.currentFilters(_.uniq(filters));
self._updateFiles();
}
self.removeFilter = function(filter) {
if (filter != "printed")
return;
self.currentFilters(_.without(self.currentFilters(), filter));
self._updateFiles();
}
self._updateFiles = function() {
// determine comparator
var comparator = undefined;
if (self.currentSorting() == "name") {
comparator = function(a, b) {
// sorts ascending
if (a["name"].toLocaleLowerCase() < b["name"].toLocaleLowerCase()) return -1;
if (a["name"].toLocaleLowerCase() > b["name"].toLocaleLowerCase()) return 1;
return 0;
}
} else if (self.currentSorting() == "upload") {
comparator = function(a, b) {
// sorts descending
if (a["date"] > b["date"]) return -1;
if (a["date"] < b["date"]) return 1;
return 0;
}
}
// work on all files
var result = self.allFiles;
// filter if necessary
var filters = self.currentFilters();
for (var i = 0; i < filters.length; i++) {
var filterFunction = undefined;
var filter = filters[i];
switch (filter) {
case "printed": {
filterFunction = function(file) {
return !(file["prints"] && file["prints"]["success"] && file["prints"]["success"] > 0);
}
break;
}
}
if (typeof filterFunction !== undefined)
result = _.filter(result, filterFunction);
}
// sort if necessary
if (typeof comparator !== undefined)
result.sort(comparator);
// set result list
self.files(result);
}
self.getPopoverContent = function(data) {
var output = "<p><strong>Uploaded:</strong> " + data["date"] + "</p>";
if (data["gcodeAnalysis"]) {
@ -1151,6 +1231,7 @@ $(function() {
ko.applyBindings(connectionViewModel, document.getElementById("connection"));
ko.applyBindings(printerStateViewModel, document.getElementById("state"));
ko.applyBindings(gcodeFilesViewModel, document.getElementById("files"));
ko.applyBindings(gcodeFilesViewModel, document.getElementById("files-heading"));
ko.applyBindings(temperatureViewModel, document.getElementById("temp"));
ko.applyBindings(controlsViewModel, document.getElementById("controls"));
ko.applyBindings(terminalViewModel, document.getElementById("term"));

1
octoprint/static/js/underscore-min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -74,8 +74,20 @@
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<div class="accordion-heading" id="files-heading">
<a class="accordion-toggle" data-toggle="collapse" href="#files"><i class="icon-list"></i> Files</a>
<div class="settings-trigger btn-group">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="icon-wrench"></i>
</a>
<ul class="dropdown-menu">
<li><a href="#" data-bind="click: function() { $root.changeSorting('name'); }"><i class="icon-ok" data-bind="style: {visibility: currentSorting() == 'name' ? 'visible' : 'hidden'}"></i> Sort by name (ascending)</a></li>
<li><a href="#" data-bind="click: function() { $root.changeSorting('upload'); }"><i class="icon-ok" data-bind="style: {visibility: currentSorting() == 'upload' ? 'visible' : 'hidden'}"></i> Sort by upload date (descending)</a></li>
<li class="divider"></li>
<li><a href="#" data-bind="click: function() { $root.toggleFilter('printed'); }"><i class="icon-ok" data-bind="style: {visibility: _.contains(currentFilters(), 'printed') ? 'visible' : 'hidden'}"></i> Hide successfully printed files</a></li>
</ul>
</div>
</div>
<div class="accordion-body collapse in overflow_visible" id="files">
<div class="accordion-inner">
@ -198,13 +210,13 @@
<button class="btn box" data-bind="enable: isOperational() && !isPrinting(), click: function() { $root.sendJogCommand('z',-1) }"><i class="icon-arrow-down"></i></button>
</div>
</div>
<!-- Jog distance -->
<!-- Jog distance -->
<div class="distance">
<div class="btn-group" data-toggle="buttons-radio" id="jog_distance">
<button type="button" class="btn" data-distance="0.1">0.1</button>
<button type="button" class="btn" data-distance="1">1</button>
<button type="button" class="btn active" data-distance="10">10</button>
<button type="button" class="btn" data-distance="100">100</button>
<button type="button" class="btn" data-distance="1">1</button>
<button type="button" class="btn active" data-distance="10">10</button>
<button type="button" class="btn" data-distance="100">100</button>
</div>
</div>
</div>
@ -457,23 +469,24 @@
</div>
</div>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="{{ url_for('static', filename='js/knockout-2.2.1.js') }}"></script>
<script src="{{ url_for('static', filename='js/bootstrap.js') }}"></script>
<script src="{{ url_for('static', filename='js/jquery.ui.core.js') }}"></script>
<script src="{{ url_for('static', filename='js/jquery.ui.widget.js') }}"></script>
<script src="{{ url_for('static', filename='js/jquery.ui.mouse.js') }}"></script>
<script src="{{ url_for('static', filename='js/jquery.ui.slider.js') }}"></script>
<script src="{{ url_for('static', filename='js/jquery.flot.js') }}"></script>
<script src="{{ url_for('static', filename='js/jquery.iframe-transport.js') }}"></script>
<script src="{{ url_for('static', filename='js/jquery.fileupload.js') }}"></script>
<script src="{{ url_for('static', filename='js/socket.io.js') }}"></script>
<script src="{{ url_for('static', filename='js/ui.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/lib/modernizr.custom.09684.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/js/ui.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/js/gCodeReader.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/js/renderer.js') }}"></script>
<!--<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/lib/jquery-ui-1.9.0.custom.js') }}"></script>-->
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/underscore-min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/knockout-2.2.1.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.ui.core.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.ui.widget.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.ui.mouse.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.ui.slider.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.flot.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.iframe-transport.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.fileupload.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/socket.io.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/ui.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/lib/modernizr.custom.09684.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/js/ui.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/js/gCodeReader.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/js/renderer.js') }}"></script>
<!--<script type="text/javascript" src="{{ url_for('static', filename='gcodeviewer/lib/jquery-ui-1.9.0.custom.js') }}"></script>-->
</body>
</html>